diff --git a/remote/domains/content/Runtime.jsm b/remote/domains/content/Runtime.jsm index 9e9dfca0949d..285ecaa9b2bd 100644 --- a/remote/domains/content/Runtime.jsm +++ b/remote/domains/content/Runtime.jsm @@ -27,6 +27,12 @@ class Runtime extends ContentProcessDomain { this.chromeEventHandler.addEventListener("DOMWindowCreated", this, {mozSystemGroup: true}); + // Listen for pageshow and pagehide to track pages going in/out to/from the BF Cache + this.chromeEventHandler.addEventListener("pageshow", this, + {mozSystemGroup: true}); + this.chromeEventHandler.addEventListener("pagehide", this, + {mozSystemGroup: true}); + Services.obs.addObserver(this, "inner-window-destroyed"); // Spin the event loop in order to send the `executionContextCreated` event right @@ -52,11 +58,15 @@ class Runtime extends ContentProcessDomain { this.enabled = false; this.chromeEventHandler.removeEventListener("DOMWindowCreated", this, {mozSystemGroup: true}); + this.chromeEventHandler.removeEventListener("pageshow", this, + {mozSystemGroup: true}); + this.chromeEventHandler.removeEventListener("pagehide", this, + {mozSystemGroup: true}); Services.obs.removeObserver(this, "inner-window-destroyed"); } } - handleEvent({type, target}) { + handleEvent({type, target, persisted}) { const frameId = target.defaultView.windowUtils.outerWindowID; const id = target.defaultView.windowUtils.currentInnerWindowID; switch (type) { @@ -71,6 +81,32 @@ class Runtime extends ContentProcessDomain { }, }); break; + + case "pageshow": + // `persisted` is true when this is about a page being resurected from BF Cache + if (!persisted) { + return; + } + this.emit("Runtime.executionContextCreated", { + context: { + id, + auxData: { + isDefault: target == this.content.document, + frameId, + }, + }, + }); + break; + + case "pagehide": + // `persisted` is true when this is about a page being frozen into BF Cache + if (!persisted) { + return; + } + this.emit("Runtime.executionContextDestroyed", { + executionContextId: id, + }); + break; } } diff --git a/remote/test/browser/browser_runtime_executionContext.js b/remote/test/browser/browser_runtime_executionContext.js index 152dd6e0ce32..7ae2fd2d16bd 100644 --- a/remote/test/browser/browser_runtime_executionContext.js +++ b/remote/test/browser/browser_runtime_executionContext.js @@ -59,20 +59,42 @@ async function testCDP() { ok(context1.auxData.isDefault, "The execution context is the default one"); ok(!!context1.auxData.frameId, "The execution context has a frame id set"); - const executionContextCreated = Runtime.executionContextCreated(); + info("Navigate to a new URL"); + const executionContextDestroyed2 = Runtime.executionContextDestroyed(); + const executionContextCreated2 = Runtime.executionContextCreated(); const url = "data:text/html;charset=utf-8,test-page"; const { frameId } = await Page.navigate({ url }); ok(true, "A new page has been loaded"); is(frameId, context1.auxData.frameId, "Page.navigate returns the same frameId than executionContextCreated"); - const { context: context2 } = await executionContextCreated; + let { executionContextId } = await executionContextDestroyed2; + is(executionContextId, context1.id, "The destroyed event reports the previous context id"); + + const { context: context2 } = await executionContextCreated2; ok(!!context2.id, "The execution context has an id"); isnot(context1.id, context2.id, "The new execution context has a different id"); ok(context2.auxData.isDefault, "The execution context is the default one"); is(context2.auxData.frameId, frameId, "The execution context frame id is the same " + - "the one returned by Page.navigate"); + "than the one returned by Page.navigate"); + isnot(executionContextId, context2.id, "The destroyed id is different from the " + + "created one"); + + // Navigates back to the previous page. + // This should resurect the original document from the BF Cache and recreate the + // context for it + info("Navigate back to the previous document"); + const executionContextDestroyed3 = Runtime.executionContextDestroyed(); + const executionContextCreated3 = Runtime.executionContextCreated(); + gBrowser.selectedBrowser.goBack(); + const { context: context3 } = await executionContextCreated3; + is(context3.id, context1.id, "The new execution context should be the same than the first one"); + ok(context3.auxData.isDefault, "The execution context is the default one"); + is(context3.auxData.frameId, frameId, "The execution context frame id is always the same"); + + ({ executionContextId } = await executionContextDestroyed3); + is(executionContextId, context2.id, "The destroyed event reports the previous context id"); await client.close(); ok(true, "The client is closed");