Bug 1648064 - Make service workers of the original document intercept the static document's requests. r=smaug,asuth

We need to set the original document ASAP so images triggered from the
clone use the right service worker.

It is a bit unfortunate to have the static document checks twice, but we
may get to Document::GetClientInfo before the cloned doc has a window,
so it's not 100% clear to me how we could avoid it.

Differential Revision: https://phabricator.services.mozilla.com/D82081
This commit is contained in:
Emilio Cobos Álvarez 2020-07-22 14:39:53 +00:00
Родитель d088111466
Коммит 91062db9ac
9 изменённых файлов: 152 добавлений и 21 удалений

Просмотреть файл

@ -7564,26 +7564,44 @@ Element* Document::GetAnonRootIfInAnonymousContentContainer(
}
Maybe<ClientInfo> Document::GetClientInfo() const {
nsPIDOMWindowInner* inner = GetInnerWindow();
if (inner) {
if (const Document* orig = GetOriginalDocument()) {
if (Maybe<ClientInfo> info = orig->GetClientInfo()) {
return info;
}
}
if (nsPIDOMWindowInner* inner = GetInnerWindow()) {
return inner->GetClientInfo();
}
return Maybe<ClientInfo>();
}
Maybe<ClientState> Document::GetClientState() const {
nsPIDOMWindowInner* inner = GetInnerWindow();
if (inner) {
if (const Document* orig = GetOriginalDocument()) {
if (Maybe<ClientState> state = orig->GetClientState()) {
return state;
}
}
if (nsPIDOMWindowInner* inner = GetInnerWindow()) {
return inner->GetClientState();
}
return Maybe<ClientState>();
}
Maybe<ServiceWorkerDescriptor> Document::GetController() const {
nsPIDOMWindowInner* inner = GetInnerWindow();
if (inner) {
if (const Document* orig = GetOriginalDocument()) {
if (Maybe<ServiceWorkerDescriptor> controller = orig->GetController()) {
return controller;
}
}
if (nsPIDOMWindowInner* inner = GetInnerWindow()) {
return inner->GetController();
}
return Maybe<ServiceWorkerDescriptor>();
}
@ -11105,6 +11123,14 @@ nsresult Document::CloneDocHelper(Document* clone) const {
NS_ENSURE_SUCCESS(rv, rv);
if (mCreatingStaticClone) {
if (mOriginalDocument) {
clone->mOriginalDocument = mOriginalDocument;
} else {
clone->mOriginalDocument = const_cast<Document*>(this);
}
clone->mOriginalDocument->mLatestStaticClone = clone;
clone->mOriginalDocument->mStaticCloneCount++;
nsCOMPtr<nsILoadGroup> loadGroup;
// |mDocumentContainer| is the container of the document that is being
@ -12117,16 +12143,6 @@ already_AddRefed<Document> Document::CreateStaticClone(
nsCOMPtr<Document> clonedDoc = do_QueryInterface(clonedNode);
if (clonedDoc) {
if (IsStaticDocument()) {
clonedDoc->mOriginalDocument = mOriginalDocument;
mOriginalDocument->mLatestStaticClone = clonedDoc;
} else {
clonedDoc->mOriginalDocument = this;
mLatestStaticClone = clonedDoc;
}
clonedDoc->mOriginalDocument->mStaticCloneCount++;
size_t sheetsCount = SheetCount();
for (size_t i = 0; i < sheetsCount; ++i) {
RefPtr<StyleSheet> sheet = SheetAt(i);

Просмотреть файл

@ -2809,7 +2809,7 @@ class Document : public nsINode,
* If this document is a static clone, this returns the original
* document.
*/
Document* GetOriginalDocument() {
Document* GetOriginalDocument() const {
MOZ_ASSERT(!mOriginalDocument || !mOriginalDocument->GetOriginalDocument());
return mOriginalDocument;
}

Просмотреть файл

@ -5476,6 +5476,12 @@ CallState nsGlobalWindowInner::CallOnInProcessChildren(Method aMethod,
Maybe<ClientInfo> nsGlobalWindowInner::GetClientInfo() const {
MOZ_ASSERT(NS_IsMainThread());
if (mDoc && mDoc->IsStaticDocument()) {
if (Maybe<ClientInfo> info = mDoc->GetOriginalDocument()->GetClientInfo()) {
return info;
}
}
Maybe<ClientInfo> clientInfo;
if (mClientSource) {
clientInfo.emplace(mClientSource->Info());
@ -5485,6 +5491,13 @@ Maybe<ClientInfo> nsGlobalWindowInner::GetClientInfo() const {
Maybe<ClientState> nsGlobalWindowInner::GetClientState() const {
MOZ_ASSERT(NS_IsMainThread());
if (mDoc && mDoc->IsStaticDocument()) {
if (Maybe<ClientState> state =
mDoc->GetOriginalDocument()->GetClientState()) {
return state;
}
}
Maybe<ClientState> clientState;
if (mClientSource) {
Result<ClientState, ErrorResult> res = mClientSource->SnapshotState();
@ -5499,6 +5512,13 @@ Maybe<ClientState> nsGlobalWindowInner::GetClientState() const {
Maybe<ServiceWorkerDescriptor> nsGlobalWindowInner::GetController() const {
MOZ_ASSERT(NS_IsMainThread());
if (mDoc && mDoc->IsStaticDocument()) {
if (Maybe<ServiceWorkerDescriptor> controller =
mDoc->GetOriginalDocument()->GetController()) {
return controller;
}
}
Maybe<ServiceWorkerDescriptor> controller;
if (mClientSource) {
controller = mClientSource->GetController();

Просмотреть файл

@ -24,6 +24,9 @@ support-files =
printpreview_quirks_ref.html
printpreview_images.html
printpreview_images_ref.html
printpreview_images_sw.html
printpreview_images_sw_ref.html
printpreview_images_sw.js
test_document_adopted_styles.html
test_document_adopted_styles_ref.html
test_shadow_root_adopted_styles.html

Просмотреть файл

@ -405,20 +405,36 @@ async function runTest10() {
requestAnimationFrame(function() { setTimeout(runTest11); } );
}
async function compareFiles(src1, src2) {
async function compareFiles(src1, src2, options = {}) {
const BASE = "https://example.org/chrome/layout/base/tests/chrome/";
info(`Comparing ${src1} with ${src2}`);
const iframeElement = document.getElementsByTagName("iframe")[0];
let messagePromise = null;
if (options.waitForMessage) {
messagePromise = new Promise(resolve => {
iframeElement.addEventListener("message", resolve, { capture: true, once: true });
});
}
await new Promise((resolve) => {
iframeElement.addEventListener("load", resolve, { capture: true, once: true });
iframeElement.setAttribute("src", src1);
iframeElement.setAttribute("src", BASE + src1);
});
if (messagePromise) {
info("awaiting for message to arrive");
await messagePromise;
}
await printpreview();
ctx1.drawWindow(frameElts[1].contentWindow, 0, 0, 400, 400, "rgb(255,255,255)");
exitprintpreview();
await new Promise((resolve) => {
iframeElement.addEventListener("load", resolve, { capture: true, once: true });
iframeElement.setAttribute("src", src2);
iframeElement.setAttribute("src", BASE + src2);
});
await printpreview();
@ -470,6 +486,11 @@ async function runTest15() {
// Various image tests.
async function runTest16() {
await compareFiles("printpreview_images.html", "printpreview_images_ref.html");
requestAnimationFrame(function() { setTimeout(runTest17); } );
}
async function runTest17() {
await compareFiles("printpreview_images_sw.html", "printpreview_images_sw_ref.html", { waitForMessage: true });
finish();
}

Просмотреть файл

@ -19,7 +19,7 @@
</picture>
<img src="blue-32x32.png" width=32 height=32>
<object data="blue-32x32.png" width=32 height=32></object>
<input type="image" src="blue-32x32.png" width=32 height=32>
<svg width="32" height="32">
<image x=0 y=0 href="blue-32x32.png" width=32 height=32></image>
</svg>
<input type="image" src="blue-32x32.png" width=32 height=32>

Просмотреть файл

@ -0,0 +1,46 @@
<!doctype html>
<style>
img, object, svg, input { display: block }
div {
content: url(nonexistent.png?1);
width: 32px;
height: 32px;
}
</style>
<script>
const WORKER = "printpreview_images_sw.js";
if (location.href.includes("registered")) {
console.log("REGISTERED");
onload = function() {
postMessage("ready", "*");
}
onbeforeunload = function() {
navigator.serviceWorker.getRegistrations().then(function(registrations) {
for(let registration of registrations) {
registration.unregister()
}
})
navigator.serviceWorker.unregister(WORKER);
}
} else {
navigator.serviceWorker.oncontrollerchange = function() {
location.href = location.href + "?registered";
};
navigator.serviceWorker.register(WORKER);
}
</script>
<div></div>
<picture>
<source srcset="nonexistent.png?2">
<img width=32 height=32>
</picture>
<picture>
<source srcset="nonexistent.png?3" media="print">
<source srcset="animated.gif" media="not print">
<img width=32 height=32>
</picture>
<img src="nonexistent.png?4" width=32 height=32>
<svg width="32" height="32">
<image x=0 y=0 href="nonexistent.png?7" width=32 height=32></image>
</svg>
<input type="image" src="nonexistent.png?6" width=32 height=32>

Просмотреть файл

@ -0,0 +1,11 @@
self.addEventListener("fetch", event => {
if (event.request.url.includes("nonexistent.png")) {
event.respondWith(
fetch(event.request.url.replace("nonexistent.png", "blue-32x32.png"))
);
}
});
self.addEventListener("activate", event => {
event.waitUntil(clients.claim());
});

Просмотреть файл

@ -0,0 +1,14 @@
<!doctype html>
<style>
div {
width: 32px;
height: 32px;
background-color: blue;
}
</style>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>