From 5a549b8d58966bd5d5f7b6d67bdb7519ee0e4a19 Mon Sep 17 00:00:00 2001 From: Alexandre Poirot Date: Tue, 30 Apr 2019 11:09:30 +0000 Subject: [PATCH] Bug 1543095 - Implement Page.frameNavigated. r=ato Differential Revision: https://phabricator.services.mozilla.com/D27522 --HG-- extra : moz-landing-system : lando --- remote/domains/content/Page.jsm | 28 ++++- remote/test/browser/browser.ini | 1 + .../browser/browser_page_frameNavigated.js | 106 ++++++++++++++++++ 3 files changed, 133 insertions(+), 2 deletions(-) create mode 100644 remote/test/browser/browser_page_frameNavigated.js diff --git a/remote/domains/content/Page.jsm b/remote/domains/content/Page.jsm index 177fd1f222ff..8cc6087679b4 100644 --- a/remote/domains/content/Page.jsm +++ b/remote/domains/content/Page.jsm @@ -34,6 +34,8 @@ class Page extends ContentProcessDomain { async enable() { if (!this.enabled) { this.enabled = true; + this.chromeEventHandler.addEventListener("DOMWindowCreated", this, + {mozSystemGroup: true}); this.chromeEventHandler.addEventListener("DOMContentLoaded", this, {mozSystemGroup: true}); this.chromeEventHandler.addEventListener("pageshow", this, @@ -43,6 +45,8 @@ class Page extends ContentProcessDomain { disable() { if (this.enabled) { + this.chromeEventHandler.removeEventListener("DOMWindowCreated", this, + {mozSystemGroup: true}); this.chromeEventHandler.removeEventListener("DOMContentLoaded", this, {mozSystemGroup: true}); this.chromeEventHandler.removeEventListener("pageshow", this, @@ -64,7 +68,9 @@ class Page extends ContentProcessDomain { this.docShell.QueryInterface(Ci.nsIWebNavigation); this.docShell.loadURI(url, opts); - return {frameId: "42"}; + return { + frameId: this.content.windowUtils.outerWindowID, + }; } getFrameTree() { @@ -86,10 +92,28 @@ class Page extends ContentProcessDomain { return this.content.location.href; } - handleEvent({type}) { + handleEvent({type, target}) { + if (target.defaultView != this.content) { + // Ignore iframes for now + return; + } + const timestamp = Date.now(); + const frameId = target.defaultView.windowUtils.outerWindowID; + const url = target.location.href; switch (type) { + case "DOMWindowCreated": + this.emit("Page.frameNavigated", { + frame: { + id: frameId, + // frameNavigated is only emitted for the top level document + // so that it never has a parent. + parentId: null, + url, + }, + }); + break; case "DOMContentLoaded": this.emit("Page.domContentEventFired", {timestamp}); break; diff --git a/remote/test/browser/browser.ini b/remote/test/browser/browser.ini index dd4ace94bc95..8177e379f30b 100644 --- a/remote/test/browser/browser.ini +++ b/remote/test/browser/browser.ini @@ -8,5 +8,6 @@ skip-if = debug || asan # bug 1546945 [browser_cdp.js] [browser_main_target.js] +[browser_page_frameNavigated.js] [browser_tabs.js] [browser_target.js] diff --git a/remote/test/browser/browser_page_frameNavigated.js b/remote/test/browser/browser_page_frameNavigated.js new file mode 100644 index 000000000000..c3f6e5080a48 --- /dev/null +++ b/remote/test/browser/browser_page_frameNavigated.js @@ -0,0 +1,106 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/* global getCDP */ + +const {RemoteAgent} = ChromeUtils.import("chrome://remote/content/RemoteAgent.jsm"); +const {RemoteAgentError} = ChromeUtils.import("chrome://remote/content/Error.jsm"); + +// Test the Page navigation events + +const TEST_URI = "data:text/html;charset=utf-8,default-test-page"; + +add_task(async function() { + try { + await testCDP(); + } catch (e) { + // Display better error message with the server side stacktrace + // if an error happened on the server side: + if (e.response) { + throw RemoteAgentError.fromJSON(e.response); + } else { + throw e; + } + } +}); + +async function testCDP() { + // Open a test page, to prevent debugging the random default page + await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URI); + + // Start the CDP server + RemoteAgent.init(); + RemoteAgent.tabs.start(); + RemoteAgent.listen(Services.io.newURI("http://localhost:9222")); + + // Retrieve the chrome-remote-interface library object + const CDP = await getCDP(); + + // Connect to the server + const client = await CDP({ + target(list) { + // Ensure debugging the right target, i.e. the one for our test tab. + return list.find(target => target.url == TEST_URI); + }, + }); + ok(true, "CDP client has been instantiated"); + + const {Page} = client; + + // turn on navigation related events, such as DOMContentLoaded et al. + await Page.enable(); + ok(true, "Page domain has been enabled"); + + const promises = []; + const resolutions = new Map(); + function recordPromise(name, promise) { + promise.then(event => { + ok(true, `Received Page.${name}`); + resolutions.set(name, event); + }); + promises.push(promise); + } + recordPromise("frameStoppedLoading", Page.frameStoppedLoading()); + recordPromise("navigatedWithinDocument", Page.navigatedWithinDocument()); + recordPromise("domContentEventFired", Page.domContentEventFired()); + recordPromise("loadEventFired", Page.loadEventFired()); + recordPromise("frameNavigated", Page.frameNavigated()); + const url = "data:text/html;charset=utf-8,test-page"; + const { frameId } = await Page.navigate({ url }); + ok(true, "A new page has been loaded"); + ok(frameId, "Page.navigate returned a frameId"); + + // Wait for all the promises to resolve + await Promise.all(promises); + + // Assert the order in which they resolved + const expectedResolutions = [ + "frameNavigated", + "domContentEventFired", + "loadEventFired", + "navigatedWithinDocument", + "frameStoppedLoading", + ]; + Assert.deepEqual([...resolutions.keys()], + expectedResolutions, + "Received various Page navigation events in the expected order"); + + // Now assert the data exposed by each of these events + const frameNavigated = resolutions.get("frameNavigated"); + ok(!frameNavigated.frame.parentId, "frameNavigated is for the top level document and" + + " has a null parentId"); + is(frameNavigated.frame.id, frameId, "frameNavigated id is the same than the one " + + "returned by Page.navigate"); + is(frameNavigated.frame.name, undefined, "frameNavigated name isn't implemented yet"); + is(frameNavigated.frame.url, url, "frameNavigated url is the same being given to " + + "Page.navigate"); + + await client.close(); + ok(true, "The client is closed"); + + BrowserTestUtils.removeTab(gBrowser.selectedTab); + + await RemoteAgent.close(); +}