diff --git a/remote/test/browser/browser.ini b/remote/test/browser/browser.ini index 5bc72367365f..8e3211b0979c 100644 --- a/remote/test/browser/browser.ini +++ b/remote/test/browser/browser.ini @@ -3,5 +3,7 @@ tags = remote subsuite = remote support-files = chrome-remote-interface.js + head.js [browser_cdp.js] +[browser_tabs.js] diff --git a/remote/test/browser/browser_tabs.js b/remote/test/browser/browser_tabs.js new file mode 100644 index 000000000000..9b70f9fd90da --- /dev/null +++ b/remote/test/browser/browser_tabs.js @@ -0,0 +1,37 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test very basic CDP features. + +const TEST_URI = "data:text/html;charset=utf-8,default-test-page"; + +add_task(async function() { + // Start the CDP server + const RemoteAgent = Cc["@mozilla.org/remote/agent"].getService(Ci.nsISupports).wrappedJSObject; + RemoteAgent.tabs.start(); + RemoteAgent.listen(Services.io.newURI("http://localhost:9222")); + + const CDP = await getCDP(); + + // Use gBrowser.addTab instead of BrowserTestUtils as it creates the tab differently. + // It demonstrates a race around tab.linkedBrowser.browsingContext being undefined + // when accessing this property early. + const tab = gBrowser.addTab(TEST_URI, { + skipAnimation: true, + triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(), + }); + + await BrowserTestUtils.browserLoaded(tab.linkedBrowser); + + let targets = await getTargets(CDP); + ok(targets.some(target => target.url == TEST_URI), "Found the tab in target list"); + + BrowserTestUtils.removeTab(tab); + + targets = await getTargets(CDP); + ok(!targets.some(target => target.url == TEST_URI), "Tab has been removed from the target list"); + + await RemoteAgent.close(); +}); diff --git a/remote/test/browser/head.js b/remote/test/browser/head.js new file mode 100644 index 000000000000..d947aead87c5 --- /dev/null +++ b/remote/test/browser/head.js @@ -0,0 +1,80 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const CRI_URI = "http://example.com/browser/remote/test/browser/chrome-remote-interface.js"; + +/** + * Create a test document in an invisible window. + * This window will be automatically closed on test teardown. + */ +function createTestDocument() { + const browser = Services.appShell.createWindowlessBrowser(true); + const webNavigation = browser.docShell + .QueryInterface(Ci.nsIWebNavigation); + // Create a system principal content viewer to ensure there is a valid + // empty document using system principal and avoid any wrapper issues + // when using document's JS Objects. + const system = Services.scriptSecurityManager.getSystemPrincipal(); + webNavigation.createAboutBlankContentViewer(system); + + registerCleanupFunction(() => { + browser.close(); + }); + return webNavigation.document; +} + +/** + * Retrieve an intance of CDP object from chrome-remote-interface library + */ +async function getCDP() { + // Instantiate a background test document in order to load the library + // as in a web page + const document = createTestDocument(); + + // Load chrome-remote-interface.js into this background test document + const script = document.createElement("script"); + script.setAttribute("src", CRI_URI); + document.documentElement.appendChild(script); + await new Promise(resolve => { + script.addEventListener("load", resolve, {once: true}); + }); + + const window = document.defaultView.wrappedJSObject; + + // Implements `criRequest` to be called by chrome-remeote-interface + // library in order to do the cross-domain http request, which, + // in a regular Web page, is impossible. + window.criRequest = (options, callback) => { + const { host, port, secure, ustHostName, path } = options; + const url = `http://${host}:${port}${path}`; + const xhr = new XMLHttpRequest(); + xhr.open("GET", url, true); + + // Prevent "XML Parsing Error: syntax error" error messages + xhr.overrideMimeType("text/plain"); + + xhr.send(null); + xhr.onload = () => { + callback(null, xhr.responseText); + }; + xhr.onerror = (e) => { + callback(e, null); + }; + }; + + return window.CDP; +} + +function getTargets(CDP) { + return new Promise((resolve, reject) => { + CDP.List(null, (err, targets) => { + if (err) { + reject(err); + return; + } + resolve(targets); + }); + }); +}