author | Carsten "Tomcat" Book <cbook@mozilla.com> |
Wed, 09 Nov 2016 16:38:04 +0100 | |
changeset 321837 | 336759fad4621dfcd0a3293840edbed67018accd |
parent 321836 | 6b7f1acfbb4b3fab041817a7dfbe39743416181f (current diff) |
parent 321745 | eac5fd08280a9cfa83925050cd70facc8252eac9 (diff) |
child 321838 | 310ae43d23b7392aad985af26c9907a598360b36 |
child 321921 | 3e3b7cff19a51048d6c633630187f2233c882705 |
child 321933 | c06f9e99eeb80b4695f0b44b6e2e6675b272bd00 |
push id | 83680 |
push user | cbook@mozilla.com |
push date | Wed, 09 Nov 2016 15:39:46 +0000 |
treeherder | mozilla-inbound@310ae43d23b7 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 52.0a1 |
first release with | nightly linux32
336759fad462
/
52.0a1
/
20161110030211
/
files
nightly linux64
336759fad462
/
52.0a1
/
20161110030211
/
files
nightly mac
336759fad462
/
52.0a1
/
20161110030211
/
files
nightly win32
336759fad462
/
52.0a1
/
20161110030211
/
files
nightly win64
336759fad462
/
52.0a1
/
20161110030211
/
files
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
releases | nightly linux32
52.0a1
/
20161110030211
/
pushlog to previous
nightly linux64
52.0a1
/
20161110030211
/
pushlog to previous
nightly mac
52.0a1
/
20161110030211
/
pushlog to previous
nightly win32
52.0a1
/
20161110030211
/
pushlog to previous
nightly win64
52.0a1
/
20161110030211
/
pushlog to previous
|
--- a/CLOBBER +++ b/CLOBBER @@ -17,9 +17,9 @@ # # Modifying this file will now automatically clobber the buildbot machines \o/ # # Are you updating CLOBBER because you think it's needed for your WebIDL # changes to stick? As of bug 928195, this shouldn't be necessary! Please # don't change CLOBBER for WebIDL changes any more. -bug 1313485 - OS X bustage requires clobber to fix +Bug 1277704 - jemalloc may need a clobber
--- a/accessible/atk/AccessibleWrap.cpp +++ b/accessible/atk/AccessibleWrap.cpp @@ -368,16 +368,19 @@ AccessibleWrap::CreateMaiInterfaces(void if (IsLink()) interfacesBits |= 1 << MAI_INTERFACE_HYPERLINK_IMPL; if (!nsAccUtils::MustPrune(this)) { // These interfaces require children // Table interface. if (AsTable()) interfacesBits |= 1 << MAI_INTERFACE_TABLE; + if (AsTableCell()) + interfacesBits |= 1 << MAI_INTERFACE_TABLE_CELL; + // Selection interface. if (IsSelect()) { interfacesBits |= 1 << MAI_INTERFACE_SELECTION; } } return interfacesBits; } @@ -1125,16 +1128,19 @@ GetInterfacesForProxy(ProxyAccessible* a interfaces |= 1 << MAI_INTERFACE_HYPERLINK_IMPL; if (aInterfaces & Interfaces::VALUE) interfaces |= 1 << MAI_INTERFACE_VALUE; if (aInterfaces & Interfaces::TABLE) interfaces |= 1 << MAI_INTERFACE_TABLE; + if (aInterfaces & Interfaces::TABLECELL) + interfaces |= 1 << MAI_INTERFACE_TABLE_CELL; + if (aInterfaces & Interfaces::IMAGE) interfaces |= 1 << MAI_INTERFACE_IMAGE; if (aInterfaces & Interfaces::DOCUMENT) interfaces |= 1 << MAI_INTERFACE_DOCUMENT; if (aInterfaces & Interfaces::SELECTION) { interfaces |= 1 << MAI_INTERFACE_SELECTION;
--- a/browser/base/content/test/general/head.js +++ b/browser/base/content/test/general/head.js @@ -623,18 +623,16 @@ function promiseTabLoadEvent(tab, url) info(`Skipping spurious load event for ${loadedUrl}`); return false; } info("Tab event received: load"); return true; } - // Create two promises: one resolved from the content process when the page - // loads and one that is rejected if we take too long to load the url. let loaded = BrowserTestUtils.browserLoaded(tab.linkedBrowser, false, handle); if (url) BrowserTestUtils.loadURI(tab.linkedBrowser, url); return loaded; }
--- a/browser/base/content/test/plugins/head.js +++ b/browser/base/content/test/plugins/head.js @@ -85,18 +85,16 @@ function promiseTabLoadEvent(tab, url) { info(`Skipping spurious load event for ${loadedUrl}`); return false; } info("Tab event received: load"); return true; } - // Create two promises: one resolved from the content process when the page - // loads and one that is rejected if we take too long to load the url. let loaded = BrowserTestUtils.browserLoaded(tab.linkedBrowser, false, handle); if (url) BrowserTestUtils.loadURI(tab.linkedBrowser, url); return loaded; }
--- a/browser/components/extensions/ext-contextMenus.js +++ b/browser/components/extensions/ext-contextMenus.js @@ -4,16 +4,17 @@ Cu.import("resource://gre/modules/ExtensionUtils.jsm"); Cu.import("resource://gre/modules/MatchPattern.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); var { EventManager, + ExtensionError, IconDetails, } = ExtensionUtils; // Map[Extension -> Map[ID -> MenuItem]] // Note: we want to enumerate all the menu items so // this cannot be a weak map. var gContextMenuMap = new Map(); @@ -321,17 +322,17 @@ MenuItem.prototype = { return; } let menuMap = gContextMenuMap.get(this.extension); if (!menuMap.has(parentId)) { throw new Error("Could not find any MenuItem with id: " + parentId); } for (let item = menuMap.get(parentId); item; item = item.parent) { if (item === this) { - throw new Error("MenuItem cannot be an ancestor (or self) of its new parent."); + throw new ExtensionError("MenuItem cannot be an ancestor (or self) of its new parent."); } } }, set parentId(parentId) { this.ensureValidParentId(parentId); if (this.parent) {
--- a/browser/components/extensions/test/browser/.eslintrc.js +++ b/browser/components/extensions/test/browser/.eslintrc.js @@ -24,9 +24,13 @@ module.exports = { // eslint-disable-li "closeContextMenu": true, "closeExtensionContextMenu": true, "focusWindow": true, "makeWidgetId": true, "openContextMenu": true, "openExtensionContextMenu": true, "CustomizableUI": true, }, + + "rules": { + "no-shadow": 0, + }, };
--- a/browser/components/extensions/test/browser/browser_ext_browserAction_context.js +++ b/browser/components/extensions/test/browser/browser_ext_browserAction_context.js @@ -1,45 +1,31 @@ /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ /* vim: set sts=2 sw=2 et tw=80: */ "use strict"; function* runTests(options) { - function background(getTests) { - // Gets the current details of the browser action, and returns a - // promise that resolves to an object containing them. - function getDetails(tabId) { - return Promise.all([ - browser.browserAction.getTitle({tabId}), - browser.browserAction.getPopup({tabId}), - browser.browserAction.getBadgeText({tabId}), - browser.browserAction.getBadgeBackgroundColor({tabId})] - ).then(details => { - return Promise.resolve({title: details[0], - popup: details[1], - badge: details[2], - badgeBackgroundColor: details[3]}); - }); - } + async function background(getTests) { + async function checkDetails(expecting, tabId) { + let title = await browser.browserAction.getTitle({tabId}); + browser.test.assertEq(expecting.title, title, + "expected value from getTitle"); - function checkDetails(expecting, tabId) { - return getDetails(tabId).then(details => { - browser.test.assertEq(expecting.title, details.title, - "expected value from getTitle"); - - browser.test.assertEq(expecting.popup, details.popup, - "expected value from getPopup"); + let popup = await browser.browserAction.getPopup({tabId}); + browser.test.assertEq(expecting.popup, popup, + "expected value from getPopup"); - browser.test.assertEq(expecting.badge, details.badge, - "expected value from getBadge"); + let badge = await browser.browserAction.getBadgeText({tabId}); + browser.test.assertEq(expecting.badge, badge, + "expected value from getBadge"); - browser.test.assertEq(String(expecting.badgeBackgroundColor), - String(details.badgeBackgroundColor), - "expected value from getBadgeBackgroundColor"); - }); + let badgeBackgroundColor = await browser.browserAction.getBadgeBackgroundColor({tabId}); + browser.test.assertEq(String(expecting.badgeBackgroundColor), + String(badgeBackgroundColor), + "expected value from getBadgeBackgroundColor"); } let expectDefaults = expecting => { return checkDetails(expecting); }; let tabs = []; let tests = getTests(tabs, expectDefaults); @@ -52,49 +38,37 @@ function* runTests(options) { () => browser.browserAction.setTitle({tabId, title: "foo"}), () => browser.browserAction.setIcon({tabId, path: "foo.png"}), () => browser.browserAction.setPopup({tabId, popup: "foo.html"}), () => browser.browserAction.setBadgeText({tabId, text: "foo"}), () => browser.browserAction.setBadgeBackgroundColor({tabId, color: [0xff, 0, 0, 0xff]}), ]; for (let call of calls) { - let checkError = e => { - browser.test.assertTrue(e.message.includes(`Invalid tab ID: ${tabId}`), - `Expected invalid tab ID error, got ${e}`); - }; - try { - call().then(() => { - browser.test.fail(`Expected call to fail: ${call}`); - }, e => { - checkError(e); - }); - } catch (e) { - checkError(e); - } + await browser.test.assertRejects( + new Promise(resolve => resolve(call())), + RegExp(`Invalid tab ID: ${tabId}`), + "Expected invalid tab ID error"); } } // Runs the next test in the `tests` array, checks the results, // and passes control back to the outer test scope. function nextTest() { let test = tests.shift(); - test(expecting => { + test(async expecting => { // Check that the API returns the expected values, and then // run the next test. - new Promise(resolve => { - return browser.tabs.query({active: true, currentWindow: true}, resolve); - }).then(tabs => { - return checkDetails(expecting, tabs[0].id); - }).then(() => { - // Check that the actual icon has the expected values, then - // run the next test. - browser.test.sendMessage("nextTest", expecting, tests.length); - }); + let tabs = await browser.tabs.query({active: true, currentWindow: true}); + await checkDetails(expecting, tabs[0].id); + + // Check that the actual icon has the expected values, then + // run the next test. + browser.test.sendMessage("nextTest", expecting, tests.length); }); } browser.test.onMessage.addListener((msg) => { if (msg != "runNextTest") { browser.test.fail("Expecting 'runNextTest' message"); } @@ -236,117 +210,110 @@ add_task(function* testTabSwitchContext( {"icon": browser.runtime.getURL("default-2.png"), "popup": browser.runtime.getURL("default-2.html"), "title": "Default Title 2", "badge": "d2", "badgeBackgroundColor": [0, 0xff, 0, 0xff]}, ]; return [ - expect => { + async expect => { browser.test.log("Initial state, expect default properties."); - expectDefaults(details[0]).then(() => { - expect(details[0]); - }); + + await expectDefaults(details[0]); + expect(details[0]); }, - expect => { + async expect => { browser.test.log("Change the icon in the current tab. Expect default properties excluding the icon."); browser.browserAction.setIcon({tabId: tabs[0], path: "1.png"}); - expectDefaults(details[0]).then(() => { - expect(details[1]); - }); + + await expectDefaults(details[0]); + expect(details[1]); }, - expect => { + async expect => { browser.test.log("Create a new tab. Expect default properties."); - browser.tabs.create({active: true, url: "about:blank?0"}, tab => { - tabs.push(tab.id); - expectDefaults(details[0]).then(() => { - expect(details[0]); - }); - }); + let tab = await browser.tabs.create({active: true, url: "about:blank?0"}); + tabs.push(tab.id); + + await expectDefaults(details[0]); + expect(details[0]); }, - expect => { + async expect => { browser.test.log("Change properties. Expect new properties."); let tabId = tabs[1]; browser.browserAction.setIcon({tabId, path: "2.png"}); browser.browserAction.setPopup({tabId, popup: "2.html"}); browser.browserAction.setTitle({tabId, title: "Title 2"}); browser.browserAction.setBadgeText({tabId, text: "2"}); browser.browserAction.setBadgeBackgroundColor({tabId, color: "#ff0000"}); browser.browserAction.disable(tabId); - expectDefaults(details[0]).then(() => { - expect(details[2]); - }); + await expectDefaults(details[0]); + expect(details[2]); }, expect => { browser.test.log("Navigate to a new page. Expect no changes."); // TODO: This listener should not be necessary, but the |tabs.update| // callback currently fires too early in e10s windows. browser.tabs.onUpdated.addListener(function listener(tabId, changed) { if (tabId == tabs[1] && changed.url) { browser.tabs.onUpdated.removeListener(listener); expect(details[2]); } }); browser.tabs.update(tabs[1], {url: "about:blank?1"}); }, - expect => { + async expect => { browser.test.log("Switch back to the first tab. Expect previously set properties."); - browser.tabs.update(tabs[0], {active: true}, () => { - expect(details[1]); - }); + await browser.tabs.update(tabs[0], {active: true}); + expect(details[1]); }, - expect => { + async expect => { browser.test.log("Change default values, expect those changes reflected."); browser.browserAction.setIcon({path: "default-2.png"}); browser.browserAction.setPopup({popup: "default-2.html"}); browser.browserAction.setTitle({title: "Default Title 2"}); browser.browserAction.setBadgeText({text: "d2"}); browser.browserAction.setBadgeBackgroundColor({color: [0, 0xff, 0, 0xff]}); browser.browserAction.disable(); - expectDefaults(details[3]).then(() => { - expect(details[3]); - }); + + await expectDefaults(details[3]); + expect(details[3]); }, - expect => { + async expect => { browser.test.log("Re-enable by default. Expect enabled."); browser.browserAction.enable(); - expectDefaults(details[4]).then(() => { - expect(details[4]); - }); + + await expectDefaults(details[4]); + expect(details[4]); }, - expect => { + async expect => { browser.test.log("Switch back to tab 2. Expect former value, unaffected by changes to defaults in previous step."); - browser.tabs.update(tabs[1], {active: true}, () => { - expectDefaults(details[3]).then(() => { - expect(details[2]); - }); - }); + await browser.tabs.update(tabs[1], {active: true}); + + await expectDefaults(details[3]); + expect(details[2]); }, - expect => { + async expect => { browser.test.log("Delete tab, switch back to tab 1. Expect previous results again."); - browser.tabs.remove(tabs[1], () => { - expect(details[4]); - }); + await browser.tabs.remove(tabs[1]); + expect(details[4]); }, - expect => { + async expect => { browser.test.log("Create a new tab. Expect new default properties."); - browser.tabs.create({active: true, url: "about:blank?2"}, tab => { - tabs.push(tab.id); - expect(details[5]); - }); + let tab = await browser.tabs.create({active: true, url: "about:blank?2"}); + tabs.push(tab.id); + expect(details[5]); }, - expect => { + async expect => { browser.test.log("Delete tab."); - browser.tabs.remove(tabs[2], () => { - expect(details[4]); - }); + await browser.tabs.remove(tabs[2]); + expect(details[4]); }, ]; }, }); }); add_task(function* testDefaultTitle() { yield runTests({ @@ -386,46 +353,46 @@ add_task(function* testDefaultTitle() { {"title": "", "popup": "", "badge": "", "badgeBackgroundColor": DEFAULT_BADGE_COLOR, "icon": browser.runtime.getURL("icon.png")}, ]; return [ - expect => { + async expect => { browser.test.log("Initial state. Expect extension title as default title."); - expectDefaults(details[0]).then(() => { - expect(details[0]); - }); + + await expectDefaults(details[0]); + expect(details[0]); }, - expect => { + async expect => { browser.test.log("Change the title. Expect new title."); browser.browserAction.setTitle({tabId: tabs[0], title: "Foo Title"}); - expectDefaults(details[0]).then(() => { - expect(details[1]); - }); + + await expectDefaults(details[0]); + expect(details[1]); }, - expect => { + async expect => { browser.test.log("Change the default. Expect same properties."); browser.browserAction.setTitle({title: "Bar Title"}); - expectDefaults(details[2]).then(() => { - expect(details[1]); - }); + + await expectDefaults(details[2]); + expect(details[1]); }, - expect => { + async expect => { browser.test.log("Clear the title. Expect new default title."); browser.browserAction.setTitle({tabId: tabs[0], title: ""}); - expectDefaults(details[2]).then(() => { - expect(details[2]); - }); + + await expectDefaults(details[2]); + expect(details[2]); }, - expect => { + async expect => { browser.test.log("Set default title to null string. Expect null string from API, extension title in UI."); browser.browserAction.setTitle({title: ""}); - expectDefaults(details[3]).then(() => { - expect(details[3]); - }); + + await expectDefaults(details[3]); + expect(details[3]); }, ]; }, }); });
--- a/browser/components/extensions/test/browser/browser_ext_browserAction_pageAction_icon_permissions.js +++ b/browser/components/extensions/test/browser/browser_ext_browserAction_pageAction_icon_permissions.js @@ -15,24 +15,20 @@ add_task(function* testInvalidIconSizes( let tabId = tabs[0].id; let promises = []; for (let api of ["pageAction", "browserAction"]) { // helper function to run setIcon and check if it fails let assertSetIconThrows = function(detail, error, message) { detail.tabId = tabId; promises.push( - browser[api].setIcon(detail).then( - () => { - browser.test.fail("Expected an error on invalid icon size."); - browser.test.notifyFail("setIcon with invalid icon size"); - }, - error => { - browser.test.succeed("setIcon with invalid icon size"); - })); + browser.test.assertRejects( + browser[api].setIcon(detail), + /must be an integer/, + "setIcon with invalid icon size")); }; let imageData = new ImageData(1, 1); // test invalid icon size inputs for (let type of ["path", "imageData"]) { let img = type == "imageData" ? imageData : "test.png"; @@ -143,24 +139,20 @@ add_task(function* testSecureURLsDenied( let urls = ["chrome://browser/content/browser.xul", "javascript:true"]; let promises = []; for (let url of urls) { for (let api of ["pageAction", "browserAction"]) { promises.push( - browser[api].setIcon({tabId, path: url}).then( - () => { - browser.test.fail(`Load of '${url}' succeeded. Expected failure.`); - browser.test.notifyFail("setIcon security tests"); - }, - error => { - browser.test.succeed(`Load of '${url}' failed. Expected failure. ${error}`); - })); + browser.test.assertRejects( + browser[api].setIcon({tabId, path: url}), + /Illegal URL/, + `Load of '${url}' should fail.`)); } } Promise.all(promises).then(() => { browser.test.notifyPass("setIcon security tests"); }); }); },
--- a/browser/components/extensions/test/browser/browser_ext_contextMenus.js +++ b/browser/components/extensions/test/browser/browser_ext_contextMenus.js @@ -47,17 +47,17 @@ add_task(function* () { gBrowser.selectedTab = tab1; let extension = ExtensionTestUtils.loadExtension({ manifest: { "permissions": ["contextMenus"], }, - background: function() { + background: async function() { // A generic onclick callback function. function genericOnClick(info, tab) { browser.test.sendMessage("onclick", {info, tab}); } browser.contextMenus.onClicked.addListener((info, tab) => { browser.test.sendMessage("browser.contextMenus.onClicked", {info, tab}); }); @@ -117,24 +117,22 @@ add_task(function* () { }); browser.contextMenus.remove(parentToDel); browser.contextMenus.create({ title: "Without onclick property", id: "ext-without-onclick", }); - browser.contextMenus.update(parent, {parentId: child2}).then( - () => { - browser.test.notifyFail("contextmenus"); - }, - () => { - browser.test.notifyPass("contextmenus"); - } - ); + await browser.test.assertRejects( + browser.contextMenus.update(parent, {parentId: child2}), + /cannot be an ancestor/, + "Should not be able to reparent an item as descendent of itself"); + + browser.test.notifyPass("contextmenus"); }, }); yield extension.startup(); yield extension.awaitFinish("contextmenus"); let expectedClickInfo = { menuItemId: "ext-image",
--- a/browser/components/extensions/test/browser/browser_ext_incognito_popup.js +++ b/browser/components/extensions/test/browser/browser_ext_incognito_popup.js @@ -9,91 +9,92 @@ add_task(function* testIncognitoPopup() "browser_action": { "default_popup": "popup.html", }, "page_action": { "default_popup": "popup.html", }, }, - background() { + background: async function() { let resolveMessage; browser.runtime.onMessage.addListener(msg => { if (resolveMessage && msg.message == "popup-details") { resolveMessage(msg); } }); let awaitPopup = windowId => { return new Promise(resolve => { resolveMessage = resolve; }).then(msg => { browser.test.assertEq(windowId, msg.windowId, "Got popup message from correct window"); return msg; }); }; - let testWindow = window => { - return browser.tabs.query({active: true, windowId: window.id}).then(([tab]) => { - return browser.pageAction.show(tab.id); - }).then(() => { - browser.test.sendMessage("click-pageAction"); + let testWindow = async window => { + let [tab] = await browser.tabs.query({active: true, windowId: window.id}); + + await browser.pageAction.show(tab.id); + browser.test.sendMessage("click-pageAction"); - return awaitPopup(window.id); - }).then(msg => { - browser.test.assertEq(window.incognito, msg.incognito, "Correct incognito status in pageAction popup"); + let msg = await awaitPopup(window.id); + browser.test.assertEq(window.incognito, msg.incognito, "Correct incognito status in pageAction popup"); - browser.test.sendMessage("click-browserAction"); + browser.test.sendMessage("click-browserAction"); - return awaitPopup(window.id); - }).then(msg => { - browser.test.assertEq(window.incognito, msg.incognito, "Correct incognito status in browserAction popup"); - }); + msg = await awaitPopup(window.id); + browser.test.assertEq(window.incognito, msg.incognito, "Correct incognito status in browserAction popup"); }; const URL = "http://example.com/incognito"; let windowReady = new Promise(resolve => { browser.tabs.onUpdated.addListener(function listener(tabId, changed, tab) { if (changed.status == "complete" && tab.url == URL) { browser.tabs.onUpdated.removeListener(listener); resolve(); } }); }); - browser.windows.getCurrent().then(window => { - return testWindow(window); - }).then(() => { - return browser.windows.create({incognito: true, url: URL}); - }).then(window => { - return windowReady.then(() => { - return testWindow(window); - }).then(() => { - return browser.windows.remove(window.id); - }); - }).then(() => { + try { + { + let window = await browser.windows.getCurrent(); + + await testWindow(window); + } + + { + let window = await browser.windows.create({incognito: true, url: URL}); + await windowReady; + + await testWindow(window); + + await browser.windows.remove(window.id); + } + browser.test.notifyPass("incognito"); - }).catch(error => { + } catch (error) { browser.test.fail(`Error: ${error} :: ${error.stack}`); browser.test.notifyFail("incognito"); - }); + } }, files: { "popup.html": '<html><head><meta charset="utf-8"><script src="popup.js"></script></head></html>', - "popup.js": function() { - browser.windows.getCurrent().then(win => { - browser.runtime.sendMessage({ - message: "popup-details", - windowId: win.id, - incognito: browser.extension.inIncognitoContext, - }); - window.close(); + "popup.js": async function() { + let win = await browser.windows.getCurrent(); + browser.runtime.sendMessage({ + message: "popup-details", + windowId: win.id, + incognito: browser.extension.inIncognitoContext, }); + window.close(); }, }, }); extension.onMessage("click-browserAction", () => { clickBrowserAction(extension, Services.wm.getMostRecentWindow("navigator:browser")); });
--- a/browser/components/extensions/test/browser/browser_ext_legacy_extension_context_contentscript.js +++ b/browser/components/extensions/test/browser/browser_ext_legacy_extension_context_contentscript.js @@ -22,27 +22,29 @@ function promiseAddonStartup(extension) * tab info. */ add_task(function* test_legacy_extension_context_contentscript_connection() { function backgroundScript() { // Extract the assigned uuid from the background page url and send it // in a test message. let uuid = window.location.hostname; - browser.test.onMessage.addListener(msg => { + browser.test.onMessage.addListener(async msg => { if (msg == "open-test-tab") { - browser.tabs.create({url: "http://example.com/"}) - .then(tab => browser.test.sendMessage("get-expected-sender-info", { - uuid, tab, - })); + let tab = await browser.tabs.create({url: "http://example.com/"}); + browser.test.sendMessage("get-expected-sender-info", + {uuid, tab}); } else if (msg == "close-current-tab") { - browser.tabs.query({active: true}) - .then(tabs => browser.tabs.remove(tabs[0].id)) - .then(() => browser.test.sendMessage("current-tab-closed", true)) - .catch(() => browser.test.sendMessage("current-tab-closed", false)); + try { + let [tab] = await browser.tabs.query({active: true}); + await browser.tabs.remove(tab.id); + browser.test.sendMessage("current-tab-closed", true); + } catch (e) { + browser.test.sendMessage("current-tab-closed", false); + } } }); browser.test.sendMessage("ready"); } function contentScript() { browser.runtime.sendMessage("webextension -> legacy_extension message", (reply) => {
--- a/browser/components/extensions/test/browser/browser_ext_optionsPage_privileges.js +++ b/browser/components/extensions/test/browser/browser_ext_optionsPage_privileges.js @@ -12,26 +12,27 @@ add_task(function* test_tab_options_priv browser.test.log(`Error: ${error} :: ${error.stack}`); browser.test.notifyFail("options-ui-privileges"); }); } }); browser.runtime.openOptionsPage(); } - function optionsScript() { - browser.tabs.query({url: "http://example.com/"}).then(tabs => { - browser.test.assertEq("http://example.com/", tabs[0].url, "Got the expect tab"); - return browser.tabs.getCurrent(); - }).then(tab => { + async function optionsScript() { + try { + let [tab] = await browser.tabs.query({url: "http://example.com/"}); + browser.test.assertEq("http://example.com/", tab.url, "Got the expect tab"); + + tab = await browser.tabs.getCurrent(); browser.runtime.sendMessage({msgName: "removeTabId", tabId: tab.id}); - }).catch(error => { + } catch (error) { browser.test.log(`Error: ${error} :: ${error.stack}`); browser.test.notifyFail("options-ui-privileges"); - }); + } } const ID = "options_privileges@tests.mozilla.org"; let extension = ExtensionTestUtils.loadExtension({ useAddonManager: "temporary", manifest: { applications: {gecko: {id: ID}},
--- a/browser/components/extensions/test/browser/browser_ext_pageAction_context.js +++ b/browser/components/extensions/test/browser/browser_ext_pageAction_context.js @@ -74,108 +74,105 @@ add_task(function* testTabSwitchContext( browser.tabs.onUpdated.addListener(function listener(tabId, changed) { if (tabId == details.id && changed.url == details.url) { browser.tabs.onUpdated.removeListener(listener); resolve(); } }); }); }; + return [ expect => { browser.test.log("Initial state. No icon visible."); expect(null); }, - expect => { + async expect => { browser.test.log("Show the icon on the first tab, expect default properties."); - browser.pageAction.show(tabs[0]).then(() => { - expect(details[0]); - }); + await browser.pageAction.show(tabs[0]); + expect(details[0]); }, expect => { browser.test.log("Change the icon. Expect default properties excluding the icon."); browser.pageAction.setIcon({tabId: tabs[0], path: "1.png"}); expect(details[1]); }, - expect => { + async expect => { browser.test.log("Create a new tab. No icon visible."); - browser.tabs.create({active: true, url: "about:blank?0"}, tab => { - tabs.push(tab.id); - expect(null); - }); + let tab = await browser.tabs.create({active: true, url: "about:blank?0"}); + tabs.push(tab.id); + expect(null); }, expect => { browser.test.log("Await tab load. No icon visible."); expect(null); }, - expect => { + async expect => { browser.test.log("Change properties. Expect new properties."); let tabId = tabs[1]; - browser.pageAction.show(tabId).then(() => { - browser.pageAction.setIcon({tabId, path: "2.png"}); - browser.pageAction.setPopup({tabId, popup: "2.html"}); - browser.pageAction.setTitle({tabId, title: "Title 2"}); + await browser.pageAction.show(tabId); - expect(details[2]); - }); + browser.pageAction.setIcon({tabId, path: "2.png"}); + browser.pageAction.setPopup({tabId, popup: "2.html"}); + browser.pageAction.setTitle({tabId, title: "Title 2"}); + + expect(details[2]); }, - expect => { + async expect => { browser.test.log("Change the hash. Expect same properties."); - promiseTabLoad({id: tabs[1], url: "about:blank?0#ref"}).then(() => { - expect(details[2]); - }); + let promise = promiseTabLoad({id: tabs[1], url: "about:blank?0#ref"}); + browser.tabs.update(tabs[1], {url: "about:blank?0#ref"}); + await promise; - browser.tabs.update(tabs[1], {url: "about:blank?0#ref"}); + expect(details[2]); }, expect => { browser.test.log("Clear the title. Expect default title."); browser.pageAction.setTitle({tabId: tabs[1], title: ""}); expect(details[3]); }, - expect => { + async expect => { browser.test.log("Navigate to a new page. Expect icon hidden."); // TODO: This listener should not be necessary, but the |tabs.update| // callback currently fires too early in e10s windows. - promiseTabLoad({id: tabs[1], url: "about:blank?1"}).then(() => { - expect(null); - }); + let promise = promiseTabLoad({id: tabs[1], url: "about:blank?1"}); browser.tabs.update(tabs[1], {url: "about:blank?1"}); - }, - expect => { - browser.test.log("Show the icon. Expect default properties again."); - browser.pageAction.show(tabs[1]).then(() => { - expect(details[0]); - }); + + await promise; + expect(null); }, - expect => { + async expect => { + browser.test.log("Show the icon. Expect default properties again."); + + await browser.pageAction.show(tabs[1]); + expect(details[0]); + }, + async expect => { browser.test.log("Switch back to the first tab. Expect previously set properties."); - browser.tabs.update(tabs[0], {active: true}, () => { - expect(details[1]); - }); + await browser.tabs.update(tabs[0], {active: true}); + expect(details[1]); }, - expect => { + async expect => { browser.test.log("Hide the icon on tab 2. Switch back, expect hidden."); - browser.pageAction.hide(tabs[1]).then(() => { - browser.tabs.update(tabs[1], {active: true}, () => { - expect(null); - }); - }); + await browser.pageAction.hide(tabs[1]); + + await browser.tabs.update(tabs[1], {active: true}); + expect(null); }, - expect => { + async expect => { browser.test.log("Switch back to tab 1. Expect previous results again."); - browser.tabs.remove(tabs[1], () => { - expect(details[1]); - }); + await browser.tabs.remove(tabs[1]); + expect(details[1]); }, - expect => { + async expect => { browser.test.log("Hide the icon. Expect hidden."); - browser.pageAction.hide(tabs[0]).then(() => { - expect(null); - }); + + await browser.pageAction.hide(tabs[0]); + expect(null); }, ]; }, }); });
--- a/browser/components/extensions/test/browser/browser_ext_pageAction_popup.js +++ b/browser/components/extensions/test/browser/browser_ext_pageAction_popup.js @@ -34,17 +34,17 @@ add_task(function* testPageActionPopup() "data/popup-b.html": scriptPage("popup-b.js"), "data/popup-b.js": function() { browser.runtime.sendMessage("from-popup-b"); }, "data/background.html": scriptPage("background.js"), - "data/background.js": function() { + "data/background.js": async function() { let tabId; let sendClick; let tests = [ () => { sendClick({expectEvent: false, expectPopup: "a"}); }, () => { @@ -110,17 +110,17 @@ add_task(function* testPageActionPopup() } else { browser.test.fail("unexpected click event"); } expect.event = false; browser.test.sendMessage("next-test"); }); - browser.test.onMessage.addListener((msg) => { + browser.test.onMessage.addListener(msg => { if (msg == "close-popup") { browser.runtime.sendMessage("close-popup"); return; } if (msg != "next-test") { browser.test.fail("Expecting 'next-test' message"); } @@ -128,23 +128,21 @@ add_task(function* testPageActionPopup() if (tests.length) { let test = tests.shift(); test(); } else { browser.test.notifyPass("pageaction-tests-done"); } }); - browser.tabs.query({active: true, currentWindow: true}, tabs => { - tabId = tabs[0].id; + let [tab] = await browser.tabs.query({active: true, currentWindow: true}); + tabId = tab.id; - browser.pageAction.show(tabId).then(() => { - browser.test.sendMessage("next-test"); - }); - }); + await browser.pageAction.show(tabId); + browser.test.sendMessage("next-test"); }, }, }); extension.onMessage("send-click", () => { clickPageAction(extension); });
--- a/browser/components/extensions/test/browser/browser_ext_pageAction_title.js +++ b/browser/components/extensions/test/browser/browser_ext_pageAction_title.js @@ -79,106 +79,100 @@ add_task(function* testTabSwitchContext( }); }); }; return [ expect => { browser.test.log("Initial state. No icon visible."); expect(null); }, - expect => { + async expect => { browser.test.log("Show the icon on the first tab, expect default properties."); - browser.pageAction.show(tabs[0]).then(() => { - expect(details[0]); - }); + await browser.pageAction.show(tabs[0]); + expect(details[0]); }, expect => { browser.test.log("Change the icon. Expect default properties excluding the icon."); browser.pageAction.setIcon({tabId: tabs[0], path: "1.png"}); expect(details[1]); }, - expect => { + async expect => { browser.test.log("Create a new tab. No icon visible."); - browser.tabs.create({active: true, url: "about:blank?0"}, tab => { - tabs.push(tab.id); - expect(null); - }); + let tab = await browser.tabs.create({active: true, url: "about:blank?0"}); + tabs.push(tab.id); + expect(null); }, expect => { browser.test.log("Await tab load. No icon visible."); expect(null); }, - expect => { + async expect => { browser.test.log("Change properties. Expect new properties."); let tabId = tabs[1]; - browser.pageAction.show(tabId).then(() => { - browser.pageAction.setIcon({tabId, path: "2.png"}); - browser.pageAction.setPopup({tabId, popup: "2.html"}); - browser.pageAction.setTitle({tabId, title: "Title 2"}); - expect(details[2]); - }); + await browser.pageAction.show(tabId); + browser.pageAction.setIcon({tabId, path: "2.png"}); + browser.pageAction.setPopup({tabId, popup: "2.html"}); + browser.pageAction.setTitle({tabId, title: "Title 2"}); + + expect(details[2]); }, - expect => { + async expect => { browser.test.log("Change the hash. Expect same properties."); - promiseTabLoad({id: tabs[1], url: "about:blank?0#ref"}).then(() => { - expect(details[2]); - }); + let promise = promiseTabLoad({id: tabs[1], url: "about:blank?0#ref"}); browser.tabs.update(tabs[1], {url: "about:blank?0#ref"}); + + await promise; + expect(details[2]); }, expect => { browser.test.log("Clear the title. Expect default title."); browser.pageAction.setTitle({tabId: tabs[1], title: ""}); expect(details[3]); }, - expect => { + async expect => { browser.test.log("Navigate to a new page. Expect icon hidden."); // TODO: This listener should not be necessary, but the |tabs.update| // callback currently fires too early in e10s windows. - promiseTabLoad({id: tabs[1], url: "about:blank?1"}).then(() => { - expect(null); - }); + let promise = promiseTabLoad({id: tabs[1], url: "about:blank?1"}); browser.tabs.update(tabs[1], {url: "about:blank?1"}); - }, - expect => { - browser.test.log("Show the icon. Expect default properties again."); - browser.pageAction.show(tabs[1]).then(() => { - expect(details[0]); - }); + + await promise; + expect(null); }, - expect => { + async expect => { + browser.test.log("Show the icon. Expect default properties again."); + await browser.pageAction.show(tabs[1]); + expect(details[0]); + }, + async expect => { browser.test.log("Switch back to the first tab. Expect previously set properties."); - browser.tabs.update(tabs[0], {active: true}, () => { - expect(details[1]); - }); + await browser.tabs.update(tabs[0], {active: true}); + expect(details[1]); }, - expect => { + async expect => { browser.test.log("Hide the icon on tab 2. Switch back, expect hidden."); - browser.pageAction.hide(tabs[1]).then(() => { - browser.tabs.update(tabs[1], {active: true}, () => { - expect(null); - }); - }); + await browser.pageAction.hide(tabs[1]); + await browser.tabs.update(tabs[1], {active: true}); + expect(null); }, - expect => { + async expect => { browser.test.log("Switch back to tab 1. Expect previous results again."); - browser.tabs.remove(tabs[1], () => { - expect(details[1]); - }); + await browser.tabs.remove(tabs[1]); + expect(details[1]); }, - expect => { + async expect => { browser.test.log("Hide the icon. Expect hidden."); - browser.pageAction.hide(tabs[0]).then(() => { - expect(null); - }); + await browser.pageAction.hide(tabs[0]); + expect(null); }, ]; }, }); }); add_task(function* testDefaultTitle() { yield runTests({ @@ -206,21 +200,20 @@ add_task(function* testDefaultTitle() { "icon": browser.runtime.getURL("icon.png")}, ]; return [ expect => { browser.test.log("Initial state. No icon visible."); expect(null); }, - expect => { + async expect => { browser.test.log("Show the icon on the first tab, expect extension title as default title."); - browser.pageAction.show(tabs[0]).then(() => { - expect(details[0]); - }); + await browser.pageAction.show(tabs[0]); + expect(details[0]); }, expect => { browser.test.log("Change the title. Expect new title."); browser.pageAction.setTitle({tabId: tabs[0], title: "Foo Title"}); expect(details[1]); }, expect => { browser.test.log("Clear the title. Expect extension title.");
--- a/browser/components/extensions/test/browser/browser_ext_popup_sendMessage.js +++ b/browser/components/extensions/test/browser/browser_ext_popup_sendMessage.js @@ -15,55 +15,54 @@ add_task(function* test_popup_sendMessag "page_action": { "default_popup": "popup.html", "browser_style": true, }, }, files: { "popup.html": scriptPage("popup.js"), - "popup.js": function() { - browser.runtime.onMessage.addListener(msg => { + "popup.js": async function() { + browser.runtime.onMessage.addListener(async msg => { if (msg == "popup-ping") { return Promise.resolve("popup-pong"); } }); - browser.runtime.sendMessage("background-ping").then(response => { - browser.test.sendMessage("background-ping-response", response); - }); + let response = await browser.runtime.sendMessage("background-ping"); + browser.test.sendMessage("background-ping-response", response); }, }, - background() { - browser.tabs.query({active: true, currentWindow: true}).then(([tab]) => { - return browser.pageAction.show(tab.id); - }).then(() => { - browser.test.sendMessage("page-action-ready"); - }); + async background() { + browser.runtime.onMessage.addListener(async msg => { + if (msg == "background-ping") { + let response = await browser.runtime.sendMessage("popup-ping"); - browser.runtime.onMessage.addListener(msg => { - if (msg == "background-ping") { - browser.runtime.sendMessage("popup-ping").then(response => { - browser.test.sendMessage("popup-ping-response", response); - }); + browser.test.sendMessage("popup-ping-response", response); - return new Promise(resolve => { + await new Promise(resolve => { // Wait long enough that we're relatively sure the docShells have // been swapped. Note that this value is fairly arbitrary. The load // event that triggers the swap should happen almost immediately // after the message is sent. The extra quarter of a second gives us // enough leeway that we can expect to respond after the swap in the // vast majority of cases. setTimeout(resolve, 250); - }).then(() => { - return "background-pong"; }); + + return "background-pong"; } }); + + let [tab] = await browser.tabs.query({active: true, currentWindow: true}); + + await browser.pageAction.show(tab.id); + + browser.test.sendMessage("page-action-ready"); }, }); yield extension.startup(); { clickBrowserAction(extension);
--- a/browser/components/extensions/test/browser/browser_ext_popup_shutdown.js +++ b/browser/components/extensions/test/browser/browser_ext_popup_shutdown.js @@ -1,19 +1,18 @@ /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ /* vim: set sts=2 sw=2 et tw=80: */ "use strict"; let getExtension = () => { return ExtensionTestUtils.loadExtension({ - background() { - browser.tabs.query({active: true, currentWindow: true}, tabs => { - browser.pageAction.show(tabs[0].id) - .then(() => { browser.test.sendMessage("pageAction ready"); }); - }); + background: async function() { + let [tab] = await browser.tabs.query({active: true, currentWindow: true}); + await browser.pageAction.show(tab.id); + browser.test.sendMessage("pageAction ready"); }, manifest: { "browser_action": { "default_popup": "popup.html", "browser_style": false, },
--- a/browser/components/extensions/test/browser/browser_ext_runtime_openOptionsPage.js +++ b/browser/components/extensions/test/browser/browser_ext_runtime_openOptionsPage.js @@ -56,17 +56,17 @@ add_tasks(function* test_inline_options( let extension = yield loadExtension(Object.assign({}, extraOptions, { manifest: { applications: {gecko: {id: "inline_options@tests.mozilla.org"}}, "options_ui": { "page": "options.html", }, }, - background: function() { + background: async function() { let _optionsPromise; let awaitOptions = () => { browser.test.assertFalse(_optionsPromise, "Should not be awaiting options already"); return new Promise(resolve => { _optionsPromise = {resolve}; }); }; @@ -77,76 +77,73 @@ add_tasks(function* test_inline_options( _optionsPromise.resolve(sender.tab); _optionsPromise = null; } else { browser.test.fail("Saw unexpected options page load"); } } }); - let firstTab, optionsTab; - browser.tabs.query({currentWindow: true, active: true}).then(tabs => { - firstTab = tabs[0].id; + try { + let [firstTab] = await browser.tabs.query({currentWindow: true, active: true}); browser.test.log("Open options page. Expect fresh load."); - return Promise.all([ + + let [, optionsTab] = await Promise.all([ browser.runtime.openOptionsPage(), awaitOptions(), ]); - }).then(([, tab]) => { - browser.test.assertEq("about:addons", tab.url, "Tab contains AddonManager"); - browser.test.assertTrue(tab.active, "Tab is active"); - browser.test.assertTrue(tab.id != firstTab, "Tab is a new tab"); - optionsTab = tab.id; + browser.test.assertEq("about:addons", optionsTab.url, "Tab contains AddonManager"); + browser.test.assertTrue(optionsTab.active, "Tab is active"); + browser.test.assertTrue(optionsTab.id != firstTab.id, "Tab is a new tab"); + browser.test.assertEq(0, browser.extension.getViews({type: "popup"}).length, "viewType is not popup"); browser.test.assertEq(1, browser.extension.getViews({type: "tab"}).length, "viewType is tab"); - browser.test.assertEq(1, browser.extension.getViews({windowId: tab.windowId}).length, "windowId matches"); + browser.test.assertEq(1, browser.extension.getViews({windowId: optionsTab.windowId}).length, "windowId matches"); + let views = browser.extension.getViews(); browser.test.assertEq(2, views.length, "Expected the options page and the background page"); browser.test.assertTrue(views.includes(window), "One of the views is the background page"); browser.test.assertTrue(views.some(w => w.iAmOption), "One of the views is the options page"); browser.test.log("Switch tabs."); - return browser.tabs.update(firstTab, {active: true}); - }).then(() => { + await browser.tabs.update(firstTab.id, {active: true}); + browser.test.log("Open options page again. Expect tab re-selected, no new load."); - return browser.runtime.openOptionsPage(); - }).then(() => { - return browser.tabs.query({currentWindow: true, active: true}); - }).then(([tab]) => { - browser.test.assertEq(optionsTab, tab.id, "Tab is the same as the previous options tab"); + await browser.runtime.openOptionsPage(); + let [tab] = await browser.tabs.query({currentWindow: true, active: true}); + + browser.test.assertEq(optionsTab.id, tab.id, "Tab is the same as the previous options tab"); browser.test.assertEq("about:addons", tab.url, "Tab contains AddonManager"); browser.test.log("Ping options page."); - return browser.runtime.sendMessage("ping"); - }).then((pong) => { + let pong = await browser.runtime.sendMessage("ping"); browser.test.assertEq("pong", pong, "Got pong."); browser.test.log("Remove options tab."); - return browser.tabs.remove(optionsTab); - }).then(() => { + await browser.tabs.remove(optionsTab.id); + browser.test.log("Open options page again. Expect fresh load."); - return Promise.all([ + [, tab] = await Promise.all([ browser.runtime.openOptionsPage(), awaitOptions(), ]); - }).then(([, tab]) => { browser.test.assertEq("about:addons", tab.url, "Tab contains AddonManager"); browser.test.assertTrue(tab.active, "Tab is active"); - browser.test.assertTrue(tab.id != optionsTab, "Tab is a new tab"); + browser.test.assertTrue(tab.id != optionsTab.id, "Tab is a new tab"); - return browser.tabs.remove(tab.id); - }).then(() => { + await browser.tabs.remove(tab.id); + browser.test.notifyPass("options-ui"); - }).catch(error => { - browser.test.log(`Error: ${error} :: ${error.stack}`); + } catch (error) { + browser.test.fail(`Error: ${error} :: ${error.stack}`); browser.test.notifyFail("options-ui"); - }); + } }, })); yield extension.awaitFinish("options-ui"); yield extension.unload(); yield BrowserTestUtils.removeTab(tab); }); @@ -160,17 +157,17 @@ add_tasks(function* test_tab_options(ext manifest: { applications: {gecko: {id: "tab_options@tests.mozilla.org"}}, "options_ui": { "page": "options.html", "open_in_tab": true, }, }, - background: function() { + background: async function() { let _optionsPromise; let awaitOptions = () => { browser.test.assertFalse(_optionsPromise, "Should not be awaiting options already"); return new Promise(resolve => { _optionsPromise = {resolve}; }); }; @@ -183,77 +180,73 @@ add_tasks(function* test_tab_options(ext } else { browser.test.fail("Saw unexpected options page load"); } } }); let optionsURL = browser.extension.getURL("options.html"); - let firstTab, optionsTab; - browser.tabs.query({currentWindow: true, active: true}).then(tabs => { - firstTab = tabs[0].id; + try { + let [firstTab] = await browser.tabs.query({currentWindow: true, active: true}); browser.test.log("Open options page. Expect fresh load."); - return Promise.all([ + let [, optionsTab] = await Promise.all([ browser.runtime.openOptionsPage(), awaitOptions(), ]); - }).then(([, tab]) => { - browser.test.assertEq(optionsURL, tab.url, "Tab contains options.html"); - browser.test.assertTrue(tab.active, "Tab is active"); - browser.test.assertTrue(tab.id != firstTab, "Tab is a new tab"); + browser.test.assertEq(optionsURL, optionsTab.url, "Tab contains options.html"); + browser.test.assertTrue(optionsTab.active, "Tab is active"); + browser.test.assertTrue(optionsTab.id != firstTab.id, "Tab is a new tab"); - optionsTab = tab.id; browser.test.assertEq(0, browser.extension.getViews({type: "popup"}).length, "viewType is not popup"); browser.test.assertEq(1, browser.extension.getViews({type: "tab"}).length, "viewType is tab"); - browser.test.assertEq(1, browser.extension.getViews({windowId: tab.windowId}).length, "windowId matches"); + browser.test.assertEq(1, browser.extension.getViews({windowId: optionsTab.windowId}).length, "windowId matches"); + let views = browser.extension.getViews(); browser.test.assertEq(2, views.length, "Expected the options page and the background page"); browser.test.assertTrue(views.includes(window), "One of the views is the background page"); browser.test.assertTrue(views.some(w => w.iAmOption), "One of the views is the options page"); browser.test.log("Switch tabs."); - return browser.tabs.update(firstTab, {active: true}); - }).then(() => { + await browser.tabs.update(firstTab.id, {active: true}); + browser.test.log("Open options page again. Expect tab re-selected, no new load."); - return browser.runtime.openOptionsPage(); - }).then(() => { - return browser.tabs.query({currentWindow: true, active: true}); - }).then(([tab]) => { - browser.test.assertEq(optionsTab, tab.id, "Tab is the same as the previous options tab"); + await browser.runtime.openOptionsPage(); + let [tab] = await browser.tabs.query({currentWindow: true, active: true}); + + browser.test.assertEq(optionsTab.id, tab.id, "Tab is the same as the previous options tab"); browser.test.assertEq(optionsURL, tab.url, "Tab contains options.html"); // Unfortunately, we can't currently do this, since onMessage doesn't // currently support responses when there are multiple listeners. // // browser.test.log("Ping options page."); // return new Promise(resolve => browser.runtime.sendMessage("ping", resolve)); browser.test.log("Remove options tab."); - return browser.tabs.remove(optionsTab); - }).then(() => { + await browser.tabs.remove(optionsTab.id); + browser.test.log("Open options page again. Expect fresh load."); - return Promise.all([ + [, tab] = await Promise.all([ browser.runtime.openOptionsPage(), awaitOptions(), ]); - }).then(([, tab]) => { browser.test.assertEq(optionsURL, tab.url, "Tab contains options.html"); browser.test.assertTrue(tab.active, "Tab is active"); - browser.test.assertTrue(tab.id != optionsTab, "Tab is a new tab"); + browser.test.assertTrue(tab.id != optionsTab.id, "Tab is a new tab"); - return browser.tabs.remove(tab.id); - }).then(() => { + await browser.tabs.remove(tab.id); + browser.test.notifyPass("options-ui-tab"); - }).catch(error => { - browser.test.log(`Error: ${error} :: ${error.stack}`); + } catch (error) { + browser.test.fail(`Error: ${error} :: ${error.stack}`); browser.test.notifyFail("options-ui-tab"); - }); + } }, })); yield extension.awaitFinish("options-ui-tab"); yield extension.unload(); yield BrowserTestUtils.removeTab(tab); }); @@ -261,34 +254,23 @@ add_tasks(function* test_tab_options(ext add_tasks(function* test_options_no_manifest(extraOptions) { info(`Test with no manifest key (${JSON.stringify(extraOptions)})`); let extension = yield loadExtension(Object.assign({}, extraOptions, { manifest: { applications: {gecko: {id: "no_options@tests.mozilla.org"}}, }, - background: function() { + async background() { browser.test.log("Try to open options page when not specified in the manifest."); - browser.runtime.openOptionsPage().then( - () => { - browser.test.fail("Opening options page without one specified in the manifest generated an error"); - browser.test.notifyFail("options-no-manifest"); - }, - error => { - let expected = "No `options_ui` declared"; - browser.test.assertTrue( - error.message.includes(expected), - `Got expected error (got: '${error.message}', expected: '${expected}'`); - } - ).then(() => { - browser.test.notifyPass("options-no-manifest"); - }).catch(error => { - browser.test.log(`Error: ${error} :: ${error.stack}`); - browser.test.notifyFail("options-no-manifest"); - }); + await browser.test.assertRejects( + browser.runtime.openOptionsPage(), + /No `options_ui` declared/, + "Expected error from openOptionsPage()"); + + browser.test.notifyPass("options-no-manifest"); }, })); yield extension.awaitFinish("options-no-manifest"); yield extension.unload(); });
--- a/browser/components/extensions/test/browser/browser_ext_runtime_openOptionsPage_uninstall.js +++ b/browser/components/extensions/test/browser/browser_ext_runtime_openOptionsPage_uninstall.js @@ -43,17 +43,17 @@ add_task(function* test_inline_options_u let extension = yield loadExtension({ manifest: { applications: {gecko: {id: "inline_options_uninstall@tests.mozilla.org"}}, "options_ui": { "page": "options.html", }, }, - background: function() { + background: async function() { let _optionsPromise; let awaitOptions = () => { browser.test.assertFalse(_optionsPromise, "Should not be awaiting options already"); return new Promise(resolve => { _optionsPromise = {resolve}; }); }; @@ -64,34 +64,33 @@ add_task(function* test_inline_options_u _optionsPromise.resolve(sender.tab); _optionsPromise = null; } else { browser.test.fail("Saw unexpected options page load"); } } }); - let firstTab; - browser.tabs.query({currentWindow: true, active: true}).then(tabs => { - firstTab = tabs[0].id; + try { + let [firstTab] = await browser.tabs.query({currentWindow: true, active: true}); browser.test.log("Open options page. Expect fresh load."); - return Promise.all([ + let [, tab] = await Promise.all([ browser.runtime.openOptionsPage(), awaitOptions(), ]); - }).then(([, tab]) => { + browser.test.assertEq("about:addons", tab.url, "Tab contains AddonManager"); browser.test.assertTrue(tab.active, "Tab is active"); - browser.test.assertTrue(tab.id != firstTab, "Tab is a new tab"); + browser.test.assertTrue(tab.id != firstTab.id, "Tab is a new tab"); browser.test.sendMessage("options-ui-open"); - }).catch(error => { + } catch (error) { browser.test.fail(`Error: ${error} :: ${error.stack}`); - }); + } }, }); yield extension.awaitMessage("options-ui-open"); yield extension.unload(); is(gBrowser.selectedBrowser.currentURI.spec, "about:addons", "Add-on manager tab should still be open");
--- a/browser/components/extensions/test/browser/browser_ext_runtime_setUninstallURL.js +++ b/browser/components/extensions/test/browser/browser_ext_runtime_setUninstallURL.js @@ -25,71 +25,62 @@ function* makeAndInstallXPI(id, backgrou let loadTab = yield loadPromise; yield BrowserTestUtils.removeTab(loadTab); return addon; } add_task(function* test_setuninstallurl_badargs() { - function backgroundScript() { - let promises = [ - browser.runtime.setUninstallURL("this is not a url") - .then(() => { - browser.test.notifyFail("setUninstallURL should have failed with bad url"); - }) - .catch(error => { - browser.test.assertTrue(/Invalid URL/.test(error.message), "error message indicates malformed url"); - }), + async function background() { + await browser.test.assertRejects( + browser.runtime.setUninstallURL("this is not a url"), + /Invalid URL/, + "setUninstallURL with an invalid URL should fail"); - browser.runtime.setUninstallURL("file:///etc/passwd") - .then(() => { - browser.test.notifyFail("setUninstallURL should have failed with non-http[s] url"); - }) - .catch(error => { - browser.test.assertTrue(/must have the scheme http or https/.test(error.message), "error message indicates bad scheme"); - }), - ]; + await browser.test.assertRejects( + browser.runtime.setUninstallURL("file:///etc/passwd"), + /must have the scheme http or https/, + "setUninstallURL with an illegal URL should fail"); - Promise.all(promises) - .then(() => browser.test.notifyPass("setUninstallURL bad params")); + browser.test.notifyPass("setUninstallURL bad params"); } let extension = ExtensionTestUtils.loadExtension({ - background: "(" + backgroundScript.toString() + ")()", + background, }); yield extension.startup(); yield extension.awaitFinish(); yield extension.unload(); }); // Test the documented behavior of setUninstallURL() that passing an // empty string is equivalent to not setting an uninstall URL // (i.e., no new tab is opened upon uninstall) add_task(function* test_setuninstall_empty_url() { - function backgroundScript() { - browser.runtime.setUninstallURL("") - .then(() => browser.tabs.create({url: "http://example.com/addon_loaded"})); + async function backgroundScript() { + await browser.runtime.setUninstallURL(""); + browser.tabs.create({url: "http://example.com/addon_loaded"}); } let addon = yield makeAndInstallXPI("test_uinstallurl2@tests.mozilla.org", backgroundScript, "http://example.com/addon_loaded"); addon.uninstall(true); info("uninstalled"); // no need to explicitly check for the absence of a new tab, // BrowserTestUtils will eventually complain if one is opened. }); add_task(function* test_setuninstallurl() { - function backgroundScript() { - browser.runtime.setUninstallURL("http://example.com/addon_uninstalled") - .then(() => browser.tabs.create({url: "http://example.com/addon_loaded"})); + async function backgroundScript() { + await browser.runtime.setUninstallURL("http://example.com/addon_uninstalled"); + browser.tabs.create({url: "http://example.com/addon_loaded"}); } let addon = yield makeAndInstallXPI("test_uinstallurl@tests.mozilla.org", backgroundScript, "http://example.com/addon_loaded"); // look for a new tab with the uninstall url. let uninstallPromise = BrowserTestUtils.waitForNewTab(gBrowser, "http://example.com/addon_uninstalled");
--- a/browser/components/extensions/test/browser/browser_ext_tabs_audio.js +++ b/browser/components/extensions/test/browser/browser_ext_tabs_audio.js @@ -3,27 +3,17 @@ "use strict"; add_task(function* () { let tab1 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank?1"); let tab2 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank?2"); gBrowser.selectedTab = tab1; - function background() { - // Wrap API methods in promise-based variants. - let promiseTabs = {}; - Object.keys(browser.tabs).forEach(method => { - promiseTabs[method] = (...args) => { - return new Promise(resolve => { - browser.tabs[method](...args, resolve); - }); - }; - }); - + async function background() { function promiseUpdated(tabId, attr) { return new Promise(resolve => { let onUpdated = (tabId_, changeInfo, tab) => { if (tabId == tabId_ && attr in changeInfo) { browser.tabs.onUpdated.removeListener(onUpdated); resolve({changeInfo, tab}); } @@ -42,134 +32,129 @@ add_task(function* () { function changeTab(tabId, attr, on) { return new Promise((resolve, reject) => { deferred[tabId] = {resolve, reject}; browser.test.sendMessage("change-tab", tabId, attr, on); }); } - let windowId; - let tabIds; - promiseTabs.query({lastFocusedWindow: true}).then(tabs => { + try { + let tabs = await browser.tabs.query({lastFocusedWindow: true}); browser.test.assertEq(tabs.length, 3, "We have three tabs"); for (let tab of tabs) { // Note: We want to check that these are actual boolean values, not // just that they evaluate as false. browser.test.assertEq(false, tab.mutedInfo.muted, "Tab is not muted"); browser.test.assertEq(undefined, tab.mutedInfo.reason, "Tab has no muted info reason"); browser.test.assertEq(false, tab.audible, "Tab is not audible"); } - windowId = tabs[0].windowId; - tabIds = [tabs[1].id, tabs[2].id]; + let windowId = tabs[0].windowId; + let tabIds = [tabs[1].id, tabs[2].id]; browser.test.log("Test initial queries for muted and audible return no tabs"); - return Promise.all([ - promiseTabs.query({windowId, audible: false}), - promiseTabs.query({windowId, audible: true}), - promiseTabs.query({windowId, muted: true}), - promiseTabs.query({windowId, muted: false}), - ]); - }).then(([silent, audible, muted, nonMuted]) => { + let silent = await browser.tabs.query({windowId, audible: false}); + let audible = await browser.tabs.query({windowId, audible: true}); + let muted = await browser.tabs.query({windowId, muted: true}); + let nonMuted = await browser.tabs.query({windowId, muted: false}); + browser.test.assertEq(3, silent.length, "Three silent tabs"); browser.test.assertEq(0, audible.length, "No audible tabs"); browser.test.assertEq(0, muted.length, "No muted tabs"); browser.test.assertEq(3, nonMuted.length, "Three non-muted tabs"); browser.test.log("Toggle muted and audible externally on one tab each, and check results"); - return Promise.all([ + [muted, audible] = await Promise.all([ promiseUpdated(tabIds[0], "mutedInfo"), promiseUpdated(tabIds[1], "audible"), changeTab(tabIds[0], "muted", true), changeTab(tabIds[1], "audible", true), ]); - }).then(([muted, audible]) => { + for (let obj of [muted.changeInfo, muted.tab]) { browser.test.assertEq(true, obj.mutedInfo.muted, "Tab is muted"); browser.test.assertEq("user", obj.mutedInfo.reason, "Tab was muted by the user"); } browser.test.assertEq(true, audible.changeInfo.audible, "Tab audible state changed"); browser.test.assertEq(true, audible.tab.audible, "Tab is audible"); browser.test.log("Re-check queries. Expect one audible and one muted tab"); - return Promise.all([ - promiseTabs.query({windowId, audible: false}), - promiseTabs.query({windowId, audible: true}), - promiseTabs.query({windowId, muted: true}), - promiseTabs.query({windowId, muted: false}), - ]); - }).then(([silent, audible, muted, nonMuted]) => { + silent = await browser.tabs.query({windowId, audible: false}); + audible = await browser.tabs.query({windowId, audible: true}); + muted = await browser.tabs.query({windowId, muted: true}); + nonMuted = await browser.tabs.query({windowId, muted: false}); + browser.test.assertEq(2, silent.length, "Two silent tabs"); browser.test.assertEq(1, audible.length, "One audible tab"); browser.test.assertEq(1, muted.length, "One muted tab"); browser.test.assertEq(2, nonMuted.length, "Two non-muted tabs"); browser.test.assertEq(true, muted[0].mutedInfo.muted, "Tab is muted"); browser.test.assertEq("user", muted[0].mutedInfo.reason, "Tab was muted by the user"); browser.test.assertEq(true, audible[0].audible, "Tab is audible"); browser.test.log("Toggle muted internally on two tabs, and check results"); - return Promise.all([ + [nonMuted, muted] = await Promise.all([ promiseUpdated(tabIds[0], "mutedInfo"), promiseUpdated(tabIds[1], "mutedInfo"), - promiseTabs.update(tabIds[0], {muted: false}), - promiseTabs.update(tabIds[1], {muted: true}), + browser.tabs.update(tabIds[0], {muted: false}), + browser.tabs.update(tabIds[1], {muted: true}), ]); - }).then(([unmuted, muted]) => { - for (let obj of [unmuted.changeInfo, unmuted.tab]) { + + for (let obj of [nonMuted.changeInfo, nonMuted.tab]) { browser.test.assertEq(false, obj.mutedInfo.muted, "Tab is not muted"); } for (let obj of [muted.changeInfo, muted.tab]) { browser.test.assertEq(true, obj.mutedInfo.muted, "Tab is muted"); } - for (let obj of [unmuted.changeInfo, unmuted.tab, muted.changeInfo, muted.tab]) { + for (let obj of [nonMuted.changeInfo, nonMuted.tab, muted.changeInfo, muted.tab]) { browser.test.assertEq("extension", obj.mutedInfo.reason, "Mute state changed by extension"); // FIXME: browser.runtime.id is currently broken. browser.test.assertEq(browser.i18n.getMessage("@@extension_id"), obj.mutedInfo.extensionId, "Mute state changed by extension"); } browser.test.log("Test that mutedInfo is preserved by sessionstore"); - return changeTab(tabIds[1], "duplicate").then(promiseTabs.get); - }).then(tab => { + let tab = await changeTab(tabIds[1], "duplicate").then(browser.tabs.get); + browser.test.assertEq(true, tab.mutedInfo.muted, "Tab is muted"); browser.test.assertEq("extension", tab.mutedInfo.reason, "Mute state changed by extension"); // FIXME: browser.runtime.id is currently broken. browser.test.assertEq(browser.i18n.getMessage("@@extension_id"), tab.mutedInfo.extensionId, "Mute state changed by extension"); browser.test.log("Unmute externally, and check results"); - return Promise.all([ + [nonMuted] = await Promise.all([ promiseUpdated(tabIds[1], "mutedInfo"), changeTab(tabIds[1], "muted", false), - promiseTabs.remove(tab.id), + browser.tabs.remove(tab.id), ]); - }).then(([unmuted]) => { - for (let obj of [unmuted.changeInfo, unmuted.tab]) { + + for (let obj of [nonMuted.changeInfo, nonMuted.tab]) { browser.test.assertEq(false, obj.mutedInfo.muted, "Tab is not muted"); browser.test.assertEq("user", obj.mutedInfo.reason, "Mute state changed by user"); } browser.test.notifyPass("tab-audio"); - }).catch(e => { + } catch (e) { browser.test.fail(`${e} :: ${e.stack}`); browser.test.notifyFail("tab-audio"); - }); + } } let extension = ExtensionTestUtils.loadExtension({ manifest: { "permissions": ["tabs"], }, background,
--- a/browser/components/extensions/test/browser/browser_ext_tabs_captureVisibleTab.js +++ b/browser/components/extensions/test/browser/browser_ext_tabs_captureVisibleTab.js @@ -21,103 +21,95 @@ function* runTest(options) { </html> `; let url = `data:text/html,${encodeURIComponent(html)}`; let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, url, true); tab.linkedBrowser.fullZoom = options.fullZoom; - function background(options) { - // Wrap API methods in promise-based variants. - let promiseTabs = {}; - Object.keys(browser.tabs).forEach(method => { - promiseTabs[method] = (...args) => { - return new Promise(resolve => { - browser.tabs[method](...args, resolve); - }); - }; - }); - + async function background(options) { browser.test.log(`Test color ${options.color} at fullZoom=${options.fullZoom}`); - promiseTabs.query({currentWindow: true, active: true}).then(([tab]) => { - return Promise.all([ - promiseTabs.captureVisibleTab(tab.windowId, {format: "jpeg", quality: 95}), - promiseTabs.captureVisibleTab(tab.windowId, {format: "png", quality: 95}), - promiseTabs.captureVisibleTab(tab.windowId, {quality: 95}), - promiseTabs.captureVisibleTab(tab.windowId), - ]).then(([jpeg, png, ...pngs]) => { - browser.test.assertTrue(pngs.every(url => url == png), "All PNGs are identical"); + try { + let [tab] = await browser.tabs.query({currentWindow: true, active: true}); - browser.test.assertTrue(jpeg.startsWith("data:image/jpeg;base64,"), "jpeg is JPEG"); - browser.test.assertTrue(png.startsWith("data:image/png;base64,"), "png is PNG"); + let [jpeg, png, ...pngs] = await Promise.all([ + browser.tabs.captureVisibleTab(tab.windowId, {format: "jpeg", quality: 95}), + browser.tabs.captureVisibleTab(tab.windowId, {format: "png", quality: 95}), + browser.tabs.captureVisibleTab(tab.windowId, {quality: 95}), + browser.tabs.captureVisibleTab(tab.windowId), + ]); + + browser.test.assertTrue(pngs.every(url => url == png), "All PNGs are identical"); + + browser.test.assertTrue(jpeg.startsWith("data:image/jpeg;base64,"), "jpeg is JPEG"); + browser.test.assertTrue(png.startsWith("data:image/png;base64,"), "png is PNG"); - let promises = [jpeg, png].map(url => new Promise(resolve => { - let img = new Image(); - img.src = url; - img.onload = () => resolve(img); - })); - return Promise.all(promises); - }).then(([jpeg, png]) => { - let tabDims = `${tab.width}\u00d7${tab.height}`; + let promises = [jpeg, png].map(url => new Promise(resolve => { + let img = new Image(); + img.src = url; + img.onload = () => resolve(img); + })); - let images = {jpeg, png}; - for (let format of Object.keys(images)) { - let img = images[format]; + [jpeg, png] = await Promise.all(promises); + let tabDims = `${tab.width}\u00d7${tab.height}`; - let dims = `${img.width}\u00d7${img.height}`; - browser.test.assertEq(tabDims, dims, `${format} dimensions are correct`); + let images = {jpeg, png}; + for (let format of Object.keys(images)) { + let img = images[format]; - let canvas = document.createElement("canvas"); - canvas.width = img.width; - canvas.height = img.height; - canvas.mozOpaque = true; + let dims = `${img.width}\u00d7${img.height}`; + browser.test.assertEq(tabDims, dims, `${format} dimensions are correct`); - let ctx = canvas.getContext("2d"); - ctx.drawImage(img, 0, 0); + let canvas = document.createElement("canvas"); + canvas.width = img.width; + canvas.height = img.height; + canvas.mozOpaque = true; - // Check the colors of the first and last pixels of the image, to make - // sure we capture the entire frame, and scale it correctly. - let coords = [ - {x: 0, y: 0, - color: options.color}, - {x: img.width - 1, - y: img.height - 1, - color: options.color}, - {x: img.width / 2 | 0, - y: img.height / 2 | 0, - color: options.neutral}, - ]; + let ctx = canvas.getContext("2d"); + ctx.drawImage(img, 0, 0); - for (let {x, y, color} of coords) { - let imageData = ctx.getImageData(x, y, 1, 1).data; + // Check the colors of the first and last pixels of the image, to make + // sure we capture the entire frame, and scale it correctly. + let coords = [ + {x: 0, y: 0, + color: options.color}, + {x: img.width - 1, + y: img.height - 1, + color: options.color}, + {x: img.width / 2 | 0, + y: img.height / 2 | 0, + color: options.neutral}, + ]; - if (format == "png") { - browser.test.assertEq(`rgba(${color},255)`, `rgba(${[...imageData]})`, `${format} image color is correct at (${x}, ${y})`); - } else { - // Allow for some deviation in JPEG version due to lossy compression. - const SLOP = 3; + for (let {x, y, color} of coords) { + let imageData = ctx.getImageData(x, y, 1, 1).data; - browser.test.log(`Testing ${format} image color at (${x}, ${y}), have rgba(${[...imageData]}), expecting approx. rgba(${color},255)`); + if (format == "png") { + browser.test.assertEq(`rgba(${color},255)`, `rgba(${[...imageData]})`, `${format} image color is correct at (${x}, ${y})`); + } else { + // Allow for some deviation in JPEG version due to lossy compression. + const SLOP = 3; - browser.test.assertTrue(Math.abs(color[0] - imageData[0]) <= SLOP, `${format} image color.red is correct at (${x}, ${y})`); - browser.test.assertTrue(Math.abs(color[1] - imageData[1]) <= SLOP, `${format} image color.green is correct at (${x}, ${y})`); - browser.test.assertTrue(Math.abs(color[2] - imageData[2]) <= SLOP, `${format} image color.blue is correct at (${x}, ${y})`); - browser.test.assertEq(255, imageData[3], `${format} image color.alpha is correct at (${x}, ${y})`); - } + browser.test.log(`Testing ${format} image color at (${x}, ${y}), have rgba(${[...imageData]}), expecting approx. rgba(${color},255)`); + + browser.test.assertTrue(Math.abs(color[0] - imageData[0]) <= SLOP, `${format} image color.red is correct at (${x}, ${y})`); + browser.test.assertTrue(Math.abs(color[1] - imageData[1]) <= SLOP, `${format} image color.green is correct at (${x}, ${y})`); + browser.test.assertTrue(Math.abs(color[2] - imageData[2]) <= SLOP, `${format} image color.blue is correct at (${x}, ${y})`); + browser.test.assertEq(255, imageData[3], `${format} image color.alpha is correct at (${x}, ${y})`); } } + } - browser.test.notifyPass("captureVisibleTab"); - }); - }).catch(e => { + browser.test.notifyPass("captureVisibleTab"); + } catch (e) { browser.test.fail(`Error: ${e} :: ${e.stack}`); browser.test.notifyFail("captureVisibleTab"); - }); + } } let extension = ExtensionTestUtils.loadExtension({ manifest: { "permissions": ["<all_urls>"], }, background: `(${background})(${JSON.stringify(options)})`,
--- a/browser/components/extensions/test/browser/browser_ext_tabs_cookieStoreId.js +++ b/browser/components/extensions/test/browser/browser_ext_tabs_cookieStoreId.js @@ -37,107 +37,93 @@ add_task(function* () { background: function() { function testTab(data, tab) { browser.test.assertTrue(data.success, "we want a success"); browser.test.assertTrue(!!tab, "we have a tab"); browser.test.assertEq(data.expectedCookieStoreId, tab.cookieStoreId, "tab should have the correct cookieStoreId"); } - function runTest(data) { - // Tab Creation - browser.tabs.create({windowId: data.privateTab ? this.privateWindowId : this.defaultWindowId, - cookieStoreId: data.cookieStoreId}) + async function runTest(data) { + try { + // Tab Creation + let tab; + try { + tab = await browser.tabs.create({ + windowId: data.privateTab ? this.privateWindowId : this.defaultWindowId, + cookieStoreId: data.cookieStoreId, + }); + + browser.test.assertTrue(!data.failure, "we want a success"); + } catch (error) { + browser.test.assertTrue(!!data.failure, "we want a failure"); - // Tests for tab creation - .then((tab) => { - testTab(data, tab); - return tab; - }, (error) => { - browser.test.assertTrue(!!data.failure, "we want a failure"); - if (data.failure == "illegal") { - browser.test.assertTrue(/Illegal cookieStoreId/.test(error.message), - "runtime.lastError should report the expected error message"); - } else if (data.failure == "defaultToPrivate") { - browser.test.assertTrue("Illegal to set private cookieStorageId in a non private window", - error.message, - "runtime.lastError should report the expected error message"); - } else if (data.failure == "privateToDefault") { - browser.test.assertTrue("Illegal to set non private cookieStorageId in a private window", - error.message, - "runtime.lastError should report the expected error message"); - } else if (data.failure == "exist") { - browser.test.assertTrue(/No cookie store exists/.test(error.message), - "runtime.lastError should report the expected error message"); - } else { - browser.test.fail("The test is broken"); + if (data.failure == "illegal") { + browser.test.assertTrue(/Illegal cookieStoreId/.test(error.message), + "runtime.lastError should report the expected error message"); + } else if (data.failure == "defaultToPrivate") { + browser.test.assertTrue("Illegal to set private cookieStorageId in a non private window", + error.message, + "runtime.lastError should report the expected error message"); + } else if (data.failure == "privateToDefault") { + browser.test.assertTrue("Illegal to set non private cookieStorageId in a private window", + error.message, + "runtime.lastError should report the expected error message"); + } else if (data.failure == "exist") { + browser.test.assertTrue(/No cookie store exists/.test(error.message), + "runtime.lastError should report the expected error message"); + } else { + browser.test.fail("The test is broken"); + } + + browser.test.sendMessage("test-done"); + return; } - return null; - }) + // Tests for tab creation + testTab(data, tab); - // Tests for tab querying - .then((tab) => { - if (tab) { - return browser.tabs.query({windowId: data.privateTab ? this.privateWindowId : this.defaultWindowId, - cookieStoreId: data.cookieStoreId}) - .then((tabs) => { - browser.test.assertTrue(tabs.length >= 1, "Tab found!"); - testTab(data, tabs[0]); - return tab; - }); - } - }) + { + // Tests for tab querying + let [tab] = await browser.tabs.query({ + windowId: data.privateTab ? this.privateWindowId : this.defaultWindowId, + cookieStoreId: data.cookieStoreId, + }); - .then((tab) => { - if (tab) { - return browser.cookies.getAllCookieStores() - .then(stores => { - let store = stores.find(store => store.id === tab.cookieStoreId); - browser.test.assertTrue(!!store, "We have a store for this tab."); - return tab; - }); + browser.test.assertTrue(tab != undefined, "Tab found!"); + testTab(data, tab); } - }) + + let stores = await browser.cookies.getAllCookieStores(); - .then((tab) => { - if (tab) { - return browser.tabs.remove(tab.id); - } - }) + let store = stores.find(store => store.id === tab.cookieStoreId); + browser.test.assertTrue(!!store, "We have a store for this tab."); + + await browser.tabs.remove(tab.id); - .then(() => { browser.test.sendMessage("test-done"); - }, () => { - browser.test.fail("An exception has ben thrown"); - }); + } catch (e) { + browser.test.fail("An exception has been thrown"); + } } - function initialize() { - browser.windows.create({incognito: true}) - .then((win) => { - this.privateWindowId = win.id; - return browser.windows.create({incognito: false}); - }) - .then((win) => { - this.defaultWindowId = win.id; - }) - .then(() => { - browser.test.sendMessage("ready"); - }); + async function initialize() { + let win = await browser.windows.create({incognito: true}); + this.privateWindowId = win.id; + + win = await browser.windows.create({incognito: false}); + this.defaultWindowId = win.id; + + browser.test.sendMessage("ready"); } - function shutdown() { - browser.windows.remove(this.privateWindowId) - .then(() => { - browser.windows.remove(this.defaultWindowId); - }) - .then(() => { - browser.test.sendMessage("gone"); - }); + async function shutdown() { + await browser.windows.remove(this.privateWindowId); + await browser.windows.remove(this.defaultWindowId); + browser.test.sendMessage("gone"); } // Waiting for messages browser.test.onMessage.addListener((msg, data) => { if (msg == "be-ready") { initialize(); } else if (msg == "test") { runTest(data);
--- a/browser/components/extensions/test/browser/browser_ext_tabs_create.js +++ b/browser/components/extensions/test/browser/browser_ext_tabs_create.js @@ -85,17 +85,17 @@ add_task(function* () { result: {index: 1, active: false}, }, { create: {windowId: activeWindow}, result: {windowId: activeWindow}, }, ]; - function nextTest() { + async function nextTest() { if (!tests.length) { browser.test.notifyPass("tabs.create"); return; } let test = tests.shift(); let expected = Object.assign({}, DEFAULTS, test.result); @@ -114,43 +114,39 @@ add_task(function* () { let createdPromise = new Promise(resolve => { let onCreated = tab => { browser.test.assertTrue("id" in tab, `Expected tabs.onCreated callback to receive tab object`); resolve(); }; browser.tabs.onCreated.addListener(onCreated); }); - let tabId; - Promise.all([ + let [tab] = await Promise.all([ browser.tabs.create(test.create), createdPromise, - ]).then(([tab]) => { - tabId = tab.id; + ]); + let tabId = tab.id; - for (let key of Object.keys(expected)) { - if (key === "url") { - // FIXME: This doesn't get updated until later in the load cycle. - continue; - } - - browser.test.assertEq(expected[key], tab[key], `Expected value for tab.${key}`); + for (let key of Object.keys(expected)) { + if (key === "url") { + // FIXME: This doesn't get updated until later in the load cycle. + continue; } - return updatedPromise; - }).then(updated => { - browser.test.assertEq(tabId, updated.tabId, `Expected value for tab.id`); - browser.test.assertEq(expected.url, updated.url, `Expected value for tab.url`); + browser.test.assertEq(expected[key], tab[key], `Expected value for tab.${key}`); + } - return browser.tabs.remove(tabId); - }).then(() => { - return browser.tabs.update(activeTab, {active: true}); - }).then(() => { - nextTest(); - }); + let updated = await updatedPromise; + browser.test.assertEq(tabId, updated.tabId, `Expected value for tab.id`); + browser.test.assertEq(expected.url, updated.url, `Expected value for tab.url`); + + await browser.tabs.remove(tabId); + await browser.tabs.update(activeTab, {active: true}); + + nextTest(); } nextTest(); } browser.tabs.query({active: true, currentWindow: true}, tabs => { activeTab = tabs[0].id; activeWindow = tabs[0].windowId;
--- a/browser/components/extensions/test/browser/browser_ext_tabs_detectLanguage.js +++ b/browser/components/extensions/test/browser/browser_ext_tabs_detectLanguage.js @@ -3,48 +3,44 @@ "use strict"; add_task(function* testDetectLanguage() { let extension = ExtensionTestUtils.loadExtension({ manifest: { "permissions": ["tabs"], }, - background() { + background: async function() { const BASE_PATH = "browser/browser/components/extensions/test/browser"; function loadTab(url) { return browser.tabs.create({url}); } - loadTab(`http://example.co.jp/${BASE_PATH}/file_language_ja.html`).then(tab => { - return browser.tabs.detectLanguage(tab.id).then(lang => { - browser.test.assertEq("ja", lang, "Japanese document should be detected as Japanese"); - return browser.tabs.remove(tab.id); - }); - }).then(() => { - return loadTab(`http://example.co.jp/${BASE_PATH}/file_language_fr_en.html`); - }).then(tab => { - return browser.tabs.detectLanguage(tab.id).then(lang => { - browser.test.assertEq("fr", lang, "French/English document should be detected as primarily French"); - return browser.tabs.remove(tab.id); - }); - }).then(() => { - return loadTab(`http://example.co.jp/${BASE_PATH}/file_language_tlh.html`); - }).then(tab => { - return browser.tabs.detectLanguage(tab.id).then(lang => { - browser.test.assertEq("und", lang, "Klingon document should not be detected, should return 'und'"); - return browser.tabs.remove(tab.id); - }); - }).then(() => { + try { + let tab = await loadTab(`http://example.co.jp/${BASE_PATH}/file_language_ja.html`); + let lang = await browser.tabs.detectLanguage(tab.id); + browser.test.assertEq("ja", lang, "Japanese document should be detected as Japanese"); + await browser.tabs.remove(tab.id); + + tab = await loadTab(`http://example.co.jp/${BASE_PATH}/file_language_fr_en.html`); + lang = await browser.tabs.detectLanguage(tab.id); + browser.test.assertEq("fr", lang, "French/English document should be detected as primarily French"); + await browser.tabs.remove(tab.id); + + tab = await loadTab(`http://example.co.jp/${BASE_PATH}/file_language_tlh.html`); + lang = await browser.tabs.detectLanguage(tab.id); + browser.test.assertEq("und", lang, "Klingon document should not be detected, should return 'und'"); + await browser.tabs.remove(tab.id); + browser.test.notifyPass("detectLanguage"); - }).catch(e => { + } catch (e) { browser.test.fail(`Error: ${e} :: ${e.stack}`); browser.test.notifyFail("detectLanguage"); - }); + } }, }); yield extension.startup(); yield extension.awaitFinish("detectLanguage"); yield extension.unload();
--- a/browser/components/extensions/test/browser/browser_ext_tabs_duplicate.js +++ b/browser/components/extensions/test/browser/browser_ext_tabs_duplicate.js @@ -36,17 +36,17 @@ add_task(function* testDuplicateTab() { yield extension.unload(); while (gBrowser.tabs[0].linkedBrowser.currentURI.spec === "http://example.net/") { yield BrowserTestUtils.removeTab(gBrowser.tabs[0]); } }); add_task(function* testDuplicateTabLazily() { - function background() { + async function background() { let tabLoadComplete = new Promise(resolve => { browser.test.onMessage.addListener((message, tabId, result) => { if (message == "duplicate-tab-done") { resolve(tabId); } }); }); @@ -56,42 +56,38 @@ add_task(function* testDuplicateTabLazil if (tabId == tabId_ && changed.status == "complete") { browser.tabs.onUpdated.removeListener(listener); resolve(); } }); }); } - let startTabId; - let url = "http://example.com/browser/browser/components/extensions/test/browser/file_dummy.html"; - browser.tabs.create({url}, tab => { - startTabId = tab.id; + try { + let url = "http://example.com/browser/browser/components/extensions/test/browser/file_dummy.html"; + let tab = await browser.tabs.create({url}); + let startTabId = tab.id; - awaitLoad(startTabId).then(() => { - browser.test.sendMessage("duplicate-tab", startTabId); + await awaitLoad(startTabId); + browser.test.sendMessage("duplicate-tab", startTabId); - tabLoadComplete.then(unloadedTabId => { - browser.tabs.get(startTabId, loadedtab => { - browser.test.assertEq("Dummy test page", loadedtab.title, "Title should be returned for loaded pages"); - browser.test.assertEq("complete", loadedtab.status, "Tab status should be complete for loaded pages"); - }); + let unloadedTabId = await tabLoadComplete; + let loadedtab = await browser.tabs.get(startTabId); + browser.test.assertEq("Dummy test page", loadedtab.title, "Title should be returned for loaded pages"); + browser.test.assertEq("complete", loadedtab.status, "Tab status should be complete for loaded pages"); - browser.tabs.get(unloadedTabId, unloadedtab => { - browser.test.assertEq("Dummy test page", unloadedtab.title, "Title should be returned after page has been unloaded"); - }); + let unloadedtab = await browser.tabs.get(unloadedTabId); + browser.test.assertEq("Dummy test page", unloadedtab.title, "Title should be returned after page has been unloaded"); - browser.tabs.remove([tab.id, unloadedTabId]); - browser.test.notifyPass("tabs.hasCorrectTabTitle"); - }); - }).catch(e => { - browser.test.fail(`${e} :: ${e.stack}`); - browser.test.notifyFail("tabs.hasCorrectTabTitle"); - }); - }); + await browser.tabs.remove([tab.id, unloadedTabId]); + browser.test.notifyPass("tabs.hasCorrectTabTitle"); + } catch (e) { + browser.test.fail(`${e} :: ${e.stack}`); + browser.test.notifyFail("tabs.hasCorrectTabTitle"); + } } let extension = ExtensionTestUtils.loadExtension({ manifest: { "permissions": ["tabs"], }, background,
--- a/browser/components/extensions/test/browser/browser_ext_tabs_events.js +++ b/browser/components/extensions/test/browser/browser_ext_tabs_events.js @@ -1,14 +1,14 @@ /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ /* vim: set sts=2 sw=2 et tw=80: */ "use strict"; add_task(function* testTabEvents() { - function background() { + async function background() { let events = []; browser.tabs.onCreated.addListener(tab => { events.push({type: "onCreated", tab}); }); browser.tabs.onAttached.addListener((tabId, info) => { events.push(Object.assign({type: "onAttached", tabId}, info)); }); @@ -20,136 +20,131 @@ add_task(function* testTabEvents() { browser.tabs.onRemoved.addListener((tabId, info) => { events.push(Object.assign({type: "onRemoved", tabId}, info)); }); browser.tabs.onMoved.addListener((tabId, info) => { events.push(Object.assign({type: "onMoved", tabId}, info)); }); - function expectEvents(names) { + async function expectEvents(names) { browser.test.log(`Expecting events: ${names.join(", ")}`); - return new Promise(resolve => { - setTimeout(resolve, 0); - }).then(() => { - browser.test.assertEq(names.length, events.length, "Got expected number of events"); - for (let [i, name] of names.entries()) { - browser.test.assertEq(name, i in events && events[i].type, - `Got expected ${name} event`); - } - return events.splice(0); - }); + await new Promise(resolve => setTimeout(resolve, 0)); + + browser.test.assertEq(names.length, events.length, "Got expected number of events"); + for (let [i, name] of names.entries()) { + browser.test.assertEq(name, i in events && events[i].type, + `Got expected ${name} event`); + } + return events.splice(0); } - browser.test.log("Create second browser window"); - let windowId; - Promise.all([ - browser.windows.getCurrent(), - browser.windows.create({url: "about:blank"}), - ]).then(windows => { - windowId = windows[0].id; + try { + browser.test.log("Create second browser window"); + + let windows = await Promise.all([ + browser.windows.getCurrent(), + browser.windows.create({url: "about:blank"}), + ]); + + let windowId = windows[0].id; let otherWindowId = windows[1].id; - let initialTab; - return expectEvents(["onCreated"]).then(([created]) => { - initialTab = created.tab; + let [created] = await expectEvents(["onCreated"]); + let initialTab = created.tab; + - browser.test.log("Create tab in window 1"); - return browser.tabs.create({windowId, index: 0, url: "about:blank"}); - }).then(tab => { - let oldIndex = tab.index; - browser.test.assertEq(0, oldIndex, "Tab has the expected index"); + browser.test.log("Create tab in window 1"); + let tab = await browser.tabs.create({windowId, index: 0, url: "about:blank"}); + let oldIndex = tab.index; + browser.test.assertEq(0, oldIndex, "Tab has the expected index"); + + [created] = await expectEvents(["onCreated"]); + browser.test.assertEq(tab.id, created.tab.id, "Got expected tab ID"); + browser.test.assertEq(oldIndex, created.tab.index, "Got expected tab index"); + - return expectEvents(["onCreated"]).then(([created]) => { - browser.test.assertEq(tab.id, created.tab.id, "Got expected tab ID"); - browser.test.assertEq(oldIndex, created.tab.index, "Got expected tab index"); + browser.test.log("Move tab to window 2"); + await browser.tabs.move([tab.id], {windowId: otherWindowId, index: 0}); - browser.test.log("Move tab to window 2"); - return browser.tabs.move([tab.id], {windowId: otherWindowId, index: 0}); - }).then(() => { - return expectEvents(["onDetached", "onAttached"]); - }).then(([detached, attached]) => { - browser.test.assertEq(oldIndex, detached.oldPosition, "Expected old index"); - browser.test.assertEq(windowId, detached.oldWindowId, "Expected old window ID"); + let [detached, attached] = await expectEvents(["onDetached", "onAttached"]); + browser.test.assertEq(oldIndex, detached.oldPosition, "Expected old index"); + browser.test.assertEq(windowId, detached.oldWindowId, "Expected old window ID"); + + browser.test.assertEq(0, attached.newPosition, "Expected new index"); + browser.test.assertEq(otherWindowId, attached.newWindowId, "Expected new window ID"); + - browser.test.assertEq(0, attached.newPosition, "Expected new index"); - browser.test.assertEq(otherWindowId, attached.newWindowId, "Expected new window ID"); + browser.test.log("Move tab within the same window"); + let [moved] = await browser.tabs.move([tab.id], {index: 1}); + browser.test.assertEq(1, moved.index, "Expected new index"); + + [moved] = await expectEvents(["onMoved"]); + browser.test.assertEq(tab.id, moved.tabId, "Expected tab ID"); + browser.test.assertEq(0, moved.fromIndex, "Expected old index"); + browser.test.assertEq(1, moved.toIndex, "Expected new index"); + browser.test.assertEq(otherWindowId, moved.windowId, "Expected window ID"); - browser.test.log("Move tab within the same window"); - return browser.tabs.move([tab.id], {index: 1}); - }).then(([moved]) => { - browser.test.assertEq(1, moved.index, "Expected new index"); + + browser.test.log("Remove tab"); + await browser.tabs.remove(tab.id); + let [removed] = await expectEvents(["onRemoved"]); - return expectEvents(["onMoved"]); - }).then(([moved]) => { - browser.test.assertEq(tab.id, moved.tabId, "Expected tab ID"); - browser.test.assertEq(0, moved.fromIndex, "Expected old index"); - browser.test.assertEq(1, moved.toIndex, "Expected new index"); - browser.test.assertEq(otherWindowId, moved.windowId, "Expected window ID"); + browser.test.assertEq(tab.id, removed.tabId, "Expected removed tab ID"); + browser.test.assertEq(otherWindowId, removed.windowId, "Expected removed tab window ID"); + // Note: We want to test for the actual boolean value false here. + browser.test.assertEq(false, removed.isWindowClosing, "Expected isWindowClosing value"); + - browser.test.log("Remove tab"); - return browser.tabs.remove(tab.id); - }).then(() => { - return expectEvents(["onRemoved"]); - }).then(([removed]) => { - browser.test.assertEq(tab.id, removed.tabId, "Expected removed tab ID"); - browser.test.assertEq(otherWindowId, removed.windowId, "Expected removed tab window ID"); - // Note: We want to test for the actual boolean value false here. - browser.test.assertEq(false, removed.isWindowClosing, "Expected isWindowClosing value"); + browser.test.log("Close second window"); + await browser.windows.remove(otherWindowId); + [removed] = await expectEvents(["onRemoved"]); + browser.test.assertEq(initialTab.id, removed.tabId, "Expected removed tab ID"); + browser.test.assertEq(otherWindowId, removed.windowId, "Expected removed tab window ID"); + browser.test.assertEq(true, removed.isWindowClosing, "Expected isWindowClosing value"); + - browser.test.log("Close second window"); - return browser.windows.remove(otherWindowId); - }).then(() => { - return expectEvents(["onRemoved"]); - }).then(([removed]) => { - browser.test.assertEq(initialTab.id, removed.tabId, "Expected removed tab ID"); - browser.test.assertEq(otherWindowId, removed.windowId, "Expected removed tab window ID"); - browser.test.assertEq(true, removed.isWindowClosing, "Expected isWindowClosing value"); + browser.test.log("Create additional tab in window 1"); + tab = await browser.tabs.create({windowId, url: "about:blank"}); + await expectEvents(["onCreated"]); + + + browser.test.log("Create a new window, adopting the new tab"); + // We have to explicitly wait for the event here, since its timing is + // not predictable. + let promiseAttached = new Promise(resolve => { + browser.tabs.onAttached.addListener(function listener(tabId) { + browser.tabs.onAttached.removeListener(listener); + resolve(); }); }); - }).then(() => { - browser.test.log("Create additional tab in window 1"); - return browser.tabs.create({windowId, url: "about:blank"}); - }).then(tab => { - return expectEvents(["onCreated"]).then(() => { - browser.test.log("Create a new window, adopting the new tab"); - // We have to explicitly wait for the event here, since its timing is - // not predictable. - let promiseAttached = new Promise(resolve => { - browser.tabs.onAttached.addListener(function listener(tabId) { - browser.tabs.onAttached.removeListener(listener); - resolve(); - }); - }); + let [window] = await Promise.all([ + browser.windows.create({tabId: tab.id}), + promiseAttached, + ]); + + [detached, attached] = await expectEvents(["onDetached", "onAttached"]); + + browser.test.assertEq(tab.id, detached.tabId, "Expected onDetached tab ID"); - return Promise.all([ - browser.windows.create({tabId: tab.id}), - promiseAttached, - ]); - }).then(([window]) => { - return expectEvents(["onDetached", "onAttached"]).then(([detached, attached]) => { - browser.test.assertEq(tab.id, detached.tabId, "Expected onDetached tab ID"); + browser.test.assertEq(tab.id, attached.tabId, "Expected onAttached tab ID"); + browser.test.assertEq(0, attached.newPosition, "Expected onAttached new index"); + browser.test.assertEq(window.id, attached.newWindowId, + "Expected onAttached new window id"); - browser.test.assertEq(tab.id, attached.tabId, "Expected onAttached tab ID"); - browser.test.assertEq(0, attached.newPosition, "Expected onAttached new index"); - browser.test.assertEq(window.id, attached.newWindowId, - "Expected onAttached new window id"); + browser.test.log("Close the new window"); + await browser.windows.remove(window.id); - browser.test.log("Close the new window"); - return browser.windows.remove(window.id); - }); - }); - }).then(() => { browser.test.notifyPass("tabs-events"); - }).catch(e => { + } catch (e) { browser.test.fail(`${e} :: ${e.stack}`); browser.test.notifyFail("tabs-events"); - }); + } } let extension = ExtensionTestUtils.loadExtension({ manifest: { "permissions": ["tabs"], }, background, @@ -171,26 +166,24 @@ add_task(function* testTabEventsSize() { }); browser.tabs.onUpdated.addListener((tabId, changeInfo, tab) => { if (changeInfo.status == "complete") { sendSizeMessages(tab, "on-updated"); } }); - browser.test.onMessage.addListener((msg, arg) => { + browser.test.onMessage.addListener(async (msg, arg) => { if (msg === "create-tab") { - browser.tabs.create({url: "http://example.com/"}).then(tab => { - sendSizeMessages(tab, "create"); - browser.test.sendMessage("created-tab-id", tab.id); - }); + let tab = await browser.tabs.create({url: "http://example.com/"}); + sendSizeMessages(tab, "create"); + browser.test.sendMessage("created-tab-id", tab.id); } else if (msg === "update-tab") { - browser.tabs.update(arg, {url: "http://example.org/"}).then(tab => { - sendSizeMessages(tab, "update"); - }); + let tab = await browser.tabs.update(arg, {url: "http://example.org/"}); + sendSizeMessages(tab, "update"); } else if (msg === "remove-tab") { browser.tabs.remove(arg); browser.test.sendMessage("tab-removed"); } }); browser.test.sendMessage("ready"); } @@ -233,19 +226,17 @@ add_task(function* testTabEventsSize() { yield extension.awaitMessage("tab-removed"); } yield extension.unload(); SpecialPowers.clearUserPref(RESOLUTION_PREF); }); add_task(function* testTabRemovalEvent() { - function background() { - let removalTabId; - + async function background() { function awaitLoad(tabId) { return new Promise(resolve => { browser.tabs.onUpdated.addListener(function listener(tabId_, changed, tab) { if (tabId == tabId_ && changed.status == "complete") { browser.tabs.onUpdated.removeListener(listener); resolve(); } }); @@ -257,27 +248,26 @@ add_task(function* testTabRemovalEvent() chrome.tabs.query({}, tabs => { for (let tab of tabs) { browser.test.assertTrue(tab.id != tabId, "Tab query should not include removed tabId"); } browser.test.notifyPass("tabs-events"); }); }); - let url = "http://example.com/browser/browser/components/extensions/test/browser/context.html"; - browser.tabs.create({url: url}) - .then(tab => { - removalTabId = tab.id; - return awaitLoad(tab.id); - }).then(() => { - return browser.tabs.remove(removalTabId); - }).catch(e => { + try { + let url = "http://example.com/browser/browser/components/extensions/test/browser/context.html"; + let tab = await browser.tabs.create({url: url}); + await awaitLoad(tab.id); + + await browser.tabs.remove(tab.id); + } catch (e) { browser.test.fail(`${e} :: ${e.stack}`); browser.test.notifyFail("tabs-events"); - }); + } } let extension = ExtensionTestUtils.loadExtension({ manifest: { "permissions": ["tabs"], }, background,
--- a/browser/components/extensions/test/browser/browser_ext_tabs_executeScript.js +++ b/browser/components/extensions/test/browser/browser_ext_tabs_executeScript.js @@ -31,22 +31,23 @@ add_task(function* testExecuteScript() { let messageManagersSize = countMM(MessageChannel.messageManagers); let responseManagersSize = countMM(MessageChannel.responseManagers); const BASE = "http://mochi.test:8888/browser/browser/components/extensions/test/browser/"; const URL = BASE + "file_iframe_document.html"; let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, URL, true); - function background() { - browser.tabs.query({active: true, currentWindow: true}).then(tabs => { - return browser.webNavigation.getAllFrames({tabId: tabs[0].id}); - }).then(frames => { + async function background() { + try { + let [tab] = await browser.tabs.query({active: true, currentWindow: true}); + let frames = await browser.webNavigation.getAllFrames({tabId: tab.id}); + browser.test.log(`FRAMES: ${frames[1].frameId} ${JSON.stringify(frames)}\n`); - return Promise.all([ + await Promise.all([ browser.tabs.executeScript({ code: "42", }).then(result => { browser.test.assertEq(1, result.length, "Expected one callback result"); browser.test.assertEq(42, result[0], "Expected callback result"); }), browser.tabs.executeScript({ @@ -122,30 +123,30 @@ add_task(function* testExecuteScript() { let details = { frame_id: Number.MAX_SAFE_INTEGER, matchesHost: ["http://mochi.test/", "http://example.com/"], }; browser.test.assertEq(`No window matching ${JSON.stringify(details)}`, error.message, "Got expected error"); }), - browser.tabs.create({url: "http://example.net/", active: false}).then(tab => { - return browser.tabs.executeScript(tab.id, { + browser.tabs.create({url: "http://example.net/", active: false}).then(async tab => { + await browser.tabs.executeScript(tab.id, { code: "42", }).then(result => { browser.test.fail("Expected error when trying to execute on invalid domain"); }, error => { let details = { matchesHost: ["http://mochi.test/", "http://example.com/"], }; browser.test.assertEq(`No window matching ${JSON.stringify(details)}`, error.message, "Got expected error"); - }).then(() => { - return browser.tabs.remove(tab.id); }); + + await browser.tabs.remove(tab.id); }), browser.tabs.executeScript({ code: "Promise.resolve(42)", }).then(result => { browser.test.assertEq(42, result[0], "Got expected promise resolution value as result"); }), @@ -173,37 +174,37 @@ add_task(function* testExecuteScript() { browser.tabs.executeScript({ code: "location.href;", frameId: frames[1].frameId, }).then(result => { browser.test.assertEq(1, result.length, "Expected one result"); browser.test.assertEq("http://mochi.test:8888/", result[0], "Result for frameId[1] is correct"); }), - browser.tabs.create({url: "http://example.com/"}).then(tab => { - return browser.tabs.executeScript(tab.id, {code: "location.href"}).then(result => { - browser.test.assertEq("http://example.com/", result[0], "Script executed correctly in new tab"); + browser.tabs.create({url: "http://example.com/"}).then(async tab => { + let result = await browser.tabs.executeScript(tab.id, {code: "location.href"}); - return browser.tabs.remove(tab.id); - }); + browser.test.assertEq("http://example.com/", result[0], "Script executed correctly in new tab"); + + await browser.tabs.remove(tab.id); }), new Promise(resolve => { browser.runtime.onMessage.addListener(message => { browser.test.assertEq("script ran", message, "Expected runtime message"); resolve(); }); }), ]); - }).then(() => { + browser.test.notifyPass("executeScript"); - }).catch(e => { + } catch (e) { browser.test.fail(`Error: ${e} :: ${e.stack}`); browser.test.notifyFail("executeScript"); - }); + } } let extension = ExtensionTestUtils.loadExtension({ manifest: { "permissions": ["http://mochi.test/", "http://example.com/", "webNavigation"], }, background,
--- a/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_bad.js +++ b/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_bad.js @@ -1,16 +1,16 @@ "use strict"; // This is a pretty terrible hack, but it's the best we can do until we // support |executeScript| callbacks and |lastError|. function* testHasNoPermission(params) { let contentSetup = params.contentSetup || (() => Promise.resolve()); - function background(contentSetup) { + async function background(contentSetup) { browser.runtime.onMessage.addListener((msg, sender) => { browser.test.assertEq(msg, "second script ran", "second script ran"); browser.test.notifyPass("executeScript"); }); browser.test.onMessage.addListener(msg => { browser.test.assertEq(msg, "execute-script"); @@ -25,19 +25,19 @@ function* testHasNoPermission(params) { // it, but it's just about the best we can do until we // support callbacks for executeScript. browser.tabs.executeScript(tabs[1].id, { file: "second-script.js", }); }); }); - contentSetup().then(() => { - browser.test.sendMessage("ready"); - }); + await contentSetup(); + + browser.test.sendMessage("ready"); } let extension = ExtensionTestUtils.loadExtension({ manifest: params.manifest, background: `(${background})(${contentSetup})`, files: { @@ -127,83 +127,76 @@ add_task(function* testBadPermissions() }); info("Test active tab, page action, no click"); yield testHasNoPermission({ manifest: { "permissions": ["http://example.com/", "activeTab"], "page_action": {}, }, - contentSetup() { - return new Promise(resolve => { - browser.tabs.query({active: true, currentWindow: true}, tabs => { - browser.pageAction.show(tabs[0].id).then(() => { - resolve(); - }); - }); - }); + async contentSetup() { + let [tab] = await browser.tabs.query({active: true, currentWindow: true}); + await browser.pageAction.show(tab.id); }, }); yield BrowserTestUtils.removeTab(tab2); yield BrowserTestUtils.removeTab(tab1); }); add_task(function* testBadURL() { - function background() { - browser.tabs.query({currentWindow: true}, tabs => { - let promises = [ - new Promise(resolve => { - browser.tabs.executeScript({ - file: "http://example.com/script.js", - }, result => { - browser.test.assertEq(undefined, result, "Result value"); - - browser.test.assertTrue(browser.extension.lastError instanceof Error, - "runtime.lastError is Error"); - - browser.test.assertTrue(browser.runtime.lastError instanceof Error, - "runtime.lastError is Error"); - - browser.test.assertEq( - "Files to be injected must be within the extension", - browser.extension.lastError && browser.extension.lastError.message, - "extension.lastError value"); - - browser.test.assertEq( - "Files to be injected must be within the extension", - browser.runtime.lastError && browser.runtime.lastError.message, - "runtime.lastError value"); - - resolve(); - }); - }), - + async function background() { + let promises = [ + new Promise(resolve => { browser.tabs.executeScript({ file: "http://example.com/script.js", - }).catch(error => { - browser.test.assertTrue(error instanceof Error, "Error is Error"); + }, result => { + browser.test.assertEq(undefined, result, "Result value"); + + browser.test.assertTrue(browser.extension.lastError instanceof Error, + "runtime.lastError is Error"); - browser.test.assertEq(null, browser.extension.lastError, - "extension.lastError value"); + browser.test.assertTrue(browser.runtime.lastError instanceof Error, + "runtime.lastError is Error"); - browser.test.assertEq(null, browser.runtime.lastError, - "runtime.lastError value"); + browser.test.assertEq( + "Files to be injected must be within the extension", + browser.extension.lastError && browser.extension.lastError.message, + "extension.lastError value"); browser.test.assertEq( "Files to be injected must be within the extension", - error && error.message, - "error value"); - }), - ]; + browser.runtime.lastError && browser.runtime.lastError.message, + "runtime.lastError value"); + + resolve(); + }); + }), + + browser.tabs.executeScript({ + file: "http://example.com/script.js", + }).catch(error => { + browser.test.assertTrue(error instanceof Error, "Error is Error"); - Promise.all(promises).then(() => { - browser.test.notifyPass("executeScript-lastError"); - }); - }); + browser.test.assertEq(null, browser.extension.lastError, + "extension.lastError value"); + + browser.test.assertEq(null, browser.runtime.lastError, + "runtime.lastError value"); + + browser.test.assertEq( + "Files to be injected must be within the extension", + error && error.message, + "error value"); + }), + ]; + + await Promise.all(promises); + + browser.test.notifyPass("executeScript-lastError"); } let extension = ExtensionTestUtils.loadExtension({ manifest: { "permissions": ["<all_urls>"], }, background,
--- a/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_good.js +++ b/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_good.js @@ -2,33 +2,33 @@ /* vim: set sts=2 sw=2 et tw=80: */ "use strict"; requestLongerTimeout(2); function* testHasPermission(params) { let contentSetup = params.contentSetup || (() => Promise.resolve()); - function background(contentSetup) { + async function background(contentSetup) { browser.runtime.onMessage.addListener((msg, sender) => { browser.test.assertEq(msg, "script ran", "script ran"); browser.test.notifyPass("executeScript"); }); browser.test.onMessage.addListener(msg => { browser.test.assertEq(msg, "execute-script"); browser.tabs.executeScript({ file: "script.js", }); }); - contentSetup().then(() => { - browser.test.sendMessage("ready"); - }); + await contentSetup(); + + browser.test.sendMessage("ready"); } let extension = ExtensionTestUtils.loadExtension({ manifest: params.manifest, background: `(${background})(${contentSetup})`, files: { @@ -117,57 +117,46 @@ add_task(function* testGoodPermissions() }); info("Test activeTab permission with a page action click"); yield testHasPermission({ manifest: { "permissions": ["activeTab"], "page_action": {}, }, - contentSetup() { - return new Promise(resolve => { - browser.tabs.query({active: true, currentWindow: true}, tabs => { - browser.pageAction.show(tabs[0].id).then(() => { - resolve(); - }); - }); - }); + contentSetup: async () => { + let [tab] = await browser.tabs.query({active: true, currentWindow: true}); + await browser.pageAction.show(tab.id); }, setup: clickPageAction, tearDown: closePageAction, }); info("Test activeTab permission with a browser action w/popup click"); yield testHasPermission({ manifest: { "permissions": ["activeTab"], "browser_action": {"default_popup": "_blank.html"}, }, - setup: extension => { - return clickBrowserAction(extension).then(() => { - return awaitExtensionPanel(extension, window, "_blank.html"); - }); + setup: async extension => { + await clickBrowserAction(extension); + return awaitExtensionPanel(extension, window, "_blank.html"); }, tearDown: closeBrowserAction, }); info("Test activeTab permission with a page action w/popup click"); yield testHasPermission({ manifest: { "permissions": ["activeTab"], "page_action": {"default_popup": "_blank.html"}, }, - contentSetup() { - return new Promise(resolve => { - browser.tabs.query({active: true, currentWindow: true}, tabs => { - browser.pageAction.show(tabs[0].id).then(() => { - resolve(); - }); - }); - }); + contentSetup: async () => { + let [tab] = await browser.tabs.query({active: true, currentWindow: true}); + await browser.pageAction.show(tab.id); }, setup: clickPageAction, tearDown: closePageAction, }); info("Test activeTab permission with a context menu click"); yield testHasPermission({ manifest: {
--- a/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_runAt.js +++ b/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_runAt.js @@ -12,91 +12,86 @@ * * And since we can't actually rely on that timing, it retries any attempts that * fail to load as early as expected, but don't load at any illegal time. */ add_task(function* testExecuteScript() { let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank", true); - function background() { + async function background() { let tab; const BASE = "http://mochi.test:8888/browser/browser/components/extensions/test/browser/"; const URL = BASE + "file_iframe_document.sjs"; const MAX_TRIES = 10; - let tries = 0; + + try { + [tab] = await browser.tabs.query({active: true, currentWindow: true}); - function again() { - if (tries++ == MAX_TRIES) { - return Promise.reject(new Error("Max tries exceeded")); - } - - let url = `${URL}?r=${Math.random()}`; + let success = false; + for (let tries = 0; !success && tries < MAX_TRIES; tries++) { + let url = `${URL}?r=${Math.random()}`; - let loadingPromise = new Promise(resolve => { - browser.tabs.onUpdated.addListener(function listener(tabId, changed, tab_) { - if (tabId == tab.id && changed.status == "loading" && tab_.url == url) { - browser.tabs.onUpdated.removeListener(listener); - resolve(); - } + let loadingPromise = new Promise(resolve => { + browser.tabs.onUpdated.addListener(function listener(tabId, changed, tab_) { + if (tabId == tab.id && changed.status == "loading" && tab_.url == url) { + browser.tabs.onUpdated.removeListener(listener); + resolve(); + } + }); }); - }); - // TODO: Test allFrames and frameId. + // TODO: Test allFrames and frameId. - return browser.tabs.update({url}).then(() => { - return loadingPromise; - }).then(() => { - return Promise.all([ + await browser.tabs.update({url}); + await loadingPromise; + + let states = await Promise.all([ // Send the executeScript requests in the reverse order that we expect // them to execute in, to avoid them passing only because of timing // races. browser.tabs.executeScript({ code: "document.readyState", runAt: "document_idle", }), browser.tabs.executeScript({ code: "document.readyState", runAt: "document_end", }), browser.tabs.executeScript({ code: "document.readyState", runAt: "document_start", }), ].reverse()); - }).then(states => { + browser.test.log(`Got states: ${states}`); // Make sure that none of our scripts executed earlier than expected, // regardless of retries. browser.test.assertTrue(states[1] == "interactive" || states[1] == "complete", `document_end state is valid: ${states[1]}`); browser.test.assertTrue(states[2] == "complete", `document_idle state is valid: ${states[2]}`); // If we have the earliest valid states for each script, we're done. // Otherwise, try again. - if (states[0] != "loading" || states[1] != "interactive" || states[2] != "complete") { - return again(); - } - }); - } + success = (states[0] == "loading" && + states[1] == "interactive" && + states[2] == "complete"); + } - browser.tabs.query({active: true, currentWindow: true}).then(tabs => { - tab = tabs[0]; + browser.test.assertTrue(success, "Got the earliest expected states at least once"); - return again(); - }).then(() => { browser.test.notifyPass("executeScript-runAt"); - }).catch(e => { + } catch (e) { browser.test.fail(`Error: ${e} :: ${e.stack}`); browser.test.notifyFail("executeScript-runAt"); - }); + } } let extension = ExtensionTestUtils.loadExtension({ manifest: { "permissions": ["http://mochi.test/", "tabs"], }, background,
--- a/browser/components/extensions/test/browser/browser_ext_tabs_insertCSS.js +++ b/browser/components/extensions/test/browser/browser_ext_tabs_insertCSS.js @@ -5,18 +5,18 @@ add_task(function* testExecuteScript() { let {MessageChannel} = Cu.import("resource://gre/modules/MessageChannel.jsm", {}); let messageManagersSize = MessageChannel.messageManagers.size; let responseManagersSize = MessageChannel.responseManagers.size; let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://mochi.test:8888/", true); - function background() { - let promises = [ + async function background() { + let tasks = [ { background: "transparent", foreground: "rgb(0, 113, 4)", promise: () => { return browser.tabs.insertCSS({ file: "file2.css", }); }, @@ -32,41 +32,35 @@ add_task(function* testExecuteScript() { }, ]; function checkCSS() { let computedStyle = window.getComputedStyle(document.body); return [computedStyle.backgroundColor, computedStyle.color]; } - function next() { - if (!promises.length) { - return; - } + try { + for (let {promise, background, foreground} of tasks) { + let result = await promise(); - let {promise, background, foreground} = promises.shift(); - return promise().then(result => { browser.test.assertEq(undefined, result, "Expected callback result"); - return browser.tabs.executeScript({ + [result] = await browser.tabs.executeScript({ code: `(${checkCSS})()`, }); - }).then(([result]) => { + browser.test.assertEq(background, result[0], "Expected background color"); browser.test.assertEq(foreground, result[1], "Expected foreground color"); - return next(); - }); - } + } - next().then(() => { browser.test.notifyPass("insertCSS"); - }).catch(e => { + } catch (e) { browser.test.fail(`Error: ${e} :: ${e.stack}`); browser.test.notifyFailure("insertCSS"); - }); + } } let extension = ExtensionTestUtils.loadExtension({ manifest: { "permissions": ["http://mochi.test/"], }, background,
--- a/browser/components/extensions/test/browser/browser_ext_tabs_move.js +++ b/browser/components/extensions/test/browser/browser_ext_tabs_move.js @@ -8,116 +8,94 @@ add_task(function* () { gBrowser.selectedTab = tab1; let extension = ExtensionTestUtils.loadExtension({ manifest: { "permissions": ["tabs"], }, - background: function() { - browser.tabs.query({ - lastFocusedWindow: true, - }, function(tabs) { - let tab = tabs[0]; - browser.tabs.move(tab.id, {index: 0}); - browser.tabs.query( - {lastFocusedWindow: true}, - tabs => { - browser.test.assertEq(tabs[0].url, tab.url, "should be first tab"); - browser.test.notifyPass("tabs.move.single"); - }); - }); + background: async function() { + let [tab] = await browser.tabs.query({lastFocusedWindow: true}); + + browser.tabs.move(tab.id, {index: 0}); + let tabs = await browser.tabs.query({lastFocusedWindow: true}); + + browser.test.assertEq(tabs[0].url, tab.url, "should be first tab"); + browser.test.notifyPass("tabs.move.single"); }, }); yield extension.startup(); yield extension.awaitFinish("tabs.move.single"); yield extension.unload(); extension = ExtensionTestUtils.loadExtension({ manifest: { "permissions": ["tabs"], }, - background: function() { - browser.tabs.query( - {lastFocusedWindow: true}, - tabs => { - tabs.sort(function(a, b) { return a.url > b.url; }); - browser.tabs.move(tabs.map(tab => tab.id), {index: 0}); - browser.tabs.query( - {lastFocusedWindow: true}, - tabs => { - browser.test.assertEq(tabs[0].url, "about:blank", "should be first tab"); - browser.test.assertEq(tabs[1].url, "about:config", "should be second tab"); - browser.test.assertEq(tabs[2].url, "about:robots", "should be third tab"); - browser.test.notifyPass("tabs.move.multiple"); - }); - }); + background: async function() { + let tabs = await browser.tabs.query({lastFocusedWindow: true}); + + tabs.sort(function(a, b) { return a.url > b.url; }); + + browser.tabs.move(tabs.map(tab => tab.id), {index: 0}); + + tabs = await browser.tabs.query({lastFocusedWindow: true}); + + browser.test.assertEq(tabs[0].url, "about:blank", "should be first tab"); + browser.test.assertEq(tabs[1].url, "about:config", "should be second tab"); + browser.test.assertEq(tabs[2].url, "about:robots", "should be third tab"); + + browser.test.notifyPass("tabs.move.multiple"); }, }); yield extension.startup(); yield extension.awaitFinish("tabs.move.multiple"); yield extension.unload(); extension = ExtensionTestUtils.loadExtension({ manifest: { "permissions": ["tabs"], }, - background: function() { - browser.tabs.query( - {lastFocusedWindow: true}, - tabs => { - let tab = tabs[1]; - // Assuming that tab.id of 12345 does not exist. - browser.tabs.move([tab.id, 12345], {index: 0}) - .then( - tabs => { browser.test.fail("Promise should not resolve"); }, - e => { - browser.test.assertTrue(/Invalid tab/.test(e), - "Invalid tab should be in error"); - }) - .then( - browser.tabs.query({lastFocusedWindow: true}) - .then( - (tabs) => { - browser.test.assertEq(tabs[1].url, tab.url, "should be second tab"); - browser.test.notifyPass("tabs.move.invalid"); - } - ) - ); - }); + async background() { + let [, tab] = await browser.tabs.query({lastFocusedWindow: true}); + + // Assuming that tab.id of 12345 does not exist. + await browser.test.assertRejects( + browser.tabs.move([tab.id, 12345], {index: 0}), + /Invalid tab/, + "Should receive invalid tab error"); + + let tabs = await browser.tabs.query({lastFocusedWindow: true}); + browser.test.assertEq(tabs[1].url, tab.url, "should be second tab"); + browser.test.notifyPass("tabs.move.invalid"); }, }); yield extension.startup(); yield extension.awaitFinish("tabs.move.invalid"); yield extension.unload(); extension = ExtensionTestUtils.loadExtension({ manifest: { "permissions": ["tabs"], }, - background: function() { - browser.tabs.query( - {lastFocusedWindow: true}, - tabs => { - let tab = tabs[0]; - browser.tabs.move(tab.id, {index: -1}); - browser.tabs.query( - {lastFocusedWindow: true}, - tabs => { - browser.test.assertEq(tabs[2].url, tab.url, "should be last tab"); - browser.test.notifyPass("tabs.move.last"); - }); - }); + background: async function() { + let [tab] = await browser.tabs.query({lastFocusedWindow: true}); + browser.tabs.move(tab.id, {index: -1}); + + let tabs = await browser.tabs.query({lastFocusedWindow: true}); + + browser.test.assertEq(tabs[2].url, tab.url, "should be last tab"); + browser.test.notifyPass("tabs.move.last"); }, }); yield extension.startup(); yield extension.awaitFinish("tabs.move.last"); yield extension.unload(); yield BrowserTestUtils.removeTab(tab1);
--- a/browser/components/extensions/test/browser/browser_ext_tabs_move_window.js +++ b/browser/components/extensions/test/browser/browser_ext_tabs_move_window.js @@ -7,42 +7,33 @@ add_task(function* () { let window1 = yield BrowserTestUtils.openNewBrowserWindow(); yield BrowserTestUtils.openNewForegroundTab(window1.gBrowser, "http://example.com/"); let extension = ExtensionTestUtils.loadExtension({ manifest: { "permissions": ["tabs"], }, - background: function() { - browser.tabs.query({ - url: "<all_urls>", - }, function(tabs) { - let destination = tabs[0]; - let source = tabs[1]; // skip over about:blank in window1 - browser.tabs.move(source.id, {windowId: destination.windowId, index: 0}); + async background() { + let tabs = await browser.tabs.query({url: "<all_urls>"}); + let destination = tabs[0]; + let source = tabs[1]; // skip over about:blank in window1 - browser.tabs.query( - {url: "<all_urls>"}, - tabs => { - browser.test.assertEq(tabs[0].url, "http://example.com/"); - browser.test.assertEq(tabs[0].windowId, destination.windowId); - browser.test.notifyPass("tabs.move.window"); - }); + // Assuming that this windowId does not exist. + await browser.test.assertRejects( + browser.tabs.move(source.id, {windowId: 123144576, index: 0}), + /Invalid window/, + "Should receive invalid window error"); - // Assuming that this windowId does not exist. - browser.tabs.move(source.id, {windowId: 123144576, index: 0}) - .then( - tabs => { browser.test.fail("Promise should not resolve"); }, - e => { - browser.test.assertTrue(/Invalid window/.test(e), - "Invalid window should be in error"); - } - ); - }); + browser.tabs.move(source.id, {windowId: destination.windowId, index: 0}); + + tabs = await browser.tabs.query({url: "<all_urls>"}); + browser.test.assertEq(tabs[0].url, "http://example.com/"); + browser.test.assertEq(tabs[0].windowId, destination.windowId); + browser.test.notifyPass("tabs.move.window"); }, }); yield extension.startup(); yield extension.awaitFinish("tabs.move.window"); yield extension.unload(); for (let tab of window.gBrowser.tabs) { @@ -61,33 +52,33 @@ add_task(function* test_currentWindowAft browser.test.sendMessage("id", win.id); }); } }); browser.test.sendMessage("ready"); }, }; - function background() { + async function background() { let tabId; + const url = browser.extension.getURL("current.html"); - browser.tabs.create({url}).then(tab => { - tabId = tab.id; - }); - browser.test.onMessage.addListener(msg => { + + browser.test.onMessage.addListener(async msg => { if (msg === "move") { - browser.windows.create({tabId}).then(() => { - browser.test.sendMessage("moved"); - }); + await browser.windows.create({tabId}); + browser.test.sendMessage("moved"); } else if (msg === "close") { - browser.tabs.remove(tabId).then(() => { - browser.test.sendMessage("done"); - }); + await browser.tabs.remove(tabId); + browser.test.sendMessage("done"); } }); + + let tab = await browser.tabs.create({url}); + tabId = tab.id; } const extension = ExtensionTestUtils.loadExtension({files, background}); yield extension.startup(); yield extension.awaitMessage("ready"); extension.sendMessage("current");
--- a/browser/components/extensions/test/browser/browser_ext_tabs_onHighlighted.js +++ b/browser/components/extensions/test/browser/browser_ext_tabs_onHighlighted.js @@ -1,14 +1,14 @@ /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ /* vim: set sts=2 sw=2 et tw=80: */ "use strict"; add_task(function* testTabEvents() { - function background() { + async function background() { /** The list of active tab ID's */ let tabIds = []; /** * Stores the events that fire for each tab. * * events { * tabId1: [event1, event2, ...], @@ -34,89 +34,87 @@ add_task(function* testTabEvents() { }); /** * Asserts that the expected events are fired for the tab with id = tabId. * The events associated to the specified tab are removed after this check is made. * * @param {number} tabId * @param {Array<string>} expectedEvents - * @returns {Promise} */ - function expectEvents(tabId, expectedEvents) { + async function expectEvents(tabId, expectedEvents) { browser.test.log(`Expecting events: ${expectedEvents.join(", ")}`); - return new Promise(resolve => { - setTimeout(resolve, 0); - }).then(() => { - browser.test.assertEq(expectedEvents.length, events[tabId].length, - `Got expected number of events for ${tabId}`); - for (let [i, name] of expectedEvents.entries()) { - browser.test.assertEq(name, i in events[tabId] && events[tabId][i], - `Got expected ${name} event`); - } - delete events[tabId]; - }); + await new Promise(resolve => setTimeout(resolve, 0)); + + browser.test.assertEq(expectedEvents.length, events[tabId].length, + `Got expected number of events for ${tabId}`); + + for (let [i, name] of expectedEvents.entries()) { + browser.test.assertEq(name, i in events[tabId] && events[tabId][i], + `Got expected ${name} event`); + } + delete events[tabId]; } /** * Opens a new tab and asserts that the correct events are fired. * * @param {number} windowId - * @returns {Promise} */ - function openTab(windowId) { - return browser.tabs.create({windowId}).then(tab => { - tabIds.push(tab.id); - browser.test.log(`Opened tab ${tab.id}`); - return expectEvents(tab.id, [ - "onActivated", - "onHighlighted", - ]); - }); + async function openTab(windowId) { + let tab = await browser.tabs.create({windowId}); + + tabIds.push(tab.id); + browser.test.log(`Opened tab ${tab.id}`); + + await expectEvents(tab.id, [ + "onActivated", + "onHighlighted", + ]); } /** * Highlights an existing tab and asserts that the correct events are fired. * * @param {number} tabId - * @returns {Promise} */ - function highlightTab(tabId) { + async function highlightTab(tabId) { browser.test.log(`Highlighting tab ${tabId}`); - return browser.tabs.update(tabId, {active: true}).then(tab => { - browser.test.assertEq(tab.id, tabId, `Tab ${tab.id} highlighted`); - return expectEvents(tab.id, [ - "onActivated", - "onHighlighted", - ]); - }); + let tab = await browser.tabs.update(tabId, {active: true}); + + browser.test.assertEq(tab.id, tabId, `Tab ${tab.id} highlighted`); + + await expectEvents(tab.id, [ + "onActivated", + "onHighlighted", + ]); } /** * The main entry point to the tests. */ - browser.tabs.query({active: true, currentWindow: true}, tabs => { - let activeWindow = tabs[0].windowId; - Promise.all([ - openTab(activeWindow), - openTab(activeWindow), - openTab(activeWindow), - ]).then(() => { - return Promise.all([ - highlightTab(tabIds[0]), - highlightTab(tabIds[1]), - highlightTab(tabIds[2]), - ]); - }).then(() => { - return Promise.all(tabIds.map(id => browser.tabs.remove(id))); - }).then(() => { - browser.test.notifyPass("tabs.highlight"); - }); - }); + let tabs = await browser.tabs.query({active: true, currentWindow: true}); + + let activeWindow = tabs[0].windowId; + await Promise.all([ + openTab(activeWindow), + openTab(activeWindow), + openTab(activeWindow), + ]); + + await Promise.all([ + highlightTab(tabIds[0]), + highlightTab(tabIds[1]), + highlightTab(tabIds[2]), + ]); + + await Promise.all(tabIds.map(id => browser.tabs.remove(id))); + + browser.test.notifyPass("tabs.highlight"); } let extension = ExtensionTestUtils.loadExtension({ manifest: { "permissions": ["tabs"], }, background,
--- a/browser/components/extensions/test/browser/browser_ext_tabs_query.js +++ b/browser/components/extensions/test/browser/browser_ext_tabs_query.js @@ -134,21 +134,21 @@ add_task(function* () { // test width and height extension = ExtensionTestUtils.loadExtension({ manifest: { "permissions": ["tabs"], }, background: function() { - browser.test.onMessage.addListener((msg) => { - browser.tabs.query({active: true}).then(tabs => { - browser.test.assertEq(tabs.length, 1, "should have one tab"); - browser.test.sendMessage("dims", {width: tabs[0].width, height: tabs[0].height}); - }); + browser.test.onMessage.addListener(async msg => { + let tabs = await browser.tabs.query({active: true}); + + browser.test.assertEq(tabs.length, 1, "should have one tab"); + browser.test.sendMessage("dims", {width: tabs[0].width, height: tabs[0].height}); }); browser.test.sendMessage("ready"); }, }); const RESOLUTION_PREF = "layout.css.devPixelsPerPx"; registerCleanupFunction(() => { SpecialPowers.clearUserPref(RESOLUTION_PREF); @@ -177,47 +177,47 @@ add_task(function* () { }); add_task(function* testQueryPermissions() { let extension = ExtensionTestUtils.loadExtension({ manifest: { "permissions": [], }, - background: function(x) { - browser.tabs.query({currentWindow: true, active: true}).then((tabs) => { + async background() { + try { + let tabs = await browser.tabs.query({currentWindow: true, active: true}); browser.test.assertEq(tabs.length, 1, "Expect query to return tabs"); browser.test.notifyPass("queryPermissions"); - }).catch((e) => { + } catch (e) { browser.test.notifyFail("queryPermissions"); - }); + } }, }); yield extension.startup(); yield extension.awaitFinish("queryPermissions"); yield extension.unload(); }); add_task(function* testQueryWithURLPermissions() { let extension = ExtensionTestUtils.loadExtension({ manifest: { "permissions": [], }, - background: function(x) { - browser.tabs.query({"url": "http://www.bbc.com/"}).then(() => { - browser.test.notifyFail("queryWithURLPermissions"); - }).catch((e) => { - browser.test.assertEq('The "tabs" permission is required to use the query API with the "url" parameter', - e.message, "Expected permissions error message"); - browser.test.notifyPass("queryWithURLPermissions"); - }); + async background() { + await browser.test.assertRejects( + browser.tabs.query({"url": "http://www.bbc.com/"}), + 'The "tabs" permission is required to use the query API with the "url" parameter', + "Expected tabs.query with 'url' to fail with permissions error message"); + + browser.test.notifyPass("queryWithURLPermissions"); }, }); yield extension.startup(); yield extension.awaitFinish("queryWithURLPermissions"); yield extension.unload();
--- a/browser/components/extensions/test/browser/browser_ext_tabs_reload.js +++ b/browser/components/extensions/test/browser/browser_ext_tabs_reload.js @@ -14,41 +14,41 @@ add_task(function* () { }, "tab.html": `<head> <meta charset="utf-8"> <script src="tab.js"></script> </head>`, }, - background: function() { + async background() { let tabLoadedCount = 0; - browser.tabs.create({url: "tab.html", active: true}).then(tab => { - browser.runtime.onMessage.addListener(msg => { - if (msg == "tab-loaded") { - tabLoadedCount++; + let tab = await browser.tabs.create({url: "tab.html", active: true}); - if (tabLoadedCount == 1) { - // Reload the tab once passing no arguments. - return browser.tabs.reload(); - } + browser.runtime.onMessage.addListener(msg => { + if (msg == "tab-loaded") { + tabLoadedCount++; + + if (tabLoadedCount == 1) { + // Reload the tab once passing no arguments. + return browser.tabs.reload(); + } - if (tabLoadedCount == 2) { - // Reload the tab again with explicit arguments. - return browser.tabs.reload(tab.id, { - bypassCache: false, - }); - } + if (tabLoadedCount == 2) { + // Reload the tab again with explicit arguments. + return browser.tabs.reload(tab.id, { + bypassCache: false, + }); + } - if (tabLoadedCount == 3) { - browser.test.notifyPass("tabs.reload"); - } + if (tabLoadedCount == 3) { + browser.test.notifyPass("tabs.reload"); } - }); + } }); }, }); yield extension.startup(); yield extension.awaitFinish("tabs.reload"); yield extension.unload(); });
--- a/browser/components/extensions/test/browser/browser_ext_tabs_reload_bypass_cache.js +++ b/browser/components/extensions/test/browser/browser_ext_tabs_reload_bypass_cache.js @@ -3,57 +3,56 @@ "use strict"; add_task(function* () { let extension = ExtensionTestUtils.loadExtension({ manifest: { "permissions": ["tabs", "<all_urls>"], }, - background: function() { + async background() { const BASE = "http://mochi.test:8888/browser/browser/components/extensions/test/browser/"; const URL = BASE + "file_bypass_cache.sjs"; function awaitLoad(tabId) { return new Promise(resolve => { browser.tabs.onUpdated.addListener(function listener(tabId_, changed, tab) { if (tabId == tabId_ && changed.status == "complete" && tab.url == URL) { browser.tabs.onUpdated.removeListener(listener); resolve(); } }); }); } - let tabId; - browser.tabs.create({url: URL}).then((tab) => { - tabId = tab.id; - return awaitLoad(tabId); - }).then(() => { - return browser.tabs.reload(tabId, {bypassCache: false}); - }).then(() => { - return awaitLoad(tabId); - }).then(() => { - return browser.tabs.executeScript(tabId, {code: "document.body.textContent"}); - }).then(([textContent]) => { + try { + let tab = await browser.tabs.create({url: URL}); + await awaitLoad(tab.id); + + await browser.tabs.reload(tab.id, {bypassCache: false}); + await awaitLoad(tab.id); + + let [textContent] = await browser.tabs.executeScript(tab.id, {code: "document.body.textContent"}); browser.test.assertEq("", textContent, "`textContent` should be empty when bypassCache=false"); - return browser.tabs.reload(tabId, {bypassCache: true}); - }).then(() => { - return awaitLoad(tabId); - }).then(() => { - return browser.tabs.executeScript(tabId, {code: "document.body.textContent"}); - }).then(([textContent]) => { + + await browser.tabs.reload(tab.id, {bypassCache: true}); + await awaitLoad(tab.id); + + [textContent] = await browser.tabs.executeScript(tab.id, {code: "document.body.textContent"}); + let [pragma, cacheControl] = textContent.split(":"); browser.test.assertEq("no-cache", pragma, "`pragma` should be set to `no-cache` when bypassCache is true"); browser.test.assertEq("no-cache", cacheControl, "`cacheControl` should be set to `no-cache` when bypassCache is true"); - browser.tabs.remove(tabId); + + await browser.tabs.remove(tab.id); + browser.test.notifyPass("tabs.reload_bypass_cache"); - }).catch(error => { + } catch (error) { browser.test.fail(`${error} :: ${error.stack}`); browser.test.notifyFail("tabs.reload_bypass_cache"); - }); + } }, }); yield extension.startup(); yield extension.awaitFinish("tabs.reload_bypass_cache"); yield extension.unload(); });
--- a/browser/components/extensions/test/browser/browser_ext_tabs_removeCSS.js +++ b/browser/components/extensions/test/browser/browser_ext_tabs_removeCSS.js @@ -1,17 +1,17 @@ /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ /* vim: set sts=2 sw=2 et tw=80: */ "use strict"; add_task(function* testExecuteScript() { let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://mochi.test:8888/", true); - function background() { - let promises = [ + async function background() { + let tasks = [ // Insert CSS file. { background: "transparent", foreground: "rgb(0, 113, 4)", promise: () => { return browser.tabs.insertCSS({ file: "file2.css", }); @@ -49,41 +49,33 @@ add_task(function* testExecuteScript() { }, ]; function checkCSS() { let computedStyle = window.getComputedStyle(document.body); return [computedStyle.backgroundColor, computedStyle.color]; } - function next() { - if (!promises.length) { - return; - } - - let {promise, background, foreground} = promises.shift(); - return promise().then(result => { + try { + for (let {promise, background, foreground} of tasks) { + let result = await promise(); browser.test.assertEq(undefined, result, "Expected callback result"); - return browser.tabs.executeScript({ + [result] = await browser.tabs.executeScript({ code: `(${checkCSS})()`, }); - }).then(([result]) => { browser.test.assertEq(background, result[0], "Expected background color"); browser.test.assertEq(foreground, result[1], "Expected foreground color"); - return next(); - }); - } + } - next().then(() => { browser.test.notifyPass("removeCSS"); - }).catch(e => { + } catch (e) { browser.test.fail(`Error: ${e} :: ${e.stack}`); browser.test.notifyFailure("removeCSS"); - }); + } } let extension = ExtensionTestUtils.loadExtension({ manifest: { "permissions": ["http://mochi.test/"], }, background,
--- a/browser/components/extensions/test/browser/browser_ext_tabs_sendMessage.js +++ b/browser/components/extensions/test/browser/browser_ext_tabs_sendMessage.js @@ -9,17 +9,17 @@ add_task(function* tabsSendMessageReply( "content_scripts": [{ "matches": ["http://example.com/"], "js": ["content-script.js"], "run_at": "document_start", }], }, - background: function() { + background: async function() { let firstTab; let promiseResponse = new Promise(resolve => { browser.runtime.onMessage.addListener((msg, sender, respond) => { if (msg == "content-script-ready") { let tabId = sender.tab.id; Promise.all([ promiseResponse, @@ -61,50 +61,50 @@ add_task(function* tabsSendMessageReply( return Promise.resolve("expected-response"); } else if (msg[0] == "got-response") { resolve(msg[1]); } }); }); - browser.tabs.query({currentWindow: true, active: true}).then(tabs => { - firstTab = tabs[0].id; - browser.tabs.create({url: "http://example.com/"}); - }); + let tabs = await browser.tabs.query({currentWindow: true, active: true}); + firstTab = tabs[0].id; + browser.tabs.create({url: "http://example.com/"}); }, files: { - "content-script.js": function() { + "content-script.js": async function() { browser.runtime.onMessage.addListener((msg, sender, respond) => { if (msg == "respond-now") { respond(msg); } else if (msg == "respond-soon") { setTimeout(() => { respond(msg); }, 0); return true; } else if (msg == "respond-promise") { return Promise.resolve(msg); } else if (msg == "respond-never") { return; } else if (msg == "respond-error") { return Promise.reject(new Error(msg)); } else if (msg == "throw-error") { throw new Error(msg); } }); + browser.runtime.onMessage.addListener((msg, sender, respond) => { if (msg == "respond-now") { respond("hello"); } else if (msg == "respond-now-2") { respond(msg); } }); - browser.runtime.sendMessage("content-script-ready").then(response => { - browser.runtime.sendMessage(["got-response", response]); - }); + + let response = await browser.runtime.sendMessage("content-script-ready"); + browser.runtime.sendMessage(["got-response", response]); }, }, }); yield extension.startup(); yield extension.awaitFinish("sendMessage"); @@ -119,62 +119,60 @@ add_task(function* tabsSendHidden() { "content_scripts": [{ "matches": ["http://example.com/content*"], "js": ["content-script.js"], "run_at": "document_start", }], }, - background: function() { + background: async function() { let resolveContent; browser.runtime.onMessage.addListener((msg, sender) => { if (msg[0] == "content-ready") { resolveContent(msg[1]); } }); let awaitContent = url => { return new Promise(resolve => { resolveContent = resolve; }).then(result => { browser.test.assertEq(url, result, "Expected content script URL"); }); }; - const URL1 = "http://example.com/content1.html"; - const URL2 = "http://example.com/content2.html"; - browser.tabs.create({url: URL1}).then(tab => { - return awaitContent(URL1).then(() => { - return browser.tabs.sendMessage(tab.id, URL1); - }).then(url => { - browser.test.assertEq(URL1, url, "Should get response from expected content window"); + try { + const URL1 = "http://example.com/content1.html"; + const URL2 = "http://example.com/content2.html"; + + let tab = await browser.tabs.create({url: URL1}); + await awaitContent(URL1); + + let url = await browser.tabs.sendMessage(tab.id, URL1); + browser.test.assertEq(URL1, url, "Should get response from expected content window"); + + await browser.tabs.update(tab.id, {url: URL2}); + await awaitContent(URL2); - return browser.tabs.update(tab.id, {url: URL2}); - }).then(() => { - return awaitContent(URL2); - }).then(() => { - return browser.tabs.sendMessage(tab.id, URL2); - }).then(url => { - browser.test.assertEq(URL2, url, "Should get response from expected content window"); + url = await browser.tabs.sendMessage(tab.id, URL2); + browser.test.assertEq(URL2, url, "Should get response from expected content window"); - // Repeat once just to be sure the first message was processed by all - // listeners before we exit the test. - return browser.tabs.sendMessage(tab.id, URL2); - }).then(url => { - browser.test.assertEq(URL2, url, "Should get response from expected content window"); + // Repeat once just to be sure the first message was processed by all + // listeners before we exit the test. + url = await browser.tabs.sendMessage(tab.id, URL2); + browser.test.assertEq(URL2, url, "Should get response from expected content window"); - return browser.tabs.remove(tab.id); - }); - }).then(() => { + await browser.tabs.remove(tab.id); + browser.test.notifyPass("contentscript-bfcache-window"); - }).catch(error => { + } catch (error) { browser.test.fail(`Error: ${error} :: ${error.stack}`); browser.test.notifyFail("contentscript-bfcache-window"); - }); + } }, files: { "content-script.js": function() { // Store this in a local variable to make sure we don't touch any // properties of the possibly-hidden content window. let href = window.location.href; @@ -198,32 +196,30 @@ add_task(function* tabsSendHidden() { add_task(function* tabsSendMessageNoExceptionOnNonExistentTab() { let extension = ExtensionTestUtils.loadExtension({ manifest: { "permissions": ["tabs"], }, - background: function() { + async background() { let url = "http://example.com/mochitest/browser/browser/components/extensions/test/browser/file_dummy.html"; - browser.tabs.create({url}, tab => { - let exception; - try { - browser.tabs.sendMessage(tab.id, "message"); - browser.tabs.sendMessage(tab.id + 100, "message"); - } catch (e) { - exception = e; - } + let tab = await browser.tabs.create({url}); - browser.test.assertEq(undefined, exception, "no exception should be raised on tabs.sendMessage to nonexistent tabs"); - browser.tabs.remove(tab.id, function() { - browser.test.notifyPass("tabs.sendMessage"); - }); - }); + try { + browser.tabs.sendMessage(tab.id, "message"); + browser.tabs.sendMessage(tab.id + 100, "message"); + } catch (e) { + browser.test.fail("no exception should be raised on tabs.sendMessage to nonexistent tabs"); + } + + await browser.tabs.remove(tab.id); + + browser.test.notifyPass("tabs.sendMessage"); }, }); yield Promise.all([ extension.startup(), extension.awaitFinish("tabs.sendMessage"), ]);
--- a/browser/components/extensions/test/browser/browser_ext_tabs_update_url.js +++ b/browser/components/extensions/test/browser/browser_ext_tabs_update_url.js @@ -19,41 +19,31 @@ function* testTabsUpdateURL(existentTabU <h1>tab page</h1> </body> </html> `.trim(), }, background: function() { browser.test.sendMessage("ready", browser.runtime.getURL("tab.html")); - browser.test.onMessage.addListener((msg, tabsUpdateURL, isErrorExpected) => { - let onTabsUpdated = (tab) => { - if (isErrorExpected) { - browser.test.fail(`tabs.update with URL ${tabsUpdateURL} should be rejected`); - } else { - browser.test.assertTrue(tab, "on success the tab should be defined"); - } - }; + browser.test.onMessage.addListener(async (msg, tabsUpdateURL, isErrorExpected) => { + let tabs = await browser.tabs.query({lastFocusedWindow: true}); + + try { + let tab = await browser.tabs.update(tabs[1].id, {url: tabsUpdateURL}); - let onTabsUpdateError = (error) => { - if (!isErrorExpected) { - browser.test.fails(`tabs.update with URL ${tabsUpdateURL} should not be rejected`); - } else { - browser.test.assertTrue(/^Illegal URL/.test(error.message), - "tabs.update should be rejected with the expected error message"); - } - }; + browser.test.assertFalse(isErrorExpected, `tabs.update with URL ${tabsUpdateURL} should be rejected`); + browser.test.assertTrue(tab, "on success the tab should be defined"); + } catch (error) { + browser.test.assertTrue(isErrorExpected, `tabs.update with URL ${tabsUpdateURL} should not be rejected`); + browser.test.assertTrue(/^Illegal URL/.test(error.message), + "tabs.update should be rejected with the expected error message"); + } - let onTabsUpdateDone = () => browser.test.sendMessage("done"); - - browser.tabs.query({lastFocusedWindow: true}, (tabs) => { - browser.tabs.update(tabs[1].id, {url: tabsUpdateURL}) - .then(onTabsUpdated, onTabsUpdateError) - .then(onTabsUpdateDone); - }); + browser.test.sendMessage("done"); }); }, }); yield extension.startup(); let mozExtTabURL = yield extension.awaitMessage("ready");
--- a/browser/components/extensions/test/browser/browser_ext_tabs_zoom.js +++ b/browser/components/extensions/test/browser/browser_ext_tabs_zoom.js @@ -5,17 +5,17 @@ const SITE_SPECIFIC_PREF = "browser.zoom.siteSpecific"; add_task(function* () { let tab1 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/"); let tab2 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.net/"); gBrowser.selectedTab = tab1; - function background() { + async function background() { function promiseUpdated(tabId, attr) { return new Promise(resolve => { let onUpdated = (tabId_, changeInfo, tab) => { if (tabId == tabId_ && attr in changeInfo) { browser.tabs.onUpdated.removeListener(onUpdated); resolve({changeInfo, tab}); } @@ -45,156 +45,144 @@ add_task(function* () { let eventPromises = []; browser.tabs.onZoomChange.addListener(info => { zoomEvents.push(info); if (eventPromises.length) { eventPromises.shift().resolve(); } }); - let awaitZoom = (tabId, newValue) => { + let awaitZoom = async (tabId, newValue) => { let listener; - return new Promise(resolve => { + await new Promise(async resolve => { listener = info => { if (info.tabId == tabId && info.newZoomFactor == newValue) { resolve(); } }; browser.tabs.onZoomChange.addListener(listener); - browser.tabs.getZoom(tabId).then(zoomFactor => { - if (zoomFactor == newValue) { - resolve(); - } - }); - }).then(() => { - browser.tabs.onZoomChange.removeListener(listener); + let zoomFactor = await browser.tabs.getZoom(tabId); + if (zoomFactor == newValue) { + resolve(); + } }); + + browser.tabs.onZoomChange.removeListener(listener); }; - let checkZoom = (tabId, newValue, oldValue = null) => { + let checkZoom = async (tabId, newValue, oldValue = null) => { let awaitEvent; if (oldValue != null && !zoomEvents.length) { awaitEvent = new Promise(resolve => { eventPromises.push({resolve}); }); } - return Promise.all([ + let [apiZoom, realZoom] = await Promise.all([ browser.tabs.getZoom(tabId), msg("get-zoom", tabId), awaitEvent, - ]).then(([apiZoom, realZoom]) => { - browser.test.assertEq(newValue, apiZoom, `Got expected zoom value from API`); - browser.test.assertEq(newValue, realZoom, `Got expected zoom value from parent`); + ]); + + browser.test.assertEq(newValue, apiZoom, `Got expected zoom value from API`); + browser.test.assertEq(newValue, realZoom, `Got expected zoom value from parent`); - if (oldValue != null) { - let event = zoomEvents.shift(); - browser.test.assertEq(tabId, event.tabId, `Got expected zoom event tab ID`); - browser.test.assertEq(newValue, event.newZoomFactor, `Got expected zoom event zoom factor`); - browser.test.assertEq(oldValue, event.oldZoomFactor, `Got expected zoom event old zoom factor`); + if (oldValue != null) { + let event = zoomEvents.shift(); + browser.test.assertEq(tabId, event.tabId, `Got expected zoom event tab ID`); + browser.test.assertEq(newValue, event.newZoomFactor, `Got expected zoom event zoom factor`); + browser.test.assertEq(oldValue, event.oldZoomFactor, `Got expected zoom event old zoom factor`); - browser.test.assertEq(3, Object.keys(event.zoomSettings).length, `Zoom settings should have 3 keys`); - browser.test.assertEq("automatic", event.zoomSettings.mode, `Mode should be "automatic"`); - browser.test.assertEq("per-origin", event.zoomSettings.scope, `Scope should be "per-origin"`); - browser.test.assertEq(1, event.zoomSettings.defaultZoomFactor, `Default zoom should be 1`); - } - }); + browser.test.assertEq(3, Object.keys(event.zoomSettings).length, `Zoom settings should have 3 keys`); + browser.test.assertEq("automatic", event.zoomSettings.mode, `Mode should be "automatic"`); + browser.test.assertEq("per-origin", event.zoomSettings.scope, `Scope should be "per-origin"`); + browser.test.assertEq(1, event.zoomSettings.defaultZoomFactor, `Default zoom should be 1`); + } }; - let tabIds; - - browser.tabs.query({lastFocusedWindow: true}).then(tabs => { + try { + let tabs = await browser.tabs.query({lastFocusedWindow: true}); browser.test.assertEq(tabs.length, 3, "We have three tabs"); - tabIds = [tabs[1].id, tabs[2].id]; + let tabIds = [tabs[1].id, tabs[2].id]; + await checkZoom(tabIds[0], 1); - return checkZoom(tabIds[0], 1); - }).then(() => { - return browser.tabs.setZoom(tabIds[0], 2); - }).then(() => { - return checkZoom(tabIds[0], 2, 1); - }).then(() => { - return browser.tabs.getZoomSettings(tabIds[0]); - }).then(zoomSettings => { + await browser.tabs.setZoom(tabIds[0], 2); + await checkZoom(tabIds[0], 2, 1); + + let zoomSettings = await browser.tabs.getZoomSettings(tabIds[0]); browser.test.assertEq(3, Object.keys(zoomSettings).length, `Zoom settings should have 3 keys`); browser.test.assertEq("automatic", zoomSettings.mode, `Mode should be "automatic"`); browser.test.assertEq("per-origin", zoomSettings.scope, `Scope should be "per-origin"`); browser.test.assertEq(1, zoomSettings.defaultZoomFactor, `Default zoom should be 1`); + browser.test.log(`Switch to tab 2`); - return browser.tabs.update(tabIds[1], {active: true}); - }).then(() => { - return checkZoom(tabIds[1], 1); - }).then(() => { + await browser.tabs.update(tabIds[1], {active: true}); + await checkZoom(tabIds[1], 1); + + browser.test.log(`Navigate tab 2 to origin of tab 1`); browser.tabs.update(tabIds[1], {url: "http://example.com"}); + await promiseUpdated(tabIds[1], "url"); + await checkZoom(tabIds[1], 2, 1); - return promiseUpdated(tabIds[1], "url"); - }).then(() => { - return checkZoom(tabIds[1], 2, 1); - }).then(() => { + browser.test.log(`Update zoom in tab 2, expect changes in both tabs`); - return browser.tabs.setZoom(tabIds[1], 1.5); - }).then(() => { - return checkZoom(tabIds[1], 1.5, 2); - }).then(() => { + await browser.tabs.setZoom(tabIds[1], 1.5); + await checkZoom(tabIds[1], 1.5, 2); + + browser.test.log(`Switch to tab 1, expect asynchronous zoom change just after the switch`); - return Promise.all([ + await Promise.all([ awaitZoom(tabIds[0], 1.5), browser.tabs.update(tabIds[0], {active: true}), ]); - }).then(() => { - return checkZoom(tabIds[0], 1.5, 2); - }).then(() => { + await checkZoom(tabIds[0], 1.5, 2); + + browser.test.log("Set zoom to 0, expect it set to 1"); - return browser.tabs.setZoom(tabIds[0], 0); - }).then(() => { - return checkZoom(tabIds[0], 1, 1.5); - }).then(() => { + await browser.tabs.setZoom(tabIds[0], 0); + await checkZoom(tabIds[0], 1, 1.5); + + browser.test.log("Change zoom externally, expect changes reflected"); - return msg("enlarge"); - }).then(() => { - return checkZoom(tabIds[0], 1.1, 1); - }).then(() => { - return Promise.all([ + await msg("enlarge"); + await checkZoom(tabIds[0], 1.1, 1); + + await Promise.all([ browser.tabs.setZoom(tabIds[0], 0), browser.tabs.setZoom(tabIds[1], 0), ]); - }).then(() => { - return Promise.all([ + await Promise.all([ checkZoom(tabIds[0], 1, 1.1), checkZoom(tabIds[1], 1, 1.5), ]); - }).then(() => { + + browser.test.log("Check that invalid zoom values throw an error"); - return browser.tabs.setZoom(tabIds[0], 42).then( - () => { - browser.test.fail("Expected an error"); - }, - error => { - browser.test.assertTrue(error.message.includes("Zoom value 42 out of range"), - "Got expected error"); - }); - }).then(() => { + await browser.test.assertRejects( + browser.tabs.setZoom(tabIds[0], 42), + /Zoom value 42 out of range/, + "Expected an out of range error"); + browser.test.log("Disable site-specific zoom, expect correct scope"); - return msg("site-specific", false); - }).then(() => { - return browser.tabs.getZoomSettings(tabIds[0]); - }).then(zoomSettings => { + await msg("site-specific", false); + zoomSettings = await browser.tabs.getZoomSettings(tabIds[0]); + browser.test.assertEq("per-tab", zoomSettings.scope, `Scope should be "per-tab"`); - }).then(() => { - return msg("site-specific", null); - }).then(() => { + await msg("site-specific", null); + browser.test.notifyPass("tab-zoom"); - }).catch(e => { + } catch (e) { browser.test.fail(`Error: ${e} :: ${e.stack}`); browser.test.notifyFail("tab-zoom"); - }); + } } let extension = ExtensionTestUtils.loadExtension({ manifest: { "permissions": ["tabs"], }, background,
--- a/browser/components/extensions/test/browser/browser_ext_webNavigation_frameId0.js +++ b/browser/components/extensions/test/browser/browser_ext_webNavigation_frameId0.js @@ -15,25 +15,23 @@ add_task(function* webNavigation_getFram // main process: // Cu.import("resource://gre/modules/ExtensionManagement.jsm", {}); // // Or simply run the test again. const BASE = "http://mochi.test:8888/browser/browser/components/extensions/test/browser/"; const DUMMY_URL = BASE + "file_dummy.html"; let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, DUMMY_URL, true); - function background(DUMMY_URL) { - browser.tabs.query({active: true, currentWindow: true}).then(tabs => { - return browser.webNavigation.getAllFrames({tabId: tabs[0].id}); - }).then(frames => { - browser.test.assertEq(1, frames.length, "The dummy page has one frame"); - browser.test.assertEq(0, frames[0].frameId, "Main frame's ID must be 0"); - browser.test.assertEq(DUMMY_URL, frames[0].url, "Main frame URL must match"); - browser.test.notifyPass("frameId checked"); - }); + async function background(DUMMY_URL) { + let tabs = await browser.tabs.query({active: true, currentWindow: true}); + let frames = await browser.webNavigation.getAllFrames({tabId: tabs[0].id}); + browser.test.assertEq(1, frames.length, "The dummy page has one frame"); + browser.test.assertEq(0, frames[0].frameId, "Main frame's ID must be 0"); + browser.test.assertEq(DUMMY_URL, frames[0].url, "Main frame URL must match"); + browser.test.notifyPass("frameId checked"); } let extension = ExtensionTestUtils.loadExtension({ manifest: { "permissions": ["webNavigation"], }, background: `(${background})(${JSON.stringify(DUMMY_URL)});`,
--- a/browser/components/extensions/test/browser/browser_ext_webNavigation_getFrames.js +++ b/browser/components/extensions/test/browser/browser_ext_webNavigation_getFrames.js @@ -1,38 +1,31 @@ /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ /* vim: set sts=2 sw=2 et tw=80: */ "use strict"; add_task(function* testWebNavigationGetNonExistentTab() { let extension = ExtensionTestUtils.loadExtension({ - background: "(" + function() { - let results = [ - // There is no "tabId = 0" because the id assigned by TabManager (defined in ext-utils.js) - // starts from 1. - browser.webNavigation.getAllFrames({tabId: 0}).then(() => { - browser.test.fail("getAllFrames Promise should be rejected on error"); - }, (error) => { - browser.test.assertEq("Invalid tab ID: 0", error.message, - "getAllFrames rejected Promise should pass the expected error"); - }), - // There is no "tabId = 0" because the id assigned by TabManager (defined in ext-utils.js) - // starts from 1, processId is currently marked as optional and it is ignored. - browser.webNavigation.getFrame({tabId: 0, frameId: 15, processId: 20}).then(() => { - browser.test.fail("getFrame Promise should be rejected on error"); - }, (error) => { - browser.test.assertEq("Invalid tab ID: 0", error.message, - "getFrame rejected Promise should pass the expected error"); - }), - ]; + background: async function() { + // There is no "tabId = 0" because the id assigned by TabManager (defined in ext-utils.js) + // starts from 1. + await browser.test.assertRejects( + browser.webNavigation.getAllFrames({tabId: 0}), + "Invalid tab ID: 0", + "getAllFrames rejected Promise should pass the expected error"); - Promise.all(results).then(() => { - browser.test.sendMessage("getNonExistentTab.done"); - }); - } + ")();", + // There is no "tabId = 0" because the id assigned by TabManager (defined in ext-utils.js) + // starts from 1, processId is currently marked as optional and it is ignored. + await browser.test.assertRejects( + browser.webNavigation.getFrame({tabId: 0, frameId: 15, processId: 20}), + "Invalid tab ID: 0", + "getFrame rejected Promise should pass the expected error"); + + browser.test.sendMessage("getNonExistentTab.done"); + }, manifest: { permissions: ["webNavigation"], }, }); info("load complete"); yield extension.startup(); info("startup complete"); @@ -40,68 +33,61 @@ add_task(function* testWebNavigationGetN yield extension.awaitMessage("getNonExistentTab.done"); yield extension.unload(); info("extension unloaded"); }); add_task(function* testWebNavigationFrames() { let extension = ExtensionTestUtils.loadExtension({ - background: "(" + function() { + background: async function() { let tabId; let collectedDetails = []; - browser.webNavigation.onCompleted.addListener((details) => { + browser.webNavigation.onCompleted.addListener(async details => { collectedDetails.push(details); if (details.frameId !== 0) { // wait for the top level iframe to be complete return; } - browser.webNavigation.getAllFrames({tabId}).then((getAllFramesDetails) => { - let getFramePromises = getAllFramesDetails.map((frameDetail) => { - let {frameId} = frameDetail; - // processId is currently marked as optional and it is ignored. - return browser.webNavigation.getFrame({tabId, frameId, processId: 0}); - }); + let getAllFramesDetails = await browser.webNavigation.getAllFrames({tabId}); - Promise.all(getFramePromises).then((getFrameResults) => { - browser.test.sendMessage("webNavigationFrames.done", { - collectedDetails, getAllFramesDetails, getFrameResults, - }); - }, () => { - browser.test.assertTrue(false, "None of the getFrame promises should have been rejected"); - }); + let getFramePromises = getAllFramesDetails.map(({frameId}) => { + // processId is currently marked as optional and it is ignored. + return browser.webNavigation.getFrame({tabId, frameId, processId: 0}); + }); + + let getFrameResults = await Promise.all(getFramePromises); + browser.test.sendMessage("webNavigationFrames.done", { + collectedDetails, getAllFramesDetails, getFrameResults, + }); - // Pick a random frameId. - let nonExistentFrameId = Math.floor(Math.random() * 10000); + // Pick a random frameId. + let nonExistentFrameId = Math.floor(Math.random() * 10000); - // Increment the picked random nonExistentFrameId until it doesn't exists. - while (getAllFramesDetails.filter((details) => details.frameId == nonExistentFrameId).length > 0) { - nonExistentFrameId += 1; - } + // Increment the picked random nonExistentFrameId until it doesn't exists. + while (getAllFramesDetails.filter((details) => details.frameId == nonExistentFrameId).length > 0) { + nonExistentFrameId += 1; + } - // Check that getFrame Promise is rejected with the expected error message on nonexistent frameId. - browser.webNavigation.getFrame({tabId, frameId: nonExistentFrameId, processId: 20}).then(() => { - browser.test.fail("getFrame promise should be rejected for an unexistent frameId"); - }, (error) => { - browser.test.assertEq(`No frame found with frameId: ${nonExistentFrameId}`, error.message, - "getFrame promise should be rejected with the expected error message on unexistent frameId"); - }).then(() => { - browser.tabs.remove(tabId); - browser.test.sendMessage("webNavigationFrames.done"); - }); - }); + // Check that getFrame Promise is rejected with the expected error message on nonexistent frameId. + await browser.test.assertRejects( + browser.webNavigation.getFrame({tabId, frameId: nonExistentFrameId, processId: 20}), + `No frame found with frameId: ${nonExistentFrameId}`, + "getFrame promise should be rejected with the expected error message on unexistent frameId"); + + await browser.tabs.remove(tabId); + browser.test.sendMessage("webNavigationFrames.done"); }); - browser.tabs.create({url: "tab.html"}, (tab) => { - tabId = tab.id; - }); - } + ")();", + let tab = await browser.tabs.create({url: "tab.html"}); + tabId = tab.id; + }, manifest: { permissions: ["webNavigation", "tabs"], }, files: { "tab.html": ` <!DOCTYPE html> <html> <head>
--- a/browser/components/extensions/test/browser/browser_ext_windows_create.js +++ b/browser/components/extensions/test/browser/browser_ext_windows_create.js @@ -1,15 +1,15 @@ /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ /* vim: set sts=2 sw=2 et tw=80: */ "use strict"; add_task(function* testWindowCreate() { let extension = ExtensionTestUtils.loadExtension({ - background() { + async background() { let _checkWindowPromise; browser.test.onMessage.addListener(msg => { if (msg == "checked-window") { _checkWindowPromise.resolve(); _checkWindowPromise = null; } }); @@ -17,69 +17,71 @@ add_task(function* testWindowCreate() { function checkWindow(expected) { return new Promise(resolve => { _checkWindowPromise = {resolve}; browser.test.sendMessage("check-window", expected); }); } - function createWindow(params, expected, keep = false) { - return browser.windows.create(params).then(window => { - for (let key of Object.keys(params)) { - if (key == "state" && os == "mac" && params.state == "normal") { - // OS-X doesn't have a hard distinction between "normal" and - // "maximized" states. - browser.test.assertTrue(window.state == "normal" || window.state == "maximized", - `Expected window.state (currently ${window.state}) to be "normal" but will accept "maximized"`); - } else { - browser.test.assertEq(params[key], window[key], `Got expected value for window.${key}`); - } + async function createWindow(params, expected, keep = false) { + let window = await browser.windows.create(params); + + for (let key of Object.keys(params)) { + if (key == "state" && os == "mac" && params.state == "normal") { + // OS-X doesn't have a hard distinction between "normal" and + // "maximized" states. + browser.test.assertTrue(window.state == "normal" || window.state == "maximized", + `Expected window.state (currently ${window.state}) to be "normal" but will accept "maximized"`); + } else { + browser.test.assertEq(params[key], window[key], `Got expected value for window.${key}`); } + } - browser.test.assertEq(1, window.tabs.length, "tabs property got populated"); - return checkWindow(expected).then(() => { - if (keep) { - return window; - } - if (params.state == "fullscreen" && os == "win") { - // FIXME: Closing a fullscreen window causes a window leak in - // Windows tests. - return browser.windows.update(window.id, {state: "normal"}).then(() => { - return browser.windows.remove(window.id); - }); - } - return browser.windows.remove(window.id); - }); - }); + browser.test.assertEq(1, window.tabs.length, "tabs property got populated"); + + await checkWindow(expected); + if (keep) { + return window; + } + + if (params.state == "fullscreen" && os == "win") { + // FIXME: Closing a fullscreen window causes a window leak in + // Windows tests. + await browser.windows.update(window.id, {state: "normal"}); + } + await browser.windows.remove(window.id); } - browser.runtime.getPlatformInfo().then(info => { os = info.os; }) - .then(() => createWindow({state: "maximized"}, {state: "STATE_MAXIMIZED"})) - .then(() => createWindow({state: "minimized"}, {state: "STATE_MINIMIZED"})) - .then(() => createWindow({state: "normal"}, {state: "STATE_NORMAL", hiddenChrome: []})) - .then(() => createWindow({state: "fullscreen"}, {state: "STATE_FULLSCREEN"})) - .then(() => { - return createWindow({type: "popup"}, - {hiddenChrome: ["menubar", "toolbar", "location", "directories", "status", "extrachrome"], - chromeFlags: ["CHROME_OPENAS_DIALOG"]}, - true); - }).then(window => { - return browser.tabs.query({windowType: "popup", active: true}).then(tabs => { - browser.test.assertEq(1, tabs.length, "Expected only one popup"); - browser.test.assertEq(window.id, tabs[0].windowId, "Expected new window to be returned in query"); + try { + ({os} = await browser.runtime.getPlatformInfo()); + + await createWindow({state: "maximized"}, {state: "STATE_MAXIMIZED"}); + await createWindow({state: "minimized"}, {state: "STATE_MINIMIZED"}); + await createWindow({state: "normal"}, {state: "STATE_NORMAL", hiddenChrome: []}); + await createWindow({state: "fullscreen"}, {state: "STATE_FULLSCREEN"}); - return browser.windows.remove(window.id); - }); - }).then(() => { + let window = await createWindow( + {type: "popup"}, + {hiddenChrome: ["menubar", "toolbar", "location", "directories", "status", "extrachrome"], + chromeFlags: ["CHROME_OPENAS_DIALOG"]}, + true); + + let tabs = await browser.tabs.query({windowType: "popup", active: true}); + + browser.test.assertEq(1, tabs.length, "Expected only one popup"); + browser.test.assertEq(window.id, tabs[0].windowId, "Expected new window to be returned in query"); + + await browser.windows.remove(window.id); + browser.test.notifyPass("window-create"); - }).catch(e => { + } catch (e) { browser.test.fail(`${e} :: ${e.stack}`); browser.test.notifyFail("window-create"); - }); + } }, }); let latestWindow; let windowListener = (window, topic) => { if (topic == "domwindowopened") { latestWindow = window; }
--- a/browser/components/extensions/test/browser/browser_ext_windows_create_params.js +++ b/browser/components/extensions/test/browser/browser_ext_windows_create_params.js @@ -1,40 +1,33 @@ /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ /* vim: set sts=2 sw=2 et tw=80: */ "use strict"; // Tests that incompatible parameters can't be used together. add_task(function* testWindowCreateParams() { let extension = ExtensionTestUtils.loadExtension({ - background() { - function* getCalls() { + async background() { + try { for (let state of ["minimized", "maximized", "fullscreen"]) { for (let param of ["left", "top", "width", "height"]) { let expected = `"state": "${state}" may not be combined with "left", "top", "width", or "height"`; - yield browser.windows.create({state, [param]: 100}).then( - val => { - browser.test.fail(`Expected error but got "${val}" instead`); - }, - error => { - browser.test.assertTrue( - error.message.includes(expected), - `Got expected error (got: '${error.message}', expected: '${expected}'`); - }); + await browser.test.assertRejects( + browser.windows.create({state, [param]: 100}), + RegExp(expected), + `Got expected error from create(${param}=100)`); } } - } - Promise.all(getCalls()).then(() => { browser.test.notifyPass("window-create-params"); - }).catch(e => { + } catch (e) { browser.test.fail(`${e} :: ${e.stack}`); browser.test.notifyFail("window-create-params"); - }); + } }, }); yield extension.startup(); yield extension.awaitFinish("window-create-params"); yield extension.unload(); });
--- a/browser/components/extensions/test/browser/browser_ext_windows_create_tabId.js +++ b/browser/components/extensions/test/browser/browser_ext_windows_create_tabId.js @@ -1,14 +1,14 @@ /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ /* vim: set sts=2 sw=2 et tw=80: */ "use strict"; add_task(function* testWindowCreate() { - function background() { + async function background() { let promiseTabAttached = () => { return new Promise(resolve => { browser.tabs.onAttached.addListener(function listener() { browser.tabs.onAttached.removeListener(listener); resolve(); }); }); }; @@ -19,130 +19,113 @@ add_task(function* testWindowCreate() { if (changeInfo.url === expected) { browser.tabs.onUpdated.removeListener(listener); resolve(); } }); }); }; - let windowId, tabId; - browser.windows.getCurrent().then(window => { - windowId = window.id; + try { + let window = await browser.windows.getCurrent(); + let windowId = window.id; browser.test.log("Create additional tab in window 1"); - return browser.tabs.create({windowId, url: "about:blank"}); - }).then(tab => { - tabId = tab.id; + let tab = await browser.tabs.create({windowId, url: "about:blank"}); + let tabId = tab.id; + browser.test.log("Create a new window, adopting the new tab"); // Note that we want to check against actual boolean values for // all of the `incognito` property tests. browser.test.assertEq(false, tab.incognito, "Tab is not private"); - return Promise.all([ - promiseTabAttached(), - browser.windows.create({tabId: tabId}), - ]); - }).then(([, window]) => { - browser.test.assertEq(false, window.incognito, "New window is not private"); - browser.test.assertEq(tabId, window.tabs[0].id, "tabs property populated correctly"); + { + let [, window] = await Promise.all([ + promiseTabAttached(), + browser.windows.create({tabId: tabId}), + ]); + browser.test.assertEq(false, window.incognito, "New window is not private"); + browser.test.assertEq(tabId, window.tabs[0].id, "tabs property populated correctly"); - browser.test.log("Close the new window"); - return browser.windows.remove(window.id); - }).then(() => { - browser.test.log("Create a new private window"); + browser.test.log("Close the new window"); + await browser.windows.remove(window.id); + } - return browser.windows.create({incognito: true}); - }).then(privateWindow => { - browser.test.assertEq(true, privateWindow.incognito, "Private window is private"); + { + browser.test.log("Create a new private window"); + let privateWindow = await browser.windows.create({incognito: true}); + browser.test.assertEq(true, privateWindow.incognito, "Private window is private"); - browser.test.log("Create additional tab in private window"); - return browser.tabs.create({windowId: privateWindow.id}).then(privateTab => { + browser.test.log("Create additional tab in private window"); + let privateTab = await browser.tabs.create({windowId: privateWindow.id}); browser.test.assertEq(true, privateTab.incognito, "Private tab is private"); browser.test.log("Create a new window, adopting the new private tab"); - - return Promise.all([ + let [, newWindow] = await Promise.all([ promiseTabAttached(), browser.windows.create({tabId: privateTab.id}), ]); - }).then(([, newWindow]) => { browser.test.assertEq(true, newWindow.incognito, "New private window is private"); browser.test.log("Close the new private window"); - return browser.windows.remove(newWindow.id); - }).then(() => { + await browser.windows.remove(newWindow.id); + browser.test.log("Close the private window"); - return browser.windows.remove(privateWindow.id); - }); - }).then(() => { - return browser.tabs.query({windowId, active: true}); - }).then(([tab]) => { - browser.test.log("Try to create a window with both a tab and a URL"); + await browser.windows.remove(privateWindow.id); + } + - return browser.windows.create({tabId: tab.id, url: "http://example.com/"}).then( - window => { - browser.test.fail("Create call should have failed"); - }, - error => { - browser.test.assertTrue(/`tabId` may not be used in conjunction with `url`/.test(error.message), - "Create call failed as expected"); - }).then(() => { - browser.test.log("Try to create a window with both a tab and an invalid incognito setting"); + browser.test.log("Try to create a window with both a tab and a URL"); + [tab] = await browser.tabs.query({windowId, active: true}); + await browser.test.assertRejects( + browser.windows.create({tabId: tab.id, url: "http://example.com/"}), + /`tabId` may not be used in conjunction with `url`/, + "Create call failed as expected"); - return browser.windows.create({tabId: tab.id, incognito: true}); - }).then( - window => { - browser.test.fail("Create call should have failed"); - }, - error => { - browser.test.assertTrue(/`incognito` property must match the incognito state of tab/.test(error.message), - "Create call failed as expected"); - }); - }).then(() => { - browser.test.log("Try to create a window with an invalid tabId"); + browser.test.log("Try to create a window with both a tab and an invalid incognito setting"); + await browser.test.assertRejects( + browser.windows.create({tabId: tab.id, incognito: true}), + /`incognito` property must match the incognito state of tab/, + "Create call failed as expected"); + - return browser.windows.create({tabId: 0}).then( - window => { - browser.test.fail("Create call should have failed"); - }, - error => { - browser.test.assertTrue(/Invalid tab ID: 0/.test(error.message), - "Create call failed as expected"); - } - ); - }).then(() => { + browser.test.log("Try to create a window with an invalid tabId"); + await browser.test.assertRejects( + browser.windows.create({tabId: 0}), + /Invalid tab ID: 0/, + "Create call failed as expected"); + + browser.test.log("Try to create a window with two URLs"); - - return Promise.all([ + [, , window] = await Promise.all([ // tabs.onUpdated can be invoked between the call of windows.create and // the invocation of its callback/promise, so set up the listeners // before creating the window. promiseTabUpdated("http://example.com/"), promiseTabUpdated("http://example.org/"), browser.windows.create({url: ["http://example.com/", "http://example.org/"]}), ]); - }).then(([, , window]) => { browser.test.assertEq(2, window.tabs.length, "2 tabs were opened in new window"); browser.test.assertEq("about:blank", window.tabs[0].url, "about:blank, page not loaded yet"); browser.test.assertEq("about:blank", window.tabs[1].url, "about:blank, page not loaded yet"); - return browser.windows.get(window.id, {populate: true}); - }).then(window => { + window = await browser.windows.get(window.id, {populate: true}); + browser.test.assertEq(2, window.tabs.length, "2 tabs were opened in new window"); browser.test.assertEq("http://example.com/", window.tabs[0].url, "Correct URL was loaded in tab 1"); browser.test.assertEq("http://example.org/", window.tabs[1].url, "Correct URL was loaded in tab 2"); - return browser.windows.remove(window.id); - }).then(() => { + + await browser.windows.remove(window.id); + browser.test.notifyPass("window-create"); - }).catch(e => { + } catch (e) { browser.test.fail(`${e} :: ${e.stack}`); browser.test.notifyFail("window-create"); - }); + } } let extension = ExtensionTestUtils.loadExtension({ manifest: { "permissions": ["tabs"], }, background,
--- a/browser/components/extensions/test/browser/browser_ext_windows_create_url.js +++ b/browser/components/extensions/test/browser/browser_ext_windows_create_url.js @@ -3,17 +3,17 @@ "use strict"; add_task(function* testWindowCreate() { let extension = ExtensionTestUtils.loadExtension({ manifest: { permissions: ["tabs"], }, - background() { + background: async function() { const EXTENSION_URL = browser.runtime.getURL("test.html"); const REMOTE_URL = browser.runtime.getURL("test.html"); let windows = new class extends Map { // eslint-disable-line new-parens get(id) { if (!this.has(id)) { let window = { tabs: new Map(), @@ -35,47 +35,47 @@ add_task(function* testWindowCreate() { window.tabs.set(tab.index, tab); if (window.tabs.size === window.expectedTabs) { window.resolvePromise(window); } } }); - function create(options) { - return browser.windows.create(options).then(window => { - let win = windows.get(window.id); + async function create(options) { + let window = await browser.windows.create(options); + let win = windows.get(window.id); - win.expectedTabs = Array.isArray(options.url) ? options.url.length : 1; + win.expectedTabs = Array.isArray(options.url) ? options.url.length : 1; - return win.promise; - }); + return win.promise; } - Promise.all([ - create({url: REMOTE_URL}), - create({url: "test.html"}), - create({url: EXTENSION_URL}), - create({url: [REMOTE_URL, "test.html", EXTENSION_URL]}), - ]).then(windows => { + try { + let windows = await Promise.all([ + create({url: REMOTE_URL}), + create({url: "test.html"}), + create({url: EXTENSION_URL}), + create({url: [REMOTE_URL, "test.html", EXTENSION_URL]}), + ]); browser.test.assertEq(REMOTE_URL, windows[0].tabs.get(0).url, "Single, absolute, remote URL"); browser.test.assertEq(REMOTE_URL, windows[1].tabs.get(0).url, "Single, relative URL"); browser.test.assertEq(REMOTE_URL, windows[2].tabs.get(0).url, "Single, absolute, extension URL"); browser.test.assertEq(REMOTE_URL, windows[3].tabs.get(0).url, "url[0]: Absolute, remote URL"); browser.test.assertEq(EXTENSION_URL, windows[3].tabs.get(1).url, "url[1]: Relative URL"); browser.test.assertEq(EXTENSION_URL, windows[3].tabs.get(2).url, "url[2]: Absolute, extension URL"); - }).then(() => { + browser.test.notifyPass("window-create-url"); - }).catch(e => { + } catch (e) { browser.test.fail(`${e} :: ${e.stack}`); browser.test.notifyFail("window-create-url"); - }); + } }, files: { "test.html": `<DOCTYPE html><html><head><meta charset="utf-8"></head></html>`, }, }); yield extension.startup();
--- a/browser/components/extensions/test/browser/browser_ext_windows_events.js +++ b/browser/components/extensions/test/browser/browser_ext_windows_events.js @@ -1,50 +1,50 @@ /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ /* vim: set sts=2 sw=2 et tw=80: */ "use strict"; SimpleTest.requestCompleteLog(); add_task(function* testWindowsEvents() { function background() { - browser.windows.onCreated.addListener(function listener(window) { + browser.windows.onCreated.addListener(window => { browser.test.log(`onCreated: windowId=${window.id}`); browser.test.assertTrue(Number.isInteger(window.id), "Window object's id is an integer"); browser.test.assertEq("normal", window.type, "Window object returned with the correct type"); browser.test.sendMessage("window-created", window.id); }); let lastWindowId, os; - browser.windows.onFocusChanged.addListener(function listener(windowId) { + browser.windows.onFocusChanged.addListener(async windowId => { browser.test.log(`onFocusChange: windowId=${windowId} lastWindowId=${lastWindowId}`); if (windowId === browser.windows.WINDOW_ID_NONE && os === "linux") { browser.test.log("Ignoring a superfluous WINDOW_ID_NONE (blur) event on Linux"); return; } browser.test.assertTrue(lastWindowId !== windowId, "onFocusChanged fired once for the given window"); lastWindowId = windowId; browser.test.assertTrue(Number.isInteger(windowId), "windowId is an integer"); - browser.windows.getLastFocused().then(window => { - browser.test.assertEq(windowId, window.id, - "Last focused window has the correct id"); - browser.test.sendMessage(`window-focus-changed`, window.id); - }); + let window = await browser.windows.getLastFocused(); + + browser.test.assertEq(windowId, window.id, + "Last focused window has the correct id"); + browser.test.sendMessage(`window-focus-changed`, window.id); }); - browser.windows.onRemoved.addListener(function listener(windowId) { + browser.windows.onRemoved.addListener(windowId => { browser.test.log(`onRemoved: windowId=${windowId}`); browser.test.assertTrue(Number.isInteger(windowId), "windowId is an integer"); browser.test.sendMessage(`window-removed`, windowId); browser.test.notifyPass("windows.events"); });
--- a/browser/components/extensions/test/browser/browser_ext_windows_size.js +++ b/browser/components/extensions/test/browser/browser_ext_windows_size.js @@ -1,15 +1,15 @@ /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ /* vim: set sts=2 sw=2 et tw=80: */ "use strict"; add_task(function* testWindowCreate() { let extension = ExtensionTestUtils.loadExtension({ - background() { + async background() { let _checkWindowPromise; browser.test.onMessage.addListener((msg, arg) => { if (msg == "checked-window") { _checkWindowPromise.resolve(arg); _checkWindowPromise = null; } }); @@ -23,83 +23,73 @@ add_task(function* testWindowCreate() { const KEYS = ["left", "top", "width", "height"]; function checkGeom(expected, actual) { for (let key of KEYS) { browser.test.assertEq(expected[key], actual[key], `Expected '${key}' value`); } } let windowId; - function checkWindow(expected, retries = 5) { - return getWindowSize().then(geom => { - if (retries && KEYS.some(key => expected[key] != geom[key])) { - browser.test.log(`Got mismatched size (${JSON.stringify(expected)} != ${JSON.stringify(geom)}). ` + - `Retrying after a short delay.`); + async function checkWindow(expected, retries = 5) { + let geom = await getWindowSize(); + + if (retries && KEYS.some(key => expected[key] != geom[key])) { + browser.test.log(`Got mismatched size (${JSON.stringify(expected)} != ${JSON.stringify(geom)}). ` + + `Retrying after a short delay.`); + + await new Promise(resolve => setTimeout(resolve, 200)); - return new Promise(resolve => { - setTimeout(resolve, 200); - }).then(() => { - return checkWindow(expected, retries - 1); - }); - } + return checkWindow(expected, retries - 1); + } + + browser.test.log(`Check actual window size`); + checkGeom(expected, geom); - browser.test.log(`Check actual window size`); - checkGeom(expected, geom); + browser.test.log("Check API-reported window size"); - browser.test.log("Check API-reported window size"); - return browser.windows.get(windowId).then(geom => { - checkGeom(expected, geom); - }); - }); + geom = await browser.windows.get(windowId); + + checkGeom(expected, geom); } - let geom = {left: 100, top: 100, width: 500, height: 300}; + try { + let geom = {left: 100, top: 100, width: 500, height: 300}; - return browser.windows.create(geom).then(window => { + let window = await browser.windows.create(geom); windowId = window.id; - return checkWindow(geom); - }).then(() => { + await checkWindow(geom); + let update = {left: 150, width: 600}; Object.assign(geom, update); - - return browser.windows.update(windowId, update); - }).then(() => { - return checkWindow(geom); - }).then(() => { - let update = {top: 150, height: 400}; - Object.assign(geom, update); + await browser.windows.update(windowId, update); + await checkWindow(geom); - return browser.windows.update(windowId, update); - }).then(() => { - return checkWindow(geom); - }).then(() => { - geom = {left: 200, top: 200, width: 800, height: 600}; + update = {top: 150, height: 400}; + Object.assign(geom, update); + await browser.windows.update(windowId, update); + await checkWindow(geom); - return browser.windows.update(windowId, geom); - }).then(() => { - return checkWindow(geom); - }).then(() => { - return browser.runtime.getPlatformInfo(); - }).then((platformInfo) => { + geom = {left: 200, top: 200, width: 800, height: 600}; + await browser.windows.update(windowId, geom); + await checkWindow(geom); + + let platformInfo = await browser.runtime.getPlatformInfo(); if (platformInfo.os != "linux") { geom = {left: -50, top: -50, width: 800, height: 600}; - - return browser.windows.update(windowId, geom).then(() => { - return checkWindow(geom); - }); + await browser.windows.update(windowId, geom); + await checkWindow(geom); } - }).then(() => { - return browser.windows.remove(windowId); - }).then(() => { + + await browser.windows.remove(windowId); browser.test.notifyPass("window-size"); - }).catch(e => { + } catch (e) { browser.test.fail(`${e} :: ${e.stack}`); browser.test.notifyFail("window-size"); - }); + } }, }); let latestWindow; let windowListener = (window, topic) => { if (topic == "domwindowopened") { latestWindow = window; }
--- a/browser/components/extensions/test/browser/browser_ext_windows_update.js +++ b/browser/components/extensions/test/browser/browser_ext_windows_update.js @@ -46,17 +46,17 @@ add_task(function* () { yield extension.unload(); yield BrowserTestUtils.closeWindow(window2); }); add_task(function* testWindowUpdate() { let extension = ExtensionTestUtils.loadExtension({ - background() { + async background() { let _checkWindowPromise; browser.test.onMessage.addListener(msg => { if (msg == "checked-window") { _checkWindowPromise.resolve(); _checkWindowPromise = null; } }); @@ -64,49 +64,53 @@ add_task(function* testWindowUpdate() { function checkWindow(expected) { return new Promise(resolve => { _checkWindowPromise = {resolve}; browser.test.sendMessage("check-window", expected); }); } let currentWindowId; - function updateWindow(windowId, params, expected) { - return browser.windows.update(windowId, params).then(window => { - browser.test.assertEq(currentWindowId, window.id, "Expected WINDOW_ID_CURRENT to refer to the same window"); - for (let key of Object.keys(params)) { - if (key == "state" && os == "mac" && params.state == "normal") { - // OS-X doesn't have a hard distinction between "normal" and - // "maximized" states. - browser.test.assertTrue(window.state == "normal" || window.state == "maximized", - `Expected window.state (currently ${window.state}) to be "normal" but will accept "maximized"`); - } else { - browser.test.assertEq(params[key], window[key], `Got expected value for window.${key}`); - } + async function updateWindow(windowId, params, expected) { + let window = await browser.windows.update(windowId, params); + + browser.test.assertEq(currentWindowId, window.id, "Expected WINDOW_ID_CURRENT to refer to the same window"); + for (let key of Object.keys(params)) { + if (key == "state" && os == "mac" && params.state == "normal") { + // OS-X doesn't have a hard distinction between "normal" and + // "maximized" states. + browser.test.assertTrue(window.state == "normal" || window.state == "maximized", + `Expected window.state (currently ${window.state}) to be "normal" but will accept "maximized"`); + } else { + browser.test.assertEq(params[key], window[key], `Got expected value for window.${key}`); } + } - return checkWindow(expected); - }); + return checkWindow(expected); } - let windowId = browser.windows.WINDOW_ID_CURRENT; + try { + let windowId = browser.windows.WINDOW_ID_CURRENT; + + ({os} = await browser.runtime.getPlatformInfo()); + + let window = await browser.windows.getCurrent(); + currentWindowId = window.id; - browser.runtime.getPlatformInfo().then(info => { os = info.os; }) - .then(() => browser.windows.getCurrent().then(window => { currentWindowId = window.id; })) - .then(() => updateWindow(windowId, {state: "maximized"}, {state: "STATE_MAXIMIZED"})) - .then(() => updateWindow(windowId, {state: "minimized"}, {state: "STATE_MINIMIZED"})) - .then(() => updateWindow(windowId, {state: "normal"}, {state: "STATE_NORMAL"})) - .then(() => updateWindow(windowId, {state: "fullscreen"}, {state: "STATE_FULLSCREEN"})) - .then(() => updateWindow(windowId, {state: "normal"}, {state: "STATE_NORMAL"})) - .then(() => { + await updateWindow(windowId, {state: "maximized"}, {state: "STATE_MAXIMIZED"}); + await updateWindow(windowId, {state: "minimized"}, {state: "STATE_MINIMIZED"}); + await updateWindow(windowId, {state: "normal"}, {state: "STATE_NORMAL"}); + await updateWindow(windowId, {state: "fullscreen"}, {state: "STATE_FULLSCREEN"}); + await updateWindow(windowId, {state: "normal"}, {state: "STATE_NORMAL"}); + browser.test.notifyPass("window-update"); - }).catch(e => { + } catch (e) { browser.test.fail(`${e} :: ${e.stack}`); browser.test.notifyFail("window-update"); - }); + } }, }); extension.onMessage("check-window", expected => { if (expected.state != null) { let {windowState} = window; if (window.fullScreen) { windowState = window.STATE_FULLSCREEN; @@ -152,41 +156,34 @@ add_task(function* () { yield BrowserTestUtils.closeWindow(window2); }); // Tests that incompatible parameters can't be used together. add_task(function* testWindowUpdateParams() { let extension = ExtensionTestUtils.loadExtension({ - background() { - function* getCalls() { + async background() { + try { for (let state of ["minimized", "maximized", "fullscreen"]) { for (let param of ["left", "top", "width", "height"]) { let expected = `"state": "${state}" may not be combined with "left", "top", "width", or "height"`; let windowId = browser.windows.WINDOW_ID_CURRENT; - yield browser.windows.update(windowId, {state, [param]: 100}).then( - val => { - browser.test.fail(`Expected error but got "${val}" instead`); - }, - error => { - browser.test.assertTrue( - error.message.includes(expected), - `Got expected error (got: '${error.message}', expected: '${expected}'`); - }); + await browser.test.assertRejects( + browser.windows.update(windowId, {state, [param]: 100}), + RegExp(expected), + `Got expected error for create(${param}=100`); } } - } - Promise.all(getCalls()).then(() => { browser.test.notifyPass("window-update-params"); - }).catch(e => { + } catch (e) { browser.test.fail(`${e} :: ${e.stack}`); browser.test.notifyFail("window-update-params"); - }); + } }, }); yield extension.startup(); yield extension.awaitFinish("window-update-params"); yield extension.unload(); });
--- a/browser/components/extensions/test/browser/head_pageAction.js +++ b/browser/components/extensions/test/browser/head_pageAction.js @@ -7,71 +7,66 @@ function* runTests(options) { function background(getTests) { let tabs; let tests; // Gets the current details of the page action, and returns a // promise that resolves to an object containing them. - function getDetails() { - return new Promise(resolve => { - return browser.tabs.query({active: true, currentWindow: true}, resolve); - }).then(([tab]) => { - let tabId = tab.id; - browser.test.log(`Get details: tab={id: ${tabId}, url: ${JSON.stringify(tab.url)}}`); - return Promise.all([ - browser.pageAction.getTitle({tabId}), - browser.pageAction.getPopup({tabId})]); - }).then(details => { - return Promise.resolve({title: details[0], - popup: details[1]}); - }); + async function getDetails() { + let [tab] = await browser.tabs.query({active: true, currentWindow: true}); + let tabId = tab.id; + + browser.test.log(`Get details: tab={id: ${tabId}, url: ${JSON.stringify(tab.url)}}`); + + return { + title: await browser.pageAction.getTitle({tabId}), + popup: await browser.pageAction.getPopup({tabId}), + }; } // Runs the next test in the `tests` array, checks the results, // and passes control back to the outer test scope. function nextTest() { let test = tests.shift(); - test(expecting => { + test(async expecting => { function finish() { // Check that the actual icon has the expected values, then // run the next test. browser.test.sendMessage("nextTest", expecting, tests.length); } if (expecting) { // Check that the API returns the expected values, and then // run the next test. - getDetails().then(details => { - browser.test.assertEq(expecting.title, details.title, - "expected value from getTitle"); + let details = await getDetails(); + + browser.test.assertEq(expecting.title, details.title, + "expected value from getTitle"); - browser.test.assertEq(expecting.popup, details.popup, - "expected value from getPopup"); + browser.test.assertEq(expecting.popup, details.popup, + "expected value from getPopup"); + } - finish(); - }); - } else { - finish(); - } + finish(); }); } - function runTests() { + async function runTests() { tabs = []; tests = getTests(tabs); - browser.tabs.query({active: true, currentWindow: true}, resultTabs => { - tabs[0] = resultTabs[0].id; + let resultTabs = await browser.tabs.query({active: true, currentWindow: true}); - nextTest(); - }); + tabs[0] = resultTabs[0].id; + + nextTest(); } browser.test.onMessage.addListener((msg) => { if (msg == "runTests") { runTests(); } else if (msg == "runNextTest") { nextTest(); } else {
--- a/browser/components/originattributes/moz.build +++ b/browser/components/originattributes/moz.build @@ -3,10 +3,14 @@ # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. BROWSER_CHROME_MANIFESTS += [ 'test/browser/browser.ini', ] +MOCHITEST_MANIFESTS += [ + 'test/mochitest/mochitest.ini' +] + with Files('**'): BUG_COMPONENT = ('Firefox', 'OriginAttributes')
--- a/browser/components/originattributes/test/browser/browser_cache.js +++ b/browser/components/originattributes/test/browser/browser_cache.js @@ -216,16 +216,24 @@ function* doTest(aBrowser) { // The check function, which checks the number of cache entries. function* doCheck(aShouldIsolate, aInputA, aInputB) { let expectedEntryCount = 1; let data = []; data = data.concat(yield cacheDataForContext(LoadContextInfo.default)); data = data.concat(yield cacheDataForContext(LoadContextInfo.private)); data = data.concat(yield cacheDataForContext(LoadContextInfo.custom(true, {}))); + data = data.concat(yield cacheDataForContext(LoadContextInfo.custom(false, { userContextId: 1 }))); + data = data.concat(yield cacheDataForContext(LoadContextInfo.custom(true, { userContextId: 1 }))); + data = data.concat(yield cacheDataForContext(LoadContextInfo.custom(false, { userContextId: 2 }))); + data = data.concat(yield cacheDataForContext(LoadContextInfo.custom(true, { userContextId: 2 }))); + data = data.concat(yield cacheDataForContext(LoadContextInfo.custom(false, { firstPartyDomain: "example.com" }))); + data = data.concat(yield cacheDataForContext(LoadContextInfo.custom(true, { firstPartyDomain: "example.com" }))); + data = data.concat(yield cacheDataForContext(LoadContextInfo.custom(false, { firstPartyDomain: "example.org" }))); + data = data.concat(yield cacheDataForContext(LoadContextInfo.custom(true, { firstPartyDomain: "example.org" }))); if (aShouldIsolate) { expectedEntryCount = 2; } for (let suffix of suffixes) { let foundEntryCount = countMatchingCacheEntries(data, "example.net", suffix); let result = (expectedEntryCount === foundEntryCount);
new file mode 100644 --- /dev/null +++ b/browser/components/originattributes/test/mochitest/file_empty.html @@ -0,0 +1,2 @@ +<h1>I'm just a support file</h1> +<p>I get loaded to do permission testing.</p> \ No newline at end of file
new file mode 100644 --- /dev/null +++ b/browser/components/originattributes/test/mochitest/mochitest.ini @@ -0,0 +1,5 @@ +[DEFAULT] +support-files = + file_empty.html + +[test_permissions_api.html]
new file mode 100644 --- /dev/null +++ b/browser/components/originattributes/test/mochitest/test_permissions_api.html @@ -0,0 +1,207 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> + +<head> + <meta charset="utf-8"> + <title>Test for Permissions API</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" href="/tests/SimpleTest/test.css"> +</head> + +<body> + <pre id="test"></pre> + <script type="application/javascript;version=1.8"> + /*globals SpecialPowers, SimpleTest, is, ok, */ + 'use strict'; + + const { + UNKNOWN_ACTION, + PROMPT_ACTION, + ALLOW_ACTION, + DENY_ACTION + } = SpecialPowers.Ci.nsIPermissionManager; + + SimpleTest.waitForExplicitFinish(); + + const PERMISSIONS = [{ + name: 'geolocation', + type: 'geo' + }, { + name: 'notifications', + type: 'desktop-notification' + }, { + name: 'push', + type: 'desktop-notification' + }, ]; + + const UNSUPPORTED_PERMISSIONS = [ + 'foobarbaz', // Not in spec, for testing only. + 'midi', + ]; + + // Create a closure, so that tests are run on the correct window object. + function createPermissionTester(aWindow) { + return { + setPermissions(allow) { + const permissions = PERMISSIONS.map(({ type }) => { + return { + type, + allow, + 'context': aWindow.document + }; + }); + return new Promise((resolve) => { + SpecialPowers.popPermissions(() => { + SpecialPowers.pushPermissions(permissions, resolve); + }); + }); + }, + revokePermissions() { + const promisesToRevoke = PERMISSIONS.map(({ name }) => { + return aWindow.navigator.permissions + .revoke({ name }) + .then( + ({ state }) => is(state, 'prompt', `correct state for '${name}'`), + () => ok(false, `revoke should not have rejected for '${name}'`) + ); + }); + return Promise.all(promisesToRevoke); + }, + revokeUnsupportedPermissions() { + const promisesToRevoke = UNSUPPORTED_PERMISSIONS.map(({ name }) => { + return aWindow.navigator.permissions + .revoke({ name }) + .then( + () => ok(false, `revoke should not have resolved for '${name}'`), + error => is(error.name, 'TypeError', `revoke should have thrown TypeError for '${name}'`) + ); + }); + return Promise.all(promisesToRevoke); + }, + checkPermissions(state) { + const promisesToQuery = PERMISSIONS.map(({ name }) => { + return aWindow.navigator.permissions + .query({ name }) + .then( + () => is(state, state, `correct state for '${name}'`), + () => ok(false, `query should not have rejected for '${name}'`) + ); + }); + return Promise.all(promisesToQuery); + }, + checkUnsupportedPermissions() { + const promisesToQuery = UNSUPPORTED_PERMISSIONS.map(({ name }) => { + return aWindow.navigator.permissions + .query({ name }) + .then( + () => ok(false, `query should not have resolved for '${name}'`), + error => { + is(error.name, 'TypeError', + `query should have thrown TypeError for '${name}'`); + } + ); + }); + return Promise.all(promisesToQuery); + }, + promiseStateChanged(name, state) { + return aWindow.navigator.permissions + .query({ name }) + .then(status => { + return new Promise( resolve => { + status.onchange = () => { + status.onchange = null; + is(status.state, state, `state changed for '${name}'`); + resolve(); + }; + }); + }, + () => ok(false, `query should not have rejected for '${name}'`)); + }, + testStatusOnChange() { + return new Promise((resolve) => { + SpecialPowers.popPermissions(() => { + const permission = 'geolocation'; + const promiseGranted = this.promiseStateChanged(permission, 'granted'); + this.setPermissions(ALLOW_ACTION); + promiseGranted.then(() => { + const promisePrompt = this.promiseStateChanged(permission, 'prompt'); + SpecialPowers.popPermissions(); + return promisePrompt; + }).then(resolve); + }); + }); + }, + testInvalidQuery() { + return aWindow.navigator.permissions + .query({ name: 'invalid' }) + .then( + () => ok(false, 'invalid query should not have resolved'), + () => ok(true, 'invalid query should have rejected') + ); + }, + testInvalidRevoke() { + return aWindow.navigator.permissions + .revoke({ name: 'invalid' }) + .then( + () => ok(false, 'invalid revoke should not have resolved'), + () => ok(true, 'invalid revoke should have rejected') + ); + }, + }; + } + + function enablePrefs() { + const ops = { + 'set': [ + ['dom.permissions.revoke.enable', true], + ['privacy.firstparty.isolate', true], + ], + }; + return SpecialPowers.pushPrefEnv(ops); + } + + function createIframe() { + return new Promise((resolve) => { + const iframe = document.createElement('iframe'); + iframe.src = 'file_empty.html'; + iframe.onload = () => resolve(iframe.contentWindow); + document.body.appendChild(iframe); + }); + } + debugger; + window.onload = () => { + enablePrefs() + .then(createIframe) + .then(createPermissionTester) + .then((tester) => { + return tester + .checkUnsupportedPermissions() + .then(() => tester.setPermissions(UNKNOWN_ACTION)) + .then(() => tester.checkPermissions('prompt')) + .then(() => tester.setPermissions(PROMPT_ACTION)) + .then(() => tester.checkPermissions('prompt')) + .then(() => tester.setPermissions(ALLOW_ACTION)) + .then(() => tester.checkPermissions('granted')) + .then(() => tester.setPermissions(DENY_ACTION)) + .then(() => tester.checkPermissions('denied')) + .then(() => tester.testStatusOnChange()) + .then(() => tester.testInvalidQuery()) + .then(() => tester.revokeUnsupportedPermissions()) + .then(() => tester.revokePermissions()) + .then(() => tester.checkPermissions('prompt')) + .then(() => tester.testInvalidRevoke()); + }) + .then(SimpleTest.finish) + .catch((e) => { + ok(false, `Unexpected error ${e}`); + SimpleTest.finish(); + }); + }; + </script> +</body> + +</html>
--- a/browser/components/safebrowsing/content/test/head.js +++ b/browser/components/safebrowsing/content/test/head.js @@ -28,18 +28,16 @@ function promiseTabLoadEvent(tab, url, e info(`Skipping spurious load event for ${loadedUrl}`); return false; } info("Tab event received: load"); return true; } - // Create two promises: one resolved from the content process when the page - // loads and one that is rejected if we take too long to load the url. let loaded; if (eventType === "load") { loaded = BrowserTestUtils.browserLoaded(tab.linkedBrowser, false, handle); } else { // No need to use handle. loaded = BrowserTestUtils.waitForContentEvent(tab.linkedBrowser, eventType, true, undefined, true);
--- a/browser/components/search/test/head.js +++ b/browser/components/search/test/head.js @@ -107,18 +107,16 @@ function promiseTabLoadEvent(tab, url) info(`Skipping spurious load event for ${loadedUrl}`); return false; } info("Tab event received: load"); return true; } - // Create two promises: one resolved from the content process when the page - // loads and one that is rejected if we take too long to load the url. let loaded = BrowserTestUtils.browserLoaded(tab.linkedBrowser, false, handle); if (url) BrowserTestUtils.loadURI(tab.linkedBrowser, url); return loaded; }
--- a/browser/locales/en-US/chrome/browser/tabbrowser.properties +++ b/browser/locales/en-US/chrome/browser/tabbrowser.properties @@ -12,16 +12,17 @@ # tabs.downloading = Firefox is downloading a file for a helper application (PDF) tabs.connecting=Connecting… tabs.encryptingConnection=Securing connection… tabs.searching=Searching… tabs.loading=Loading… tabs.waiting=Waiting… tabs.downloading=Downloading… +tabs.restoreLastTabs=Restore Tabs From Last Time tabs.emptyTabTitle=New Tab tabs.closeTab=Close Tab tabs.close=Close tabs.closeWarningTitle=Confirm close # LOCALIZATION NOTE (tabs.closeWarningMultiple): # Semicolon-separated list of plural forms. See: # http://developer.mozilla.org/en/docs/Localization_and_Plurals # The singular form is not considered since this string is used only for
--- a/browser/themes/osx/browser.css +++ b/browser/themes/osx/browser.css @@ -142,22 +142,16 @@ } /** End titlebar **/ #main-window[chromehidden~="toolbar"][chromehidden~="location"][chromehidden~="directories"] { border-top: 1px solid rgba(0,0,0,0.65); } -/* Because of -moz-box-align: center above, separators will be invisible unless - we set their min-height. See bug 583510 for more information. */ -toolbarseparator { - min-height: 22px; -} - #navigator-toolbox > toolbar:not(#TabsToolbar):not(#nav-bar):not(:-moz-lwtheme) { -moz-appearance: none; background: url(chrome://browser/skin/Toolbar-background-noise.png) hsl(0,0%,83%); } /* remove noise texture on Yosemite */ @media (-moz-mac-yosemite-theme) { #navigator-toolbox > toolbar:not(#TabsToolbar):not(#nav-bar):not(:-moz-lwtheme) {
--- a/build/gecko_templates.mozbuild +++ b/build/gecko_templates.mozbuild @@ -53,22 +53,24 @@ def GeckoBinary(linkage='dependent', msv ] elif linkage != None: error('`linkage` must be "dependent", "standalone" or None') if mozglue: LDFLAGS += CONFIG['MOZ_GLUE_WRAP_LDFLAGS'] if mozglue == 'program': USE_LIBS += ['mozglue'] + DEFINES['MOZ_HAS_MOZGLUE'] = True if CONFIG['MOZ_GLUE_IN_PROGRAM']: if CONFIG['GNU_CC']: LDFLAGS += ['-rdynamic'] if CONFIG['MOZ_MEMORY']: USE_LIBS += ['memory'] elif mozglue == 'library': + LIBRARY_DEFINES['MOZ_HAS_MOZGLUE'] = True if not CONFIG['MOZ_GLUE_IN_PROGRAM']: USE_LIBS += ['mozglue'] else: error('`mozglue` must be "program" or "library"') if not CONFIG['JS_STANDALONE']: USE_LIBS += [ 'fallible',
--- a/config/external/fdlibm/moz.build +++ b/config/external/fdlibm/moz.build @@ -1,14 +1,12 @@ # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- # vim: set filetype=python: # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -Library('fdlibm') - with Files('**'): BUG_COMPONENT = ('Core', 'JavaScript Engine') DIRS += [ '../../../modules/fdlibm', ]
--- a/devtools/client/netmonitor/test/browser_net_content-type.js +++ b/devtools/client/netmonitor/test/browser_net_content-type.js @@ -19,17 +19,17 @@ function* content_type_test(isHTTPS) { let { tab, monitor } = yield initNetMonitor(pageURL); info("Starting test... "); let { document, Editor, NetMonitorView } = monitor.panelWin; let { RequestsMenu } = NetMonitorView; RequestsMenu.lazyUpdate = false; - let wait = waitForNetworkEvents(monitor, 8); + let wait = waitForNetworkEvents(monitor, CONTENT_TYPE_WITHOUT_CACHE_REQUESTS); yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { content.wrappedJSObject.performRequests(); }); yield wait; let okStatus = isHTTPS ? "Connected" : "OK"; verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0),
--- a/devtools/client/netmonitor/test/browser_net_copy_image_as_data_uri.js +++ b/devtools/client/netmonitor/test/browser_net_copy_image_as_data_uri.js @@ -11,17 +11,17 @@ add_task(function* () { let { tab, monitor } = yield initNetMonitor(CONTENT_TYPE_WITHOUT_CACHE_URL); info("Starting test... "); let { NetMonitorView } = monitor.panelWin; let { RequestsMenu } = NetMonitorView; RequestsMenu.lazyUpdate = false; - let wait = waitForNetworkEvents(monitor, 8); + let wait = waitForNetworkEvents(monitor, CONTENT_TYPE_WITHOUT_CACHE_REQUESTS); yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { content.wrappedJSObject.performRequests(); }); yield wait; let requestItem = RequestsMenu.getItemAtIndex(5); RequestsMenu.selectedItem = requestItem;
--- a/devtools/client/netmonitor/test/browser_net_copy_response.js +++ b/devtools/client/netmonitor/test/browser_net_copy_response.js @@ -13,17 +13,17 @@ add_task(function* () { const EXPECTED_RESULT = '{ "greeting": "Hello JSON!" }'; let { NetMonitorView } = monitor.panelWin; let { RequestsMenu } = NetMonitorView; RequestsMenu.lazyUpdate = false; - let wait = waitForNetworkEvents(monitor, 8); + let wait = waitForNetworkEvents(monitor, CONTENT_TYPE_WITHOUT_CACHE_REQUESTS); yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { content.wrappedJSObject.performRequests(); }); yield wait; let requestItem = RequestsMenu.getItemAtIndex(3); RequestsMenu.selectedItem = requestItem;
--- a/devtools/client/netmonitor/test/browser_net_icon-preview.js +++ b/devtools/client/netmonitor/test/browser_net_icon-preview.js @@ -39,17 +39,17 @@ add_task(function* () { info("Checking the image thumbnail after a reload."); checkImageThumbnail(); yield teardown(monitor); function waitForEvents() { return promise.all([ - waitForNetworkEvents(monitor, 8), + waitForNetworkEvents(monitor, CONTENT_TYPE_WITHOUT_CACHE_REQUESTS), monitor.panelWin.once(EVENTS.RESPONSE_IMAGE_THUMBNAIL_DISPLAYED) ]); } function performRequests() { return ContentTask.spawn(tab.linkedBrowser, {}, function* () { content.wrappedJSObject.performRequests(); });
--- a/devtools/client/netmonitor/test/browser_net_image-tooltip.js +++ b/devtools/client/netmonitor/test/browser_net_image-tooltip.js @@ -10,28 +10,28 @@ add_task(function* test() { let { tab, monitor } = yield initNetMonitor(CONTENT_TYPE_WITHOUT_CACHE_URL); info("Starting test... "); let { $, EVENTS, ACTIVITY_TYPE, NetMonitorView, NetMonitorController } = monitor.panelWin; let { RequestsMenu } = NetMonitorView; RequestsMenu.lazyUpdate = true; - let onEvents = waitForNetworkEvents(monitor, 8); + let onEvents = waitForNetworkEvents(monitor, CONTENT_TYPE_WITHOUT_CACHE_REQUESTS); let onThumbnail = monitor.panelWin.once(EVENTS.RESPONSE_IMAGE_THUMBNAIL_DISPLAYED); yield performRequests(); yield onEvents; yield onThumbnail; info("Checking the image thumbnail after a few requests were made..."); yield showTooltipAndVerify(RequestsMenu.tooltip, RequestsMenu.items[5]); - // 7 XHRs as before + 1 extra document reload - onEvents = waitForNetworkEvents(monitor, 8); + // +1 extra document reload + onEvents = waitForNetworkEvents(monitor, CONTENT_TYPE_WITHOUT_CACHE_REQUESTS + 1); onThumbnail = monitor.panelWin.once(EVENTS.RESPONSE_IMAGE_THUMBNAIL_DISPLAYED); info("Reloading the debuggee and performing all requests again..."); yield NetMonitorController.triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED); yield performRequests(); yield onEvents; yield onThumbnail;
--- a/devtools/client/netmonitor/test/head.js +++ b/devtools/client/netmonitor/test/head.js @@ -16,16 +16,17 @@ const EXAMPLE_URL = "http://example.com/ const HTTPS_EXAMPLE_URL = "https://example.com/browser/devtools/client/netmonitor/test/"; const API_CALLS_URL = EXAMPLE_URL + "html_api-calls-test-page.html"; const SIMPLE_URL = EXAMPLE_URL + "html_simple-test-page.html"; const NAVIGATE_URL = EXAMPLE_URL + "html_navigate-test-page.html"; const CONTENT_TYPE_URL = EXAMPLE_URL + "html_content-type-test-page.html"; const CONTENT_TYPE_WITHOUT_CACHE_URL = EXAMPLE_URL + "html_content-type-without-cache-test-page.html"; const HTTPS_CONTENT_TYPE_WITHOUT_CACHE_URL = HTTPS_EXAMPLE_URL + "html_content-type-without-cache-test-page.html"; +const CONTENT_TYPE_WITHOUT_CACHE_REQUESTS = 8; const CYRILLIC_URL = EXAMPLE_URL + "html_cyrillic-test-page.html"; const STATUS_CODES_URL = EXAMPLE_URL + "html_status-codes-test-page.html"; const POST_DATA_URL = EXAMPLE_URL + "html_post-data-test-page.html"; const POST_JSON_URL = EXAMPLE_URL + "html_post-json-test-page.html"; const POST_RAW_URL = EXAMPLE_URL + "html_post-raw-test-page.html"; const POST_RAW_WITH_HEADERS_URL = EXAMPLE_URL + "html_post-raw-with-headers-test-page.html"; const PARAMS_URL = EXAMPLE_URL + "html_params-test-page.html"; const JSONP_URL = EXAMPLE_URL + "html_jsonp-test-page.html";
--- a/devtools/client/storage/test/storage-listings.html +++ b/devtools/client/storage/test/storage-listings.html @@ -14,17 +14,17 @@ Bug 970517 - Storage inspector front end "use strict"; let partialHostname = location.hostname.match(/^[^.]+(\..*)$/)[1]; let cookieExpiresTime1 = 2000000000000; let cookieExpiresTime2 = 2000000001000; // Setting up some cookies to eat. document.cookie = "c1=foobar; expires=" + new Date(cookieExpiresTime1).toGMTString() + "; path=/browser"; document.cookie = "cs2=sessionCookie; path=/; domain=" + partialHostname; -document.cookie = "c3=foobar-2; secure=true; expires=" + +document.cookie = "c3=foobar-2; expires=" + new Date(cookieExpiresTime2).toGMTString() + "; path=/"; // ... and some local storage items .. localStorage.setItem("ls1", "foobar"); localStorage.setItem("ls2", "foobar-2"); // ... and finally some session storage items too sessionStorage.setItem("ss1", "foobar-3"); dump("added cookies and storage from main page\n");
--- a/devtools/client/storage/test/storage-unsecured-iframe.html +++ b/devtools/client/storage/test/storage-unsecured-iframe.html @@ -4,16 +4,16 @@ Iframe for testing multiple host detetion in storage actor --> <head> <meta charset="utf-8"> </head> <body> <script> "use strict"; -document.cookie = "uc1=foobar; domain=.example.org; path=/; secure=true"; +document.cookie = "uc1=foobar; domain=.example.org; path=/"; localStorage.setItem("iframe-u-ls1", "foobar"); sessionStorage.setItem("iframe-u-ss1", "foobar1"); sessionStorage.setItem("iframe-u-ss2", "foobar2"); dump("added cookies and storage from unsecured iframe\n"); </script> </body> </html>
--- a/devtools/client/themes/computed.css +++ b/devtools/client/themes/computed.css @@ -31,22 +31,22 @@ align-items: center; } #browser-style-checkbox { /* Bug 1200073 - extra space before the browser styles checkbox so they aren't squished together in a small window. Put also an extra space after. */ margin-inline-start: 5px; - margin-inline-end: 5px; - + margin-inline-end: 0; } #browser-style-checkbox-label { - margin-right: 5px; + padding-inline-start: 5px; + margin-inline-end: 5px; } #propertyContainer { -moz-user-select: text; overflow-y: auto; overflow-x: hidden; flex: auto; }
--- a/devtools/server/tests/browser/browser_storage_listings.js +++ b/devtools/server/tests/browser/browser_storage_listings.js @@ -30,17 +30,17 @@ const storeMap = { }, { name: "c3", value: "foobar-2", expires: 2000000001000, path: "/", host: "test1.example.org", isDomain: false, - isSecure: true, + isSecure: false, }, { name: "uc1", value: "foobar", host: ".example.org", path: "/", expires: 0, isDomain: true, @@ -339,17 +339,24 @@ function* testStores(data) { function testCookies(cookiesActor) { is(Object.keys(cookiesActor.hosts).length, 2, "Correct number of host entries for cookies"); return testCookiesObjects(0, cookiesActor.hosts, cookiesActor); } var testCookiesObjects = Task.async(function* (index, hosts, cookiesActor) { let host = Object.keys(hosts)[index]; let matchItems = data => { - is(data.total, storeMap.cookies[host].length, + let cookiesLength = 0; + for (let secureCookie of storeMap.cookies[host]) { + if (secureCookie.isSecure) { + ++cookiesLength; + } + } + // Any secure cookies did not get stored in the database. + is(data.total, storeMap.cookies[host].length - cookiesLength, "Number of cookies in host " + host + " matches"); for (let item of data.data) { let found = false; for (let toMatch of storeMap.cookies[host]) { if (item.name == toMatch.name) { found = true; ok(true, "Found cookie " + item.name + " in response"); is(item.value.str, toMatch.value, "The value matches.");
--- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -12263,16 +12263,17 @@ nsIDocument::InlineScriptAllowedByCSP() { nsCOMPtr<nsIContentSecurityPolicy> csp; nsresult rv = NodePrincipal()->GetCsp(getter_AddRefs(csp)); NS_ENSURE_SUCCESS(rv, true); bool allowsInlineScript = true; if (csp) { nsresult rv = csp->GetAllowsInline(nsIContentPolicy::TYPE_SCRIPT, EmptyString(), // aNonce + false, // parserCreated EmptyString(), // FIXME get script sample (bug 1314567) 0, // aLineNumber &allowsInlineScript); NS_ENSURE_SUCCESS(rv, true); } return allowsInlineScript; }
--- a/dom/base/nsScriptLoader.cpp +++ b/dom/base/nsScriptLoader.cpp @@ -1377,24 +1377,25 @@ CSPAllowsInlineScript(nsIScriptElement * // no CSP --> allow return true; } // query the nonce nsCOMPtr<nsIContent> scriptContent = do_QueryInterface(aElement); nsAutoString nonce; scriptContent->GetAttr(kNameSpaceID_None, nsGkAtoms::nonce, nonce); + bool parserCreated = aElement->GetParserCreated() != mozilla::dom::NOT_FROM_PARSER; // query the scripttext nsAutoString scriptText; aElement->GetScriptText(scriptText); bool allowInlineScript = false; rv = csp->GetAllowsInline(nsIContentPolicy::TYPE_SCRIPT, - nonce, scriptText, + nonce, parserCreated, scriptText, aElement->GetScriptLineNumber(), &allowInlineScript); return allowInlineScript; } nsScriptLoadRequest* nsScriptLoader::CreateLoadRequest(nsScriptKind aKind, nsIScriptElement* aElement,
--- a/dom/cache/CacheOpParent.cpp +++ b/dom/cache/CacheOpParent.cpp @@ -49,30 +49,30 @@ CacheOpParent::~CacheOpParent() void CacheOpParent::Execute(ManagerId* aManagerId) { NS_ASSERT_OWNINGTHREAD(CacheOpParent); MOZ_ASSERT(!mManager); MOZ_ASSERT(!mVerifier); - RefPtr<Manager> manager; - nsresult rv = Manager::GetOrCreate(aManagerId, getter_AddRefs(manager)); + RefPtr<cache::Manager> manager; + nsresult rv = cache::Manager::GetOrCreate(aManagerId, getter_AddRefs(manager)); if (NS_WARN_IF(NS_FAILED(rv))) { ErrorResult result(rv); Unused << Send__delete__(this, result, void_t()); result.SuppressException(); return; } Execute(manager); } void -CacheOpParent::Execute(Manager* aManager) +CacheOpParent::Execute(cache::Manager* aManager) { NS_ASSERT_OWNINGTHREAD(CacheOpParent); MOZ_ASSERT(!mManager); MOZ_ASSERT(!mVerifier); mManager = aManager; // Handle put op
--- a/dom/cache/CacheOpParent.h +++ b/dom/cache/CacheOpParent.h @@ -32,17 +32,17 @@ public: CacheOpParent(mozilla::ipc::PBackgroundParent* aIpcManager, Namespace aNamespace, const CacheOpArgs& aOpArgs); ~CacheOpParent(); void Execute(ManagerId* aManagerId); void - Execute(Manager* aManager); + Execute(cache::Manager* aManager); void WaitForVerification(PrincipalVerifier* aVerifier); private: // PCacheOpParent methods virtual void ActorDestroy(ActorDestroyReason aReason) override; @@ -62,17 +62,17 @@ private: // utility methods already_AddRefed<nsIInputStream> DeserializeCacheStream(const CacheReadStreamOrVoid& aStreamOrVoid); mozilla::ipc::PBackgroundParent* mIpcManager; const CacheId mCacheId; const Namespace mNamespace; const CacheOpArgs mOpArgs; - RefPtr<Manager> mManager; + RefPtr<cache::Manager> mManager; RefPtr<PrincipalVerifier> mVerifier; NS_DECL_OWNINGTHREAD }; } // namespace cache } // namespace dom } // namespace mozilla
--- a/dom/cache/CacheTypes.ipdlh +++ b/dom/cache/CacheTypes.ipdlh @@ -45,22 +45,22 @@ union CacheReadStreamOrVoid CacheReadStream; }; struct HeadersEntry { nsCString name; nsCString value; }; - struct CacheRequest { nsCString method; nsCString urlWithoutQuery; nsCString urlQuery; + nsCString urlFragment; HeadersEntry[] headers; HeadersGuardEnum headersGuard; nsString referrer; ReferrerPolicy referrerPolicy; RequestMode mode; RequestCredentials credentials; CacheReadStreamOrVoid body; uint32_t contentPolicyType;
--- a/dom/cache/DBSchema.cpp +++ b/dom/cache/DBSchema.cpp @@ -27,30 +27,25 @@ #include "nsNetCID.h" #include "nsPrintfCString.h" #include "nsTArray.h" namespace mozilla { namespace dom { namespace cache { namespace db { - const int32_t kFirstShippedSchemaVersion = 15; - namespace { - // Update this whenever the DB schema is changed. -const int32_t kLatestSchemaVersion = 23; - +const int32_t kLatestSchemaVersion = 24; // --------- // The following constants define the SQL schema. These are defined in the // same order the SQL should be executed in CreateOrMigrateSchema(). They are // broken out as constants for convenient use in validation and migration. // --------- - // The caches table is the single source of truth about what Cache // objects exist for the origin. The contents of the Cache are stored // in the entries table that references back to caches. // // The caches table is also referenced from storage. Rows in storage // represent named Cache objects. There are cases, however, where // a Cache can still exist, but not be in a named Storage. For example, // when content is still using the Cache after CacheStorage::Delete() @@ -98,24 +93,23 @@ const char* const kTableEntries = "response_type INTEGER NOT NULL, " "response_status INTEGER NOT NULL, " "response_status_text TEXT NOT NULL, " "response_headers_guard INTEGER NOT NULL, " "response_body_id TEXT NULL, " "response_security_info_id INTEGER NULL REFERENCES security_info(id), " "response_principal_info TEXT NOT NULL, " "cache_id INTEGER NOT NULL REFERENCES caches(id) ON DELETE CASCADE, " - "request_redirect INTEGER NOT NULL, " "request_referrer_policy INTEGER NOT NULL, " - "request_integrity TEXT NOT NULL" + "request_integrity TEXT NOT NULL, " + "request_url_fragment TEXT NOT NULL" // New columns must be added at the end of table to migrate and // validate properly. ")"; - // Create an index to support the QueryCache() matching algorithm. This // needs to quickly find entries in a given Cache that match the request // URL. The url query is separated in order to support the ignoreSearch // option. Finally, we index hashes of the URL values instead of the // actual strings to avoid excessive disk bloat. The index will duplicate // the contents of the columsn in the index. The hash index will prune // the vast majority of values from the query result so that normal // scanning only has to be done on a few values to find an exact URL match. @@ -1650,16 +1644,17 @@ InsertEntry(mozIStorageConnection* aConn nsCOMPtr<mozIStorageStatement> state; rv = aConn->CreateStatement(NS_LITERAL_CSTRING( "INSERT INTO entries (" "request_method, " "request_url_no_query, " "request_url_no_query_hash, " "request_url_query, " "request_url_query_hash, " + "request_url_fragment, " "request_referrer, " "request_referrer_policy, " "request_headers_guard, " "request_mode, " "request_credentials, " "request_contentpolicytype, " "request_cache, " "request_redirect, " @@ -1674,16 +1669,17 @@ InsertEntry(mozIStorageConnection* aConn "response_principal_info, " "cache_id " ") VALUES (" ":request_method, " ":request_url_no_query, " ":request_url_no_query_hash, " ":request_url_query, " ":request_url_query_hash, " + ":request_url_fragment, " ":request_referrer, " ":request_referrer_policy, " ":request_headers_guard, " ":request_mode, " ":request_credentials, " ":request_contentpolicytype, " ":request_cache, " ":request_redirect, " @@ -1719,29 +1715,29 @@ InsertEntry(mozIStorageConnection* aConn rv = state->BindUTF8StringByName(NS_LITERAL_CSTRING("request_url_query"), aRequest.urlQuery()); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } nsAutoCString urlQueryHash; rv = HashCString(crypto, aRequest.urlQuery(), urlQueryHash); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - rv = state->BindUTF8StringAsBlobByName( NS_LITERAL_CSTRING("request_url_query_hash"), urlQueryHash); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + rv = state->BindUTF8StringByName(NS_LITERAL_CSTRING("request_url_fragment"), + aRequest.urlFragment()); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } rv = state->BindStringByName(NS_LITERAL_CSTRING("request_referrer"), aRequest.referrer()); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - rv = state->BindInt32ByName(NS_LITERAL_CSTRING("request_referrer_policy"), static_cast<int32_t>(aRequest.referrerPolicy())); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - rv = state->BindInt32ByName(NS_LITERAL_CSTRING("request_headers_guard"), static_cast<int32_t>(aRequest.headersGuard())); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } rv = state->BindInt32ByName(NS_LITERAL_CSTRING("request_mode"), static_cast<int32_t>(aRequest.mode())); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -2038,23 +2034,23 @@ ReadResponse(mozIStorageConnection* aCon nsresult ReadRequest(mozIStorageConnection* aConn, EntryId aEntryId, SavedRequest* aSavedRequestOut) { MOZ_ASSERT(!NS_IsMainThread()); MOZ_ASSERT(aConn); MOZ_ASSERT(aSavedRequestOut); - nsCOMPtr<mozIStorageStatement> state; nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING( "SELECT " "request_method, " "request_url_no_query, " "request_url_query, " + "request_url_fragment, " "request_referrer, " "request_referrer_policy, " "request_headers_guard, " "request_mode, " "request_credentials, " "request_contentpolicytype, " "request_cache, " "request_redirect, " @@ -2069,80 +2065,69 @@ ReadRequest(mozIStorageConnection* aConn if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } bool hasMoreData = false; rv = state->ExecuteStep(&hasMoreData); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } rv = state->GetUTF8String(0, aSavedRequestOut->mValue.method()); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - rv = state->GetUTF8String(1, aSavedRequestOut->mValue.urlWithoutQuery()); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - rv = state->GetUTF8String(2, aSavedRequestOut->mValue.urlQuery()); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - - rv = state->GetString(3, aSavedRequestOut->mValue.referrer()); + rv = state->GetUTF8String(3, aSavedRequestOut->mValue.urlFragment()); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + rv = state->GetString(4, aSavedRequestOut->mValue.referrer()); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } int32_t referrerPolicy; - rv = state->GetInt32(4, &referrerPolicy); + rv = state->GetInt32(5, &referrerPolicy); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } aSavedRequestOut->mValue.referrerPolicy() = static_cast<ReferrerPolicy>(referrerPolicy); - int32_t guard; - rv = state->GetInt32(5, &guard); + rv = state->GetInt32(6, &guard); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } aSavedRequestOut->mValue.headersGuard() = static_cast<HeadersGuardEnum>(guard); - int32_t mode; - rv = state->GetInt32(6, &mode); + rv = state->GetInt32(7, &mode); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } aSavedRequestOut->mValue.mode() = static_cast<RequestMode>(mode); - int32_t credentials; - rv = state->GetInt32(7, &credentials); + rv = state->GetInt32(8, &credentials); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } aSavedRequestOut->mValue.credentials() = static_cast<RequestCredentials>(credentials); - int32_t requestContentPolicyType; - rv = state->GetInt32(8, &requestContentPolicyType); + rv = state->GetInt32(9, &requestContentPolicyType); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } aSavedRequestOut->mValue.contentPolicyType() = static_cast<nsContentPolicyType>(requestContentPolicyType); - int32_t requestCache; - rv = state->GetInt32(9, &requestCache); + rv = state->GetInt32(10, &requestCache); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } aSavedRequestOut->mValue.requestCache() = static_cast<RequestCache>(requestCache); - int32_t requestRedirect; - rv = state->GetInt32(10, &requestRedirect); + rv = state->GetInt32(11, &requestRedirect); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } aSavedRequestOut->mValue.requestRedirect() = static_cast<RequestRedirect>(requestRedirect); - - rv = state->GetString(11, aSavedRequestOut->mValue.integrity()); + rv = state->GetString(12, aSavedRequestOut->mValue.integrity()); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - bool nullBody = false; - rv = state->GetIsNull(12, &nullBody); + rv = state->GetIsNull(13, &nullBody); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } aSavedRequestOut->mHasBodyId = !nullBody; - if (aSavedRequestOut->mHasBodyId) { - rv = ExtractId(state, 12, &aSavedRequestOut->mBodyId); + rv = ExtractId(state, 13, &aSavedRequestOut->mBodyId); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } } - rv = aConn->CreateStatement(NS_LITERAL_CSTRING( "SELECT " "name, " "value " "FROM request_headers " "WHERE entry_id=:entry_id;" ), getter_AddRefs(state)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -2487,31 +2472,30 @@ struct Migration nsresult MigrateFrom15To16(mozIStorageConnection* aConn, bool& aRewriteSchema); nsresult MigrateFrom16To17(mozIStorageConnection* aConn, bool& aRewriteSchema); nsresult MigrateFrom17To18(mozIStorageConnection* aConn, bool& aRewriteSchema); nsresult MigrateFrom18To19(mozIStorageConnection* aConn, bool& aRewriteSchema); nsresult MigrateFrom19To20(mozIStorageConnection* aConn, bool& aRewriteSchema); nsresult MigrateFrom20To21(mozIStorageConnection* aConn, bool& aRewriteSchema); nsresult MigrateFrom21To22(mozIStorageConnection* aConn, bool& aRewriteSchema); nsresult MigrateFrom22To23(mozIStorageConnection* aConn, bool& aRewriteSchema); - +nsresult MigrateFrom23To24(mozIStorageConnection* aConn, bool& aRewriteSchema); // Configure migration functions to run for the given starting version. Migration sMigrationList[] = { Migration(15, MigrateFrom15To16), Migration(16, MigrateFrom16To17), Migration(17, MigrateFrom17To18), Migration(18, MigrateFrom18To19), Migration(19, MigrateFrom19To20), Migration(20, MigrateFrom20To21), Migration(21, MigrateFrom21To22), Migration(22, MigrateFrom22To23), + Migration(23, MigrateFrom23To24), }; - uint32_t sMigrationListLength = sizeof(sMigrationList) / sizeof(Migration); - nsresult RewriteEntriesSchema(mozIStorageConnection* aConn) { nsresult rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( "PRAGMA writable_schema = ON" )); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -2996,21 +2980,37 @@ nsresult MigrateFrom21To22(mozIStorageCo nsresult MigrateFrom22To23(mozIStorageConnection* aConn, bool& aRewriteSchema) { MOZ_ASSERT(!NS_IsMainThread()); MOZ_ASSERT(aConn); // The only change between 22 and 23 was a different snappy compression // format, but it's backwards-compatible. - nsresult rv = aConn->SetSchemaVersion(23); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + return rv; +} +nsresult MigrateFrom23To24(mozIStorageConnection* aConn, bool& aRewriteSchema) +{ + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(aConn); + + // Add the request_url_fragment column. + nsresult rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "ALTER TABLE entries " + "ADD COLUMN request_url_fragment TEXT NOT NULL DEFAULT ''" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + rv = aConn->SetSchemaVersion(24); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + aRewriteSchema = true; return rv; } } // anonymous namespace - } // namespace db } // namespace cache } // namespace dom } // namespace mozilla
--- a/dom/cache/TypeUtils.cpp +++ b/dom/cache/TypeUtils.cpp @@ -117,40 +117,35 @@ TypeUtils::ToInternalRequest(const Ownin void TypeUtils::ToCacheRequest(CacheRequest& aOut, InternalRequest* aIn, BodyAction aBodyAction, SchemeAction aSchemeAction, nsTArray<UniquePtr<AutoIPCStream>>& aStreamCleanupList, ErrorResult& aRv) { MOZ_ASSERT(aIn); - aIn->GetMethod(aOut.method()); - - nsAutoCString url; - aIn->GetURL(url); - + nsCString url(aIn->GetURLWithoutFragment()); bool schemeValid; ProcessURL(url, &schemeValid, &aOut.urlWithoutQuery(), &aOut.urlQuery(), aRv); if (aRv.Failed()) { return; } - if (!schemeValid) { if (aSchemeAction == TypeErrorOnInvalidScheme) { NS_ConvertUTF8toUTF16 urlUTF16(url); aRv.ThrowTypeError<MSG_INVALID_URL_SCHEME>(NS_LITERAL_STRING("Request"), urlUTF16); return; } } + aOut.urlFragment() = aIn->GetFragment(); aIn->GetReferrer(aOut.referrer()); aOut.referrerPolicy() = aIn->ReferrerPolicy_(); - RefPtr<InternalHeaders> headers = aIn->Headers(); MOZ_ASSERT(headers); ToHeadersEntryList(aOut.headers(), headers); aOut.headersGuard() = headers->Guard(); aOut.mode() = aIn->Mode(); aOut.credentials() = aIn->GetCredentialsMode(); aOut.contentPolicyType() = aIn->ContentPolicyType(); aOut.requestCache() = aIn->GetCacheMode(); @@ -301,27 +296,24 @@ TypeUtils::ToResponse(const CacheRespons default: MOZ_CRASH("Unexpected ResponseType!"); } MOZ_ASSERT(ir); RefPtr<Response> ref = new Response(GetGlobalObject(), ir); return ref.forget(); } - already_AddRefed<InternalRequest> TypeUtils::ToInternalRequest(const CacheRequest& aIn) { nsAutoCString url(aIn.urlWithoutQuery()); url.Append(aIn.urlQuery()); - - RefPtr<InternalRequest> internalRequest = new InternalRequest(url); - + RefPtr<InternalRequest> internalRequest = + new InternalRequest(url, aIn.urlFragment()); internalRequest->SetMethod(aIn.method()); - internalRequest->SetReferrer(aIn.referrer()); internalRequest->SetReferrerPolicy(aIn.referrerPolicy()); internalRequest->SetMode(aIn.mode()); internalRequest->SetCredentialsMode(aIn.credentials()); internalRequest->SetContentPolicyType(aIn.contentPolicyType()); internalRequest->SetCacheMode(aIn.requestCache()); internalRequest->SetRedirectMode(aIn.requestRedirect()); internalRequest->SetIntegrity(aIn.integrity());
--- a/dom/cache/test/mochitest/test_cache_keys.js +++ b/dom/cache/test/mochitest/test_cache_keys.js @@ -10,16 +10,21 @@ caches.open(name).then(function(cache) { c = cache; return c.addAll(tests); }).then(function() { // Add another cache entry using Cache.add var another = "//mochi.test:8888/?yetanother" + context; tests.push(another); return c.add(another); }).then(function() { + // Add another cache entry with URL fragment using Cache.add + var anotherWithFragment = "//mochi.test:8888/?fragment" + context + "#fragment"; + tests.push(anotherWithFragment); + return c.add(anotherWithFragment); +}).then(function() { return c.keys(); }).then(function(keys) { is(keys.length, tests.length, "Same number of elements"); // Verify both the insertion order of the requests and their validity. keys.forEach(function(r, i) { ok(r instanceof Request, "Valid request object"); ok(r.url.indexOf(tests[i]) >= 0, "Valid URL"); });
--- a/dom/cache/test/mochitest/test_cache_match_request.js +++ b/dom/cache/test/mochitest/test_cache_match_request.js @@ -20,31 +20,29 @@ function checkResponse(r, expectedBody) "Both responses should have the same status text"); return r.text().then(function(text) { // Avoid dumping out the large response text to the log if they're equal. if (text !== expectedBody) { is(text, responseText, "The response body should be correct"); } }); } - fetch(new Request(request)).then(function(r) { response = r; return response.text(); }).then(function(text) { responseText = text; return testRequest(request, unknownRequest, requestWithAltQS, request.url.replace("#fragment", "#other")); }).then(function() { return testRequest(request.url, unknownRequest.url, requestWithAltQS.url, request.url.replace("#fragment", "#other")); }).then(function() { testDone(); }); - // The request argument can either be a URL string, or a Request object. function testRequest(request, unknownRequest, requestWithAlternateQueryString, requestWithDifferentFragment) { return caches.open(name).then(function(cache) { c = cache; return c.add(request); }).then(function() { return Promise.all(
--- a/dom/events/EventListenerManager.cpp +++ b/dom/events/EventListenerManager.cpp @@ -867,16 +867,17 @@ EventListenerManager::SetEventHandler(ns scriptSample.Assign(attr); scriptSample.AppendLiteral(" attribute on "); scriptSample.Append(tagName); scriptSample.AppendLiteral(" element"); bool allowsInlineScript = true; rv = csp->GetAllowsInline(nsIContentPolicy::TYPE_SCRIPT, EmptyString(), // aNonce + false, // aParserCreated scriptSample, 0, // aLineNumber &allowsInlineScript); NS_ENSURE_SUCCESS(rv, rv); // return early if CSP wants us to block inline scripts if (!allowsInlineScript) { return NS_OK;
--- a/dom/fetch/FetchDriver.cpp +++ b/dom/fetch/FetchDriver.cpp @@ -370,29 +370,25 @@ FetchDriver::HttpFetch() } rv = chan->AsyncOpen2(this); NS_ENSURE_SUCCESS(rv, rv); // Step 4 onwards of "HTTP Fetch" is handled internally by Necko. return NS_OK; } - already_AddRefed<InternalResponse> FetchDriver::BeginAndGetFilteredResponse(InternalResponse* aResponse, bool aFoundOpaqueRedirect) { MOZ_ASSERT(aResponse); - AutoTArray<nsCString, 4> reqURLList; - mRequest->GetURLList(reqURLList); - + mRequest->GetURLListWithoutFragment(reqURLList); MOZ_ASSERT(!reqURLList.IsEmpty()); aResponse->SetURLList(reqURLList); - RefPtr<InternalResponse> filteredResponse; if (aFoundOpaqueRedirect) { filteredResponse = aResponse->OpaqueRedirectResponse(); } else { switch (mRequest->GetResponseTainting()) { case LoadTainting::Basic: filteredResponse = aResponse->BasicResponse(); break; @@ -803,25 +799,28 @@ FetchDriver::AsyncOnChannelRedirect(nsIC nsCOMPtr<nsIURI> uri; MOZ_ALWAYS_SUCCEEDS(aNewChannel->GetURI(getter_AddRefs(uri))); nsCOMPtr<nsIURI> uriClone; nsresult rv = uri->CloneIgnoringRef(getter_AddRefs(uriClone)); if(NS_WARN_IF(NS_FAILED(rv))){ return rv; } - nsCString spec; rv = uriClone->GetSpec(spec); if(NS_WARN_IF(NS_FAILED(rv))){ return rv; } + nsCString fragment; + rv = uri->GetRef(fragment); + if(NS_WARN_IF(NS_FAILED(rv))){ + return rv; + } - mRequest->AddURL(spec); - + mRequest->AddURL(spec, fragment); NS_ConvertUTF8toUTF16 tRPHeaderValue(tRPHeaderCValue); // updates request’s associated referrer policy according to the // Referrer-Policy header (if any). if (!tRPHeaderValue.IsEmpty()) { net::ReferrerPolicy net_referrerPolicy = nsContentUtils::GetReferrerPolicyFromHeader(tRPHeaderValue); if (net_referrerPolicy != net::RP_Unset) { ReferrerPolicy referrerPolicy = mRequest->ReferrerPolicy_();
--- a/dom/fetch/InternalRequest.cpp +++ b/dom/fetch/InternalRequest.cpp @@ -14,28 +14,26 @@ #include "mozilla/dom/FetchTypes.h" #include "mozilla/dom/ScriptSettings.h" #include "mozilla/dom/workers/Workers.h" #include "WorkerPrivate.h" namespace mozilla { namespace dom { - // The global is used to extract the principal. already_AddRefed<InternalRequest> InternalRequest::GetRequestConstructorCopy(nsIGlobalObject* aGlobal, ErrorResult& aRv) const { MOZ_RELEASE_ASSERT(!mURLList.IsEmpty(), "Internal Request's urlList should not be empty when copied from constructor."); - - RefPtr<InternalRequest> copy = new InternalRequest(mURLList.LastElement()); + RefPtr<InternalRequest> copy = new InternalRequest(mURLList.LastElement(), + mFragment); copy->SetMethod(mMethod); copy->mHeaders = new InternalHeaders(*mHeaders); copy->SetUnsafeRequest(); - copy->mBodyStream = mBodyStream; copy->mForceOriginHeader = true; // The "client" is not stored in our implementation. Fetch API users should // use the appropriate window/document/principal and other Gecko security // mechanisms as appropriate. copy->mSameOriginDataURL = true; copy->mPreserveContentCodings = true; copy->mReferrer = mReferrer; @@ -70,21 +68,20 @@ InternalRequest::Clone() nsresult rv = NS_CloneInputStream(mBodyStream, getter_AddRefs(clonedBody), getter_AddRefs(replacementBody)); if (NS_WARN_IF(NS_FAILED(rv))) { return nullptr; } clone->mBodyStream.swap(clonedBody); if (replacementBody) { mBodyStream.swap(replacementBody); } - return clone.forget(); } - -InternalRequest::InternalRequest(const nsACString& aURL) +InternalRequest::InternalRequest(const nsACString& aURL, + const nsACString& aFragment) : mMethod("GET") , mHeaders(new InternalHeaders(HeadersGuardEnum::None)) , mContentPolicyType(nsIContentPolicy::TYPE_FETCH) , mReferrer(NS_LITERAL_STRING(kFETCH_CLIENT_REFERRER_STR)) , mReferrerPolicy(ReferrerPolicy::_empty) , mEnvironmentReferrerPolicy(net::RP_Default) , mMode(RequestMode::No_cors) , mCredentialsMode(RequestCredentials::Omit) @@ -100,20 +97,20 @@ InternalRequest::InternalRequest(const n // specification does not handle this yet. , mSameOriginDataURL(true) , mSkipServiceWorker(false) , mSynchronous(false) , mUnsafeRequest(false) , mUseURLCredentials(false) { MOZ_ASSERT(!aURL.IsEmpty()); - AddURL(aURL); + AddURL(aURL, aFragment); } - InternalRequest::InternalRequest(const nsACString& aURL, + const nsACString& aFragment, const nsACString& aMethod, already_AddRefed<InternalHeaders> aHeaders, RequestCache aCacheMode, RequestMode aMode, RequestRedirect aRequestRedirect, RequestCredentials aRequestCredentials, const nsAString& aReferrer, ReferrerPolicy aReferrerPolicy, @@ -137,33 +134,33 @@ InternalRequest::InternalRequest(const n // FIXME See the above comment in the default constructor. , mSameOriginDataURL(true) , mSkipServiceWorker(false) , mSynchronous(false) , mUnsafeRequest(false) , mUseURLCredentials(false) { MOZ_ASSERT(!aURL.IsEmpty()); - AddURL(aURL); + AddURL(aURL, aFragment); } - InternalRequest::InternalRequest(const InternalRequest& aOther) : mMethod(aOther.mMethod) , mURLList(aOther.mURLList) , mHeaders(new InternalHeaders(*aOther.mHeaders)) , mContentPolicyType(aOther.mContentPolicyType) , mReferrer(aOther.mReferrer) , mReferrerPolicy(aOther.mReferrerPolicy) , mEnvironmentReferrerPolicy(aOther.mEnvironmentReferrerPolicy) , mMode(aOther.mMode) , mCredentialsMode(aOther.mCredentialsMode) , mResponseTainting(aOther.mResponseTainting) , mCacheMode(aOther.mCacheMode) , mRedirectMode(aOther.mRedirectMode) , mIntegrity(aOther.mIntegrity) + , mFragment(aOther.mFragment) , mAuthenticationFlag(aOther.mAuthenticationFlag) , mForceOriginHeader(aOther.mForceOriginHeader) , mPreserveContentCodings(aOther.mPreserveContentCodings) , mSameOriginDataURL(aOther.mSameOriginDataURL) , mSkipServiceWorker(aOther.mSkipServiceWorker) , mSynchronous(aOther.mSynchronous) , mUnsafeRequest(aOther.mUnsafeRequest) , mUseURLCredentials(aOther.mUseURLCredentials)
--- a/dom/fetch/InternalRequest.h +++ b/dom/fetch/InternalRequest.h @@ -82,27 +82,24 @@ namespace dom { * TODO: Add a content type for favicon * TODO: Add a content type for download */ class Request; class IPCInternalRequest; #define kFETCH_CLIENT_REFERRER_STR "about:client" - class InternalRequest final { friend class Request; - public: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(InternalRequest) - - explicit InternalRequest(const nsACString& aURL); - + InternalRequest(const nsACString& aURL, const nsACString& aFragment); InternalRequest(const nsACString& aURL, + const nsACString& aFragment, const nsACString& aMethod, already_AddRefed<InternalHeaders> aHeaders, RequestCache aCacheMode, RequestMode aMode, RequestRedirect aRequestRedirect, RequestCredentials aRequestCredentials, const nsAString& aReferrer, ReferrerPolicy aReferrerPolicy, @@ -129,47 +126,59 @@ public: bool HasSimpleMethod() const { return mMethod.LowerCaseEqualsASCII("get") || mMethod.LowerCaseEqualsASCII("post") || mMethod.LowerCaseEqualsASCII("head"); } - - // GetURL should get the request's current url. A request has an associated - // current url. It is a pointer to the last fetch URL in request's url list. + // GetURL should get the request's current url with fragment. A request has + // an associated current url. It is a pointer to the last fetch URL in + // request's url list. void GetURL(nsACString& aURL) const { - MOZ_RELEASE_ASSERT(!mURLList.IsEmpty(), "Internal Request's urlList should not be empty."); - - aURL.Assign(mURLList.LastElement()); + aURL.Assign(GetURLWithoutFragment()); + if (GetFragment().IsEmpty()) { + return; + } + aURL.Append(NS_LITERAL_CSTRING("#")); + aURL.Append(GetFragment()); } + const nsCString& + GetURLWithoutFragment() const + { + MOZ_RELEASE_ASSERT(!mURLList.IsEmpty(), + "Internal Request's urlList should not be empty."); + + return mURLList.LastElement(); + } // AddURL should append the url into url list. - // Normally we strip the fragment from the URL in Request::Constructor. - // If internal code is directly constructing this object they must - // strip the fragment first. Since these should be well formed URLs we - // can use a simple check for a fragment here. The full parser is - // difficult to use off the main thread. + // Normally we strip the fragment from the URL in Request::Constructor and + // pass the fragment as the second argument into it. + // If a fragment is present in the URL it must be stripped and passed in + // separately. void - AddURL(const nsACString& aURL) + AddURL(const nsACString& aURL, const nsACString& aFragment) { MOZ_ASSERT(!aURL.IsEmpty()); + MOZ_ASSERT(!aURL.Contains('#')); + mURLList.AppendElement(aURL); - MOZ_ASSERT(mURLList.LastElement().Find(NS_LITERAL_CSTRING("#")) == kNotFound); + + mFragment.Assign(aFragment); } - + // Get the URL list without their fragments. void - GetURLList(nsTArray<nsCString>& aURLList) + GetURLListWithoutFragment(nsTArray<nsCString>& aURLList) { aURLList.Assign(mURLList); } - void GetReferrer(nsAString& aReferrer) const { aReferrer.Assign(mReferrer); } void SetReferrer(const nsAString& aReferrer) @@ -316,30 +325,33 @@ public: mRedirectMode = aRedirectMode; } const nsString& GetIntegrity() const { return mIntegrity; } - void SetIntegrity(const nsAString& aIntegrity) { MOZ_ASSERT(mIntegrity.IsEmpty()); mIntegrity.Assign(aIntegrity); } + const nsCString& + GetFragment() const + { + return mFragment; + } nsContentPolicyType ContentPolicyType() const { return mContentPolicyType; } - void SetContentPolicyType(nsContentPolicyType aContentPolicyType); void OverrideContentPolicyType(nsContentPolicyType aContentPolicyType); RequestContext Context() const @@ -486,25 +498,23 @@ private: ReferrerPolicy mReferrerPolicy; // This will be used for request created from Window or Worker contexts // In case there's no Referrer Policy in Request, this will be passed to // channel. // The Environment Referrer Policy should be net::ReferrerPolicy so that it // could be associated with nsIHttpChannel. net::ReferrerPolicy mEnvironmentReferrerPolicy; - RequestMode mMode; RequestCredentials mCredentialsMode; MOZ_INIT_OUTSIDE_CTOR LoadTainting mResponseTainting; RequestCache mCacheMode; RequestRedirect mRedirectMode; - nsString mIntegrity; - + nsCString mFragment; MOZ_INIT_OUTSIDE_CTOR bool mAuthenticationFlag; MOZ_INIT_OUTSIDE_CTOR bool mForceOriginHeader; MOZ_INIT_OUTSIDE_CTOR bool mPreserveContentCodings; MOZ_INIT_OUTSIDE_CTOR bool mSameOriginDataURL; MOZ_INIT_OUTSIDE_CTOR bool mSkipServiceWorker; MOZ_INIT_OUTSIDE_CTOR bool mSynchronous; MOZ_INIT_OUTSIDE_CTOR bool mUnsafeRequest; MOZ_INIT_OUTSIDE_CTOR bool mUseURLCredentials;
--- a/dom/fetch/InternalResponse.h +++ b/dom/fetch/InternalResponse.h @@ -80,47 +80,40 @@ public: return mType; } bool IsError() const { return Type() == ResponseType::Error; } - // GetUrl should return last fetch URL in response's url list and null if // response's url list is the empty list. - void - GetURL(nsCString& aURL) const + const nsCString& + GetURL() const { // Empty urlList when response is a synthetic response. if (mURLList.IsEmpty()) { - aURL.Truncate(); - return; + return EmptyCString(); } - - aURL.Assign(mURLList.LastElement()); + return mURLList.LastElement(); } - void GetURLList(nsTArray<nsCString>& aURLList) const { aURLList.Assign(mURLList); } - - void - GetUnfilteredURL(nsCString& aURL) const + const nsCString& + GetUnfilteredURL() const { if (mWrappedResponse) { - return mWrappedResponse->GetURL(aURL); + return mWrappedResponse->GetURL(); } - - return GetURL(aURL); + return GetURL(); } - void GetUnfilteredURLList(nsTArray<nsCString>& aURLList) const { if (mWrappedResponse) { return mWrappedResponse->GetURLList(aURLList); } return GetURLList(aURLList);
--- a/dom/fetch/Request.cpp +++ b/dom/fetch/Request.cpp @@ -84,147 +84,158 @@ ParseURLFromDocument(nsIDocument* aDocum nsCOMPtr<nsIURI> baseURI = aDocument->GetBaseURI(); nsCOMPtr<nsIURI> resolvedURI; aRv = NS_NewURI(getter_AddRefs(resolvedURI), aInput, nullptr, baseURI); if (NS_WARN_IF(aRv.Failed())) { aRv.ThrowTypeError<MSG_INVALID_URL>(aInput); } return resolvedURI.forget(); } - void GetRequestURLFromDocument(nsIDocument* aDocument, const nsAString& aInput, - nsAString& aRequestURL, ErrorResult& aRv) + nsAString& aRequestURL, nsACString& aURLfragment, + ErrorResult& aRv) { nsCOMPtr<nsIURI> resolvedURI = ParseURLFromDocument(aDocument, aInput, aRv); if (aRv.Failed()) { return; } - // This fails with URIs with weird protocols, even when they are valid, // so we ignore the failure nsAutoCString credentials; Unused << resolvedURI->GetUserPass(credentials); if (!credentials.IsEmpty()) { aRv.ThrowTypeError<MSG_URL_HAS_CREDENTIALS>(aInput); return; } nsCOMPtr<nsIURI> resolvedURIClone; // We use CloneIgnoringRef to strip away the fragment even if the original URI // is immutable. aRv = resolvedURI->CloneIgnoringRef(getter_AddRefs(resolvedURIClone)); if (NS_WARN_IF(aRv.Failed())) { return; } - nsAutoCString spec; aRv = resolvedURIClone->GetSpec(spec); if (NS_WARN_IF(aRv.Failed())) { return; } + CopyUTF8toUTF16(spec, aRequestURL); - CopyUTF8toUTF16(spec, aRequestURL); + // Get the fragment from nsIURI. + aRv = resolvedURI->GetRef(aURLfragment); + if (NS_WARN_IF(aRv.Failed())) { + return; + } } - already_AddRefed<nsIURI> ParseURLFromChrome(const nsAString& aInput, ErrorResult& aRv) { MOZ_ASSERT(NS_IsMainThread()); - nsCOMPtr<nsIURI> uri; aRv = NS_NewURI(getter_AddRefs(uri), aInput, nullptr, nullptr); if (NS_WARN_IF(aRv.Failed())) { aRv.ThrowTypeError<MSG_INVALID_URL>(aInput); } return uri.forget(); } - void GetRequestURLFromChrome(const nsAString& aInput, nsAString& aRequestURL, - ErrorResult& aRv) + nsACString& aURLfragment, ErrorResult& aRv) { nsCOMPtr<nsIURI> uri = ParseURLFromChrome(aInput, aRv); if (aRv.Failed()) { return; } - // This fails with URIs with weird protocols, even when they are valid, // so we ignore the failure nsAutoCString credentials; Unused << uri->GetUserPass(credentials); if (!credentials.IsEmpty()) { aRv.ThrowTypeError<MSG_URL_HAS_CREDENTIALS>(aInput); return; } nsCOMPtr<nsIURI> uriClone; // We use CloneIgnoringRef to strip away the fragment even if the original URI // is immutable. aRv = uri->CloneIgnoringRef(getter_AddRefs(uriClone)); if (NS_WARN_IF(aRv.Failed())) { return; } - nsAutoCString spec; aRv = uriClone->GetSpec(spec); if (NS_WARN_IF(aRv.Failed())) { return; } + CopyUTF8toUTF16(spec, aRequestURL); - CopyUTF8toUTF16(spec, aRequestURL); + // Get the fragment from nsIURI. + aRv = uri->GetRef(aURLfragment); + if (NS_WARN_IF(aRv.Failed())) { + return; + } } - already_AddRefed<URL> ParseURLFromWorker(const GlobalObject& aGlobal, const nsAString& aInput, ErrorResult& aRv) { workers::WorkerPrivate* worker = workers::GetCurrentThreadWorkerPrivate(); MOZ_ASSERT(worker); worker->AssertIsOnWorkerThread(); NS_ConvertUTF8toUTF16 baseURL(worker->GetLocationInfo().mHref); RefPtr<URL> url = URL::WorkerConstructor(aGlobal, aInput, baseURL, aRv); if (NS_WARN_IF(aRv.Failed())) { aRv.ThrowTypeError<MSG_INVALID_URL>(aInput); } return url.forget(); } - void GetRequestURLFromWorker(const GlobalObject& aGlobal, const nsAString& aInput, - nsAString& aRequestURL, ErrorResult& aRv) + nsAString& aRequestURL, nsACString& aURLfragment, + ErrorResult& aRv) { RefPtr<URL> url = ParseURLFromWorker(aGlobal, aInput, aRv); if (aRv.Failed()) { return; } - nsString username; url->GetUsername(username, aRv); if (NS_WARN_IF(aRv.Failed())) { return; } nsString password; url->GetPassword(password, aRv); if (NS_WARN_IF(aRv.Failed())) { return; } - if (!username.IsEmpty() || !password.IsEmpty()) { aRv.ThrowTypeError<MSG_URL_HAS_CREDENTIALS>(aInput); return; } + // Get the fragment from URL. + nsAutoString fragment; + url->GetHash(fragment, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + // Note: URL::GetHash() includes the "#" and we want the fragment with out + // the hash symbol. + if (!fragment.IsEmpty()) { + CopyUTF16toUTF8(Substring(fragment, 1), aURLfragment); + } url->SetHash(EmptyString(), aRv); if (NS_WARN_IF(aRv.Failed())) { return; } - url->Stringify(aRequestURL, aRv); if (NS_WARN_IF(aRv.Failed())) { return; } } class ReferrerSameOriginChecker final : public workers::WorkerMainThreadRunnable { @@ -279,48 +290,43 @@ Request::Constructor(const GlobalObject& aRv.ThrowTypeError<MSG_FETCH_BODY_CONSUMED_ERROR>(); return nullptr; } if (body) { temporaryBody = body; } request = inputReq->GetInternalRequest(); - } else { // aInput is USVString. // We need to get url before we create a InternalRequest. nsAutoString input; input.Assign(aInput.GetAsUSVString()); - nsAutoString requestURL; + nsCString fragment; if (NS_IsMainThread()) { nsIDocument* doc = GetEntryDocument(); if (doc) { - GetRequestURLFromDocument(doc, input, requestURL, aRv); + GetRequestURLFromDocument(doc, input, requestURL, fragment, aRv); } else { // If we don't have a document, we must assume that this is a full URL. - GetRequestURLFromChrome(input, requestURL, aRv); + GetRequestURLFromChrome(input, requestURL, fragment, aRv); } } else { - GetRequestURLFromWorker(aGlobal, input, requestURL, aRv); + GetRequestURLFromWorker(aGlobal, input, requestURL, fragment, aRv); } - if (NS_WARN_IF(aRv.Failed())) { return nullptr; } - - request = new InternalRequest(NS_ConvertUTF16toUTF8(requestURL)); + request = new InternalRequest(NS_ConvertUTF16toUTF8(requestURL), fragment); } - request = request->GetRequestConstructorCopy(global, aRv); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } - RequestMode fallbackMode = RequestMode::EndGuard_; RequestCredentials fallbackCredentials = RequestCredentials::EndGuard_; RequestCache fallbackCache = RequestCache::EndGuard_; if (aInput.IsUSVString()) { fallbackMode = RequestMode::Cors; fallbackCredentials = RequestCredentials::Omit; fallbackCache = RequestCache::Default; }
--- a/dom/fetch/Response.h +++ b/dom/fetch/Response.h @@ -43,31 +43,26 @@ public: return ResponseBinding::Wrap(aCx, this, aGivenProto); } ResponseType Type() const { return mInternalResponse->Type(); } - void GetUrl(nsAString& aUrl) const { - nsCString url; - mInternalResponse->GetURL(url); - CopyUTF8toUTF16(url, aUrl); + CopyUTF8toUTF16(mInternalResponse->GetURL(), aUrl); } - bool Redirected() const { return mInternalResponse->IsRedirected(); } - uint16_t Status() const { return mInternalResponse->GetStatus(); } bool Ok() const
--- a/dom/flyweb/HttpServer.cpp +++ b/dom/flyweb/HttpServer.cpp @@ -580,26 +580,23 @@ HttpServer::Connection::ConsumeLine(cons MOZ_ASSERT(!mPendingReq); // Process request line nsCWhitespaceTokenizer tokens(Substring(aBuffer, aLength)); NS_ENSURE_TRUE(tokens.hasMoreTokens(), NS_ERROR_UNEXPECTED); nsDependentCSubstring method = tokens.nextToken(); NS_ENSURE_TRUE(NS_IsValidHTTPToken(method), NS_ERROR_UNEXPECTED); - NS_ENSURE_TRUE(tokens.hasMoreTokens(), NS_ERROR_UNEXPECTED); nsDependentCSubstring url = tokens.nextToken(); // Seems like it's also allowed to pass full urls with scheme+host+port. // May need to support that. NS_ENSURE_TRUE(url.First() == '/', NS_ERROR_UNEXPECTED); - - mPendingReq = new InternalRequest(url); + mPendingReq = new InternalRequest(url, /* aURLFragment */ EmptyCString()); mPendingReq->SetMethod(method); - NS_ENSURE_TRUE(tokens.hasMoreTokens(), NS_ERROR_UNEXPECTED); nsDependentCSubstring version = tokens.nextToken(); NS_ENSURE_TRUE(StringBeginsWith(version, NS_LITERAL_CSTRING("HTTP/1.")), NS_ERROR_UNEXPECTED); nsresult rv; // This integer parsing is likely not strict enough. nsCString reqVersion; reqVersion = Substring(version, MOZ_ARRAY_LENGTH("HTTP/1.") - 1);
--- a/dom/interfaces/security/nsIContentSecurityPolicy.idl +++ b/dom/interfaces/security/nsIContentSecurityPolicy.idl @@ -126,26 +126,28 @@ interface nsIContentSecurityPolicy : nsI void appendPolicy(in AString policyString, in boolean reportOnly, in boolean deliveredViaMetaTag); /* * Whether this policy allows inline script or style. * @param aContentPolicyType Either TYPE_SCRIPT or TYPE_STYLESHEET * @param aNonce The nonce string to check against the policy + * @param aParserCreated If the script element was created by the HTML Parser * @param aContent The content of the inline resource to hash * (and compare to the hashes listed in the policy) * @param aLineNumber The line number of the inline resource * (used for reporting) * @return * Whether or not the effects of the inline style should be allowed * (block the rules if false). */ boolean getAllowsInline(in nsContentPolicyType aContentPolicyType, in AString aNonce, + in boolean aParserCreated, in AString aContent, in unsigned long aLineNumber); /** * whether this policy allows eval and eval-like functions * such as setTimeout("code string", time). * @param shouldReportViolations * Whether or not the use of eval should be reported.
--- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -20,16 +20,17 @@ #include "mozilla/LookAndFeel.h" #include "mozilla/Preferences.h" #include "mozilla/ProcessHangMonitorIPC.h" #include "mozilla/Unused.h" #include "mozilla/devtools/HeapSnapshotTempFileHelperChild.h" #include "mozilla/docshell/OfflineCacheUpdateChild.h" #include "mozilla/dom/ContentBridgeChild.h" #include "mozilla/dom/ContentBridgeParent.h" +#include "mozilla/dom/VideoDecoderManagerChild.h" #include "mozilla/dom/ContentParent.h" #include "mozilla/dom/DataTransfer.h" #include "mozilla/dom/DOMStorageIPC.h" #include "mozilla/dom/ExternalHelperAppChild.h" #include "mozilla/dom/FlyWebPublishedServerIPC.h" #include "mozilla/dom/GetFilesHelper.h" #include "mozilla/dom/PCrashReporterChild.h" #include "mozilla/dom/ProcessGlobal.h" @@ -1176,34 +1177,37 @@ ContentChild::RecvGMPsChanged(nsTArray<G { GeckoMediaPluginServiceChild::UpdateGMPCapabilities(Move(capabilities)); return true; } bool ContentChild::RecvInitRendering(Endpoint<PCompositorBridgeChild>&& aCompositor, Endpoint<PImageBridgeChild>&& aImageBridge, - Endpoint<PVRManagerChild>&& aVRBridge) + Endpoint<PVRManagerChild>&& aVRBridge, + Endpoint<PVideoDecoderManagerChild>&& aVideoManager) { if (!CompositorBridgeChild::InitForContent(Move(aCompositor))) { return false; } if (!ImageBridgeChild::InitForContent(Move(aImageBridge))) { return false; } if (!gfx::VRManagerChild::InitForContent(Move(aVRBridge))) { return false; } + VideoDecoderManagerChild::InitForContent(Move(aVideoManager)); return true; } bool ContentChild::RecvReinitRendering(Endpoint<PCompositorBridgeChild>&& aCompositor, Endpoint<PImageBridgeChild>&& aImageBridge, - Endpoint<PVRManagerChild>&& aVRBridge) + Endpoint<PVRManagerChild>&& aVRBridge, + Endpoint<PVideoDecoderManagerChild>&& aVideoManager) { nsTArray<RefPtr<TabChild>> tabs = TabChild::GetAll(); // Zap all the old layer managers we have lying around. for (const auto& tabChild : tabs) { if (tabChild->LayersId()) { tabChild->InvalidateLayers(); } @@ -1221,16 +1225,18 @@ ContentChild::RecvReinitRendering(Endpoi } // Establish new PLayerTransactions. for (const auto& tabChild : tabs) { if (tabChild->LayersId()) { tabChild->ReinitRendering(); } } + + VideoDecoderManagerChild::InitForContent(Move(aVideoManager)); return true; } PBackgroundChild* ContentChild::AllocPBackgroundChild(Transport* aTransport, ProcessId aOtherProcess) { return BackgroundChild::Alloc(aTransport, aOtherProcess);
--- a/dom/ipc/ContentChild.h +++ b/dom/ipc/ContentChild.h @@ -164,23 +164,25 @@ public: bool RecvGMPsChanged(nsTArray<GMPCapabilityData>&& capabilities) override; bool RecvInitRendering( Endpoint<PCompositorBridgeChild>&& aCompositor, Endpoint<PImageBridgeChild>&& aImageBridge, - Endpoint<PVRManagerChild>&& aVRBridge) override; + Endpoint<PVRManagerChild>&& aVRBridge, + Endpoint<PVideoDecoderManagerChild>&& aVideoManager) override; bool RecvReinitRendering( Endpoint<PCompositorBridgeChild>&& aCompositor, Endpoint<PImageBridgeChild>&& aImageBridge, - Endpoint<PVRManagerChild>&& aVRBridge) override; + Endpoint<PVRManagerChild>&& aVRBridge, + Endpoint<PVideoDecoderManagerChild>&& aVideoManager) override; PProcessHangMonitorChild* AllocPProcessHangMonitorChild(Transport* aTransport, ProcessId aOtherProcess) override; virtual bool RecvSetProcessSandbox(const MaybeFileDesc& aBroker) override; PBackgroundChild*
--- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -1022,23 +1022,16 @@ ContentParent::RecvFindPlugins(const uin nsresult* aRv, nsTArray<PluginTag>* aPlugins, uint32_t* aNewPluginEpoch) { *aRv = mozilla::plugins::FindPluginsForContent(aPluginEpoch, aPlugins, aNewPluginEpoch); return true; } -bool -ContentParent::RecvInitVideoDecoderManager(Endpoint<PVideoDecoderManagerChild>* aEndpoint) -{ - GPUProcessManager::Get()->CreateContentVideoDecoderManager(OtherPid(), aEndpoint); - return true; -} - /*static*/ TabParent* ContentParent::CreateBrowserOrApp(const TabContext& aContext, Element* aFrameElement, ContentParent* aOpenerContentParent, bool aFreshProcess) { PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER); @@ -2222,28 +2215,31 @@ ContentParent::InitInternal(ProcessPrior // on demand.) bool useOffMainThreadCompositing = !!CompositorThreadHolder::Loop(); if (useOffMainThreadCompositing) { GPUProcessManager* gpm = GPUProcessManager::Get(); Endpoint<PCompositorBridgeChild> compositor; Endpoint<PImageBridgeChild> imageBridge; Endpoint<PVRManagerChild> vrBridge; + Endpoint<PVideoDecoderManagerChild> videoManager; DebugOnly<bool> opened = gpm->CreateContentBridges( OtherPid(), &compositor, &imageBridge, - &vrBridge); + &vrBridge, + &videoManager); MOZ_ASSERT(opened); Unused << SendInitRendering( Move(compositor), Move(imageBridge), - Move(vrBridge)); + Move(vrBridge), + Move(videoManager)); gpm->AddListener(this); } } if (gAppData) { // Sending all information to content process. Unused << SendAppInit(); @@ -2378,28 +2374,31 @@ ContentParent::RecvGetGfxVars(Infallible void ContentParent::OnCompositorUnexpectedShutdown() { GPUProcessManager* gpm = GPUProcessManager::Get(); Endpoint<PCompositorBridgeChild> compositor; Endpoint<PImageBridgeChild> imageBridge; Endpoint<PVRManagerChild> vrBridge; + Endpoint<PVideoDecoderManagerChild> videoManager; DebugOnly<bool> opened = gpm->CreateContentBridges( OtherPid(), &compositor, &imageBridge, - &vrBridge); + &vrBridge, + &videoManager); MOZ_ASSERT(opened); Unused << SendReinitRendering( Move(compositor), Move(imageBridge), - Move(vrBridge)); + Move(vrBridge), + Move(videoManager)); } void ContentParent::OnVarChanged(const GfxVarUpdate& aVar) { if (!mIPCOpen) { return; }
--- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -257,18 +257,16 @@ public: virtual bool RecvGetBlocklistState(const uint32_t& aPluginId, uint32_t* aIsBlocklisted) override; virtual bool RecvFindPlugins(const uint32_t& aPluginEpoch, nsresult* aRv, nsTArray<PluginTag>* aPlugins, uint32_t* aNewPluginEpoch) override; - virtual bool RecvInitVideoDecoderManager(Endpoint<PVideoDecoderManagerChild>* endpoint) override; - virtual bool RecvUngrabPointer(const uint32_t& aTime) override; virtual bool RecvRemovePermission(const IPC::Principal& aPrincipal, const nsCString& aPermissionType, nsresult* aRv) override; NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(ContentParent, nsIObserver)
--- a/dom/ipc/CrashReporterParent.cpp +++ b/dom/ipc/CrashReporterParent.cpp @@ -42,44 +42,16 @@ CrashReporterParent::ActorDestroy(ActorD bool CrashReporterParent::RecvAppendAppNotes(const nsCString& data) { mAppNotes.Append(data); return true; } -mozilla::ipc::IProtocol* -CrashReporterParent::CloneProtocol(Channel* aChannel, - mozilla::ipc::ProtocolCloneContext* aCtx) -{ -#ifdef MOZ_CRASHREPORTER - ContentParent* contentParent = aCtx->GetContentParent(); - CrashReporter::ThreadId childThreadId = contentParent->Pid(); - GeckoProcessType childProcessType = - contentParent->Process()->GetProcessType(); - - nsAutoPtr<PCrashReporterParent> actor( - contentParent->AllocPCrashReporterParent(childThreadId, - childProcessType) - ); - if (!actor || - !contentParent->RecvPCrashReporterConstructor(actor, - childThreadId, - childThreadId)) { - return nullptr; - } - - return actor.forget(); -#else - MOZ_CRASH("Not Implemented"); - return nullptr; -#endif -} - CrashReporterParent::CrashReporterParent() : #ifdef MOZ_CRASHREPORTER mNotes(4), #endif mStartTime(::time(nullptr)) , mInitialized(false) {
--- a/dom/ipc/CrashReporterParent.h +++ b/dom/ipc/CrashReporterParent.h @@ -155,20 +155,16 @@ public: const nsCString& aData) override { AnnotateCrashReport(aKey, aData); return true; } virtual bool RecvAppendAppNotes(const nsCString& aData) override; - virtual mozilla::ipc::IProtocol* - CloneProtocol(Channel* aChannel, - mozilla::ipc::ProtocolCloneContext *aCtx) override; - #ifdef MOZ_CRASHREPORTER void NotifyCrashService(); #endif #ifdef MOZ_CRASHREPORTER AnnotationTable mNotes; #endif
--- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -424,25 +424,27 @@ both: async PWebBrowserPersistDocument(nullable PBrowser aBrowser, uint64_t aOuterWindowID); child: // Give the content process its endpoints to the compositor. async InitRendering( Endpoint<PCompositorBridgeChild> compositor, Endpoint<PImageBridgeChild> imageBridge, - Endpoint<PVRManagerChild> vr); + Endpoint<PVRManagerChild> vr, + Endpoint<PVideoDecoderManagerChild> video); // Re-create the rendering stack using the given endpoints. This is sent // after the compositor process has crashed. The new endpoints may be to a // newly launched GPU process, or the compositor thread of the UI process. async ReinitRendering( Endpoint<PCompositorBridgeChild> compositor, Endpoint<PImageBridgeChild> bridge, - Endpoint<PVRManagerChild> vr); + Endpoint<PVRManagerChild> vr, + Endpoint<PVideoDecoderManagerChild> video); /** * Enable system-level sandboxing features, if available. Can * usually only be performed zero or one times. The child may * abnormally exit if this fails; the details are OS-specific. */ async SetProcessSandbox(MaybeFileDesc aBroker); @@ -737,18 +739,16 @@ parent: async PJavaScript(); async PRemoteSpellcheckEngine(); async PDeviceStorageRequest(DeviceStorageParams params); sync PCrashReporter(NativeThreadId tid, uint32_t processType); - sync InitVideoDecoderManager() returns (Endpoint<PVideoDecoderManagerChild> endpoint); - /** * Is this token compatible with the provided version? * * |version| The offered version to test * Returns |True| if the offered version is compatible */ sync NSSU2FTokenIsCompatibleVersion(nsString version) returns (bool result);
--- a/dom/jsurl/nsJSProtocolHandler.cpp +++ b/dom/jsurl/nsJSProtocolHandler.cpp @@ -178,16 +178,17 @@ nsresult nsJSThunk::EvaluateScript(nsICh // allowed. nsCOMPtr<nsIContentSecurityPolicy> csp; rv = principal->GetCsp(getter_AddRefs(csp)); NS_ENSURE_SUCCESS(rv, rv); if (csp) { bool allowsInlineScript = true; rv = csp->GetAllowsInline(nsIContentPolicy::TYPE_SCRIPT, EmptyString(), // aNonce + false, // aParserCreated EmptyString(), // aContent 0, // aLineNumber &allowsInlineScript); //return early if inline scripts are not allowed if (!allowsInlineScript) { return NS_ERROR_DOM_RETVAL_UNDEFINED; }
--- a/dom/locales/en-US/chrome/security/csp.properties +++ b/dom/locales/en-US/chrome/security/csp.properties @@ -34,16 +34,27 @@ ignoringUnknownOption = Ignoring unknown ignoringDuplicateSrc = Ignoring duplicate source %1$S # LOCALIZATION NOTE (ignoringSrcFromMetaCSP): # %1$S defines the ignored src ignoringSrcFromMetaCSP = Ignoring source ‘%1$S’ (Not supported when delivered via meta element). # LOCALIZATION NOTE (ignoringSrcWithinScriptStyleSrc): # %1$S is the ignored src # script-src and style-src are directive names and should not be localized ignoringSrcWithinScriptStyleSrc = Ignoring “%1$S” within script-src or style-src: nonce-source or hash-source specified +# LOCALIZATION NOTE (ignoringSrcForStrictDynamic): +# %1$S is the ignored src +# script-src, as well as 'strict-dynamic' should not be localized +ignoringSrcForStrictDynamic = Ignoring “%1$S” within script-src: ‘strict-dynamic’ specified +# LOCALIZATION NOTE (ignoringStrictDynamic): +# %1$S is the ignored src +ignoringStrictDynamic = Ignoring source “%1$S” (Only supported within script-src). +# LOCALIZATION NOTE (strictDynamicButNoHashOrNonce): +# %1$S is the csp directive that contains 'strict-dynamic' +# 'strict-dynamic' should not be localized +strictDynamicButNoHashOrNonce = Keyword ‘strict-dynamic’ within “%1$S” with no valid nonce or hash might block all scripts from loading # LOCALIZATION NOTE (reportURInotHttpsOrHttp2): # %1$S is the ETLD of the report URI that is not HTTP or HTTPS reportURInotHttpsOrHttp2 = The report URI (%1$S) should be an HTTP or HTTPS URI. # LOCALIZATION NOTE (reportURInotInReportOnlyHeader): # %1$S is the ETLD of the page with the policy reportURInotInReportOnlyHeader = This site (%1$S) has a Report-Only policy without a report URI. CSP will not block and cannot report violations of this policy. # LOCALIZATION NOTE (failedToParseUnrecognizedSource): # %1$S is the CSP Source that could not be parsed
--- a/dom/media/MediaFormatReader.cpp +++ b/dom/media/MediaFormatReader.cpp @@ -1507,26 +1507,30 @@ MediaFormatReader::Update(TrackType aTra if (decoder.mNeedDraining) { DrainDecoder(aTrack); return; } if (decoder.mError && !decoder.HasFatalError()) { decoder.mDecodePending = false; - if (++decoder.mNumOfConsecutiveError > decoder.mMaxConsecutiveError) { + bool needsNewDecoder = decoder.mError.ref() == NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER; + if (!needsNewDecoder && ++decoder.mNumOfConsecutiveError > decoder.mMaxConsecutiveError) { NotifyError(aTrack, decoder.mError.ref()); return; } decoder.mError.reset(); LOG("%s decoded error count %d", TrackTypeToStr(aTrack), decoder.mNumOfConsecutiveError); media::TimeUnit nextKeyframe; if (aTrack == TrackType::kVideoTrack && !decoder.HasInternalSeekPending() && NS_SUCCEEDED(decoder.mTrackDemuxer->GetNextRandomAccessPoint(&nextKeyframe))) { + if (needsNewDecoder) { + decoder.ShutdownDecoder(); + } SkipVideoDemuxToNextKeyFrame(decoder.mLastSampleTime.refOr(TimeInterval()).Length()); return; } else if (aTrack == TrackType::kAudioTrack) { decoder.Flush(); } } bool needInput = NeedInput(decoder);
--- a/dom/media/MediaFormatReader.h +++ b/dom/media/MediaFormatReader.h @@ -323,19 +323,31 @@ private: } uint32_t mNumOfConsecutiveError; uint32_t mMaxConsecutiveError; Maybe<MediaResult> mError; bool HasFatalError() const { - return mError.isSome() && - (mError.ref() != NS_ERROR_DOM_MEDIA_DECODE_ERR || - mNumOfConsecutiveError > mMaxConsecutiveError); + if (!mError.isSome()) { + return false; + } + if (mError.ref() == NS_ERROR_DOM_MEDIA_DECODE_ERR) { + // Allow decode errors to be non-fatal, but give up + // if we have too many. + return mNumOfConsecutiveError > mMaxConsecutiveError; + } else if (mError.ref() == NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER) { + // If the caller asked for a new decoder we shouldn't treat + // it as fatal. + return false; + } else { + // All other error types are fatal + return true; + } } // If set, all decoded samples prior mTimeThreshold will be dropped. // Used for internal seeking when a change of stream is detected or when // encountering data discontinuity. Maybe<InternalSeekTarget> mTimeThreshold; // Time of last sample returned. Maybe<media::TimeInterval> mLastSampleTime;
deleted file mode 100644 --- a/dom/media/compiledtest/moz.build +++ /dev/null @@ -1,20 +0,0 @@ -# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -GeckoCppUnitTests([ - 'TestAudioBuffers', - 'TestAudioMixer', - 'TestAudioPacketizer', - 'TestAudioSegment' -]) - -LOCAL_INCLUDES += [ - '..', -] - -USE_LIBS += [ - 'lgpllibs', -]
rename from dom/media/compiledtest/TestAudioBuffers.cpp rename to dom/media/gtest/TestAudioBuffers.cpp --- a/dom/media/compiledtest/TestAudioBuffers.cpp +++ b/dom/media/gtest/TestAudioBuffers.cpp @@ -1,59 +1,57 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include <stdint.h> #include "AudioBufferUtils.h" -#include <mozilla/Assertions.h> +#include "gtest/gtest.h" const uint32_t FRAMES = 256; const uint32_t CHANNELS = 2; const uint32_t SAMPLES = CHANNELS * FRAMES; -int main() { +TEST(AudioBuffers, Test) +{ mozilla::AudioCallbackBufferWrapper<float, CHANNELS> mBuffer; mozilla::SpillBuffer<float, 128, CHANNELS> b; float fromCallback[SAMPLES]; float other[SAMPLES]; for (uint32_t i = 0; i < SAMPLES; i++) { other[i] = 1.0; fromCallback[i] = 0.0; } // Set the buffer in the wrapper from the callback mBuffer.SetBuffer(fromCallback, FRAMES); // Fill the SpillBuffer with data. - MOZ_RELEASE_ASSERT(b.Fill(other, 15) == 15); - MOZ_RELEASE_ASSERT(b.Fill(other, 17) == 17); + ASSERT_TRUE(b.Fill(other, 15) == 15); + ASSERT_TRUE(b.Fill(other, 17) == 17); for (uint32_t i = 0; i < 32 * CHANNELS; i++) { other[i] = 0.0; } // Empty it in the AudioCallbackBufferWrapper - MOZ_RELEASE_ASSERT(b.Empty(mBuffer) == 32); + ASSERT_TRUE(b.Empty(mBuffer) == 32); // Check available return something reasonnable - MOZ_RELEASE_ASSERT(mBuffer.Available() == FRAMES - 32); + ASSERT_TRUE(mBuffer.Available() == FRAMES - 32); // Fill the buffer with the rest of the data mBuffer.WriteFrames(other + 32 * CHANNELS, FRAMES - 32); // Check the buffer is now full - MOZ_RELEASE_ASSERT(mBuffer.Available() == 0); + ASSERT_TRUE(mBuffer.Available() == 0); for (uint32_t i = 0 ; i < SAMPLES; i++) { - if (fromCallback[i] != 1.0) { - fprintf(stderr, "Difference at %d (%f != %f)\n", i, fromCallback[i], 1.0); - MOZ_CRASH("Samples differ"); - } + ASSERT_TRUE(fromCallback[i] == 1.0) << + "Difference at " << i << " (" << fromCallback[i] << " != " << 1.0 << + ")\n"; } - MOZ_RELEASE_ASSERT(b.Fill(other, FRAMES) == 128); - MOZ_RELEASE_ASSERT(b.Fill(other, FRAMES) == 0); - MOZ_RELEASE_ASSERT(b.Empty(mBuffer) == 0); - - return 0; + ASSERT_TRUE(b.Fill(other, FRAMES) == 128); + ASSERT_TRUE(b.Fill(other, FRAMES) == 0); + ASSERT_TRUE(b.Empty(mBuffer) == 0); }
rename from dom/media/compiledtest/TestAudioMixer.cpp rename to dom/media/gtest/TestAudioMixer.cpp --- a/dom/media/compiledtest/TestAudioMixer.cpp +++ b/dom/media/gtest/TestAudioMixer.cpp @@ -1,18 +1,21 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "AudioMixer.h" +#include "gtest/gtest.h" using mozilla::AudioDataValue; using mozilla::AudioSampleFormat; +namespace audio_mixer { + struct MixerConsumer : public mozilla::MixerCallbackReceiver { /* In this test, the different audio stream and channels are always created to * cancel each other. */ void MixerCallback(AudioDataValue* aData, AudioSampleFormat aFormat, uint32_t aChannels, uint32_t aFrames, uint32_t aSampleRate) { bool silent = true; for (uint32_t i = 0; i < aChannels * aFrames; i++) { @@ -20,19 +23,17 @@ struct MixerConsumer : public mozilla::M if (aFormat == mozilla::AUDIO_FORMAT_S16) { fprintf(stderr, "Sample at %d is not silent: %d\n", i, (short)aData[i]); } else { fprintf(stderr, "Sample at %d is not silent: %f\n", i, (float)aData[i]); } silent = false; } } - if (!silent) { - MOZ_CRASH(); - } + ASSERT_TRUE(silent); } }; /* Helper function to give us the maximum and minimum value that don't clip, * for a given sample format (integer or floating-point). */ template<typename T> T GetLowValue(); @@ -62,17 +63,18 @@ short GetHighValue<short>() { void FillBuffer(AudioDataValue* aBuffer, uint32_t aLength, AudioDataValue aValue) { AudioDataValue* end = aBuffer + aLength; while (aBuffer != end) { *aBuffer++ = aValue; } } -int main(int argc, char* argv[]) { +TEST(AudioMixer, Test) +{ const uint32_t CHANNEL_LENGTH = 256; const uint32_t AUDIO_RATE = 44100; MixerConsumer consumer; AudioDataValue a[CHANNEL_LENGTH * 2]; AudioDataValue b[CHANNEL_LENGTH * 2]; FillBuffer(a, CHANNEL_LENGTH, GetLowValue<AudioDataValue>()); FillBuffer(a + CHANNEL_LENGTH, CHANNEL_LENGTH, GetHighValue<AudioDataValue>()); FillBuffer(b, CHANNEL_LENGTH, GetHighValue<AudioDataValue>()); @@ -153,11 +155,11 @@ int main(int argc, char* argv[]) { mixer.Mix(b, 2, CHANNEL_LENGTH, AUDIO_RATE); mixer.Mix(a, 2, CHANNEL_LENGTH, AUDIO_RATE); mixer.Mix(b, 2, CHANNEL_LENGTH, AUDIO_RATE); mixer.FinishMixing(); mixer.Mix(a, 2, CHANNEL_LENGTH, AUDIO_RATE); mixer.Mix(b, 2, CHANNEL_LENGTH, AUDIO_RATE); mixer.FinishMixing(); } +} - return 0; -} +} // namespace audio_mixer
rename from dom/media/compiledtest/TestAudioPacketizer.cpp rename to dom/media/gtest/TestAudioPacketizer.cpp --- a/dom/media/compiledtest/TestAudioPacketizer.cpp +++ b/dom/media/gtest/TestAudioPacketizer.cpp @@ -1,17 +1,17 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include <stdint.h> #include <math.h> #include "../AudioPacketizer.h" -#include <mozilla/Assertions.h> +#include "gtest/gtest.h" using namespace mozilla; template<typename T> class AutoBuffer { public: explicit AutoBuffer(size_t aLength) @@ -35,39 +35,36 @@ int16_t Sequence(int16_t* aBuffer, uint3 aBuffer[i] = aStart + i; } return aStart + i; } void IsSequence(int16_t* aBuffer, uint32_t aSize, uint32_t aStart = 0) { for (uint32_t i = 0; i < aSize; i++) { - if (aBuffer[i] != static_cast<int64_t>(aStart + i)) { - fprintf(stderr, "Buffer is not a sequence at offset %u\n", i); - MOZ_CRASH("Buffer is not a sequence"); - } + ASSERT_TRUE(aBuffer[i] == static_cast<int64_t>(aStart + i)) << + "Buffer is not a sequence at offset " << i << std::endl; } // Buffer is a sequence. } void Zero(int16_t* aBuffer, uint32_t aSize) { for (uint32_t i = 0; i < aSize; i++) { - if (aBuffer[i] != 0) { - fprintf(stderr, "Buffer is not null at offset %u\n", i); - MOZ_CRASH("Buffer is not null"); - } + ASSERT_TRUE(aBuffer[i] == 0) << + "Buffer is not null at offset " << i << std::endl; } } double sine(uint32_t aPhase) { - return sin(aPhase * 2 * M_PI * 440 / 44100); + return sin(aPhase * 2 * M_PI * 440 / 44100); } -int main() { +TEST(AudioPacketizer, Test) +{ for (int16_t channels = 1; channels < 2; channels++) { // Test that the packetizer returns zero on underrun { AudioPacketizer<int16_t, int16_t> ap(441, channels); for (int16_t i = 0; i < 10; i++) { int16_t* out = ap.Output(); Zero(out, 441); delete[] out; @@ -152,22 +149,19 @@ int main() { } phase++; } ap.Input(b.Get(), 128); while (ap.PacketsAvailable()) { int16_t* packet = ap.Output(); for (uint32_t k = 0; k < ap.PacketSize(); k++) { for (int32_t c = 0; c < channels; c++) { - MOZ_RELEASE_ASSERT(packet[k * channels + c] == - static_cast<int16_t>(((2 << 14) * sine(outPhase)))); + ASSERT_TRUE(packet[k * channels + c] == + static_cast<int16_t>(((2 << 14) * sine(outPhase)))); } outPhase++; } delete [] packet; } } } } - - printf("OK\n"); - return 0; }
rename from dom/media/compiledtest/TestAudioSegment.cpp rename to dom/media/gtest/TestAudioSegment.cpp --- a/dom/media/compiledtest/TestAudioSegment.cpp +++ b/dom/media/gtest/TestAudioSegment.cpp @@ -1,26 +1,20 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "AudioSegment.h" #include <iostream> -#include <mozilla/Assertions.h> +#include "gtest/gtest.h" using namespace mozilla; -namespace mozilla { -uint32_t -GetAudioChannelsSuperset(uint32_t aChannels1, uint32_t aChannels2) -{ - return std::max(aChannels1, aChannels2); -} -} +namespace audio_segment { /* Helper function to give us the maximum and minimum value that don't clip, * for a given sample format (integer or floating-point). */ template<typename T> T GetLowValue(); template<typename T> T GetHighValue(); @@ -137,18 +131,18 @@ void TestInterleaveAndConvert() for (uint32_t channels = 1; channels < maxChannels; channels++) { const SrcT* const* src = GetPlanarChannelArray<SrcT>(channels, arraySize); DstT* dst = new DstT[channels * arraySize]; InterleaveAndConvertBuffer(src, arraySize, 1.0, channels, dst); uint32_t channelIndex = 0; for (size_t i = 0; i < arraySize * channels; i++) { - MOZ_RELEASE_ASSERT(FuzzyEqual(dst[i], - FloatToAudioSample<DstT>(1. / (channelIndex + 1)))); + ASSERT_TRUE(FuzzyEqual(dst[i], + FloatToAudioSample<DstT>(1. / (channelIndex + 1)))); channelIndex++; channelIndex %= channels; } DeletePlanarChannelsArray(src, channels); delete [] dst; } } @@ -161,18 +155,18 @@ void TestDeinterleaveAndConvert() for (uint32_t channels = 1; channels < maxChannels; channels++) { const SrcT* src = GetInterleavedChannelArray<SrcT>(channels, arraySize); DstT** dst = GetPlanarArray<DstT>(channels, arraySize); DeinterleaveAndConvertBuffer(src, arraySize, channels, dst); for (size_t channel = 0; channel < channels; channel++) { for (size_t i = 0; i < arraySize; i++) { - MOZ_RELEASE_ASSERT(FuzzyEqual(dst[channel][i], - FloatToAudioSample<DstT>(1. / (channel + 1)))); + ASSERT_TRUE(FuzzyEqual(dst[channel][i], + FloatToAudioSample<DstT>(1. / (channel + 1)))); } } DeleteInterleavedChannelArray(src); DeletePlanarArray(dst, channels); } } @@ -196,21 +190,21 @@ void TestUpmixStereo() channels[0] = new T[arraySize]; for (size_t i = 0; i < arraySize; i++) { channels[0][i] = GetHighValue<T>(); } channelsptr[0] = channels[0]; - AudioChannelsUpMix(&channelsptr, 2, ::SilentChannel<T>()); + AudioChannelsUpMix(&channelsptr, 2, SilentChannel<T>()); for (size_t channel = 0; channel < 2; channel++) { for (size_t i = 0; i < arraySize; i++) { - MOZ_RELEASE_ASSERT(channelsptr[channel][i] == GetHighValue<T>()); + ASSERT_TRUE(channelsptr[channel][i] == GetHighValue<T>()); } } delete channels[0]; } template<typename T> void TestDownmixStereo() { @@ -231,32 +225,33 @@ void TestDownmixStereo() input[channel][i] = channel == 0 ? GetLowValue<T>() : GetHighValue<T>(); } inputptr[channel] = input[channel]; } AudioChannelsDownMix(inputptr, output, 1, arraySize); for (size_t i = 0; i < arraySize; i++) { - MOZ_RELEASE_ASSERT(output[0][i] == GetSilentValue<T>()); - MOZ_RELEASE_ASSERT(output[0][i] == GetSilentValue<T>()); + ASSERT_TRUE(output[0][i] == GetSilentValue<T>()); + ASSERT_TRUE(output[0][i] == GetSilentValue<T>()); } delete output[0]; delete output; } -int main(int argc, char* argv[]) { +TEST(AudioSegment, Test) +{ TestInterleaveAndConvert<float, float>(); TestInterleaveAndConvert<float, int16_t>(); TestInterleaveAndConvert<int16_t, float>(); TestInterleaveAndConvert<int16_t, int16_t>(); TestDeinterleaveAndConvert<float, float>(); TestDeinterleaveAndConvert<float, int16_t>(); TestDeinterleaveAndConvert<int16_t, float>(); TestDeinterleaveAndConvert<int16_t, int16_t>(); TestUpmixStereo<float>(); TestUpmixStereo<int16_t>(); TestDownmixStereo<float>(); TestDownmixStereo<int16_t>(); +} - return 0; -} +} // namespace audio_segment
--- a/dom/media/gtest/TestRust.cpp +++ b/dom/media/gtest/TestRust.cpp @@ -1,8 +1,9 @@ #include <stdint.h> +#include "gtest/gtest.h" extern "C" uint8_t* test_rust(); TEST(rust, CallFromCpp) { auto greeting = test_rust(); EXPECT_STREQ(reinterpret_cast<char*>(greeting), "hello from rust."); }
--- a/dom/media/gtest/moz.build +++ b/dom/media/gtest/moz.build @@ -1,17 +1,21 @@ # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- # vim: set filetype=python: # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. UNIFIED_SOURCES += [ 'MockMediaResource.cpp', + 'TestAudioBuffers.cpp', 'TestAudioCompactor.cpp', + 'TestAudioMixer.cpp', + 'TestAudioPacketizer.cpp', + 'TestAudioSegment.cpp', 'TestGMPCrossOrigin.cpp', 'TestGMPRemoveAndDelete.cpp', 'TestGMPUtils.cpp', 'TestIntervalSet.cpp', 'TestMediaDataDecoder.cpp', 'TestMediaEventSource.cpp', 'TestMediaFormatReader.cpp', 'TestMozPromise.cpp',
--- a/dom/media/ipc/RemoteVideoDecoder.cpp +++ b/dom/media/ipc/RemoteVideoDecoder.cpp @@ -4,16 +4,17 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "RemoteVideoDecoder.h" #include "VideoDecoderChild.h" #include "VideoDecoderManagerChild.h" #include "mozilla/layers/TextureClient.h" #include "base/thread.h" #include "MediaInfo.h" +#include "MediaPrefs.h" #include "ImageContainer.h" namespace mozilla { namespace dom { using base::Thread; using namespace ipc; using namespace layers; @@ -142,17 +143,18 @@ PlatformDecoderModule::ConversionRequire RemoteDecoderModule::DecoderNeedsConversion(const TrackInfo& aConfig) const { return mWrapped->DecoderNeedsConversion(aConfig); } already_AddRefed<MediaDataDecoder> RemoteDecoderModule::CreateVideoDecoder(const CreateDecoderParams& aParams) { - if (!aParams.mKnowsCompositor || + if (!MediaPrefs::PDMUseGPUDecoder() || + !aParams.mKnowsCompositor || aParams.mKnowsCompositor->GetTextureFactoryIdentifier().mParentProcessType != GeckoProcessType_GPU) { return nullptr; } MediaDataDecoderCallback* callback = aParams.mCallback; MOZ_ASSERT(callback->OnReaderTaskQueue()); RefPtr<RemoteVideoDecoder> object = new RemoteVideoDecoder(callback);
--- a/dom/media/ipc/VideoDecoderChild.cpp +++ b/dom/media/ipc/VideoDecoderChild.cpp @@ -98,34 +98,45 @@ VideoDecoderChild::RecvInitFailed(const mInitPromise.Reject(aReason, __func__); return true; } void VideoDecoderChild::ActorDestroy(ActorDestroyReason aWhy) { if (aWhy == AbnormalShutdown) { - if (mInitialized) { - mCallback->Error(NS_ERROR_DOM_MEDIA_FATAL_ERR); - } else { - mInitPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__); - } + // Defer reporting an error until we've recreated the manager so that + // it'll be safe for MediaFormatReader to recreate decoders + RefPtr<VideoDecoderChild> ref = this; + GetManager()->RunWhenRecreated(NS_NewRunnableFunction([=]() { + if (ref->mInitialized) { + ref->mCallback->Error(NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER); + } else { + ref->mInitPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER, __func__); + } + })); } mCanSend = false; } void VideoDecoderChild::InitIPDL(MediaDataDecoderCallback* aCallback, const VideoInfo& aVideoInfo, layers::KnowsCompositor* aKnowsCompositor) { RefPtr<VideoDecoderManagerChild> manager = VideoDecoderManagerChild::GetSingleton(); - if (!manager) { + // If the manager isn't available, then don't initialize mIPDLSelfRef and leave + // us in an error state. We'll then immediately reject the promise when Init() + // is called and the caller can try again. Hopefully by then the new manager is + // ready, or we've notified the caller of it being no longer available. + // If not, then the cycle repeats until we're ready. + if (!manager || !manager->CanSend()) { return; } + mIPDLSelfRef = this; mCallback = aCallback; mVideoInfo = aVideoInfo; mKnowsCompositor = aKnowsCompositor; if (manager->SendPVideoDecoderConstructor(this)) { mCanSend = true; } } @@ -145,96 +156,99 @@ VideoDecoderChild::IPDLActorDestroyed() } // MediaDataDecoder methods RefPtr<MediaDataDecoder::InitPromise> VideoDecoderChild::Init() { AssertOnManagerThread(); - if (!mCanSend || !SendInit(mVideoInfo, mKnowsCompositor->GetTextureFactoryIdentifier())) { + + if (!mIPDLSelfRef) { return MediaDataDecoder::InitPromise::CreateAndReject( - NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__); + NS_ERROR_DOM_MEDIA_DECODE_ERR, __func__); + } + // If we failed to send this, then we'll still resolve the Init promise + // as ActorDestroy handles it. + if (mCanSend) { + SendInit(mVideoInfo, mKnowsCompositor->GetTextureFactoryIdentifier()); } return mInitPromise.Ensure(__func__); } void VideoDecoderChild::Input(MediaRawData* aSample) { AssertOnManagerThread(); if (!mCanSend) { - mCallback->Error(NS_ERROR_DOM_MEDIA_FATAL_ERR); return; } // TODO: It would be nice to add an allocator method to // MediaDataDecoder so that the demuxer could write directly // into shmem rather than requiring a copy here. Shmem buffer; if (!AllocShmem(aSample->Size(), Shmem::SharedMemory::TYPE_BASIC, &buffer)) { - mCallback->Error(NS_ERROR_DOM_MEDIA_FATAL_ERR); + mCallback->Error(NS_ERROR_DOM_MEDIA_DECODE_ERR); return; } memcpy(buffer.get<uint8_t>(), aSample->Data(), aSample->Size()); MediaRawDataIPDL sample(MediaDataIPDL(aSample->mOffset, aSample->mTime, aSample->mTimecode, aSample->mDuration, aSample->mFrames, aSample->mKeyframe), buffer); - if (!SendInput(sample)) { - mCallback->Error(NS_ERROR_DOM_MEDIA_FATAL_ERR); - } + SendInput(sample); } void VideoDecoderChild::Flush() { AssertOnManagerThread(); - if (!mCanSend || !SendFlush()) { - mCallback->Error(NS_ERROR_DOM_MEDIA_FATAL_ERR); + if (mCanSend) { + SendFlush(); } } void VideoDecoderChild::Drain() { AssertOnManagerThread(); - if (!mCanSend || !SendDrain()) { - mCallback->Error(NS_ERROR_DOM_MEDIA_FATAL_ERR); + if (mCanSend) { + SendDrain(); } } void VideoDecoderChild::Shutdown() { AssertOnManagerThread(); - if (!mCanSend || !SendShutdown()) { - mCallback->Error(NS_ERROR_DOM_MEDIA_FATAL_ERR); + if (mCanSend) { + SendShutdown(); } mInitialized = false; } bool VideoDecoderChild::IsHardwareAccelerated(nsACString& aFailureReason) const { aFailureReason = mHardwareAcceleratedReason; return mIsHardwareAccelerated; } void VideoDecoderChild::SetSeekThreshold(const media::TimeUnit& aTime) { AssertOnManagerThread(); - if (!mCanSend || !SendSetSeekThreshold(aTime.ToMicroseconds())) { - mCallback->Error(NS_ERROR_DOM_MEDIA_FATAL_ERR); + if (mCanSend) { + SendSetSeekThreshold(aTime.ToMicroseconds()); } } void VideoDecoderChild::AssertOnManagerThread() { MOZ_ASSERT(NS_GetCurrentThread() == mThread); }
--- a/dom/media/ipc/VideoDecoderManagerChild.cpp +++ b/dom/media/ipc/VideoDecoderManagerChild.cpp @@ -8,109 +8,98 @@ #include "mozilla/dom/ContentChild.h" #include "MediaPrefs.h" #include "nsThreadUtils.h" #include "mozilla/gfx/2D.h" #include "mozilla/ipc/ProtocolUtils.h" #include "mozilla/layers/SynchronousTask.h" #include "mozilla/gfx/DataSurfaceHelpers.h" #include "mozilla/layers/ISurfaceAllocator.h" +#include "base/task.h" namespace mozilla { namespace dom { using namespace ipc; using namespace layers; using namespace gfx; // Only modified on the main-thread StaticRefPtr<nsIThread> sVideoDecoderChildThread; StaticRefPtr<AbstractThread> sVideoDecoderChildAbstractThread; // Only accessed from sVideoDecoderChildThread static StaticRefPtr<VideoDecoderManagerChild> sDecoderManager; +static UniquePtr<nsTArray<RefPtr<Runnable>>> sRecreateTasks; /* static */ void -VideoDecoderManagerChild::Initialize() +VideoDecoderManagerChild::InitializeThread() { MOZ_ASSERT(NS_IsMainThread()); - MediaPrefs::GetSingleton(); - -#ifdef XP_WIN - if (!MediaPrefs::PDMUseGPUDecoder()) { - return; - } - - // Can't run remote video decoding in the parent process. - if (!ContentChild::GetSingleton()) { - return; - } - if (!sVideoDecoderChildThread) { RefPtr<nsIThread> childThread; nsresult rv = NS_NewNamedThread("VideoChild", getter_AddRefs(childThread)); NS_ENSURE_SUCCESS_VOID(rv); sVideoDecoderChildThread = childThread; sVideoDecoderChildAbstractThread = AbstractThread::CreateXPCOMThreadWrapper(childThread, false); + + sRecreateTasks = MakeUnique<nsTArray<RefPtr<Runnable>>>(); } -#else - return; -#endif +} +/* static */ void +VideoDecoderManagerChild::InitForContent(Endpoint<PVideoDecoderManagerChild>&& aVideoManager) +{ + InitializeThread(); + sVideoDecoderChildThread->Dispatch(NewRunnableFunction(&Open, Move(aVideoManager)), NS_DISPATCH_NORMAL); } /* static */ void VideoDecoderManagerChild::Shutdown() { MOZ_ASSERT(NS_IsMainThread()); if (sVideoDecoderChildThread) { sVideoDecoderChildThread->Dispatch(NS_NewRunnableFunction([]() { - if (sDecoderManager) { + if (sDecoderManager && sDecoderManager->CanSend()) { sDecoderManager->Close(); sDecoderManager = nullptr; } }), NS_DISPATCH_NORMAL); sVideoDecoderChildAbstractThread = nullptr; sVideoDecoderChildThread->Shutdown(); sVideoDecoderChildThread = nullptr; + + sRecreateTasks = nullptr; } } +void +VideoDecoderManagerChild::RunWhenRecreated(already_AddRefed<Runnable> aTask) +{ + MOZ_ASSERT(NS_GetCurrentThread() == GetManagerThread()); + + // If we've already been recreated, then run the task immediately. + if (sDecoderManager && sDecoderManager != this && sDecoderManager->CanSend()) { + RefPtr<Runnable> task = aTask; + task->Run(); + } else { + sRecreateTasks->AppendElement(aTask); + } +} + + /* static */ VideoDecoderManagerChild* VideoDecoderManagerChild::GetSingleton() { MOZ_ASSERT(NS_GetCurrentThread() == GetManagerThread()); - - if (!sDecoderManager || !sDecoderManager->mCanSend) { - RefPtr<VideoDecoderManagerChild> manager; - - NS_DispatchToMainThread(NS_NewRunnableFunction([&]() { - Endpoint<PVideoDecoderManagerChild> endpoint; - if (!ContentChild::GetSingleton()->SendInitVideoDecoderManager(&endpoint)) { - return; - } - - if (!endpoint.IsValid()) { - return; - } - - manager = new VideoDecoderManagerChild(); - - RefPtr<Runnable> task = NewRunnableMethod<Endpoint<PVideoDecoderManagerChild>&&>( - manager, &VideoDecoderManagerChild::Open, Move(endpoint)); - sVideoDecoderChildThread->Dispatch(task.forget(), NS_DISPATCH_NORMAL); - }), NS_DISPATCH_SYNC); - - sDecoderManager = manager; - } return sDecoderManager; } /* static */ nsIThread* VideoDecoderManagerChild::GetManagerThread() { return sVideoDecoderChildThread; } @@ -133,43 +122,66 @@ VideoDecoderManagerChild::DeallocPVideoD VideoDecoderChild* child = static_cast<VideoDecoderChild*>(actor); child->IPDLActorDestroyed(); return true; } void VideoDecoderManagerChild::Open(Endpoint<PVideoDecoderManagerChild>&& aEndpoint) { - if (!aEndpoint.Bind(this)) { - return; + // Make sure we always dispatch everything in sRecreateTasks, even if we + // fail since this is as close to being recreated as we will ever be. + sDecoderManager = nullptr; + if (aEndpoint.IsValid()) { + RefPtr<VideoDecoderManagerChild> manager = new VideoDecoderManagerChild(); + if (aEndpoint.Bind(manager)) { + sDecoderManager = manager; + manager->InitIPDL(); + } } - AddRef(); + for (Runnable* task : *sRecreateTasks) { + task->Run(); + } + sRecreateTasks->Clear(); +} + +void +VideoDecoderManagerChild::InitIPDL() +{ mCanSend = true; + mIPDLSelfRef = this; } void VideoDecoderManagerChild::ActorDestroy(ActorDestroyReason aWhy) { mCanSend = false; } void VideoDecoderManagerChild::DeallocPVideoDecoderManagerChild() { - Release(); + mIPDLSelfRef = nullptr; +} + +bool +VideoDecoderManagerChild::CanSend() +{ + MOZ_ASSERT(NS_GetCurrentThread() == GetManagerThread()); + return mCanSend; } bool VideoDecoderManagerChild::DeallocShmem(mozilla::ipc::Shmem& aShmem) { if (NS_GetCurrentThread() != sVideoDecoderChildThread) { RefPtr<VideoDecoderManagerChild> self = this; mozilla::ipc::Shmem shmem = aShmem; sVideoDecoderChildThread->Dispatch(NS_NewRunnableFunction([self, shmem]() { - if (self->mCanSend) { + if (self->CanSend()) { mozilla::ipc::Shmem shmemCopy = shmem; self->DeallocShmem(shmemCopy); } }), NS_DISPATCH_NORMAL); return true; } return PVideoDecoderManagerChild::DeallocShmem(aShmem); } @@ -202,17 +214,17 @@ VideoDecoderManagerChild::Readback(const // loop while it waits. This function can be called from JS and we // don't want that to happen. SynchronousTask task("Readback sync"); RefPtr<VideoDecoderManagerChild> ref = this; SurfaceDescriptor sd; sVideoDecoderChildThread->Dispatch(NS_NewRunnableFunction([&]() { AutoCompleteTask complete(&task); - if (ref->mCanSend) { + if (ref->CanSend()) { ref->SendReadback(aSD, &sd); } }), NS_DISPATCH_NORMAL); task.Wait(); if (!IsSurfaceDescriptorValid(sd)) { return nullptr; @@ -234,22 +246,22 @@ VideoDecoderManagerChild::Readback(const } void VideoDecoderManagerChild::DeallocateSurfaceDescriptorGPUVideo(const SurfaceDescriptorGPUVideo& aSD) { RefPtr<VideoDecoderManagerChild> ref = this; SurfaceDescriptorGPUVideo sd = Move(aSD); sVideoDecoderChildThread->Dispatch(NS_NewRunnableFunction([ref, sd]() { - if (ref->mCanSend) { + if (ref->CanSend()) { ref->SendDeallocateSurfaceDescriptorGPUVideo(sd); } }), NS_DISPATCH_NORMAL); } void -VideoDecoderManagerChild::FatalError(const char* const aName, const char* const aMsg) const +VideoDecoderManagerChild::HandleFatalError(const char* aName, const char* aMsg) const { dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aName, aMsg, OtherPid()); } } // namespace dom } // namespace mozilla
--- a/dom/media/ipc/VideoDecoderManagerChild.h +++ b/dom/media/ipc/VideoDecoderManagerChild.h @@ -46,35 +46,50 @@ public: return PVideoDecoderManagerChild::AllocUnsafeShmem(aSize, aShmType, aShmem); } // Can be called from any thread, dispatches the request to the IPDL thread internally // and will be ignored if the IPDL actor has been destroyed. bool DeallocShmem(mozilla::ipc::Shmem& aShmem) override; // Main thread only - static void Initialize(); + static void InitForContent(Endpoint<PVideoDecoderManagerChild>&& aVideoManager); static void Shutdown(); + // Run aTask (on the manager thread) when we next attempt to create a new manager + // (even if creation fails). Intended to be called from ActorDestroy when we get + // notified that the old manager is being destroyed. + // Can only be called from the manager thread. + void RunWhenRecreated(already_AddRefed<Runnable> aTask); + + bool CanSend(); + protected: + void InitIPDL(); + void ActorDestroy(ActorDestroyReason aWhy) override; void DeallocPVideoDecoderManagerChild() override; - void FatalError(const char* const aName, const char* const aMsg) const override; + void HandleFatalError(const char* aName, const char* aMsg) const override; PVideoDecoderChild* AllocPVideoDecoderChild() override; bool DeallocPVideoDecoderChild(PVideoDecoderChild* actor) override; private: + // Main thread only + static void InitializeThread(); + VideoDecoderManagerChild() : mCanSend(false) {} ~VideoDecoderManagerChild() {} - void Open(Endpoint<PVideoDecoderManagerChild>&& aEndpoint); + static void Open(Endpoint<PVideoDecoderManagerChild>&& aEndpoint); + + RefPtr<VideoDecoderManagerChild> mIPDLSelfRef; // Should only ever be accessed on the manager thread. bool mCanSend; }; } // namespace dom } // namespace mozilla
--- a/dom/media/ipc/VideoDecoderManagerParent.h +++ b/dom/media/ipc/VideoDecoderManagerParent.h @@ -28,17 +28,17 @@ public: protected: PVideoDecoderParent* AllocPVideoDecoderParent() override; bool DeallocPVideoDecoderParent(PVideoDecoderParent* actor) override; bool RecvReadback(const SurfaceDescriptorGPUVideo& aSD, SurfaceDescriptor* aResult) override; bool RecvDeallocateSurfaceDescriptorGPUVideo(const SurfaceDescriptorGPUVideo& aSD) override; - void ActorDestroy(mozilla::ipc::IProtocolManager<mozilla::ipc::IProtocol>::ActorDestroyReason) override {} + void ActorDestroy(mozilla::ipc::IProtocol::ActorDestroyReason) override {} void DeallocPVideoDecoderManagerParent() override; private: VideoDecoderManagerParent(); ~VideoDecoderManagerParent(); void Open(Endpoint<PVideoDecoderManagerParent>&& aEndpoint);
--- a/dom/media/moz.build +++ b/dom/media/moz.build @@ -50,17 +50,16 @@ if CONFIG['MOZ_ANDROID_OMX']: if CONFIG['MOZ_FMP4']: DIRS += ['fmp4'] if CONFIG['MOZ_WEBRTC']: DIRS += ['bridge'] TEST_DIRS += [ - 'compiledtest', 'gtest', ] MOCHITEST_MANIFESTS += [ 'test/mochitest.ini', 'tests/mochitest/identity/mochitest.ini', ]
--- a/dom/media/systemservices/MediaParent.h +++ b/dom/media/systemservices/MediaParent.h @@ -17,17 +17,17 @@ namespace media { // media::Parent implements the chrome-process side of ipc for media::Child APIs // A same-process version may also be created to service non-e10s calls. class OriginKeyStore; class NonE10s { - typedef mozilla::ipc::IProtocolManager<mozilla::ipc::IProtocol>::ActorDestroyReason + typedef mozilla::ipc::IProtocol::ActorDestroyReason ActorDestroyReason; public: virtual ~NonE10s() {} protected: virtual bool RecvGetOriginKey(const uint32_t& aRequestId, const nsCString& aOrigin, const bool& aPrivateBrowsing, const bool& aPersist) = 0; @@ -40,17 +40,17 @@ protected: nsCString aKey); }; // Super = PMediaParent or NonE10s template<class Super> class Parent : public Super { - typedef mozilla::ipc::IProtocolManager<mozilla::ipc::IProtocol>::ActorDestroyReason + typedef mozilla::ipc::IProtocol::ActorDestroyReason ActorDestroyReason; public: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Parent<Super>) virtual bool RecvGetOriginKey(const uint32_t& aRequestId, const nsCString& aOrigin, const bool& aPrivateBrowsing, const bool& aPersist) override;
--- a/dom/permission/PermissionObserver.cpp +++ b/dom/permission/PermissionObserver.cpp @@ -81,17 +81,17 @@ PermissionObserver::RemoveSink(Permissio void PermissionObserver::Notify(PermissionName aName, nsIPrincipal& aPrincipal) { for (auto* sink : mSinks) { if (sink->mName != aName) { continue; } - nsIPrincipal* sinkPrincipal = sink->GetPrincipal(); + nsCOMPtr<nsIPrincipal> sinkPrincipal = sink->GetPrincipal(); if (NS_WARN_IF(!sinkPrincipal) || !aPrincipal.Equals(sinkPrincipal)) { continue; } sink->PermissionChanged(); } }
--- a/dom/permission/PermissionStatus.cpp +++ b/dom/permission/PermissionStatus.cpp @@ -88,30 +88,34 @@ PermissionStatus::UpdateState() if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } mState = ActionToPermissionState(action); return NS_OK; } -nsIPrincipal* +already_AddRefed<nsIPrincipal> PermissionStatus::GetPrincipal() const { nsCOMPtr<nsPIDOMWindowInner> window = GetOwner(); if (NS_WARN_IF(!window)) { return nullptr; } nsIDocument* doc = window->GetExtantDoc(); if (NS_WARN_IF(!doc)) { return nullptr; } - return doc->NodePrincipal(); + nsCOMPtr<nsIPrincipal> principal = + mozilla::BasePrincipal::Cast(doc->NodePrincipal())->CloneStrippingUserContextIdAndFirstPartyDomain(); + NS_ENSURE_TRUE(principal, nullptr); + + return principal.forget(); } void PermissionStatus::PermissionChanged() { auto oldState = mState; UpdateState(); if (mState != oldState) {
--- a/dom/permission/PermissionStatus.h +++ b/dom/permission/PermissionStatus.h @@ -37,17 +37,17 @@ private: ~PermissionStatus(); PermissionStatus(nsPIDOMWindowInner* aWindow, PermissionName aName); nsresult Init(); nsresult UpdateState(); - nsIPrincipal* GetPrincipal() const; + already_AddRefed<nsIPrincipal> GetPrincipal() const; void PermissionChanged(); PermissionName mName; PermissionState mState; RefPtr<PermissionObserver> mObserver; };
--- a/dom/plugins/ipc/PluginMessageUtils.h +++ b/dom/plugins/ipc/PluginMessageUtils.h @@ -5,18 +5,19 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef DOM_PLUGINS_PLUGINMESSAGEUTILS_H #define DOM_PLUGINS_PLUGINMESSAGEUTILS_H #include "ipc/IPCMessageUtils.h" #include "base/message_loop.h" +#include "mozilla/ipc/CrossProcessMutex.h" #include "mozilla/ipc/MessageChannel.h" -#include "mozilla/ipc/CrossProcessMutex.h" +#include "mozilla/ipc/ProtocolUtils.h" #include "mozilla/UniquePtr.h" #include "gfxipc/ShadowLayerUtils.h" #include "npapi.h" #include "npruntime.h" #include "npfunctions.h" #include "nsString.h" #include "nsTArray.h"
--- a/dom/plugins/ipc/PluginModuleParent.cpp +++ b/dom/plugins/ipc/PluginModuleParent.cpp @@ -1031,18 +1031,17 @@ PluginModuleChromeParent::GetInvokingPro * * This function needs to be updated if the subprotocols are modified in * PPluginInstance.ipdl. */ PluginInstanceParent* PluginModuleChromeParent::GetManagingInstance(mozilla::ipc::IProtocol* aProtocol) { MOZ_ASSERT(aProtocol); - mozilla::ipc::MessageListener* listener = - static_cast<mozilla::ipc::MessageListener*>(aProtocol); + mozilla::ipc::IProtocol* listener = aProtocol; switch (listener->GetProtocolTypeId()) { case PPluginInstanceMsgStart: // In this case, aProtocol is the instance itself. Just cast it. return static_cast<PluginInstanceParent*>(aProtocol); case PPluginBackgroundDestroyerMsgStart: { PPluginBackgroundDestroyerParent* actor = static_cast<PPluginBackgroundDestroyerParent*>(aProtocol); return static_cast<PluginInstanceParent*>(actor->Manager());
--- a/dom/presentation/PresentationAvailability.cpp +++ b/dom/presentation/PresentationAvailability.cpp @@ -48,33 +48,36 @@ PresentationAvailability::Create(nsPIDOM } PresentationAvailability::PresentationAvailability(nsPIDOMWindowInner* aWindow, const nsTArray<nsString>& aUrls) : DOMEventTargetHelper(aWindow) , mIsAvailable(false) , mUrls(aUrls) { + for (uint32_t i = 0; i < mUrls.Length(); ++i) { + mAvailabilityOfUrl.AppendElement(false); + } } PresentationAvailability::~PresentationAvailability() { Shutdown(); } bool PresentationAvailability::Init(RefPtr<Promise>& aPromise) { nsCOMPtr<nsIPresentationService> service = do_GetService(PRESENTATION_SERVICE_CONTRACTID); if (NS_WARN_IF(!service)) { return false; } - nsresult rv = service->RegisterAvailabilityListener(this); + nsresult rv = service->RegisterAvailabilityListener(mUrls, this); if (NS_WARN_IF(NS_FAILED(rv))) { // If the user agent is unable to monitor available device, // Resolve promise with |value| set to false. mIsAvailable = false; aPromise->MaybeResolve(this); return true; } @@ -97,17 +100,18 @@ void PresentationAvailability::Shutdown( nsCOMPtr<nsIPresentationService> service = do_GetService(PRESENTATION_SERVICE_CONTRACTID); if (NS_WARN_IF(!service)) { return; } Unused << - NS_WARN_IF(NS_FAILED(service->UnregisterAvailabilityListener(this))); + NS_WARN_IF(NS_FAILED(service->UnregisterAvailabilityListener(mUrls, + this))); } /* virtual */ void PresentationAvailability::DisconnectFromOwner() { Shutdown(); DOMEventTargetHelper::DisconnectFromOwner(); } @@ -152,22 +156,31 @@ PresentationAvailability::EnqueuePromise bool PresentationAvailability::Value() const { return mIsAvailable; } NS_IMETHODIMP -PresentationAvailability::NotifyAvailableChange(bool aIsAvailable) +PresentationAvailability::NotifyAvailableChange(const nsTArray<nsString>& aAvailabilityUrls, + bool aIsAvailable) { + bool available = false; + for (uint32_t i = 0; i < mUrls.Length(); ++i) { + if (aAvailabilityUrls.Contains(mUrls[i])) { + mAvailabilityOfUrl[i] = aIsAvailable; + } + available |= mAvailabilityOfUrl[i]; + } + return NS_DispatchToCurrentThread(NewRunnableMethod <bool>(this, &PresentationAvailability::UpdateAvailabilityAndDispatchEvent, - aIsAvailable)); + available)); } void PresentationAvailability::UpdateAvailabilityAndDispatchEvent(bool aIsAvailable) { PRES_DEBUG("%s\n", __func__); bool isChanged = (aIsAvailable != mIsAvailable);
--- a/dom/presentation/PresentationAvailability.h +++ b/dom/presentation/PresentationAvailability.h @@ -60,14 +60,15 @@ private: void UpdateAvailabilityAndDispatchEvent(bool aIsAvailable); bool mIsAvailable; nsTArray<RefPtr<Promise>> mPromises; nsTArray<nsString> mUrls; + nsTArray<bool> mAvailabilityOfUrl; }; } // namespace dom } // namespace mozilla #endif // mozilla_dom_PresentationAvailability_h
--- a/dom/presentation/PresentationService.cpp +++ b/dom/presentation/PresentationService.cpp @@ -21,19 +21,16 @@ #include "nsISupportsPrimitives.h" #include "nsNetUtil.h" #include "nsServiceManagerUtils.h" #include "nsThreadUtils.h" #include "nsXPCOMCID.h" #include "nsXULAppAPI.h" #include "PresentationLog.h" -using namespace mozilla; -using namespace mozilla::dom; - namespace mozilla { namespace dom { static bool IsSameDevice(nsIPresentationDevice* aDevice, nsIPresentationDevice* aDeviceAnother) { if (!aDevice || !aDeviceAnother) { return false; } @@ -127,19 +124,16 @@ private: nsWeakPtr mChromeEventHandler; nsCOMPtr<nsIPrincipal> mPrincipal; nsCOMPtr<nsIPresentationServiceCallback> mCallback; nsCOMPtr<nsIPresentationTransportBuilderConstructor> mBuilderConstructor; }; LazyLogModule gPresentationLog("Presentation"); -} // namespace dom -} // namespace mozilla - NS_IMPL_ISUPPORTS(PresentationDeviceRequest, nsIPresentationDeviceRequest) PresentationDeviceRequest::PresentationDeviceRequest( const nsTArray<nsString>& aUrls, const nsAString& aId, const nsAString& aOrigin, uint64_t aWindowId, nsIDOMEventTarget* aEventTarget, @@ -271,17 +265,16 @@ PresentationDeviceRequest::Cancel(nsresu * Implementation of PresentationService */ NS_IMPL_ISUPPORTS(PresentationService, nsIPresentationService, nsIObserver) PresentationService::PresentationService() - : mIsAvailable(false) { } PresentationService::~PresentationService() { HandleShutdown(); } @@ -311,36 +304,42 @@ PresentationService::Init() if (NS_WARN_IF(NS_FAILED(rv))) { return false; } rv = obs->AddObserver(this, PRESENTATION_RECONNECT_REQUEST_TOPIC, false); if (NS_WARN_IF(NS_FAILED(rv))) { return false; } - nsCOMPtr<nsIPresentationDeviceManager> deviceManager = - do_GetService(PRESENTATION_DEVICE_MANAGER_CONTRACTID); - if (NS_WARN_IF(!deviceManager)) { - return false; - } - - rv = deviceManager->GetDeviceAvailable(&mIsAvailable); return !NS_WARN_IF(NS_FAILED(rv)); } NS_IMETHODIMP PresentationService::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) { if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) { HandleShutdown(); return NS_OK; } else if (!strcmp(aTopic, PRESENTATION_DEVICE_CHANGE_TOPIC)) { - return HandleDeviceChange(); + // Ignore the "update" case here, since we only care about the arrival and + // removal of the device. + if (!NS_strcmp(aData, u"add")) { + nsCOMPtr<nsIPresentationDevice> device = do_QueryInterface(aSubject); + if (NS_WARN_IF(!device)) { + return NS_ERROR_FAILURE; + } + + return HandleDeviceAdded(device); + } else if(!NS_strcmp(aData, u"remove")) { + return HandleDeviceRemoved(); + } + + return NS_OK; } else if (!strcmp(aTopic, PRESENTATION_SESSION_REQUEST_TOPIC)) { nsCOMPtr<nsIPresentationSessionRequest> request(do_QueryInterface(aSubject)); if (NS_WARN_IF(!request)) { return NS_ERROR_FAILURE; } return HandleSessionRequest(request); } else if (!strcmp(aTopic, PRESENTATION_TERMINATE_REQUEST_TOPIC)) { @@ -369,53 +368,114 @@ PresentationService::Observe(nsISupports void PresentationService::HandleShutdown() { MOZ_ASSERT(NS_IsMainThread()); Shutdown(); - mAvailabilityListeners.Clear(); + mAvailabilityManager.Clear(); mSessionInfoAtController.Clear(); mSessionInfoAtReceiver.Clear(); nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); if (obs) { obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID); obs->RemoveObserver(this, PRESENTATION_DEVICE_CHANGE_TOPIC); obs->RemoveObserver(this, PRESENTATION_SESSION_REQUEST_TOPIC); obs->RemoveObserver(this, PRESENTATION_TERMINATE_REQUEST_TOPIC); obs->RemoveObserver(this, PRESENTATION_RECONNECT_REQUEST_TOPIC); } } nsresult -PresentationService::HandleDeviceChange() +PresentationService::HandleDeviceAdded(nsIPresentationDevice* aDevice) +{ + PRES_DEBUG("%s\n", __func__); + if (!aDevice) { + MOZ_ASSERT(false, "aDevice shoud no be null."); + return NS_ERROR_INVALID_ARG; + } + + // Query for only unavailable URLs while device added. + nsTArray<nsString> unavailableUrls; + mAvailabilityManager.GetAvailbilityUrlByAvailability(unavailableUrls, false); + + nsTArray<nsString> supportedAvailabilityUrl; + for (const auto& url : unavailableUrls) { + bool isSupported; + if (NS_SUCCEEDED(aDevice->IsRequestedUrlSupported(url, &isSupported)) && + isSupported) { + supportedAvailabilityUrl.AppendElement(url); + } + } + + if (!supportedAvailabilityUrl.IsEmpty()) { + return mAvailabilityManager.DoNotifyAvailableChange(supportedAvailabilityUrl, + true); + } + + return NS_OK; +} + +nsresult +PresentationService::HandleDeviceRemoved() { PRES_DEBUG("%s\n", __func__); + // Query for only available URLs while device removed. + nsTArray<nsString> availabilityUrls; + mAvailabilityManager.GetAvailbilityUrlByAvailability(availabilityUrls, true); + + return UpdateAvailabilityUrlChange(availabilityUrls); +} + +nsresult +PresentationService::UpdateAvailabilityUrlChange( + const nsTArray<nsString>& aAvailabilityUrls) +{ nsCOMPtr<nsIPresentationDeviceManager> deviceManager = do_GetService(PRESENTATION_DEVICE_MANAGER_CONTRACTID); if (NS_WARN_IF(!deviceManager)) { return NS_ERROR_NOT_AVAILABLE; } - bool isAvailable; - nsresult rv = deviceManager->GetDeviceAvailable(&isAvailable); + nsCOMPtr<nsIArray> devices; + nsresult rv = deviceManager->GetAvailableDevices(nullptr, + getter_AddRefs(devices)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - if (isAvailable != mIsAvailable) { - mIsAvailable = isAvailable; - NotifyAvailableChange(mIsAvailable); + uint32_t numOfDevices; + devices->GetLength(&numOfDevices); + + nsTArray<nsString> supportedAvailabilityUrl; + for (const auto& url : aAvailabilityUrls) { + for (uint32_t i = 0; i < numOfDevices; ++i) { + nsCOMPtr<nsIPresentationDevice> device = do_QueryElementAt(devices, i); + if (device) { + bool isSupported; + if (NS_SUCCEEDED(device->IsRequestedUrlSupported(url, &isSupported)) && + isSupported) { + supportedAvailabilityUrl.AppendElement(url); + break; + } + } + } } - return NS_OK; + if (supportedAvailabilityUrl.IsEmpty()) { + return mAvailabilityManager.DoNotifyAvailableChange(aAvailabilityUrls, + false); + } + + return mAvailabilityManager.DoNotifyAvailableChange(supportedAvailabilityUrl, + true); } nsresult PresentationService::HandleSessionRequest(nsIPresentationSessionRequest* aRequest) { nsCOMPtr<nsIPresentationControlChannel> ctrlChannel; nsresult rv = aRequest->GetControlChannel(getter_AddRefs(ctrlChannel)); if (NS_WARN_IF(NS_FAILED(rv) || !ctrlChannel)) { @@ -592,27 +652,16 @@ PresentationService::HandleReconnectRequ if (NS_WARN_IF(!info->GetUrl().Equals(url))) { ctrlChannel->Disconnect(rv); return rv; } return HandleSessionRequest(aRequest); } -void -PresentationService::NotifyAvailableChange(bool aIsAvailable) -{ - nsTObserverArray<nsCOMPtr<nsIPresentationAvailabilityListener>>::ForwardIterator iter(mAvailabilityListeners); - while (iter.HasMore()) { - nsCOMPtr<nsIPresentationAvailabilityListener> listener = iter.GetNext(); - Unused << - NS_WARN_IF(NS_FAILED(listener->NotifyAvailableChange(aIsAvailable))); - } -} - NS_IMETHODIMP PresentationService::StartSession( const nsTArray<nsString>& aUrls, const nsAString& aSessionId, const nsAString& aOrigin, const nsAString& aDeviceId, uint64_t aWindowId, nsIDOMEventTarget* aEventTarget, @@ -873,38 +922,36 @@ PresentationService::BuildTransport(cons if (NS_WARN_IF(!info)) { return NS_ERROR_NOT_AVAILABLE; } return static_cast<PresentationControllingInfo*>(info.get())->BuildTransport(); } NS_IMETHODIMP -PresentationService::RegisterAvailabilityListener(nsIPresentationAvailabilityListener* aListener) +PresentationService::RegisterAvailabilityListener( + const nsTArray<nsString>& aAvailabilityUrls, + nsIPresentationAvailabilityListener* aListener) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!aAvailabilityUrls.IsEmpty()); + MOZ_ASSERT(aListener); + + mAvailabilityManager.AddAvailabilityListener(aAvailabilityUrls, aListener); + return UpdateAvailabilityUrlChange(aAvailabilityUrls); +} + +NS_IMETHODIMP +PresentationService::UnregisterAvailabilityListener( + const nsTArray<nsString>& aAvailabilityUrls, + nsIPresentationAvailabilityListener* aListener) { MOZ_ASSERT(NS_IsMainThread()); - if (!mAvailabilityListeners.Contains(aListener)) { - mAvailabilityListeners.AppendElement(aListener); - } - - // Leverage availablility change notification to assign - // the initial value of availability object. - Unused << - NS_WARN_IF(NS_FAILED(aListener->NotifyAvailableChange(mIsAvailable))); - - return NS_OK; -} - -NS_IMETHODIMP -PresentationService::UnregisterAvailabilityListener(nsIPresentationAvailabilityListener* aListener) -{ - MOZ_ASSERT(NS_IsMainThread()); - - mAvailabilityListeners.RemoveElement(aListener); + mAvailabilityManager.RemoveAvailabilityListener(aAvailabilityUrls, aListener); return NS_OK; } NS_IMETHODIMP PresentationService::RegisterSessionListener(const nsAString& aSessionId, uint8_t aRole, nsIPresentationSessionListener* aListener) { @@ -1114,16 +1161,19 @@ PresentationService::IsSessionAccessible aRole == nsIPresentationService::ROLE_RECEIVER); RefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId, aRole); if (NS_WARN_IF(!info)) { return false; } return info->IsAccessible(aProcessId); } +} // namespace dom +} // namespace mozilla + already_AddRefed<nsIPresentationService> NS_CreatePresentationService() { MOZ_ASSERT(NS_IsMainThread()); nsCOMPtr<nsIPresentationService> service; if (XRE_GetProcessType() == GeckoProcessType_Content) { service = new mozilla::dom::PresentationIPCService();
--- a/dom/presentation/PresentationService.h +++ b/dom/presentation/PresentationService.h @@ -4,17 +4,16 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef mozilla_dom_PresentationService_h #define mozilla_dom_PresentationService_h #include "nsCOMPtr.h" #include "nsIObserver.h" -#include "nsTObserverArray.h" #include "PresentationServiceBase.h" #include "PresentationSessionInfo.h" class nsIPresentationSessionRequest; class nsIPresentationTerminateRequest; class nsIURI; class nsIPresentationSessionTransportBuilder; @@ -41,28 +40,29 @@ public: const uint8_t aRole, base::ProcessId aProcessId); private: friend class PresentationDeviceRequest; virtual ~PresentationService(); void HandleShutdown(); - nsresult HandleDeviceChange(); + nsresult HandleDeviceAdded(nsIPresentationDevice* aDevice); + nsresult HandleDeviceRemoved(); nsresult HandleSessionRequest(nsIPresentationSessionRequest* aRequest); nsresult HandleTerminateRequest(nsIPresentationTerminateRequest* aRequest); nsresult HandleReconnectRequest(nsIPresentationSessionRequest* aRequest); - void NotifyAvailableChange(bool aIsAvailable); // This is meant to be called by PresentationDeviceRequest. already_AddRefed<PresentationSessionInfo> CreateControllingSessionInfo(const nsAString& aUrl, const nsAString& aSessionId, uint64_t aWindowId); - bool mIsAvailable; - nsTObserverArray<nsCOMPtr<nsIPresentationAvailabilityListener>> mAvailabilityListeners; + // Emumerate all devices to get the availability of each input Urls. + nsresult UpdateAvailabilityUrlChange( + const nsTArray<nsString>& aAvailabilityUrls); }; } // namespace dom } // namespace mozilla #endif // mozilla_dom_PresentationService_h
--- a/dom/presentation/PresentationServiceBase.h +++ b/dom/presentation/PresentationServiceBase.h @@ -2,24 +2,25 @@ /* vim: set sw=2 ts=8 et ft=cpp : */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef mozilla_dom_PresentationServiceBase_h #define mozilla_dom_PresentationServiceBase_h +#include "mozilla/Unused.h" #include "nsClassHashtable.h" +#include "nsCOMArray.h" +#include "nsIPresentationListener.h" #include "nsIPresentationService.h" #include "nsRefPtrHashtable.h" #include "nsString.h" #include "nsTArray.h" -class nsIPresentationRespondingListener; - namespace mozilla { namespace dom { template<class T> class PresentationServiceBase { public: PresentationServiceBase() = default; @@ -127,16 +128,191 @@ protected: mRespondingWindowIds.Clear(); } private: nsClassHashtable<nsUint64HashKey, nsTArray<nsString>> mRespondingSessionIds; nsDataHashtable<nsStringHashKey, uint64_t> mRespondingWindowIds; }; + class AvailabilityManager final + { + public: + explicit AvailabilityManager() + { + MOZ_COUNT_CTOR(AvailabilityManager); + } + + ~AvailabilityManager() + { + MOZ_COUNT_DTOR(AvailabilityManager); + } + + void AddAvailabilityListener( + const nsTArray<nsString>& aAvailabilityUrls, + nsIPresentationAvailabilityListener* aListener) + { + nsTArray<nsString> dummy; + AddAvailabilityListener(aAvailabilityUrls, aListener, dummy); + } + + void AddAvailabilityListener( + const nsTArray<nsString>& aAvailabilityUrls, + nsIPresentationAvailabilityListener* aListener, + nsTArray<nsString>& aAddedUrls) + { + if (!aListener) { + MOZ_ASSERT(false, "aListener should not be null."); + return; + } + + if (aAvailabilityUrls.IsEmpty()) { + MOZ_ASSERT(false, "aAvailabilityUrls should not be empty."); + return; + } + + aAddedUrls.Clear(); + nsTArray<nsString> knownAvailableUrls; + for (const auto& url : aAvailabilityUrls) { + AvailabilityEntry* entry; + if (!mAvailabilityUrlTable.Get(url, &entry)) { + entry = new AvailabilityEntry(); + mAvailabilityUrlTable.Put(url, entry); + aAddedUrls.AppendElement(url); + } + if (!entry->mListeners.Contains(aListener)) { + entry->mListeners.AppendElement(aListener); + } + if (entry->mAvailable) { + knownAvailableUrls.AppendElement(url); + } + } + + if (!knownAvailableUrls.IsEmpty()) { + Unused << + NS_WARN_IF( + NS_FAILED(aListener->NotifyAvailableChange(knownAvailableUrls, + true))); + } else { + // If we can't find any known available url and there is no newly + // added url, we still need to notify the listener of the result. + // So, the promise returned by |getAvailability| can be resolved. + if (aAddedUrls.IsEmpty()) { + Unused << + NS_WARN_IF( + NS_FAILED(aListener->NotifyAvailableChange(aAvailabilityUrls, + false))); + } + } + } + + void RemoveAvailabilityListener( + const nsTArray<nsString>& aAvailabilityUrls, + nsIPresentationAvailabilityListener* aListener) + { + nsTArray<nsString> dummy; + RemoveAvailabilityListener(aAvailabilityUrls, aListener, dummy); + } + + void RemoveAvailabilityListener( + const nsTArray<nsString>& aAvailabilityUrls, + nsIPresentationAvailabilityListener* aListener, + nsTArray<nsString>& aRemovedUrls) + { + if (!aListener) { + MOZ_ASSERT(false, "aListener should not be null."); + return; + } + + if (aAvailabilityUrls.IsEmpty()) { + MOZ_ASSERT(false, "aAvailabilityUrls should not be empty."); + return; + } + + aRemovedUrls.Clear(); + for (const auto& url : aAvailabilityUrls) { + AvailabilityEntry* entry; + if (mAvailabilityUrlTable.Get(url, &entry)) { + entry->mListeners.RemoveElement(aListener); + if (entry->mListeners.IsEmpty()) { + mAvailabilityUrlTable.Remove(url); + aRemovedUrls.AppendElement(url); + } + } + } + } + + nsresult DoNotifyAvailableChange(const nsTArray<nsString>& aAvailabilityUrls, + bool aAvailable) + { + typedef nsClassHashtable<nsISupportsHashKey, + nsTArray<nsString>> ListenerToUrlsMap; + ListenerToUrlsMap availabilityListenerTable; + // Create a mapping from nsIPresentationAvailabilityListener to + // availabilityUrls. + for (auto it = mAvailabilityUrlTable.ConstIter(); !it.Done(); it.Next()) { + if (aAvailabilityUrls.Contains(it.Key())) { + AvailabilityEntry* entry = it.UserData(); + entry->mAvailable = aAvailable; + + for (uint32_t i = 0; i < entry->mListeners.Length(); ++i) { + nsIPresentationAvailabilityListener* listener = + entry->mListeners.ObjectAt(i); + nsTArray<nsString>* urlArray; + if (!availabilityListenerTable.Get(listener, &urlArray)) { + urlArray = new nsTArray<nsString>(); + availabilityListenerTable.Put(listener, urlArray); + } + urlArray->AppendElement(it.Key()); + } + } + } + + for (auto it = availabilityListenerTable.Iter(); !it.Done(); it.Next()) { + auto listener = + static_cast<nsIPresentationAvailabilityListener*>(it.Key()); + + Unused << + NS_WARN_IF(NS_FAILED(listener->NotifyAvailableChange(*it.UserData(), + aAvailable))); + } + return NS_OK; + } + + void GetAvailbilityUrlByAvailability(nsTArray<nsString>& aOutArray, + bool aAvailable) + { + aOutArray.Clear(); + + for (auto it = mAvailabilityUrlTable.ConstIter(); !it.Done(); it.Next()) { + if (it.UserData()->mAvailable == aAvailable) { + aOutArray.AppendElement(it.Key()); + } + } + } + + void Clear() + { + mAvailabilityUrlTable.Clear(); + } + + private: + struct AvailabilityEntry + { + explicit AvailabilityEntry() + : mAvailable(false) + {} + + bool mAvailable; + nsCOMArray<nsIPresentationAvailabilityListener> mListeners; + }; + + nsClassHashtable<nsStringHashKey, AvailabilityEntry> mAvailabilityUrlTable; + }; + virtual ~PresentationServiceBase() = default; void Shutdown() { mRespondingListeners.Clear(); mControllerSessionIdManager.Clear(); mReceiverSessionIdManager.Clear(); } @@ -210,14 +386,16 @@ protected: // to retrieve the correspondent session ID. Besides, also keep the mapping // between the responding session ID and the window ID to help look up the // window ID. SessionIdManager mControllerSessionIdManager; SessionIdManager mReceiverSessionIdManager; nsRefPtrHashtable<nsStringHashKey, T> mSessionInfoAtController; nsRefPtrHashtable<nsStringHashKey, T> mSessionInfoAtReceiver; + + AvailabilityManager mAvailabilityManager; }; } // namespace dom } // namespace mozilla #endif // mozilla_dom_PresentationServiceBase_h
--- a/dom/presentation/interfaces/nsIPresentationListener.idl +++ b/dom/presentation/interfaces/nsIPresentationListener.idl @@ -1,21 +1,24 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsISupports.idl" -[scriptable, uuid(0105f837-4279-4715-9d5b-2dc3f8b65353)] +[ref] native URLArrayRef(const nsTArray<nsString>); + +[uuid(0105f837-4279-4715-9d5b-2dc3f8b65353)] interface nsIPresentationAvailabilityListener : nsISupports { /* * Called when device availability changes. */ - void notifyAvailableChange(in bool available); + [noscript] void notifyAvailableChange(in URLArrayRef urls, + in bool available); }; [scriptable, uuid(7dd48df8-8f8c-48c7-ac37-7b9fd1acf2f8)] interface nsIPresentationSessionListener : nsISupports { const unsigned short STATE_CONNECTING = 0; const unsigned short STATE_CONNECTED = 1; const unsigned short STATE_CLOSED = 2;
--- a/dom/presentation/interfaces/nsIPresentationService.idl +++ b/dom/presentation/interfaces/nsIPresentationService.idl @@ -153,25 +153,32 @@ interface nsIPresentationService : nsISu [noscript] void reconnectSession(in URLArrayRef urls, in DOMString sessionId, in uint8_t role, in nsIPresentationServiceCallback callback); /* * Register an availability listener. Must be called from the main thread. * + * @param availabilityUrls: The Urls that this listener is interested in. * @param listener: The listener to register. */ - void registerAvailabilityListener(in nsIPresentationAvailabilityListener listener); + [noscript] void registerAvailabilityListener( + in URLArrayRef availabilityUrls, + in nsIPresentationAvailabilityListener listener); /* * Unregister an availability listener. Must be called from the main thread. + * + * @param availabilityUrls: The Urls that are registered before. * @param listener: The listener to unregister. */ - void unregisterAvailabilityListener(in nsIPresentationAvailabilityListener listener); + [noscript] void unregisterAvailabilityListener( + in URLArrayRef availabilityUrls, + in nsIPresentationAvailabilityListener listener); /* * Register a session listener. Must be called from the main thread. * * @param sessionId: An ID to identify presentation session. * @param role: Identify the function called by controller or receiver. * @param listener: The listener to register. */
--- a/dom/presentation/ipc/PPresentation.ipdl +++ b/dom/presentation/ipc/PPresentation.ipdl @@ -72,33 +72,34 @@ union PresentationIPCRequest sync protocol PPresentation { manager PContent; manages PPresentationBuilder; manages PPresentationRequest; child: - async NotifyAvailableChange(bool aAvailable); + async NotifyAvailableChange(nsString[] aAvailabilityUrls, + bool aAvailable); async NotifySessionStateChange(nsString aSessionId, uint16_t aState, nsresult aReason); async NotifyMessage(nsString aSessionId, nsCString aData, bool aIsBinary); async NotifySessionConnect(uint64_t aWindowId, nsString aSessionId); async NotifyCloseSessionTransport(nsString aSessionId, uint8_t aRole, nsresult aReason); async PPresentationBuilder(nsString aSessionId, uint8_t aRole); parent: async __delete__(); - async RegisterAvailabilityHandler(); - async UnregisterAvailabilityHandler(); + async RegisterAvailabilityHandler(nsString[] aAvailabilityUrls); + async UnregisterAvailabilityHandler(nsString[] aAvailabilityUrls); async RegisterSessionHandler(nsString aSessionId, uint8_t aRole); async UnregisterSessionHandler(nsString aSessionId, uint8_t aRole); async RegisterRespondingHandler(uint64_t aWindowId); async UnregisterRespondingHandler(uint64_t aWindowId); async PPresentationRequest(PresentationIPCRequest aRequest);
--- a/dom/presentation/ipc/PresentationChild.cpp +++ b/dom/presentation/ipc/PresentationChild.cpp @@ -84,20 +84,24 @@ PresentationChild::DeallocPPresentationB { RefPtr<PresentationBuilderChild> actor = dont_AddRef(static_cast<PresentationBuilderChild*>(aActor)); return true; } bool -PresentationChild::RecvNotifyAvailableChange(const bool& aAvailable) +PresentationChild::RecvNotifyAvailableChange( + nsTArray<nsString>&& aAvailabilityUrls, + const bool& aAvailable) { if (mService) { - Unused << NS_WARN_IF(NS_FAILED(mService->NotifyAvailableChange(aAvailable))); + Unused << + NS_WARN_IF(NS_FAILED(mService->NotifyAvailableChange(aAvailabilityUrls, + aAvailable))); } return true; } bool PresentationChild::RecvNotifySessionStateChange(const nsString& aSessionId, const uint16_t& aState, const nsresult& aReason)
--- a/dom/presentation/ipc/PresentationChild.h +++ b/dom/presentation/ipc/PresentationChild.h @@ -38,17 +38,18 @@ public: virtual PPresentationBuilderChild* AllocPPresentationBuilderChild(const nsString& aSessionId, const uint8_t& aRole) override; virtual bool DeallocPPresentationBuilderChild(PPresentationBuilderChild* aActor) override; virtual bool - RecvNotifyAvailableChange(const bool& aAvailable) override; + RecvNotifyAvailableChange(nsTArray<nsString>&& aAvailabilityUrls, + const bool& aAvailable) override; virtual bool RecvNotifySessionStateChange(const nsString& aSessionId, const uint16_t& aState, const nsresult& aReason) override; virtual bool RecvNotifyMessage(const nsString& aSessionId,
--- a/dom/presentation/ipc/PresentationIPCService.cpp +++ b/dom/presentation/ipc/PresentationIPCService.cpp @@ -23,17 +23,19 @@ using namespace mozilla::dom; using namespace mozilla::ipc; namespace { PresentationChild* sPresentationChild; } // anonymous -NS_IMPL_ISUPPORTS(PresentationIPCService, nsIPresentationService) +NS_IMPL_ISUPPORTS(PresentationIPCService, + nsIPresentationService, + nsIPresentationAvailabilityListener) PresentationIPCService::PresentationIPCService() { ContentChild* contentChild = ContentChild::GetSingleton(); if (NS_WARN_IF(!contentChild)) { return; } sPresentationChild = new PresentationChild(this); @@ -41,17 +43,16 @@ PresentationIPCService::PresentationIPCS NS_WARN_IF(!contentChild->SendPPresentationConstructor(sPresentationChild)); } /* virtual */ PresentationIPCService::~PresentationIPCService() { Shutdown(); - mAvailabilityListeners.Clear(); mSessionListeners.Clear(); mSessionInfoAtController.Clear(); mSessionInfoAtReceiver.Clear(); sPresentationChild = nullptr;