author | shindli <shindli@mozilla.com> |
Thu, 14 Feb 2019 06:58:32 +0200 | |
changeset 459075 | a0752d7e8073 |
parent 459038 | 328de611bcd3 (current diff) |
parent 459074 | f0ea53f47215 (diff) |
child 459076 | 68a001a42406 |
push id | 111913 |
push user | shindli@mozilla.com |
push date | Thu, 14 Feb 2019 05:01:59 +0000 |
treeherder | mozilla-inbound@a0752d7e8073 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 67.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/Cargo.lock +++ b/Cargo.lock @@ -1,10 +1,8 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. [[package]] name = "Inflector" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -3084,16 +3082,17 @@ dependencies = [ "app_units 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "bincode 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", "core-graphics 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)", "core-text 13.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cstr 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "dwrote 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "freetype 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "gleam 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "malloc_size_of_derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
--- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -1809,8 +1809,10 @@ pref("browser.discovery.sites", "addons. pref("browser.engagement.recent_visited_origins.expiry", 86400); // 24 * 60 * 60 (24 hours in seconds) pref("browser.aboutConfig.showWarning", true); #if defined(XP_WIN) && defined(MOZ_LAUNCHER_PROCESS) // Launcher process is disabled by default, will be selectively enabled via SHIELD pref("browser.launcherProcess.enabled", false); #endif // defined(XP_WIN) && defined(MOZ_LAUNCHER_PROCESS) + +pref("browser.toolbars.keyboard_navigation", false);
new file mode 100644 --- /dev/null +++ b/browser/base/content/browser-toolbarKeyNav.js @@ -0,0 +1,246 @@ +/* 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/. */ + +// This file is loaded into the browser window scope. +/* eslint-env mozilla/browser-window */ + +/** + * Handle keyboard navigation for toolbars. + * Having separate tab stops for every toolbar control results in an + * unmanageable number of tab stops. Therefore, we group buttons under a single + * tab stop and allow movement between them using left/right arrows. + * However, text inputs use the arrow keys for their own purposes, so they need + * their own tab stop. There are also groups of buttons before and after the + * URL bar input which should get their own tab stop. The subsequent buttons on + * the toolbar are then another tab stop after that. + * Tab stops for groups of buttons are set using the <toolbartabstop/> element. + * This element is invisible, but gets included in the tab order. When one of + * these gets focus, it redirects focus to the appropriate button. This avoids + * the need to continually manage the tabindex of toolbar buttons in response to + * toolbarchanges. + */ + +ToolbarKeyboardNavigator = { + // Toolbars we want to be keyboard navigable. + kToolbars: [CustomizableUI.AREA_NAVBAR, CustomizableUI.AREA_BOOKMARKS], + + _isButton(aElem) { + return aElem.tagName == "toolbarbutton" || + aElem.getAttribute("role") == "button"; + }, + + // Get a TreeWalker which includes only controls which should be keyboard + // navigable. + _getWalker(aRoot) { + if (aRoot._toolbarKeyNavWalker) { + return aRoot._toolbarKeyNavWalker; + } + + let filter = (aNode) => { + if (aNode.tagName == "toolbartabstop") { + return NodeFilter.FILTER_ACCEPT; + } + + // Special case for the "View site information" button, which isn't + // actionable in some cases but is still visible. + if (aNode.id == "identity-box" && + document.getElementById("urlbar").getAttribute("pageproxystate") == + "invalid") { + return NodeFilter.FILTER_REJECT; + } + + // Skip invisible or disabled elements. + if (aNode.hidden || aNode.disabled) { + return NodeFilter.FILTER_REJECT; + } + // This width check excludes the overflow button when there's no overflow. + let bounds = window.windowUtils.getBoundsWithoutFlushing(aNode); + if (bounds.width == 0) { + return NodeFilter.FILTER_REJECT; + } + + if (this._isButton(aNode)) { + return NodeFilter.FILTER_ACCEPT; + } + return NodeFilter.FILTER_SKIP; + }; + aRoot._toolbarKeyNavWalker = document.createTreeWalker(aRoot, + NodeFilter.SHOW_ELEMENT, filter); + return aRoot._toolbarKeyNavWalker; + }, + + init() { + for (let id of this.kToolbars) { + let toolbar = document.getElementById(id); + // When enabled, no toolbar buttons should themselves be tabbable. + // We manage toolbar focus completely. This attribute ensures that CSS + // doesn't set -moz-user-focus: normal. + toolbar.setAttribute("keyNav", "true"); + for (let stop of toolbar.getElementsByTagName("toolbartabstop")) { + // These are invisible, but because they need to be in the tab order, + // they can't get display: none or similar. They must therefore be + // explicitly hidden for accessibility. + stop.setAttribute("aria-hidden", "true"); + stop.addEventListener("focus", this); + } + toolbar.addEventListener("keydown", this); + toolbar.addEventListener("keypress", this); + } + }, + + uninit() { + for (let id of this.kToolbars) { + let toolbar = document.getElementById(id); + for (let stop of toolbar.getElementsByTagName("toolbartabstop")) { + stop.removeEventListener("focus", this); + } + toolbar.removeEventListener("keydown", this); + toolbar.removeEventListener("keypress", this); + toolbar.removeAttribute("keyNav"); + } + }, + + _focusButton(aButton) { + // Toolbar buttons aren't focusable because if they were, clicking them + // would focus them, which is undesirable. Therefore, we must make a + // button focusable only when we want to focus it. + aButton.setAttribute("tabindex", "-1"); + aButton.focus(); + // We could remove tabindex now, but even though the button keeps DOM + // focus, a11y gets confused because the button reports as not being + // focusable. This results in weirdness if the user switches windows and + // then switches back. Instead, remove tabindex when the button loses + // focus. + aButton.addEventListener("blur", this); + }, + + _onButtonBlur(aEvent) { + if (document.activeElement == aEvent.target) { + // This event was fired because the user switched windows. This button + // will get focus again when the user returns. + return; + } + aEvent.target.removeEventListener("blur", this); + aEvent.target.removeAttribute("tabindex"); + }, + + _onTabStopFocus(aEvent) { + let toolbar = aEvent.target.closest("toolbar"); + let walker = this._getWalker(toolbar); + + let oldFocus = aEvent.relatedTarget; + if (oldFocus) { + // Save this because we might rewind focus and the subsequent focus event + // won't get a relatedTarget. + this._isFocusMovingBackward = + oldFocus.compareDocumentPosition(aEvent.target) & + Node.DOCUMENT_POSITION_PRECEDING; + if (this._isFocusMovingBackward && oldFocus && this._isButton(oldFocus)) { + // Shift+tabbing from a button will land on its toolbartabstop. Skip it. + document.commandDispatcher.rewindFocus(); + return; + } + } + + walker.currentNode = aEvent.target; + let button = walker.nextNode(); + if (!button || !this._isButton(button)) { + // No navigable buttons for this tab stop. Skip it. + if (this._isFocusMovingBackward) { + document.commandDispatcher.rewindFocus(); + } else { + document.commandDispatcher.advanceFocus(); + } + return; + } + + this._focusButton(button); + }, + + navigateButtons(aToolbar, aPrevious) { + let oldFocus = document.activeElement; + let walker = this._getWalker(aToolbar); + // Start from the current control and walk to the next/previous control. + walker.currentNode = oldFocus; + let newFocus; + if (aPrevious) { + newFocus = walker.previousNode(); + } else { + newFocus = walker.nextNode(); + } + if (!newFocus || newFocus.tagName == "toolbartabstop") { + // There are no more controls or we hit a tab stop placeholder. + return; + } + this._focusButton(newFocus); + }, + + _onKeyDown(aEvent) { + let focus = document.activeElement; + if (aEvent.altKey || aEvent.controlKey || aEvent.metaKey || + aEvent.shiftKey || !this._isButton(focus)) { + return; + } + + switch (aEvent.key) { + case "ArrowLeft": + this.navigateButtons(aEvent.currentTarget, true); + break; + case "ArrowRight": + this.navigateButtons(aEvent.currentTarget, false); + break; + default: + return; + } + aEvent.preventDefault(); + }, + + _onKeyPress(aEvent) { + let focus = document.activeElement; + if ((aEvent.key != "Enter" && aEvent.key != " ") || + !this._isButton(focus)) { + return; + } + + if (focus.getAttribute("type") == "menu") { + focus.open = true; + } else { + // Several buttons specifically don't use command events; e.g. because + // they want to activate for middle click. Therefore, simulate a + // click event. + // If this button does handle command events, that won't trigger here. + // Command events have their own keyboard handling: keypress for enter + // and keyup for space. We rely on that behavior, since there's no way + // for us to reliably know what events a button handles. + focus.dispatchEvent(new MouseEvent("click", { + bubbles: true, + ctrlKey: aEvent.ctrlKey, + altKey: aEvent.altKey, + shiftKey: aEvent.shiftKey, + metaKey: aEvent.metaKey, + })); + } + // We deliberately don't call aEvent.preventDefault() here so that enter + // will trigger a command event handler if appropriate. + aEvent.stopPropagation(); + }, + + handleEvent(aEvent) { + switch (aEvent.type) { + case "focus": + this._onTabStopFocus(aEvent); + break; + case "keydown": + this._onKeyDown(aEvent); + break; + case "keypress": + this._onKeyPress(aEvent); + break; + case "blur": + this._onButtonBlur(aEvent); + break; + } + }, + +};
--- a/browser/base/content/browser.css +++ b/browser/base/content/browser.css @@ -773,17 +773,17 @@ html|input.urlbar-input { #urlbar[pageproxystate=invalid] > #page-action-buttons > .urlbar-page-action, #identity-box.chromeUI ~ #page-action-buttons > .urlbar-page-action:not(#star-button-box), .urlbar-history-dropmarker[usertyping], .urlbar-go-button:not([usertyping]), .urlbar-go-button:not([parentfocused="true"]) { display: none; } -#identity-box { +#nav-bar:not([keyNav=true]) #identity-box { -moz-user-focus: normal; } /* We leave 49ch plus whatever space the download button will need when it * appears. Normally this should be 16px for the icon, plus 2 * 2px padding * plus the toolbarbutton-inner-padding. We're adding 4px to ensure things * like rounding on hidpi don't accidentally result in the button going * into overflow. @@ -976,17 +976,17 @@ html|*.pointerlockfswarning-domain-text: } html|*#fullscreen-exit-button { pointer-events: auto; } /* notification anchors should only be visible when their associated notifications are */ -.notification-anchor-icon { +#nav-bar:not([keyNav=true]) .notification-anchor-icon { -moz-user-focus: normal; } #blocked-permissions-container > .blocked-permission-icon:not([showing]), .notification-anchor-icon:not([showing]) { display: none; } @@ -1422,9 +1422,13 @@ toolbarpaletteitem > toolbaritem { } @media (min-resolution: 1.1dppx) { #sidebar-box[sidebarcommand$="-sidebar-action"] > #sidebar-header > #sidebar-switcher-target > #sidebar-icon { list-style-image: var(--webextension-menuitem-image-2x, inherit); } } +toolbar[keyNav=true]:not([collapsed=true]):not([customizing=true]) toolbartabstop { + -moz-user-focus: normal; +} + %include theme-vars.inc.css
--- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -143,16 +143,18 @@ XPCOMUtils.defineLazyScriptGetter(this, XPCOMUtils.defineLazyScriptGetter(this, "SearchOneOffs", "chrome://browser/content/search/search-one-offs.js"); if (AppConstants.NIGHTLY_BUILD) { XPCOMUtils.defineLazyScriptGetter(this, "gWebRender", "chrome://browser/content/browser-webrender.js"); } XPCOMUtils.defineLazyScriptGetter(this, "pktUI", "chrome://pocket/content/main.js"); +XPCOMUtils.defineLazyScriptGetter(this, "ToolbarKeyboardNavigator", + "chrome://browser/content/browser-toolbarKeyNav.js"); // lazy service getters XPCOMUtils.defineLazyServiceGetters(this, { classifierService: ["@mozilla.org/url-classifier/dbservice;1", "nsIURIClassifier"], Favicons: ["@mozilla.org/browser/favicon-service;1", "nsIFaviconService"], gAboutNewTabService: ["@mozilla.org/browser/aboutnewtab-service;1", "nsIAboutNewTabService"], gDNSService: ["@mozilla.org/network/dns-service;1", "nsIDNSService"], @@ -278,16 +280,26 @@ XPCOMUtils.defineLazyGetter(this, "Win7F } }, handledOpening: false, }; } return null; }); +XPCOMUtils.defineLazyPreferenceGetter(this, "gToolbarKeyNavEnabled", + "browser.toolbars.keyboard_navigation", false, + (aPref, aOldVal, aNewVal) => { + if (aNewVal) { + ToolbarKeyboardNavigator.init(); + } else { + ToolbarKeyboardNavigator.uninit(); + } + }); + customElements.setElementCreationCallback("translation-notification", () => { Services.scriptloader.loadSubScript( "chrome://browser/content/translation-notification.js", window); }); var gBrowser; var gLastValidURLStr = ""; var gInPrintPreviewMode = false; @@ -1379,16 +1391,19 @@ var gBrowserInit = { gUIDensity.init(); TabletModeUpdater.init(); CombinedStopReload.ensureInitialized(); gPrivateBrowsingUI.init(); BrowserSearch.init(); BrowserPageActions.init(); gAccessibilityServiceIndicator.init(); AccessibilityRefreshBlocker.init(); + if (gToolbarKeyNavEnabled) { + ToolbarKeyboardNavigator.init(); + } gRemoteControl.updateVisualCue(Marionette.running); // If we are given a tab to swap in, take care of it before first paint to // avoid an about:blank flash. let tabToAdopt = this.getTabToAdopt(); if (tabToAdopt) { // Stop the about:blank load @@ -1925,16 +1940,20 @@ var gBrowserInit = { SidebarUI.uninit(); DownloadsButton.uninit(); gAccessibilityServiceIndicator.uninit(); AccessibilityRefreshBlocker.uninit(); + if (gToolbarKeyNavEnabled) { + ToolbarKeyboardNavigator.uninit(); + } + LanguagePrompt.uninit(); BrowserSearch.uninit(); // Now either cancel delayedStartup, or clean up the services initialized from // it. if (this._boundDelayedStartup) { this._cancelDelayedStartup();
--- a/browser/base/content/browser.xul +++ b/browser/base/content/browser.xul @@ -830,16 +830,17 @@ xmlns="http://www.w3.org/1999/xhtml" fullscreentoolbar="true" mode="icons" customizable="true" customizationtarget="nav-bar-customization-target" overflowable="true" overflowbutton="nav-bar-overflow-button" overflowtarget="widget-overflow-list" overflowpanel="widget-overflow" context="toolbar-context-menu"> + <toolbartabstop/> <hbox id="nav-bar-customization-target" flex="1"> <toolbarbutton id="back-button" class="toolbarbutton-1 chromeclass-toolbar-additional" label="&backCmd.label;" removable="false" overflows="false" keepbroadcastattributeswhencustomizing="true" command="Browser:BackOrBackDuplicate" onclick="checkForMiddleClick(this, event);" tooltip="back-button-tooltip" @@ -883,16 +884,17 @@ xmlns="http://www.w3.org/1999/xhtml" key="goHome" onclick="BrowserHome(event);" cui-areatype="toolbar" tooltiptext="&homeButton.defaultPage.tooltip;"/> <toolbarspring cui-areatype="toolbar" class="chromeclass-toolbar-additional"/> <toolbaritem id="urlbar-container" flex="400" persist="width" removable="false" class="chromeclass-location" overflows="false"> + <toolbartabstop/> <textbox id="urlbar" flex="1" placeholder="&urlbar.placeholder2;" defaultPlaceholder="&urlbar.placeholder2;" focused="true" type="autocomplete" autocompletesearch="unifiedcomplete" autocompletesearchparam="enable-actions" autocompletepopup="PopupAutoCompleteRichResult" @@ -1002,16 +1004,17 @@ xmlns="http://www.w3.org/1999/xhtml" <label id="identity-icon-country-label" class="plain"/> </hbox> </box> <box id="urlbar-display-box" align="center"> <label id="switchtab" class="urlbar-display urlbar-display-switchtab" value="&urlbar.switchToTab.label;"/> <label id="extension" class="urlbar-display urlbar-display-extension" value="&urlbar.extension.label;"/> </box> <hbox id="page-action-buttons" context="pageActionContextMenu"> + <toolbartabstop/> <hbox id="contextual-feature-recommendation" role="button" hidden="true"> <hbox id="cfr-label-container"> <label id="cfr-label"/> </hbox> <image id="cfr-button" class="urlbar-icon urlbar-page-action" role="presentation"/> </hbox> @@ -1059,16 +1062,17 @@ xmlns="http://www.w3.org/1999/xhtml" role="button"/> <hbox id="star-button-animatable-box"> <image id="star-button-animatable-image" role="presentation"/> </hbox> </hbox> </hbox> </textbox> + <toolbartabstop/> </toolbaritem> <toolbarspring cui-areatype="toolbar" class="chromeclass-toolbar-additional"/> <!-- This is a placeholder for the Downloads Indicator. It is visible during the customization of the toolbar, in the palette, and before the Downloads Indicator overlay is loaded. --> <toolbarbutton id="downloads-button" @@ -1154,16 +1158,17 @@ xmlns="http://www.w3.org/1999/xhtml" <toolbar id="PersonalToolbar" mode="icons" class="browser-toolbar chromeclass-directories" context="toolbar-context-menu" toolbarname="&personalbarCmd.label;" accesskey="&personalbarCmd.accesskey;" collapsed="true" customizable="true"> + <toolbartabstop skipintoolbarset="true"/> <toolbaritem id="personal-bookmarks" title="&bookmarksToolbarItem.label;" cui-areatype="toolbar" removable="true"> <toolbarbutton id="bookmarks-toolbar-placeholder" class="bookmark-item" label="&bookmarksToolbarItem.label;"/> <toolbarbutton id="bookmarks-toolbar-button" @@ -1323,17 +1328,19 @@ xmlns="http://www.w3.org/1999/xhtml" </toolbarbutton> <toolbaritem id="search-container" class="chromeclass-toolbar-additional" title="&searchItem.title;" align="center" flex="100" persist="width"> + <toolbartabstop/> <searchbar id="searchbar" flex="1"/> + <toolbartabstop/> </toolbaritem> </toolbarpalette> </toolbox> <hbox id="fullscr-toggler" hidden="true"/> <deck id="content-deck" flex="1"> <hbox flex="1" id="browser">
--- a/browser/base/content/test/keyboard/browser.ini +++ b/browser/base/content/test/keyboard/browser.ini @@ -1,1 +1,3 @@ [browser_toolbarButtonKeyPress.js] +[browser_toolbarKeyNav.js] +support-files = !/browser/base/content/test/permissions/permissions.html
--- a/browser/base/content/test/keyboard/browser_toolbarButtonKeyPress.js +++ b/browser/base/content/test/keyboard/browser_toolbarButtonKeyPress.js @@ -25,16 +25,22 @@ function waitForLocationChange() { resolve(); }, }; gBrowser.addProgressListener(wpl); }); return promise; } +add_task(async function setPref() { + await SpecialPowers.pushPrefEnv({ + set: [["browser.toolbars.keyboard_navigation", true]], + }); +}); + // Test activation of the app menu button from the keyboard. // The app menu should appear and focus should move inside it. add_task(async function testAppMenuButtonPress() { let button = document.getElementById("PanelUI-menu-button"); forceFocus(button); let focused = BrowserTestUtils.waitForEvent(window.PanelUI.mainView, "focus", true); EventUtils.synthesizeKey(" "); await focused; @@ -81,16 +87,17 @@ add_task(async function testDeveloperBut EventUtils.synthesizeKey(" "); await focused; ok(true, "Focus inside Developer menu after toolbar button pressed"); let hidden = BrowserTestUtils.waitForEvent(document, "popuphidden", true); view.closest("panel").hidePopup(); await hidden; CustomizableUI.reset(); }); + // Test that the Developer menu doesn't open when a key other than space or // enter is pressed . add_task(async function testDeveloperButtonWrongKey() { CustomizableUI.addWidgetToArea("developer-button", CustomizableUI.AREA_NAVBAR); let button = document.getElementById("developer-button"); forceFocus(button); EventUtils.synthesizeKey("KEY_Tab"); await TestUtils.waitForTick(); @@ -154,8 +161,85 @@ add_task(async function testSendTabToDev ok(view.contains(document.activeElement), "Focus inside Page Actions menu after toolbar button pressed"); let hidden = BrowserTestUtils.waitForEvent(document, "popuphidden", true); view.closest("panel").hidePopup(); await hidden; PageActions.actionForID("sendToDevice").pinnedToUrlbar = false; }); }); + +// Test activation of the Reload button from the keyboard. +// This is a toolbarbutton with a click handler and no command handler, but +// the toolbar keyboard navigation code should handle keyboard activation. +add_task(async function testReloadButtonPress() { + await BrowserTestUtils.withNewTab("https://example.com", async function(aBrowser) { + let button = document.getElementById("reload-button"); + await TestUtils.waitForCondition(() => !button.disabled); + forceFocus(button); + let loaded = BrowserTestUtils.browserLoaded(aBrowser); + EventUtils.synthesizeKey(" "); + await loaded; + ok(true, "Page loaded after Reload button pressed"); + }); +}); + +// Test activation of the Sidebars button from the keyboard. +// This is a toolbarbutton with a command handler. +add_task(async function testSidebarsButtonPress() { + let button = document.getElementById("sidebar-button"); + ok(!button.checked, "Sidebars button not checked at start of test"); + let sidebarBox = document.getElementById("sidebar-box"); + ok(sidebarBox.hidden, "Sidebar hidden at start of test"); + forceFocus(button); + EventUtils.synthesizeKey(" "); + await TestUtils.waitForCondition(() => button.checked); + ok(true, "Sidebars button checked after press"); + ok(!sidebarBox.hidden, "Sidebar visible after press"); + // Make sure the sidebar is fully loaded before we hide it. + // Otherwise, the unload event might call JS which isn't loaded yet. + // We can't use BrowserTestUtils.browserLoaded because it fails on non-tab + // docs. Instead, wait for something in the JS script. + let sidebarWin = document.getElementById("sidebar").contentWindow; + await TestUtils.waitForCondition(() => sidebarWin.PlacesUIUtils); + forceFocus(button); + EventUtils.synthesizeKey(" "); + await TestUtils.waitForCondition(() => !button.checked); + ok(true, "Sidebars button not checked after press"); + ok(sidebarBox.hidden, "Sidebar hidden after press"); +}); + +// Test activation of the Bookmark this page button from the keyboard. +// This is an image with a click handler on its parent and no command handler, +// but the toolbar keyboard navigation code should handle keyboard activation. +add_task(async function testBookmarkButtonPress() { + await BrowserTestUtils.withNewTab("https://example.com", async function(aBrowser) { + let button = document.getElementById("star-button"); + forceFocus(button); + let panel = document.getElementById("editBookmarkPanel"); + let focused = BrowserTestUtils.waitForEvent(panel, "focus", true); + EventUtils.synthesizeKey(" "); + await focused; + ok(true, "Focus inside edit bookmark panel after Bookmark button pressed"); + let hidden = BrowserTestUtils.waitForEvent(panel, "popuphidden"); + EventUtils.synthesizeKey("KEY_Escape"); + await hidden; + }); +}); + +// Test activation of the Bookmarks Menu button from the keyboard. +// This is a button with type="menu". +// The Bookmarks Menu should appear. +add_task(async function testBookmarksmenuButtonPress() { + CustomizableUI.addWidgetToArea("bookmarks-menu-button", + CustomizableUI.AREA_NAVBAR); + let button = document.getElementById("bookmarks-menu-button"); + forceFocus(button); + let menu = document.getElementById("BMB_bookmarksPopup"); + let shown = BrowserTestUtils.waitForEvent(menu, "popupshown"); + EventUtils.synthesizeKey(" "); + await shown; + ok(true, "Bookmarks Menu shown after toolbar button pressed"); + let hidden = BrowserTestUtils.waitForEvent(menu, "popuphidden"); + menu.hidePopup(); + await hidden; + CustomizableUI.reset(); +});
new file mode 100644 --- /dev/null +++ b/browser/base/content/test/keyboard/browser_toolbarKeyNav.js @@ -0,0 +1,219 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Test browser toolbar keyboard navigation. + * These tests assume the default browser configuration for toolbars unless + * otherwise specified. + */ + +const PERMISSIONS_PAGE = "https://example.com/browser/browser/base/content/test/permissions/permissions.html"; + +async function expectFocusAfterKey(aKey, aFocus, aAncestorOk = false) { + let res = aKey.match(/^(Shift\+)?(?:(.)|(.+))$/); + let shift = Boolean(res[1]); + let key; + if (res[2]) { + key = res[2]; // Character. + } else { + key = "KEY_" + res[3]; // Tab, ArrowRight, etc. + } + let expected; + let friendlyExpected; + if (typeof aFocus == "string") { + expected = document.getElementById(aFocus); + friendlyExpected = aFocus; + } else { + expected = aFocus; + if (aFocus == gURLBar.inputField) { + friendlyExpected = "URL bar input"; + } else if (aFocus == gBrowser.selectedBrowser) { + friendlyExpected = "Web document"; + } + } + let focused = BrowserTestUtils.waitForEvent(expected, "focus", aAncestorOk); + EventUtils.synthesizeKey(key, {shiftKey: shift}); + await focused; + ok(true, friendlyExpected + " focused after " + aKey + " pressed"); +} + +function startFromUrlBar() { + gURLBar.focus(); + is(document.activeElement, gURLBar.inputField, + "URL bar focused for start of test"); +} + +// The Reload button is disabled for a short time even after the page finishes +// loading. Wait for it to be enabled. +async function waitUntilReloadEnabled() { + let button = document.getElementById("reload-button"); + await TestUtils.waitForCondition(() => !button.disabled); +} + +add_task(async function setPref() { + await SpecialPowers.pushPrefEnv({ + set: [ + ["browser.toolbars.keyboard_navigation", true], + ["accessibility.tabfocus", 7], + ], + }); +}); + +// Test tab stops with no page loaded. +add_task(async function testTabStopsNoPage() { + await BrowserTestUtils.withNewTab("about:blank", async function() { + startFromUrlBar(); + await expectFocusAfterKey("Shift+Tab", "home-button"); + await expectFocusAfterKey("Shift+Tab", "tabbrowser-tabs", true); + await expectFocusAfterKey("Tab", "home-button"); + await expectFocusAfterKey("Tab", gURLBar.inputField); + await expectFocusAfterKey("Tab", "library-button"); + await expectFocusAfterKey("Tab", gBrowser.selectedBrowser); + }); +}); + +// Test tab stops with a page loaded. +add_task(async function testTabStopsPageLoaded() { + await BrowserTestUtils.withNewTab("https://example.com", async function() { + await waitUntilReloadEnabled(); + startFromUrlBar(); + await expectFocusAfterKey("Shift+Tab", "identity-box"); + await expectFocusAfterKey("Shift+Tab", "reload-button"); + await expectFocusAfterKey("Shift+Tab", "tabbrowser-tabs", true); + await expectFocusAfterKey("Tab", "reload-button"); + await expectFocusAfterKey("Tab", "identity-box"); + await expectFocusAfterKey("Tab", gURLBar.inputField); + await expectFocusAfterKey("Tab", "pageActionButton"); + await expectFocusAfterKey("Tab", "library-button"); + await expectFocusAfterKey("Tab", gBrowser.selectedBrowser); + }); +}); + +// Test tab stops with a notification anchor visible. +// The notification anchor should not get its own tab stop. +add_task(async function testTabStopsWithNotification() { + await BrowserTestUtils.withNewTab(PERMISSIONS_PAGE, async function(aBrowser) { + let popupShown = BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popupshown"); + // Request a permission. + BrowserTestUtils.synthesizeMouseAtCenter("#geo", {}, aBrowser); + await popupShown; + startFromUrlBar(); + // If the notification anchor were in the tab order, the next shift+tab + // would focus it instead of #identity-box. + await expectFocusAfterKey("Shift+Tab", "identity-box"); + }); +}); + +// Test tab stops with the Bookmarks toolbar visible. +add_task(async function testTabStopsWithBookmarksToolbar() { + await BrowserTestUtils.withNewTab("about:blank", async function() { + CustomizableUI.setToolbarVisibility("PersonalToolbar", true); + startFromUrlBar(); + await expectFocusAfterKey("Tab", "library-button"); + await expectFocusAfterKey("Tab", "PersonalToolbar", true); + await expectFocusAfterKey("Tab", gBrowser.selectedBrowser); + + // Make sure the Bookmarks toolbar is no longer tabbable once hidden. + CustomizableUI.setToolbarVisibility("PersonalToolbar", false); + startFromUrlBar(); + await expectFocusAfterKey("Tab", "library-button"); + await expectFocusAfterKey("Tab", gBrowser.selectedBrowser); + }); +}); + +// Test a focusable toolbartabstop which has no navigable buttons. +add_task(async function testTabStopNoButtons() { + await BrowserTestUtils.withNewTab("about:blank", async function() { + // The Back, Forward and Reload buttons are all currently disabled. + // The Home button is the only other button at that tab stop. + CustomizableUI.removeWidgetFromArea("home-button"); + startFromUrlBar(); + await expectFocusAfterKey("Shift+Tab", "tabbrowser-tabs", true); + await expectFocusAfterKey("Tab", gURLBar.inputField); + CustomizableUI.reset(); + // Make sure the button is reachable now that it has been re-added. + await expectFocusAfterKey("Shift+Tab", "home-button", true); + }); +}); + +// Test that right/left arrows move through toolbarbuttons. +// This also verifies that: +// 1. Right/left arrows do nothing when at the edges; and +// 2. The overflow menu button can't be reached by right arrow when it isn't +// visible. +add_task(async function testArrowsToolbarbuttons() { + await BrowserTestUtils.withNewTab("about:blank", async function() { + startFromUrlBar(); + await expectFocusAfterKey("Tab", "library-button"); + EventUtils.synthesizeKey("KEY_ArrowLeft"); + is(document.activeElement.id, "library-button", + "ArrowLeft at end of button group does nothing"); + await expectFocusAfterKey("ArrowRight", "sidebar-button"); + // This next check also confirms that the overflow menu button is skipped, + // since it is currently invisible. + await expectFocusAfterKey("ArrowRight", "PanelUI-menu-button"); + EventUtils.synthesizeKey("KEY_ArrowRight"); + is(document.activeElement.id, "PanelUI-menu-button", + "ArrowRight at end of button group does nothing"); + await expectFocusAfterKey("ArrowLeft", "sidebar-button"); + await expectFocusAfterKey("ArrowLeft", "library-button"); + }); +}); + +// Test that right/left arrows move through buttons wihch aren't toolbarbuttons +// but have role="button". +add_task(async function testArrowsRoleButton() { + await BrowserTestUtils.withNewTab("https://example.com", async function() { + startFromUrlBar(); + await expectFocusAfterKey("Tab", "pageActionButton"); + await expectFocusAfterKey("ArrowRight", "pocket-button"); + await expectFocusAfterKey("ArrowRight", "star-button"); + await expectFocusAfterKey("ArrowLeft", "pocket-button"); + await expectFocusAfterKey("ArrowLeft", "pageActionButton"); + }); +}); + +// Test that right/left arrows do not land on disabled buttons. +add_task(async function testArrowsDisabledButtons() { + await BrowserTestUtils.withNewTab("https://example.com", async function(aBrowser) { + await waitUntilReloadEnabled(); + startFromUrlBar(); + await expectFocusAfterKey("Shift+Tab", "identity-box"); + // Back and Forward buttons are disabled. + await expectFocusAfterKey("Shift+Tab", "reload-button"); + EventUtils.synthesizeKey("KEY_ArrowLeft"); + is(document.activeElement.id, "reload-button", + "ArrowLeft on Reload button when prior buttons disabled does nothing"); + + BrowserTestUtils.loadURI(aBrowser, "https://example.com/2"); + await BrowserTestUtils.browserLoaded(aBrowser); + await waitUntilReloadEnabled(); + startFromUrlBar(); + await expectFocusAfterKey("Shift+Tab", "identity-box"); + await expectFocusAfterKey("Shift+Tab", "back-button"); + // Forward button is still disabled. + await expectFocusAfterKey("ArrowRight", "reload-button"); + }); +}); + +// Test that right arrow reaches the overflow menu button when it is visible. +add_task(async function testArrowsOverflowButton() { + await BrowserTestUtils.withNewTab("about:blank", async function() { + // Move something to the overflow menu to make the button appear. + CustomizableUI.addWidgetToArea("home-button", CustomizableUI.AREA_FIXED_OVERFLOW_PANEL); + startFromUrlBar(); + await expectFocusAfterKey("Tab", "library-button"); + await expectFocusAfterKey("ArrowRight", "sidebar-button"); + await expectFocusAfterKey("ArrowRight", "nav-bar-overflow-button"); + await expectFocusAfterKey("ArrowRight", "PanelUI-menu-button"); + await expectFocusAfterKey("ArrowLeft", "nav-bar-overflow-button"); + // Make sure the button is not reachable once it is invisible again. + await expectFocusAfterKey("ArrowRight", "PanelUI-menu-button"); + CustomizableUI.reset(); + // Flush layout so its invisibility can be detected. + document.getElementById("nav-bar-overflow-button").clientWidth; + await expectFocusAfterKey("ArrowLeft", "sidebar-button"); + }); +});
--- a/browser/base/jar.mn +++ b/browser/base/jar.mn @@ -50,17 +50,18 @@ browser.jar: content/browser/browser-media.js (content/browser-media.js) content/browser/browser-pageActions.js (content/browser-pageActions.js) content/browser/browser-places.js (content/browser-places.js) content/browser/browser-plugins.js (content/browser-plugins.js) content/browser/browser-safebrowsing.js (content/browser-safebrowsing.js) content/browser/browser-sidebar.js (content/browser-sidebar.js) content/browser/browser-siteIdentity.js (content/browser-siteIdentity.js) content/browser/browser-sync.js (content/browser-sync.js) - content/browser/browser-tabsintitlebar.js (content/browser-tabsintitlebar.js) + content/browser/browser-tabsintitlebar.js (content/browser-tabsintitlebar.js) + content/browser/browser-toolbarKeyNav.js (content/browser-toolbarKeyNav.js) content/browser/browser-thumbnails.js (content/browser-thumbnails.js) content/browser/browser-webrender.js (content/browser-webrender.js) content/browser/tab-content.js (content/tab-content.js) content/browser/content.js (content/content.js) content/browser/defaultthemes/1.header.jpg (content/defaultthemes/1.header.jpg) content/browser/defaultthemes/1.icon.jpg (content/defaultthemes/1.icon.jpg) content/browser/defaultthemes/1.preview.jpg (content/defaultthemes/1.preview.jpg) content/browser/defaultthemes/2.header.jpg (content/defaultthemes/2.header.jpg)
--- a/browser/themes/shared/urlbar-autocomplete.inc.css +++ b/browser/themes/shared/urlbar-autocomplete.inc.css @@ -121,31 +121,35 @@ color: var(--panel-disabled-color); margin: 0 .4em; } .urlbarView-title:empty + .urlbarView-secondary::before { display: none; } +.urlbarView-tags, .urlbarView-secondary { - color: var(--urlbar-popup-action-color); font-size: .85em; } -.urlbarView-url { - color: var(--urlbar-popup-url-color); -} - .urlbarView-title > strong, .urlbarView-url > strong, .urlbarView-tag > strong { font-weight: 600; } +.urlbarView-secondary { + color: var(--urlbar-popup-action-color); +} + +.urlbarView-url { + color: var(--urlbar-popup-url-color); +} + .urlbarView-row[selected] > .urlbarView-row-inner > .urlbarView-secondary::before, .urlbarView-row[selected] > .urlbarView-row-inner > .urlbarView-secondary { color: inherit; } .urlbarView-row[type=remotetab][selected] > .urlbarView-row-inner > .urlbarView-action, .urlbarView-row[type=remotetab]:hover > .urlbarView-row-inner > .urlbarView-action, .urlbarView-row[type=remotetab]:not([selected]):not(:hover) > .urlbarView-row-inner > .urlbarView-url,
--- a/devtools/client/debugger/new/src/client/firefox/create.js +++ b/devtools/client/debugger/new/src/client/firefox/create.js @@ -15,33 +15,27 @@ import type { } from "./types"; import { clientCommands } from "./commands"; export function createFrame(thread: ThreadId, frame: FramePacket): ?Frame { if (!frame) { return null; } - let title; - if (frame.type == "call") { - const c = frame.callee; - title = c.name || c.userDisplayName || c.displayName; - } else { - title = `(${frame.type})`; - } + const location = { sourceId: clientCommands.getSourceForActor(frame.where.actor), line: frame.where.line, column: frame.where.column }; return { id: frame.actor, thread, - displayName: title, + displayName: frame.displayName, location, generatedLocation: location, this: frame.this, source: null, scope: frame.environment }; }
--- a/devtools/client/debugger/new/src/utils/prefs.js +++ b/devtools/client/debugger/new/src/utils/prefs.js @@ -4,17 +4,17 @@ // @flow import { PrefsHelper } from "devtools-modules"; import { isDevelopment } from "devtools-environment"; import Services from "devtools-services"; import { asyncStoreHelper } from "./asyncStoreHelper"; -const prefsSchemaVersion = "1.0.7"; +const prefsSchemaVersion = "1.0.8"; const pref = Services.pref; if (isDevelopment()) { pref("devtools.debugger.logging", false); pref("devtools.debugger.alphabetize-outline", false); pref("devtools.debugger.auto-pretty-print", false); pref("devtools.source-map.client-service.enabled", true); @@ -132,13 +132,13 @@ export const asyncStore = asyncStoreHelp pendingBreakpoints: ["pending-breakpoints", {}], tabs: ["tabs", []], xhrBreakpoints: ["xhr-breakpoints", []], eventListenerBreakpoints: ["event-listener-breakpoints", []] }); if (prefs.debuggerPrefsSchemaVersion !== prefsSchemaVersion) { // clear pending Breakpoints - prefs.pendingBreakpoints = {}; - prefs.tabs = []; - prefs.xhrBreakpoints = []; + asyncStore.pendingBreakpoints = {}; + asyncStore.tabs = []; + asyncStore.xhrBreakpoints = []; prefs.debuggerPrefsSchemaVersion = prefsSchemaVersion; }
--- a/devtools/server/actors/frame.js +++ b/devtools/server/actors/frame.js @@ -6,16 +6,25 @@ "use strict"; const { ActorPool } = require("devtools/server/actors/common"); const { createValueGrip } = require("devtools/server/actors/object/utils"); const { ActorClassWithSpec } = require("devtools/shared/protocol"); const { frameSpec } = require("devtools/shared/specs/frame"); +function formatDisplayName(frame) { + if (frame.type === "call") { + const callee = frame.callee; + return callee.name || callee.userDisplayName || callee.displayName; + } + + return `(${frame.type})`; +} + /** * An actor for a specified stack frame. */ const FrameActor = ActorClassWithSpec(frameSpec, { /** * Creates the Frame actor. * * @param frame Debugger.Frame @@ -71,35 +80,32 @@ const FrameActor = ActorClassWithSpec(fr /** * Returns a frame form for use in a protocol message. */ form: function() { const threadActor = this.threadActor; const form = { actor: this.actorID, type: this.frame.type }; - if (this.frame.type === "call") { - form.callee = createValueGrip(this.frame.callee, threadActor._pausePool, - threadActor.objectGrip); - } // NOTE: ignoreFrameEnvironment lets the client explicitly avoid // populating form environments on pause. if ( !this.threadActor._options.ignoreFrameEnvironment && this.frame.environment ) { form.environment = this.getEnvironment(); } if (this.frame.type != "wasmcall") { form.this = createValueGrip(this.frame.this, threadActor._pausePool, threadActor.objectGrip); } + form.displayName = formatDisplayName(this.frame); form.arguments = this._args(); if (this.frame.script) { const generatedLocation = this.threadActor.sources.getFrameLocation(this.frame); form.where = { actor: generatedLocation.generatedSourceActor.actorID, line: generatedLocation.generatedLine, column: generatedLocation.generatedColumn, };
--- a/devtools/server/tests/unit/test_frameactor-01.js +++ b/devtools/server/tests/unit/test_frameactor-01.js @@ -28,17 +28,17 @@ function run_test() { }); do_test_pending(); } function test_pause_frame() { gThreadClient.addOneTimeListener("paused", function(event, packet) { Assert.ok(!!packet.frame); Assert.ok(!!packet.frame.actor); - Assert.equal(packet.frame.callee.name, "stopMe"); + Assert.equal(packet.frame.displayName, "stopMe"); gThreadClient.resume(function() { finishClient(gClient); }); }); gDebuggee.eval("(" + function() { function stopMe() { debugger;
--- a/devtools/server/tests/unit/test_frameactor-04.js +++ b/devtools/server/tests/unit/test_frameactor-04.js @@ -24,67 +24,46 @@ function run_test() { function(response, targetFront, threadClient) { gThreadClient = threadClient; test_pause_frame(); }); }); do_test_pending(); } -var gFrames = [ +var frameFixtures = [ // Function calls... - { type: "call", callee: { name: "depth3" } }, - { type: "call", callee: { name: "depth2" } }, - { type: "call", callee: { name: "depth1" } }, + { type: "call", displayName: "depth3" }, + { type: "call", displayName: "depth2" }, + { type: "call", displayName: "depth1" }, // Anonymous function call in our eval... - { type: "call", callee: { name: undefined } }, + { type: "call", displayName: undefined }, // The eval itself. - { type: "eval", callee: { name: undefined } }, -]; - -var gSliceTests = [ - { start: 0, count: undefined, resetActors: true }, - { start: 0, count: 1 }, - { start: 2, count: 2 }, - { start: 1, count: 15 }, - { start: 15, count: undefined }, + { type: "eval", displayName: "(eval)" }, ]; -function test_frame_slice() { - if (gSliceTests.length == 0) { - gThreadClient.resume(() => finishClient(gClient)); - return; - } - - const test = gSliceTests.shift(); - gThreadClient.getFrames(test.start, test.count, function(response) { - const testFrames = gFrames.slice(test.start, - test.count ? test.start + test.count : undefined); - Assert.equal(testFrames.length, response.frames.length); - for (let i = 0; i < testFrames.length; i++) { - const expected = testFrames[i]; +function test_frame_packet() { + gThreadClient.getFrames(0, 1000, function(response) { + for (let i = 0; i < response.frames.length; i++) { + const expected = frameFixtures[i]; const actual = response.frames[i]; - if (test.resetActors) { - expected.actor = actual.actor; - } + Assert.equal(expected.displayname, actual.displayname, "Frame displayname"); + Assert.equal(expected.type, actual.type, "Frame displayname"); + } - for (const key of ["type", "callee-name"]) { - Assert.equal(expected[key] || undefined, actual[key]); - } - } - test_frame_slice(); + gThreadClient.resume(() => finishClient(gClient)); }); } function test_pause_frame() { gThreadClient.addOneTimeListener("paused", function(event, packet) { - test_frame_slice(); + test_frame_packet(); }); gDebuggee.eval("(" + function() { function depth3() { debugger; } function depth2() { depth3();
--- a/docshell/base/BrowsingContext.cpp +++ b/docshell/base/BrowsingContext.cpp @@ -59,18 +59,17 @@ static void Sync(BrowsingContext* aBrows if (!XRE_IsContentProcess()) { return; } auto cc = ContentChild::GetSingleton(); MOZ_DIAGNOSTIC_ASSERT(cc); RefPtr<BrowsingContext> parent = aBrowsingContext->GetParent(); BrowsingContext* opener = aBrowsingContext->GetOpener(); - cc->SendAttachBrowsingContext(BrowsingContextId(parent ? parent->Id() : 0), - BrowsingContextId(opener ? opener->Id() : 0), + cc->SendAttachBrowsingContext(parent, opener, BrowsingContextId(aBrowsingContext->Id()), aBrowsingContext->Name()); } BrowsingContext* BrowsingContext::TopLevelBrowsingContext() { BrowsingContext* bc = this; while (bc->mParent) { bc = bc->mParent; @@ -98,16 +97,20 @@ BrowsingContext* BrowsingContext::TopLev uint64_t aId) { if (BrowsingContextMap<WeakPtr>::Ptr abc = sBrowsingContexts->lookup(aId)) { return do_AddRef(abc->value().get()); } return nullptr; } +CanonicalBrowsingContext* BrowsingContext::Canonical() { + return CanonicalBrowsingContext::Cast(this); +} + /* static */ already_AddRefed<BrowsingContext> BrowsingContext::Create( BrowsingContext* aParent, BrowsingContext* aOpener, const nsAString& aName, Type aType) { MOZ_DIAGNOSTIC_ASSERT(!aParent || aParent->mType == aType); uint64_t id = nsContentUtils::GenerateBrowsingContextId(); MOZ_LOG(GetLog(), LogLevel::Debug, @@ -233,18 +236,17 @@ void BrowsingContext::Detach() { Group()->Unregister(this); if (!XRE_IsContentProcess()) { return; } auto cc = ContentChild::GetSingleton(); MOZ_DIAGNOSTIC_ASSERT(cc); - cc->SendDetachBrowsingContext(BrowsingContextId(Id()), - false /* aMoveToBFCache */); + cc->SendDetachBrowsingContext(this, false /* aMoveToBFCache */); } void BrowsingContext::CacheChildren() { if (mChildren.IsEmpty()) { return; } MOZ_LOG(GetLog(), LogLevel::Debug, @@ -259,18 +261,17 @@ void BrowsingContext::CacheChildren() { mChildren.Clear(); if (!XRE_IsContentProcess()) { return; } auto cc = ContentChild::GetSingleton(); MOZ_DIAGNOSTIC_ASSERT(cc); - cc->SendDetachBrowsingContext(BrowsingContextId(Id()), - true /* aMoveToBFCache */); + cc->SendDetachBrowsingContext(this, true /* aMoveToBFCache */); } bool BrowsingContext::IsCached() { return sCachedBrowsingContexts->has(Id()); } void BrowsingContext::GetChildren( nsTArray<RefPtr<BrowsingContext>>& aChildren) { MOZ_ALWAYS_TRUE(aChildren.AppendElements(mChildren)); } @@ -283,18 +284,17 @@ void BrowsingContext::SetOpener(Browsing mOpener = aOpener; if (!XRE_IsContentProcess()) { return; } auto cc = ContentChild::GetSingleton(); MOZ_DIAGNOSTIC_ASSERT(cc); - cc->SendSetOpenerBrowsingContext( - BrowsingContextId(Id()), BrowsingContextId(aOpener ? aOpener->Id() : 0)); + cc->SendSetOpenerBrowsingContext(this, aOpener); } BrowsingContext::~BrowsingContext() { MOZ_DIAGNOSTIC_ASSERT(!mParent || !mParent->mChildren.Contains(this)); MOZ_DIAGNOSTIC_ASSERT(!mGroup || !mGroup->Toplevels().Contains(this)); MOZ_DIAGNOSTIC_ASSERT(!sCachedBrowsingContexts || !sCachedBrowsingContexts->has(Id())); @@ -321,34 +321,34 @@ void BrowsingContext::NotifyUserGestureA topLevelBC->Id()); topLevelBC->SetUserGestureActivation(); if (!XRE_IsContentProcess()) { return; } auto cc = ContentChild::GetSingleton(); MOZ_ASSERT(cc); - cc->SendSetUserGestureActivation(BrowsingContextId(topLevelBC->Id()), true); + cc->SendSetUserGestureActivation(topLevelBC, true); } void BrowsingContext::NotifyResetUserGestureActivation() { // We would reset the user gesture activation flag on the top level browsing // context, which would automatically be sync to other top level browsing // contexts which are in the different process. RefPtr<BrowsingContext> topLevelBC = TopLevelBrowsingContext(); USER_ACTIVATION_LOG("Get top level browsing context 0x%08" PRIx64, topLevelBC->Id()); topLevelBC->ResetUserGestureActivation(); if (!XRE_IsContentProcess()) { return; } auto cc = ContentChild::GetSingleton(); MOZ_ASSERT(cc); - cc->SendSetUserGestureActivation(BrowsingContextId(topLevelBC->Id()), false); + cc->SendSetUserGestureActivation(topLevelBC, false); } void BrowsingContext::SetUserGestureActivation() { MOZ_ASSERT(!mParent, "Set user activation flag on non top-level context!"); USER_ACTIVATION_LOG( "Set user gesture activation for browsing context 0x%08" PRIx64, Id()); mIsActivatedByUserGesture = true; } @@ -392,28 +392,27 @@ void BrowsingContext::Location(JSContext OOMReporter& aError) {} void BrowsingContext::Close(CallerType aCallerType, ErrorResult& aError) { // FIXME We need to set mClosed, but only once we're sending the // DOMWindowClose event (which happens in the process where the // document for this browsing context is loaded). // See https://bugzilla.mozilla.org/show_bug.cgi?id=1516343. ContentChild* cc = ContentChild::GetSingleton(); - cc->SendWindowClose(BrowsingContextId(mBrowsingContextId), - aCallerType == CallerType::System); + cc->SendWindowClose(this, aCallerType == CallerType::System); } void BrowsingContext::Focus(ErrorResult& aError) { ContentChild* cc = ContentChild::GetSingleton(); - cc->SendWindowFocus(BrowsingContextId(mBrowsingContextId)); + cc->SendWindowFocus(this); } void BrowsingContext::Blur(ErrorResult& aError) { ContentChild* cc = ContentChild::GetSingleton(); - cc->SendWindowBlur(BrowsingContextId(mBrowsingContextId)); + cc->SendWindowBlur(this); } Nullable<WindowProxyHolder> BrowsingContext::GetTop(ErrorResult& aError) { // We never return null or throw an error, but the implementation in // nsGlobalWindow does and we need to use the same signature. return WindowProxyHolder(TopLevelBrowsingContext()); } @@ -455,17 +454,17 @@ void BrowsingContext::PostMessageMoz(JSC if (!nsGlobalWindowOuter::GatherPostMessageData( aCx, aTargetOrigin, getter_AddRefs(sourceBc), data.origin(), getter_AddRefs(data.targetOriginURI()), getter_AddRefs(data.callerPrincipal()), getter_AddRefs(callerInnerWindow), getter_AddRefs(data.callerDocumentURI()), aError)) { return; } - data.source() = BrowsingContextId(sourceBc->Id()); + data.source() = sourceBc; data.isFromPrivateWindow() = callerInnerWindow && nsScriptErrorBase::ComputeIsFromPrivateWindow(callerInnerWindow); JS::Rooted<JS::Value> transferArray(aCx); aError = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransfer, &transferArray); if (NS_WARN_IF(aError.Failed())) { @@ -480,18 +479,17 @@ void BrowsingContext::PostMessageMoz(JSC ContentChild* cc = ContentChild::GetSingleton(); ClonedMessageData messageData; if (!message.BuildClonedMessageDataForChild(cc, messageData)) { aError.Throw(NS_ERROR_FAILURE); return; } - cc->SendWindowPostMessage(BrowsingContextId(mBrowsingContextId), messageData, - data); + cc->SendWindowPostMessage(this, messageData, data); } void BrowsingContext::PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage, const WindowPostMessageOptions& aOptions, nsIPrincipal& aSubjectPrincipal, ErrorResult& aError) { PostMessageMoz(aCx, aMessage, aOptions.mTargetOrigin, aOptions.mTransfer, @@ -510,9 +508,51 @@ already_AddRefed<BrowsingContext> Browsi RefPtr<BrowsingContext> bc; if (childDS) { childDS->GetBrowsingContext(getter_AddRefs(bc)); } return bc.forget(); } } // namespace dom + +namespace ipc { + +void IPDLParamTraits<dom::BrowsingContext>::Write( + IPC::Message* aMsg, IProtocol* aActor, dom::BrowsingContext* aParam) { + uint64_t id = aParam ? aParam->Id() : 0; + WriteIPDLParam(aMsg, aActor, id); + + // If his is an in-process send. We want to make sure that our BrowsingContext + // object lives long enough to make it to the other side, so we take an extra + // reference. This reference is freed in ::Read(). + if (!aActor->GetIPCChannel()->IsCrossProcess()) { + NS_IF_ADDREF(aParam); + } +} + +bool IPDLParamTraits<dom::BrowsingContext>::Read( + const IPC::Message* aMsg, PickleIterator* aIter, IProtocol* aActor, + RefPtr<dom::BrowsingContext>* aResult) { + uint64_t id = 0; + if (!ReadIPDLParam(aMsg, aIter, aActor, &id)) { + return false; + } + + if (id == 0) { + aResult = nullptr; + return true; + } + + *aResult = dom::BrowsingContext::Get(id); + MOZ_ASSERT(aResult, "Deserialized absent BrowsingContext!"); + + // If this is an in-process actor, free the reference taken in ::Write(). + if (!aActor->GetIPCChannel()->IsCrossProcess()) { + dom::BrowsingContext* bc = *aResult; + NS_IF_RELEASE(bc); + } + + return aResult != nullptr; +} + +} // namespace ipc } // namespace mozilla
--- a/docshell/base/BrowsingContext.h +++ b/docshell/base/BrowsingContext.h @@ -15,26 +15,39 @@ #include "nsCycleCollectionParticipant.h" #include "nsIDocShell.h" #include "nsString.h" #include "nsTArray.h" #include "nsWrapperCache.h" class nsGlobalWindowOuter; class nsOuterWindowProxy; +class PickleIterator; + +namespace IPC { +class Message; +} // namespace IPC namespace mozilla { class ErrorResult; class LogModule; class OOMReporter; +namespace ipc { +class IProtocol; + +template <typename T> +struct IPDLParamTraits; +} // namespace ipc + namespace dom { class BrowsingContextGroup; +class CanonicalBrowsingContext; class ContentParent; template <typename> struct Nullable; template <typename T> class Sequence; struct WindowPostMessageOptions; class WindowProxyHolder; @@ -76,16 +89,19 @@ class BrowsingContext : public nsWrapper const nsAString& aName, Type aType); // Create a BrowsingContext object from over IPC. static already_AddRefed<BrowsingContext> CreateFromIPC( BrowsingContext* aParent, BrowsingContext* aOpener, const nsAString& aName, uint64_t aId, ContentParent* aOriginProcess); + // Cast this object to a canonical browsing context, and return it. + CanonicalBrowsingContext* Canonical(); + // Get the DocShell for this BrowsingContext if it is in-process, or // null if it's not. nsIDocShell* GetDocShell() { return mDocShell; } void SetDocShell(nsIDocShell* aDocShell); // Get the outer window object for this BrowsingContext if it is in-process // and still has a docshell, or null otherwise. nsPIDOMWindowOuter* GetDOMWindow() const { @@ -249,11 +265,22 @@ class BrowsingContext : public nsWrapper * lives in this process, and a same-process WindowProxy should be used (see * nsGlobalWindowOuter). This should only be called by bindings code, ToJSValue * is the right API to get a WindowProxy for a BrowsingContext. */ extern bool GetRemoteOuterWindowProxy(JSContext* aCx, BrowsingContext* aContext, JS::MutableHandle<JSObject*> aRetVal); } // namespace dom + +// Allow sending BrowsingContext objects over IPC. +namespace ipc { +template <> +struct IPDLParamTraits<dom::BrowsingContext> { + static void Write(IPC::Message* aMsg, IProtocol* aActor, + dom::BrowsingContext* aParam); + static bool Read(const IPC::Message* aMsg, PickleIterator* aIter, + IProtocol* aActor, RefPtr<dom::BrowsingContext>* aResult); +}; +} // namespace ipc } // namespace mozilla #endif // !defined(mozilla_dom_BrowsingContext_h)
--- a/dom/base/Element.cpp +++ b/dom/base/Element.cpp @@ -972,17 +972,21 @@ int32_t Element::ScrollWidth() { return nsPresContext::AppUnitsToIntCSSPixels(width); } nsRect Element::GetClientAreaRect() { nsIFrame* frame; nsIScrollableFrame* sf = GetScrollFrame(&frame); if (sf) { - return sf->GetScrollPortRect(); + nsRect scrollPort = sf->GetScrollPortRect(); + // The scroll port value might be expanded to the minimum scale size, we + // should limit the size to the ICB in such cases. + scrollPort.SizeTo(sf->GetLayoutSize()); + return scrollPort; } if (frame && // The display check is OK even though we're not looking at the style // frame, because the style frame only differs from "frame" for tables, // and table wrappers have the same display as the table itself. (frame->StyleDisplay()->mDisplay != StyleDisplay::Inline || frame->IsFrameOfType(nsIFrame::eReplaced))) {
--- a/dom/file/uri/BlobURL.cpp +++ b/dom/file/uri/BlobURL.cpp @@ -161,19 +161,18 @@ BlobURL::Mutate(nsIURIMutator** aMutator return rv; } mutator.forget(aMutator); return NS_OK; } // nsIClassInfo methods: NS_IMETHODIMP -BlobURL::GetInterfaces(uint32_t* count, nsIID*** array) { - *count = 0; - *array = nullptr; +BlobURL::GetInterfaces(nsTArray<nsIID>& array) { + array.Clear(); return NS_OK; } NS_IMETHODIMP BlobURL::GetScriptableHelper(nsIXPCScriptable** _retval) { *_retval = nullptr; return NS_OK; }
--- a/dom/interfaces/security/nsIContentSecurityPolicy.idl +++ b/dom/interfaces/security/nsIContentSecurityPolicy.idl @@ -297,17 +297,18 @@ interface nsIContentSecurityPolicy : nsI */ short shouldLoad(in nsContentPolicyType aContentType, in nsICSPEventListener aCSPEventListener, in nsIURI aContentLocation, in nsIURI aRequestOrigin, in nsISupports aContext, in ACString aMimeTypeGuess, in nsIURI aOriginalURIIfRedirect, - in bool aSendViolationReports); + in bool aSendViolationReports, + in AString aNonce); %{ C++ // nsIObserver topic to fire when the policy encounters a violation. #define CSP_VIOLATION_TOPIC "csp-on-violate-policy" %} /** * Returns the CSP in JSON notation.
--- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -3585,90 +3585,76 @@ PContentChild::Result ContentChild::OnMe LSObject::OnSyncMessageHandled(); } return result; } mozilla::ipc::IPCResult ContentChild::RecvWindowClose( - const BrowsingContextId& aContextId, const bool& aTrustedCaller) { - RefPtr<BrowsingContext> bc = BrowsingContext::Get(aContextId); - if (!bc) { + BrowsingContext* aContext, bool aTrustedCaller) { + if (!aContext) { MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug, - ("ChildIPC: Trying to send a message to dead or detached context " - "0x%08" PRIx64, - (uint64_t)aContextId)); + ("ChildIPC: Trying to send a message to dead or detached context")); return IPC_OK(); } - nsCOMPtr<nsPIDOMWindowOuter> window = bc->GetDOMWindow(); + nsCOMPtr<nsPIDOMWindowOuter> window = aContext->GetDOMWindow(); nsGlobalWindowOuter::Cast(window)->CloseOuter(aTrustedCaller); return IPC_OK(); } -mozilla::ipc::IPCResult ContentChild::RecvWindowFocus( - const BrowsingContextId& aContextId) { - RefPtr<BrowsingContext> bc = BrowsingContext::Get(aContextId); - if (!bc) { +mozilla::ipc::IPCResult ContentChild::RecvWindowFocus(BrowsingContext* aContext) { + if (!aContext) { MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug, - ("ChildIPC: Trying to send a message to dead or detached context " - "0x%08" PRIx64, - (uint64_t)aContextId)); + ("ChildIPC: Trying to send a message to dead or detached context")); return IPC_OK(); } - nsCOMPtr<nsPIDOMWindowOuter> window = bc->GetDOMWindow(); + nsCOMPtr<nsPIDOMWindowOuter> window = aContext->GetDOMWindow(); nsGlobalWindowOuter::Cast(window)->FocusOuter(); return IPC_OK(); } mozilla::ipc::IPCResult ContentChild::RecvWindowBlur( - const BrowsingContextId& aContextId) { - RefPtr<BrowsingContext> bc = BrowsingContext::Get(aContextId); - if (!bc) { + BrowsingContext* aContext) { + if (!aContext) { MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug, - ("ChildIPC: Trying to send a message to dead or detached context " - "0x%08" PRIx64, - (uint64_t)aContextId)); + ("ChildIPC: Trying to send a message to dead or detached context")); return IPC_OK(); } - nsCOMPtr<nsPIDOMWindowOuter> window = bc->GetDOMWindow(); + nsCOMPtr<nsPIDOMWindowOuter> window = aContext->GetDOMWindow(); nsGlobalWindowOuter::Cast(window)->BlurOuter(); return IPC_OK(); } mozilla::ipc::IPCResult ContentChild::RecvWindowPostMessage( - const BrowsingContextId& aContextId, const ClonedMessageData& aMessage, + BrowsingContext* aContext, const ClonedMessageData& aMessage, const PostMessageData& aData) { - RefPtr<BrowsingContext> bc = BrowsingContext::Get(aContextId); - if (!bc) { + if (!aContext) { MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug, - ("ChildIPC: Trying to send a message to dead or detached context " - "0x%08" PRIx64, - (uint64_t)aContextId)); + ("ChildIPC: Trying to send a message to dead or detached context")); return IPC_OK(); } RefPtr<nsGlobalWindowOuter> window = - nsGlobalWindowOuter::Cast(bc->GetDOMWindow()); + nsGlobalWindowOuter::Cast(aContext->GetDOMWindow()); nsCOMPtr<nsIPrincipal> providedPrincipal; if (!window->GetPrincipalForPostMessage( aData.targetOrigin(), aData.targetOriginURI(), aData.callerPrincipal(), *aData.subjectPrincipal(), getter_AddRefs(providedPrincipal))) { return IPC_OK(); } - RefPtr<BrowsingContext> sourceBc = BrowsingContext::Get(aData.source()); + RefPtr<BrowsingContext> sourceBc = aData.source(); if (!sourceBc) { MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug, - ("ChildIPC: Trying to use a dead or detached context 0x%08" PRIx64, - (uint64_t)aData.source())); + ("ChildIPC: Trying to use a dead or detached context")); return IPC_OK(); } // Create and asynchronously dispatch a runnable which will handle actual DOM // event creation and dispatch. RefPtr<PostMessageEvent> event = new PostMessageEvent( sourceBc, aData.origin(), window, providedPrincipal, aData.callerDocumentURI(), aData.isFromPrivateWindow());
--- a/dom/ipc/ContentChild.h +++ b/dom/ipc/ContentChild.h @@ -709,22 +709,22 @@ class ContentChild final : public PConte virtual already_AddRefed<nsIEventTarget> GetConstructedEventTarget( const Message& aMsg) override; virtual already_AddRefed<nsIEventTarget> GetSpecificMessageEventTarget( const Message& aMsg) override; virtual void OnChannelReceivedMessage(const Message& aMsg) override; - mozilla::ipc::IPCResult RecvWindowClose(const BrowsingContextId& aContextId, - const bool& aTrustedCaller); - mozilla::ipc::IPCResult RecvWindowFocus(const BrowsingContextId& aContextId); - mozilla::ipc::IPCResult RecvWindowBlur(const BrowsingContextId& aContextId); + mozilla::ipc::IPCResult RecvWindowClose(BrowsingContext* aContext, + bool aTrustedCaller); + mozilla::ipc::IPCResult RecvWindowFocus(BrowsingContext* aContext); + mozilla::ipc::IPCResult RecvWindowBlur(BrowsingContext* aContext); mozilla::ipc::IPCResult RecvWindowPostMessage( - const BrowsingContextId& aContextId, const ClonedMessageData& aMessage, + BrowsingContext* aContext, const ClonedMessageData& aMessage, const PostMessageData& aData); #ifdef NIGHTLY_BUILD virtual PContentChild::Result OnMessageReceived(const Message& aMsg) override; #else using PContentChild::OnMessageReceived; #endif
--- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -5604,165 +5604,128 @@ ContentParent::RecvFirstPartyStorageAcce mozilla::ipc::IPCResult ContentParent::RecvStoreUserInteractionAsPermission( const Principal& aPrincipal) { AntiTrackingCommon::StoreUserInteractionFor(aPrincipal); return IPC_OK(); } mozilla::ipc::IPCResult ContentParent::RecvAttachBrowsingContext( - const BrowsingContextId& aParentId, const BrowsingContextId& aOpenerId, - const BrowsingContextId& aChildId, const nsString& aName) { - RefPtr<CanonicalBrowsingContext> parent = - CanonicalBrowsingContext::Get(aParentId); - if (aParentId && !parent) { - // Unless 'aParentId' is 0 (which it is when the child is a root - // BrowsingContext) there should always be a corresponding - // 'parent'. The only reason for there not beeing one is if the - // parent has already been detached, in which case the - // BrowsingContext that tries to attach itself to the context with - // 'aParentId' is surely doomed and we can safely do nothing. - - // TODO(farre): When we start syncing/moving BrowsingContexts to - // other child processes is it possible to get into races where - // constructive operations on already detached BrowsingContexts - // are requested? This needs to be answered/handled, but for now - // return early. [Bug 1471598] - MOZ_LOG( - BrowsingContext::GetLog(), LogLevel::Debug, - ("ParentIPC: Trying to attach to already detached parent 0x%08" PRIx64, - (uint64_t)aParentId)); - return IPC_OK(); - } - - if (parent && !parent->IsOwnedByProcess(ChildID())) { + BrowsingContext* aParent, BrowsingContext* aOpener, + BrowsingContextId aChildId, const nsString& aName) { + if (aParent && !aParent->Canonical()->IsOwnedByProcess(ChildID())) { // Where trying attach a child BrowsingContext to a parent // BrowsingContext in another process. This is illegal since the // only thing that could create that child BrowsingContext is a // parent docshell in the same process as that BrowsingContext. // TODO(farre): We're doing nothing now, but is that exactly what // we want? Maybe we want to crash the child currently calling // SendAttachBrowsingContext and/or the child that originally // called SendAttachBrowsingContext or possibly all children that // has a BrowsingContext connected to the child that currently // called SendAttachBrowsingContext? [Bug 1471598] MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Warning, ("ParentIPC: Trying to attach to out of process parent context " "0x%08" PRIx64, - parent->Id())); + aParent->Id())); return IPC_OK(); } RefPtr<BrowsingContext> child = BrowsingContext::Get(aChildId); if (child && !child->IsCached()) { // This is highly suspicious. BrowsingContexts should only be // attached at most once, but finding one indicates that someone // is doing something they shouldn't. // TODO(farre): To crash or not to crash. Same reasoning as in // above TODO. [Bug 1471598] MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Warning, ("ParentIPC: Trying to attach already attached 0x%08" PRIx64 " to 0x%08" PRIx64, - child->Id(), (uint64_t)aParentId)); + child->Id(), aParent ? aParent->Id() : 0)); return IPC_OK(); } if (!child) { - RefPtr<BrowsingContext> opener = BrowsingContext::Get(aOpenerId); - child = BrowsingContext::CreateFromIPC(parent, opener, aName, + child = BrowsingContext::CreateFromIPC(aParent, aOpener, aName, (uint64_t)aChildId, this); } return IPC_OK(); } mozilla::ipc::IPCResult ContentParent::RecvDetachBrowsingContext( - const BrowsingContextId& aContextId, const bool& aMoveToBFCache) { - RefPtr<CanonicalBrowsingContext> context = - CanonicalBrowsingContext::Get(aContextId); - - if (!context) { + BrowsingContext* aContext, bool aMoveToBFCache) { + if (!aContext) { MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug, - ("ParentIPC: Trying to detach already detached 0x%08" PRIx64, - (uint64_t)aContextId)); + ("ParentIPC: Trying to detach already detached")); return IPC_OK(); } - if (!context->IsOwnedByProcess(ChildID())) { + if (!aContext->Canonical()->IsOwnedByProcess(ChildID())) { // Where trying to detach a child BrowsingContext in another child // process. This is illegal since the owner of the BrowsingContext // is the proccess with the in-process docshell, which is tracked // by OwnerProcessId. // TODO(farre): To crash or not to crash. Same reasoning as in // above TODO. [Bug 1471598] MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Warning, ("ParentIPC: Trying to detach out of process context 0x%08" PRIx64, - context->Id())); + aContext->Id())); return IPC_OK(); } if (aMoveToBFCache) { - context->CacheChildren(); + aContext->CacheChildren(); } else { - context->Detach(); + aContext->Detach(); } return IPC_OK(); } mozilla::ipc::IPCResult ContentParent::RecvSetOpenerBrowsingContext( - const BrowsingContextId& aContextId, - const BrowsingContextId& aOpenerContextId) { - RefPtr<CanonicalBrowsingContext> context = - CanonicalBrowsingContext::Get(aContextId); - - if (!context) { + BrowsingContext* aContext, BrowsingContext* aOpener) { + if (!aContext) { MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug, - ("ParentIPC: Trying to set opener already detached 0x%08" PRIx64, - (uint64_t)aContextId)); + ("ParentIPC: Trying to set opener already detached")); return IPC_OK(); } - if (!context->IsOwnedByProcess(ChildID())) { + if (!aContext->Canonical()->IsOwnedByProcess(ChildID())) { // Where trying to set opener on a child BrowsingContext in // another child process. This is illegal since the owner of the // BrowsingContext is the proccess with the in-process docshell, // which is tracked by OwnerProcessId. // TODO(farre): To crash or not to crash. Same reasoning as in // above TODO. [Bug 1471598] MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Warning, ("ParentIPC: Trying to set opener on out of process context " "0x%08" PRIx64, - context->Id())); + aContext->Id())); return IPC_OK(); } - RefPtr<BrowsingContext> opener = BrowsingContext::Get(aOpenerContextId); - context->SetOpener(opener); + aContext->SetOpener(aOpener); return IPC_OK(); } mozilla::ipc::IPCResult ContentParent::RecvSetUserGestureActivation( - const BrowsingContextId& aContextId, const bool& aNewValue) { - RefPtr<CanonicalBrowsingContext> context = - CanonicalBrowsingContext::Get(aContextId); - - if (!context) { + BrowsingContext* aContext, bool aNewValue) { + if (!aContext) { MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug, - ("ParentIPC: Trying to activate wrong context 0x%08" PRIx64, - (uint64_t)aContextId)); + ("ParentIPC: Trying to activate wrong context")); return IPC_OK(); } - context->NotifySetUserGestureActivationFromIPC(aNewValue); + aContext->Canonical()->NotifySetUserGestureActivationFromIPC(aNewValue); return IPC_OK(); } void ContentParent::RegisterRemoteWorkerActor() { ++mRemoteWorkerActors; } void ContentParent::UnregisterRemoveWorkerActor() { MOZ_ASSERT(NS_IsMainThread()); @@ -5777,100 +5740,88 @@ void ContentParent::UnregisterRemoveWork // allow it to perform shutdown tasks. MessageLoop::current()->PostTask(NewRunnableMethod<ShutDownMethod>( "dom::ContentParent::ShutDownProcess", this, &ContentParent::ShutDownProcess, SEND_SHUTDOWN_MESSAGE)); } } mozilla::ipc::IPCResult ContentParent::RecvWindowClose( - const BrowsingContextId& aContextId, const bool& aTrustedCaller) { - RefPtr<CanonicalBrowsingContext> bc = - CanonicalBrowsingContext::Get(aContextId); - if (!bc) { - MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug, - ("ParentIPC: Trying to send a message to dead or detached context " - "0x%08" PRIx64, - (uint64_t)aContextId)); + BrowsingContext* aContext, bool aTrustedCaller) { + if (!aContext) { + MOZ_LOG( + BrowsingContext::GetLog(), LogLevel::Debug, + ("ParentIPC: Trying to send a message to dead or detached context")); return IPC_OK(); } // FIXME Need to check that the sending process has access to the unit of // related // browsing contexts of bc. ContentProcessManager* cpm = ContentProcessManager::GetSingleton(); - ContentParent* cp = - cpm->GetContentProcessById(ContentParentId(bc->OwnerProcessId())); - Unused << cp->SendWindowClose(aContextId, aTrustedCaller); + ContentParent* cp = cpm->GetContentProcessById( + ContentParentId(aContext->Canonical()->OwnerProcessId())); + Unused << cp->SendWindowClose(aContext, aTrustedCaller); return IPC_OK(); } mozilla::ipc::IPCResult ContentParent::RecvWindowFocus( - const BrowsingContextId& aContextId) { - RefPtr<CanonicalBrowsingContext> bc = - CanonicalBrowsingContext::Get(aContextId); - if (!bc) { - MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug, - ("ParentIPC: Trying to send a message to dead or detached context " - "0x%08" PRIx64, - (uint64_t)aContextId)); + BrowsingContext* aContext) { + if (!aContext) { + MOZ_LOG( + BrowsingContext::GetLog(), LogLevel::Debug, + ("ParentIPC: Trying to send a message to dead or detached context")); return IPC_OK(); } ContentProcessManager* cpm = ContentProcessManager::GetSingleton(); - ContentParent* cp = - cpm->GetContentProcessById(ContentParentId(bc->OwnerProcessId())); - Unused << cp->SendWindowFocus(aContextId); + ContentParent* cp = cpm->GetContentProcessById( + ContentParentId(aContext->Canonical()->OwnerProcessId())); + Unused << cp->SendWindowFocus(aContext); return IPC_OK(); } mozilla::ipc::IPCResult ContentParent::RecvWindowBlur( - const BrowsingContextId& aContextId) { - RefPtr<CanonicalBrowsingContext> bc = - CanonicalBrowsingContext::Get(aContextId); - if (!bc) { - MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug, - ("ParentIPC: Trying to send a message to dead or detached context " - "0x%08" PRIx64, - (uint64_t)aContextId)); + BrowsingContext* aContext) { + if (!aContext) { + MOZ_LOG( + BrowsingContext::GetLog(), LogLevel::Debug, + ("ParentIPC: Trying to send a message to dead or detached context")); return IPC_OK(); } ContentProcessManager* cpm = ContentProcessManager::GetSingleton(); - ContentParent* cp = - cpm->GetContentProcessById(ContentParentId(bc->OwnerProcessId())); - Unused << cp->SendWindowBlur(aContextId); + ContentParent* cp = cpm->GetContentProcessById( + ContentParentId(aContext->Canonical()->OwnerProcessId())); + Unused << cp->SendWindowBlur(aContext); return IPC_OK(); } mozilla::ipc::IPCResult ContentParent::RecvWindowPostMessage( - const BrowsingContextId& aContextId, const ClonedMessageData& aMessage, + BrowsingContext* aContext, const ClonedMessageData& aMessage, const PostMessageData& aData) { - RefPtr<CanonicalBrowsingContext> bc = - CanonicalBrowsingContext::Get(aContextId); - if (!bc) { - MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug, - ("ParentIPC: Trying to send a message to dead or detached context " - "0x%08" PRIx64, - (uint64_t)aContextId)); + if (!aContext) { + MOZ_LOG( + BrowsingContext::GetLog(), LogLevel::Debug, + ("ParentIPC: Trying to send a message to dead or detached context")); return IPC_OK(); } ContentProcessManager* cpm = ContentProcessManager::GetSingleton(); - ContentParent* cp = - cpm->GetContentProcessById(ContentParentId(bc->OwnerProcessId())); + ContentParent* cp = cpm->GetContentProcessById( + ContentParentId(aContext->Canonical()->OwnerProcessId())); StructuredCloneData messageFromChild; UnpackClonedMessageDataForParent(aMessage, messageFromChild); ClonedMessageData message; if (!BuildClonedMessageDataForParent(cp, messageFromChild, message)) { // FIXME Logging? return IPC_OK(); } - Unused << cp->SendWindowPostMessage(aContextId, message, aData); + Unused << cp->SendWindowPostMessage(aContext, message, aData); return IPC_OK(); } } // namespace dom } // namespace mozilla NS_IMPL_ISUPPORTS(ParentIdleListener, nsIObserver)
--- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -613,37 +613,35 @@ class ContentParent final : public PCont // Control the priority of the IPC messages for input events. void SetInputPriorityEventEnabled(bool aEnabled); bool IsInputPriorityEventEnabled() { return mIsInputPriorityEventEnabled; } static bool IsInputEventQueueSupported(); mozilla::ipc::IPCResult RecvAttachBrowsingContext( - const BrowsingContextId& aParentContextId, - const BrowsingContextId& aOpenerId, const BrowsingContextId& aContextId, - const nsString& aName); + BrowsingContext* aParentContext, BrowsingContext* aOpener, + BrowsingContextId aContextId, const nsString& aName); - mozilla::ipc::IPCResult RecvDetachBrowsingContext( - const BrowsingContextId& aContextId, const bool& aMoveToBFCache); + mozilla::ipc::IPCResult RecvDetachBrowsingContext(BrowsingContext* aContext, + bool aMoveToBFCache); mozilla::ipc::IPCResult RecvSetOpenerBrowsingContext( - const BrowsingContextId& aContextId, - const BrowsingContextId& aOpenerContextId); + BrowsingContext* aContext, BrowsingContext* aOpener); - mozilla::ipc::IPCResult RecvWindowClose(const BrowsingContextId& aContextId, - const bool& aTrustedCaller); - mozilla::ipc::IPCResult RecvWindowFocus(const BrowsingContextId& aContextId); - mozilla::ipc::IPCResult RecvWindowBlur(const BrowsingContextId& aContextId); + mozilla::ipc::IPCResult RecvWindowClose(BrowsingContext* aContext, + bool aTrustedCaller); + mozilla::ipc::IPCResult RecvWindowFocus(BrowsingContext* aContext); + mozilla::ipc::IPCResult RecvWindowBlur(BrowsingContext* aContext); mozilla::ipc::IPCResult RecvWindowPostMessage( - const BrowsingContextId& aContextId, const ClonedMessageData& aMessage, + BrowsingContext* aContext, const ClonedMessageData& aMessage, const PostMessageData& aData); mozilla::ipc::IPCResult RecvSetUserGestureActivation( - const BrowsingContextId& aContextId, const bool& aNewValue); + BrowsingContext* aContext, bool aNewValue); protected: void OnChannelConnected(int32_t pid) override; virtual void ActorDestroy(ActorDestroyReason why) override; bool ShouldContinueFromReplyTimeout() override;
--- a/dom/ipc/DOMTypes.ipdlh +++ b/dom/ipc/DOMTypes.ipdlh @@ -22,17 +22,17 @@ using DesktopIntRect from "Units.h"; using DesktopToLayoutDeviceScale from "Units.h"; using CSSToLayoutDeviceScale from "Units.h"; using CSSRect from "Units.h"; using CSSSize from "Units.h"; using mozilla::LayoutDeviceIntPoint from "Units.h"; using hal::ScreenOrientation from "mozilla/HalScreenConfiguration.h"; using mozilla::gfx::SurfaceFormat from "mozilla/gfx/Types.h"; using refcounted class nsIPrincipal from "mozilla/dom/PermissionMessageUtils.h"; -using mozilla::dom::BrowsingContextId from "mozilla/dom/ipc/IdType.h"; +using refcounted class mozilla::dom::BrowsingContext from "mozilla/dom/BrowsingContext.h"; using refcounted class nsIURI from "mozilla/ipc/URIUtils.h"; namespace mozilla { namespace dom { struct MessagePortIdentifier { nsID uuid; @@ -184,17 +184,17 @@ struct PerformanceInfo // Counters per category. For workers, a single entry CategoryDispatch[] items; }; struct WindowGlobalInit { nsIPrincipal principal; - BrowsingContextId browsingContextId; + BrowsingContext browsingContext; uint64_t innerWindowId; uint64_t outerWindowId; }; struct DocShellLoadStateInit { nsIURI Referrer; nsIURI URI;
--- a/dom/ipc/PBrowser.ipdl +++ b/dom/ipc/PBrowser.ipdl @@ -83,17 +83,17 @@ using mozilla::EventMessage from "mozill using nsEventStatus from "mozilla/EventForwards.h"; using mozilla::Modifiers from "mozilla/EventForwards.h"; using nsSizeMode from "nsIWidgetListener.h"; using mozilla::widget::CandidateWindowPosition from "ipc/nsGUIEventIPC.h"; using class mozilla::NativeEventData from "ipc/nsGUIEventIPC.h"; using mozilla::FontRange from "ipc/nsGUIEventIPC.h"; using mozilla::a11y::IAccessibleHolder from "mozilla/a11y/IPCTypes.h"; using mozilla::OriginAttributes from "mozilla/ipc/BackgroundUtils.h"; -using mozilla::dom::BrowsingContextId from "mozilla/dom/ipc/IdType.h"; +using refcounted class mozilla::dom::BrowsingContext from "mozilla/dom/BrowsingContext.h"; namespace mozilla { namespace dom { struct ShowInfo { nsString name; bool fullscreenAllowed; @@ -616,17 +616,17 @@ parent: bool aHideDoorHanger); sync SetSystemFont(nsCString aFontName); sync GetSystemFont() returns (nsCString retval); sync SetPrefersReducedMotionOverrideForTest(bool aValue); sync ResetPrefersReducedMotionOverrideForTest(); - async RootBrowsingContext(BrowsingContextId aId); + async RootBrowsingContext(BrowsingContext aContext); child: /** * Notify the remote browser that it has been Show()n on this * side, with the given |visibleRect|. This message is expected * to trigger creation of the remote browser's "widget". * * |Show()| and |Move()| take IntSizes rather than Rects because
--- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -97,16 +97,17 @@ using mozilla::Telemetry::HistogramAccum using mozilla::Telemetry::KeyedHistogramAccumulation from "mozilla/TelemetryComms.h"; using mozilla::Telemetry::ScalarAction from "mozilla/TelemetryComms.h"; using mozilla::Telemetry::KeyedScalarAction from "mozilla/TelemetryComms.h"; using mozilla::Telemetry::DynamicScalarDefinition from "mozilla/TelemetryComms.h"; using mozilla::Telemetry::ChildEventData from "mozilla/TelemetryComms.h"; using mozilla::Telemetry::DiscardedData from "mozilla/TelemetryComms.h"; using mozilla::CrossProcessMutexHandle from "mozilla/ipc/CrossProcessMutex.h"; using refcounted class nsIInputStream from "mozilla/ipc/IPCStreamUtils.h"; +using refcounted class mozilla::dom::BrowsingContext from "mozilla/dom/BrowsingContext.h"; using mozilla::dom::BrowsingContextId from "mozilla/dom/ipc/IdType.h"; union ChromeRegistryItem { ChromePackage; OverrideMapping; SubstitutionMapping; }; @@ -303,17 +304,17 @@ struct NotificationEventData nsString tag; nsString icon; nsString data; nsString behavior; }; struct PostMessageData { - BrowsingContextId source; + BrowsingContext source; nsString origin; nsString targetOrigin; nsIURI targetOriginURI; nsIPrincipal callerPrincipal; nsIPrincipal subjectPrincipal; nsIURI callerDocumentURI; bool isFromPrivateWindow; }; @@ -1214,57 +1215,54 @@ parent: nsCString aTrackingOrigin, nsCString aGrantedOrigin, int aAllowMode) returns (bool unused); async StoreUserInteractionAsPermission(Principal aPrincipal); /** - * Sync the BrowsingContext with id 'aContextId' and name 'aName' - * to the parent, and attach it to the BrowsingContext with id - * 'aParentContextId'. If 'aParentContextId' is '0' the - * BrowsingContext is a root in the BrowsingContext - * tree. AttachBrowsingContext must only be called at most once - * for any child BrowsingContext, and only for BrowsingContexts - * where the parent and the child context contains their - * nsDocShell. + * Sync the BrowsingContext with id 'aContextId' and name 'aName' to the + * parent, and attach it to the BrowsingContext 'aParentContext'. If + * 'aParentContext' is 'nullptr' the BrowsingContext is a root in the + * BrowsingContext tree. AttachBrowsingContext must only be called at most + * once for any child BrowsingContext, and only for BrowsingContexts where + * the parent and the child context contains their nsDocShell. */ - async AttachBrowsingContext(BrowsingContextId aParentContextId, - BrowsingContextId aOpenerId, + async AttachBrowsingContext(BrowsingContext aParentContext, + BrowsingContext aOpener, BrowsingContextId aContextId, nsString aName); /** - * Remove the synced BrowsingContext with id 'aContextId' from the - * parent. DetachBrowsingContext is only needed to be called once - * for any BrowsingContext, since detaching a node in the - * BrowsingContext detaches the entire sub-tree rooted at that - * node. Calling DetachBrowsingContext with an already detached - * BrowsingContext effectively does nothing. Note that it is not - * an error to call DetachBrowsingContext on a BrowsingContext - * belonging to an already detached subtree. The 'aMoveToBFCache' - * paramater controls if detaching a BrowsingContext should move - * it to the bfcache allowing it to be re-attached if navigated + * Remove the synced BrowsingContext 'aContext' from the parent. + * DetachBrowsingContext is only needed to be called once for any + * BrowsingContext, since detaching a node in the BrowsingContext detaches + * the entire sub-tree rooted at that node. Calling DetachBrowsingContext + * with an already detached BrowsingContext effectively does nothing. Note + * that it is not an error to call DetachBrowsingContext on a + * BrowsingContext belonging to an already detached subtree. The + * 'aMoveToBFCache' paramater controls if detaching a BrowsingContext + * should move it to the bfcache allowing it to be re-attached if navigated * to. */ - async DetachBrowsingContext(BrowsingContextId aContextId, + async DetachBrowsingContext(BrowsingContext aContext, bool aMoveToBFCache); /** - * Set the opener of browsing context with id 'aContextId' to the - * browsing context with id 'aOpenerId'. + * Set the opener of browsing context 'aContext' to the browsing context + * with id 'aOpenerId'. */ - async SetOpenerBrowsingContext(BrowsingContextId aContextId, - BrowsingContextId aOpenerContextId); + async SetOpenerBrowsingContext(BrowsingContext aContext, + BrowsingContext aOpenerContext); /** * Notify parent to update user gesture activation flag. */ - async SetUserGestureActivation(BrowsingContextId aContextId, + async SetUserGestureActivation(BrowsingContext aContext, bool aNewValue); both: async AsyncMessage(nsString aMessage, CpowEntry[] aCpows, Principal aPrincipal, ClonedMessageData aData); /** * Notify `push-subscription-modified` observers in the parent and child. @@ -1274,18 +1272,17 @@ both: /** * Send a Push error message to all service worker clients in the parent or * child. */ async PushError(nsCString scope, Principal principal, nsString message, uint32_t flags); - async WindowClose(BrowsingContextId aContextId, - bool aTrustedCaller); - async WindowFocus(BrowsingContextId aContextId); - async WindowBlur(BrowsingContextId aContextId); - async WindowPostMessage(BrowsingContextId aContextId, ClonedMessageData aMessage, + async WindowClose(BrowsingContext aContext, bool aTrustedCaller); + async WindowFocus(BrowsingContext aContext); + async WindowBlur(BrowsingContext aContext); + async WindowPostMessage(BrowsingContext aContext, ClonedMessageData aMessage, PostMessageData aData); }; } }
--- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -549,17 +549,17 @@ nsresult TabChild::Init(mozIDOMWindowPro MOZ_ASSERT(loadContext); loadContext->SetPrivateBrowsing(OriginAttributesRef().mPrivateBrowsingId > 0); loadContext->SetRemoteTabs(mChromeFlags & nsIWebBrowserChrome::CHROME_REMOTE_WINDOW); // Send our browsing context to the parent process. RefPtr<BrowsingContext> browsingContext = nsDocShell::Cast(docShell)->GetBrowsingContext(); - SendRootBrowsingContext(BrowsingContextId(browsingContext->Id())); + SendRootBrowsingContext(browsingContext); // Few lines before, baseWindow->Create() will end up creating a new // window root in nsGlobalWindow::SetDocShell. // Then this chrome event handler, will be inherited to inner windows. // We want to also set it to the docshell so that inner windows // and any code that has access to the docshell // can all listen to the same chrome event handler. // XXX: ideally, we would set a chrome event handler earlier,
--- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -3439,19 +3439,19 @@ mozilla::ipc::IPCResult TabParent::RecvG nsCOMPtr<nsIWidget> widget = GetWidget(); if (widget) { widget->GetSystemFont(*aFontName); } return IPC_OK(); } mozilla::ipc::IPCResult TabParent::RecvRootBrowsingContext( - const BrowsingContextId& aId) { + BrowsingContext* aBrowsingContext) { MOZ_ASSERT(!mBrowsingContext, "May only set browsing context once!"); - mBrowsingContext = CanonicalBrowsingContext::Get(aId); + mBrowsingContext = CanonicalBrowsingContext::Cast(aBrowsingContext); MOZ_ASSERT(mBrowsingContext, "Invalid ID!"); return IPC_OK(); } NS_IMETHODIMP FakeChannel::OnAuthAvailable(nsISupports* aContext, nsIAuthInformation* aAuthInfo) { nsAuthInformationHolder* holder =
--- a/dom/ipc/TabParent.h +++ b/dom/ipc/TabParent.h @@ -252,21 +252,20 @@ class TabParent final : public PBrowserP mozilla::ipc::IPCResult RecvEnableDisableCommands( const nsString& aAction, nsTArray<nsCString>&& aEnabledCommands, nsTArray<nsCString>&& aDisabledCommands); mozilla::ipc::IPCResult RecvSetCursor( const nsCursor& aValue, const bool& aHasCustomCursor, const nsCString& aUri, const uint32_t& aWidth, const uint32_t& aHeight, const uint32_t& aStride, const gfx::SurfaceFormat& aFormat, - const uint32_t& aHotspotX, const uint32_t& aHotspotY, - const bool& aForce); + const uint32_t& aHotspotX, const uint32_t& aHotspotY, const bool& aForce); - mozilla::ipc::IPCResult RecvSetStatus( - const uint32_t& aType, const nsString& aStatus); + mozilla::ipc::IPCResult RecvSetStatus(const uint32_t& aType, + const nsString& aStatus); mozilla::ipc::IPCResult RecvShowTooltip(const uint32_t& aX, const uint32_t& aY, const nsString& aTooltip, const nsString& aDirection); mozilla::ipc::IPCResult RecvHideTooltip(); @@ -590,17 +589,17 @@ class TabParent final : public PBrowserP const int32_t& aX, const int32_t& aY, const int32_t& aCx, const int32_t& aCy); mozilla::ipc::IPCResult RecvShowCanvasPermissionPrompt( const nsCString& aFirstPartyURI, const bool& aHideDoorHanger); - mozilla::ipc::IPCResult RecvRootBrowsingContext(const BrowsingContextId& aId); + mozilla::ipc::IPCResult RecvRootBrowsingContext(BrowsingContext* aContext); mozilla::ipc::IPCResult RecvSetSystemFont(const nsCString& aFontName); mozilla::ipc::IPCResult RecvGetSystemFont(nsCString* aFontName); mozilla::ipc::IPCResult RecvVisitURI(const URIParams& aURI, const OptionalURIParams& aLastVisitedURI, const uint32_t& aFlags);
--- a/dom/ipc/WindowGlobalChild.cpp +++ b/dom/ipc/WindowGlobalChild.cpp @@ -34,19 +34,18 @@ already_AddRefed<WindowGlobalChild> Wind RefPtr<nsDocShell> docshell = nsDocShell::Cast(aWindow->GetDocShell()); MOZ_ASSERT(docshell); // Initalize our WindowGlobalChild object. RefPtr<dom::BrowsingContext> bc = docshell->GetBrowsingContext(); RefPtr<WindowGlobalChild> wgc = new WindowGlobalChild(aWindow, bc); - WindowGlobalInit init(principal, - BrowsingContextId(wgc->BrowsingContext()->Id()), - wgc->mInnerWindowId, wgc->mOuterWindowId); + WindowGlobalInit init(principal, bc, wgc->mInnerWindowId, + wgc->mOuterWindowId); // Send the link constructor over PInProcessChild or PBrowser. if (XRE_IsParentProcess()) { InProcessChild* ipc = InProcessChild::Singleton(); if (!ipc) { return nullptr; }
--- a/dom/ipc/WindowGlobalParent.cpp +++ b/dom/ipc/WindowGlobalParent.cpp @@ -32,17 +32,17 @@ WindowGlobalParent::WindowGlobalParent(c mOuterWindowId(aInit.outerWindowId()), mInProcess(aInProcess), mIPCClosed(true) // Closed until WGP::Init { MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess(), "Parent process only"); MOZ_RELEASE_ASSERT(mDocumentPrincipal, "Must have a valid principal"); // NOTE: mBrowsingContext initialized in Init() - MOZ_RELEASE_ASSERT(aInit.browsingContextId() != 0, + MOZ_RELEASE_ASSERT(aInit.browsingContext(), "Must be made in BrowsingContext"); } void WindowGlobalParent::Init(const WindowGlobalInit& aInit) { MOZ_ASSERT(Manager(), "Should have a manager!"); MOZ_ASSERT(!mFrameLoader, "Cannot Init() a WindowGlobalParent twice!"); MOZ_ASSERT(mIPCClosed, "IPC shouldn't be open yet"); @@ -58,17 +58,17 @@ void WindowGlobalParent::Init(const Wind entry.OrInsert([&] { return this; }); // Determine which content process the window global is coming from. ContentParentId processId(0); if (!mInProcess) { processId = static_cast<ContentParent*>(Manager()->Manager())->ChildID(); } - mBrowsingContext = CanonicalBrowsingContext::Get(aInit.browsingContextId()); + mBrowsingContext = CanonicalBrowsingContext::Cast(aInit.browsingContext()); MOZ_ASSERT(mBrowsingContext); // XXX(nika): This won't be the case soon, but for now this is a good // assertion as we can't switch processes. We should relax this eventually. MOZ_ASSERT(mBrowsingContext->IsOwnedByProcess(processId)); // Attach ourself to the browsing context. mBrowsingContext->RegisterWindowGlobal(this);
--- a/dom/ipc/WindowGlobalParent.h +++ b/dom/ipc/WindowGlobalParent.h @@ -17,16 +17,17 @@ class nsIURI; class nsFrameLoader; namespace mozilla { namespace dom { class CanonicalBrowsingContext; class WindowGlobalChild; class JSWindowActorParent; +class TabParent; /** * A handle in the parent process to a specific nsGlobalWindowInner object. */ class WindowGlobalParent final : public nsISupports, public nsWrapperCache, public PWindowGlobalParent { friend class PWindowGlobalParent;
--- a/dom/script/ScriptLoader.cpp +++ b/dom/script/ScriptLoader.cpp @@ -306,16 +306,27 @@ nsresult ScriptLoader::CheckContentPolic nsCOMPtr<nsINode> requestingNode = do_QueryInterface(aContext); nsCOMPtr<nsILoadInfo> secCheckLoadInfo = new net::LoadInfo( aDocument->NodePrincipal(), // loading principal aDocument->NodePrincipal(), // triggering principal requestingNode, nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK, contentPolicyType); + // snapshot the nonce at load start time for performing CSP checks + if (contentPolicyType == nsIContentPolicy::TYPE_INTERNAL_SCRIPT || + contentPolicyType == nsIContentPolicy::TYPE_INTERNAL_MODULE) { + nsCOMPtr<Element> element = do_QueryInterface(aContext); + if (element && element->IsHTMLElement()) { + nsAutoString cspNonce; + element->GetAttribute(NS_LITERAL_STRING("nonce"), cspNonce); + secCheckLoadInfo->SetCspNonce(cspNonce); + } + } + int16_t shouldLoad = nsIContentPolicy::ACCEPT; nsresult rv = NS_CheckContentLoadPolicy( aRequest->mURI, secCheckLoadInfo, NS_LossyConvertUTF16toASCII(aType), &shouldLoad, nsContentUtils::GetContentPolicy()); if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) { if (NS_FAILED(rv) || shouldLoad != nsIContentPolicy::REJECT_TYPE) { return NS_ERROR_CONTENT_BLOCKED; } @@ -1252,16 +1263,28 @@ nsresult ScriptLoader::StartLoad(ScriptL getter_AddRefs(channel), aRequest->mURI, context, aRequest->TriggeringPrincipal(), securityFlags, contentPolicyType, nullptr, // aPerformanceStorage loadGroup, prompter, nsIRequest::LOAD_NORMAL | nsIChannel::LOAD_CLASSIFY_URI); NS_ENSURE_SUCCESS(rv, rv); + // snapshot the nonce at load start time for performing CSP checks + if (contentPolicyType == nsIContentPolicy::TYPE_INTERNAL_SCRIPT || + contentPolicyType == nsIContentPolicy::TYPE_INTERNAL_MODULE) { + nsCOMPtr<Element> element = do_QueryInterface(context); + if (element && element->IsHTMLElement()) { + nsAutoString cspNonce; + element->GetAttribute(NS_LITERAL_STRING("nonce"), cspNonce); + nsCOMPtr<nsILoadInfo> loadInfo = channel->GetLoadInfo(); + loadInfo->SetCspNonce(cspNonce); + } + } + // To avoid decoding issues, the build-id is part of the JSBytecodeMimeType // constant. aRequest->mCacheInfo = nullptr; nsCOMPtr<nsICacheInfoChannel> cic(do_QueryInterface(channel)); if (cic && nsContentUtils::IsBytecodeCacheEnabled() && // Bug 1436400: no bytecode cache support for modules yet. !aRequest->IsModuleRequest()) { if (!aRequest->IsLoadingSource()) {
--- a/dom/security/nsCSPContext.cpp +++ b/dom/security/nsCSPContext.cpp @@ -116,17 +116,18 @@ static void BlockedContentSourceToString NS_IMETHODIMP nsCSPContext::ShouldLoad(nsContentPolicyType aContentType, nsICSPEventListener* aCSPEventListener, nsIURI* aContentLocation, nsIURI* aRequestOrigin, nsISupports* aRequestContext, const nsACString& aMimeTypeGuess, nsIURI* aOriginalURIIfRedirect, - bool aSendViolationReports, int16_t* outDecision) { + bool aSendViolationReports, const nsAString& aNonce, + int16_t* outDecision) { if (CSPCONTEXTLOGENABLED()) { CSPCONTEXTLOG(("nsCSPContext::ShouldLoad, aContentLocation: %s", aContentLocation->GetSpecOrDefault().get())); CSPCONTEXTLOG((">>>> aContentType: %d", aContentType)); } bool isPreload = nsContentUtils::IsPreloadType(aContentType); @@ -150,39 +151,29 @@ nsCSPContext::ShouldLoad(nsContentPolicy // If the content type doesn't map to a CSP directive, there's nothing for // CSP to do. CSPDirective dir = CSP_ContentTypeToDirective(aContentType); if (dir == nsIContentSecurityPolicy::NO_DIRECTIVE) { return NS_OK; } - nsAutoString nonce; bool parserCreated = false; if (!isPreload) { - if (aContentType == nsIContentPolicy::TYPE_SCRIPT || - aContentType == nsIContentPolicy::TYPE_STYLESHEET) { - nsCOMPtr<Element> element = do_QueryInterface(aRequestContext); - if (element && element->IsHTMLElement()) { - // XXXbz What about SVG elements that can have nonce? - element->GetAttribute(NS_LITERAL_STRING("nonce"), nonce); - } - } - nsCOMPtr<nsIScriptElement> script = do_QueryInterface(aRequestContext); if (script && script->GetParserCreated() != mozilla::dom::NOT_FROM_PARSER) { parserCreated = true; } } bool permitted = permitsInternal(dir, nullptr, // aTriggeringElement aCSPEventListener, aContentLocation, - aOriginalURIIfRedirect, nonce, isPreload, + aOriginalURIIfRedirect, aNonce, isPreload, false, // allow fallback to default-src aSendViolationReports, true, // send blocked URI in violation reports parserCreated); *outDecision = permitted ? nsIContentPolicy::ACCEPT : nsIContentPolicy::REJECT_SERVER;
--- a/dom/security/nsCSPService.cpp +++ b/dom/security/nsCSPService.cpp @@ -167,31 +167,35 @@ CSPService::ShouldLoad(nsIURI *aContentL } else { principal = node->NodePrincipal(); } if (!principal) { // if we can't query a principal, then there is nothing to do. return NS_OK; } + nsAutoString cspNonce; + rv = aLoadInfo->GetCspNonce(cspNonce); + NS_ENSURE_SUCCESS(rv, rv); + // 1) Apply speculate CSP for preloads bool isPreload = nsContentUtils::IsPreloadType(contentType); if (isPreload) { nsCOMPtr<nsIContentSecurityPolicy> preloadCsp; rv = principal->GetPreloadCsp(getter_AddRefs(preloadCsp)); NS_ENSURE_SUCCESS(rv, rv); if (preloadCsp) { // obtain the enforcement decision rv = preloadCsp->ShouldLoad( contentType, cspEventListener, aContentLocation, requestOrigin, requestContext, aMimeTypeGuess, nullptr, // no redirect, aOriginal URL is null. - aLoadInfo->GetSendCSPViolationEvents(), aDecision); + aLoadInfo->GetSendCSPViolationEvents(), cspNonce, aDecision); NS_ENSURE_SUCCESS(rv, rv); // if the preload policy already denied the load, then there // is no point in checking the real policy if (NS_CP_REJECTED(*aDecision)) { return NS_OK; } } @@ -202,17 +206,18 @@ CSPService::ShouldLoad(nsIURI *aContentL rv = principal->GetCsp(getter_AddRefs(csp)); NS_ENSURE_SUCCESS(rv, rv); if (csp) { // obtain the enforcement decision rv = csp->ShouldLoad(contentType, cspEventListener, aContentLocation, requestOrigin, requestContext, aMimeTypeGuess, nullptr, // no redirect, aOriginal URL is null. - aLoadInfo->GetSendCSPViolationEvents(), aDecision); + aLoadInfo->GetSendCSPViolationEvents(), cspNonce, + aDecision); NS_ENSURE_SUCCESS(rv, rv); } return NS_OK; } NS_IMETHODIMP CSPService::ShouldProcess(nsIURI *aContentLocation, nsILoadInfo *aLoadInfo, const nsACString &aMimeTypeGuess, @@ -246,27 +251,16 @@ CSPService::ShouldProcess(nsIURI *aConte /* nsIChannelEventSink implementation */ NS_IMETHODIMP CSPService::AsyncOnChannelRedirect(nsIChannel *oldChannel, nsIChannel *newChannel, uint32_t flags, nsIAsyncVerifyRedirectCallback *callback) { net::nsAsyncRedirectAutoCallback autoCallback(callback); - if (XRE_IsE10sParentProcess()) { - nsCOMPtr<nsIParentChannel> parentChannel; - NS_QueryNotificationCallbacks(oldChannel, parentChannel); - if (parentChannel) { - // This is an IPC'd channel. Don't check it here, because we won't have - // access to the request context; we'll check them in the content - // process instead. Bug 1509738 covers fixing this. - return NS_OK; - } - } - nsCOMPtr<nsIURI> newUri; nsresult rv = newChannel->GetURI(getter_AddRefs(newUri)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr<nsILoadInfo> loadInfo = oldChannel->GetLoadInfo(); // if no loadInfo on the channel, nothing for us to do if (!loadInfo) { @@ -298,16 +292,20 @@ CSPService::AsyncOnChannelRedirect(nsICh nsCOMPtr<nsIURI> originalUri; rv = oldChannel->GetOriginalURI(getter_AddRefs(originalUri)); if (NS_FAILED(rv)) { autoCallback.DontCallback(); oldChannel->Cancel(NS_ERROR_DOM_BAD_URI); return rv; } + nsAutoString cspNonce; + rv = loadInfo->GetCspNonce(cspNonce); + NS_ENSURE_SUCCESS(rv, rv); + bool isPreload = nsContentUtils::IsPreloadType(policyType); /* On redirect, if the content policy is a preload type, rejecting the preload * results in the load silently failing, so we convert preloads to the actual * type. See Bug 1219453. */ policyType = nsContentUtils::InternalContentPolicyTypeToExternalOrWorker(policyType); @@ -325,16 +323,17 @@ CSPService::AsyncOnChannelRedirect(nsICh policyType, // load type per nsIContentPolicy (uint32_t) cspEventListener, newUri, // nsIURI nullptr, // nsIURI requestContext, // nsISupports EmptyCString(), // ACString - MIME guess originalUri, // Original nsIURI true, // aSendViolationReports + cspNonce, // nonce &aDecision); // if the preload policy already denied the load, then there // is no point in checking the real policy if (NS_CP_REJECTED(aDecision)) { autoCallback.DontCallback(); oldChannel->Cancel(NS_ERROR_DOM_BAD_URI); return NS_BINDING_FAILED; @@ -351,16 +350,17 @@ CSPService::AsyncOnChannelRedirect(nsICh csp->ShouldLoad(policyType, // load type per nsIContentPolicy (uint32_t) cspEventListener, newUri, // nsIURI nullptr, // nsIURI requestContext, // nsISupports EmptyCString(), // ACString - MIME guess originalUri, // Original nsIURI true, // aSendViolationReports + cspNonce, // nonce &aDecision); } // if ShouldLoad doesn't accept the load, cancel the request if (!NS_CP_ACCEPTED(aDecision)) { autoCallback.DontCallback(); oldChannel->Cancel(NS_ERROR_DOM_BAD_URI); return NS_BINDING_FAILED;
new file mode 100644 --- /dev/null +++ b/dom/security/test/csp/file_nonce_snapshot.sjs @@ -0,0 +1,48 @@ +"use strict"; + +const TEST_FRAME = + `<!DOCTYPE HTML> + <html> + <body> + <script id='myScript' nonce='123456789' type='application/javascript'></script> + <script nonce='123456789'> + let myScript = document.getElementById('myScript'); + // 1) start loading the script using the nonce 123456789 + myScript.src='file_nonce_snapshot.sjs?redir-script'; + // 2) dynamically change the nonce, load should use initial nonce + myScript.setAttribute('nonce','987654321'); + </script> + </body> + </html>`; + +const SCRIPT = "window.parent.postMessage('script-loaded', '*');"; + +function handleRequest(request, response) +{ + // avoid confusing cache behaviors + response.setHeader("Cache-Control", "no-cache", false); + + let queryString = request.queryString; + + if (queryString === "load-frame") { + response.setHeader("Content-Security-Policy", "script-src 'nonce-123456789'", false); + response.setHeader("Content-Type", "text/html", false); + response.write(TEST_FRAME); + return; + } + + if (queryString === "redir-script") { + response.setStatusLine("1.1", 302, "Found"); + response.setHeader("Location", "file_nonce_snapshot.sjs?load-script", false); + return; + } + + if (queryString === "load-script") { + response.setHeader("Content-Type", "application/javascript", false); + response.write(SCRIPT); + return; + } + + // we should never get here but just in case return something unexpected + response.write("do'h"); +}
--- a/dom/security/test/csp/mochitest.ini +++ b/dom/security/test/csp/mochitest.ini @@ -363,8 +363,11 @@ support-files = file_frame_src_inner.html [test_security_policy_violation_event.html] [test_csp_worker_inheritance.html] support-files = worker.sjs worker_helper.js main_csp_worker.html main_csp_worker.html^headers^ +[test_nonce_snapshot.html] +support-files = + file_nonce_snapshot.sjs
new file mode 100644 --- /dev/null +++ b/dom/security/test/csp/test_nonce_snapshot.html @@ -0,0 +1,35 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Bug 1509738 - Snapshot nonce at load start time</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<iframe style="width:100%;" id="testframe"></iframe> + +<script class="testbody" type="text/javascript"> + +/* Description of the test: + * a) the test starts loading a script using whitelisted nonce + * b) the nonce of the script gets modified + * c) the script hits a 302 server side redirect + * d) we ensure the script still loads and does not use the modified nonce + */ + +window.addEventListener("message", receiveMessage); +function receiveMessage(event) { + is(event.data, "script-loaded", "script loaded even though nonce was dynamically modified"); + window.removeEventListener("message", receiveMessage); + SimpleTest.finish(); +} + + +SimpleTest.waitForExplicitFinish(); +let src = "file_nonce_snapshot.sjs?load-frame"; +document.getElementById("testframe").src = src; + +</script> +</body> +</html>
--- a/dom/security/test/unit/test_csp_reports.js +++ b/dom/security/test/unit/test_csp_reports.js @@ -148,17 +148,17 @@ function run_test() { }); makeTest(2, {"blocked-uri": "http://blocked.test/foo.js"}, false, function(csp) { // shouldLoad creates and sends out the report here. csp.shouldLoad(Ci.nsIContentPolicy.TYPE_SCRIPT, null, // nsICSPEventListener NetUtil.newURI("http://blocked.test/foo.js"), - null, null, null, null, true); + null, null, null, null, true, null); }); // test that inline script violations cause a report in report-only policy makeTest(3, {"blocked-uri": "inline"}, true, function(csp) { let inlineOK = true; inlineOK = csp.getAllowsInline(Ci.nsIContentPolicy.TYPE_SCRIPT, "", // aNonce @@ -201,42 +201,42 @@ function run_test() { function(csp) { var base64data = "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12" + "P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="; // shouldLoad creates and sends out the report here. csp.shouldLoad(Ci.nsIContentPolicy.TYPE_IMAGE, null, // nsICSPEventListener NetUtil.newURI("data:image/png;base64," + base64data), - null, null, null, null, true); + null, null, null, null, true, null); }); // test that only the uri's scheme is reported for globally unique identifiers makeTest(6, {"blocked-uri": "intent"}, false, function(csp) { // shouldLoad creates and sends out the report here. csp.shouldLoad(Ci.nsIContentPolicy.TYPE_SUBDOCUMENT, null, // nsICSPEventListener NetUtil.newURI("intent://mymaps.com/maps?um=1&ie=UTF-8&fb=1&sll"), - null, null, null, null, true); + null, null, null, null, true, null); }); // test fragment removal var selfSpec = REPORT_SERVER_URI + ":" + REPORT_SERVER_PORT + "/foo/self/foo.js"; makeTest(7, {"blocked-uri": selfSpec}, false, function(csp) { // shouldLoad creates and sends out the report here. csp.shouldLoad(Ci.nsIContentPolicy.TYPE_SCRIPT, null, // nsICSPEventListener NetUtil.newURI(selfSpec + "#bar"), - null, null, null, null, true); + null, null, null, null, true, null); }); // test scheme of ftp: makeTest(8, {"blocked-uri": "ftp://blocked.test/profile.png"}, false, function(csp) { // shouldLoad creates and sends out the report here. csp.shouldLoad(Ci.nsIContentPolicy.TYPE_SCRIPT, null, // nsICSPEventListener NetUtil.newURI("ftp://blocked.test/profile.png"), - null, null, null, null, true); + null, null, null, null, true, null); }); }
--- a/gfx/webrender_bindings/src/bindings.rs +++ b/gfx/webrender_bindings/src/bindings.rs @@ -19,17 +19,17 @@ use std::os::raw::{c_int}; use gleam::gl; use webrender::api::*; use webrender::{ReadPixelsFormat, Renderer, RendererOptions, RendererStats, ThreadListener}; use webrender::{ExternalImage, ExternalImageHandler, ExternalImageSource}; use webrender::DebugFlags; use webrender::{ApiRecordingReceiver, BinaryRecorder}; use webrender::{AsyncPropertySampler, PipelineInfo, SceneBuilderHooks}; -use webrender::{UploadMethod, VertexUsageHint}; +use webrender::{UploadMethod, VertexUsageHint, ProfilerHooks, set_profiler_hooks}; use webrender::{Device, Shaders, WrShaders, ShaderPrecacheFlags}; use thread_profiler::register_thread_with_profiler; use moz2d_renderer::Moz2dBlobImageHandler; use program_cache::{WrProgramCache, remove_disk_cache}; use app_units::Au; use rayon; use euclid::SideOffsets2D; use nsstring::nsAString; @@ -774,16 +774,36 @@ pub unsafe extern "C" fn wr_pipeline_inf // the underlying vec memory } extern "C" { pub fn gecko_profiler_start_marker(name: *const c_char); pub fn gecko_profiler_end_marker(name: *const c_char); } +/// Simple implementation of the WR ProfilerHooks trait to allow profile +/// markers to be seen in the Gecko profiler. +struct GeckoProfilerHooks; + +impl ProfilerHooks for GeckoProfilerHooks { + fn begin_marker(&self, label: &CStr) { + unsafe { + gecko_profiler_start_marker(label.as_ptr()); + } + } + + fn end_marker(&self, label: &CStr) { + unsafe { + gecko_profiler_end_marker(label.as_ptr()); + } + } +} + +const PROFILER_HOOKS: GeckoProfilerHooks = GeckoProfilerHooks {}; + #[allow(improper_ctypes)] // this is needed so that rustc doesn't complain about passing the &mut Transaction to an extern function extern "C" { // These callbacks are invoked from the scene builder thread (aka the APZ // updater thread) fn apz_register_updater(window_id: WrWindowId); fn apz_pre_scene_swap(window_id: WrWindowId); // This function takes ownership of the pipeline_info and is responsible for // freeing it via wr_pipeline_info_delete. @@ -1115,16 +1135,19 @@ pub extern "C" fn wr_window_new(window_i max_texture_size: Some(8192), // Moz2D doesn't like textures bigger than this clear_color: Some(ColorF::new(0.0, 0.0, 0.0, 0.0)), precache_flags, namespace_alloc_by_client: true, enable_picture_caching, ..Default::default() }; + // Ensure the WR profiler callbacks are hooked up to the Gecko profiler. + set_profiler_hooks(Some(&PROFILER_HOOKS)); + let notifier = Box::new(CppNotifier { window_id: window_id, }); let (renderer, sender) = match Renderer::new(gl, notifier, opts, shaders) { Ok((renderer, sender)) => (renderer, sender), Err(e) => { warn!(" Failed to create a Renderer: {:?}", e); let msg = CString::new(format!("wr_window_new: {:?}", e)).unwrap();
--- a/gfx/wr/Cargo.lock +++ b/gfx/wr/Cargo.lock @@ -315,16 +315,34 @@ dependencies = [ ] [[package]] name = "crossbeam-utils" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] +name = "cstr" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cstr-macros 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "procedural-masquerade 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cstr-macros" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "procedural-masquerade 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.17 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "deflate" version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "adler32 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1071,16 +1089,21 @@ dependencies = [ name = "proc-macro2" version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] +name = "procedural-masquerade" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] name = "quick-error" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "quote" version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1602,16 +1625,17 @@ dependencies = [ "base64 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "bincode 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "core-graphics 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)", "core-text 13.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cstr 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "dwrote 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "freetype 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "gleam 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", "image 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1878,16 +1902,18 @@ dependencies = [ "checksum core-text 13.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f3f46450d6f2397261af420b4ccce23807add2e45fa206410a03d66fb7f050ae" "checksum crossbeam 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "bd66663db5a988098a89599d4857919b3acf7f61402e61365acfd3919857b9be" "checksum crossbeam-channel 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6c0a94250b0278d7fc5a894c3d276b11ea164edc8bf8feb10ca1ea517b44a649" "checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3" "checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150" "checksum crossbeam-epoch 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "30fecfcac6abfef8771151f8be4abc9e4edc112c2bcb233314cafde2680536e9" "checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9" "checksum crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "677d453a17e8bd2b913fa38e8b9cf04bcdbb5be790aa294f2389661d72036015" +"checksum cstr 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ee681252c9c0a6e84bbb53257faa3d88a49ce6fb32148ae1a9dc24b588302a71" +"checksum cstr-macros 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5e700cd6ede9b3f81b23ce4cba15f75cc8bf5b5a5dce2db293b1d31f4950ed78" "checksum deflate 0.7.18 (registry+https://github.com/rust-lang/crates.io-index)" = "32c8120d981901a9970a3a1c97cf8b630e0fa8c3ca31e75b6fd6fd5f9f427b31" "checksum derive_more 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3f57d78cf3bd45270dad4e70c21ec77a960b36c7a841ff9db76aaa775a8fb871" "checksum digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05f47366984d3ad862010e22c7ce81a7dbcaebbdfb37241a620f8b6596ee135c" "checksum dlib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "77e51249a9d823a4cb79e3eca6dcd756153e8ed0157b6c04775d04bf1b13b76a" "checksum downcast-rs 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "18df8ce4470c189d18aa926022da57544f31e154631eb4cfe796aea97051fe6c" "checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab" "checksum dwrote 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c31c624339dab99c223a4b26c2e803b7c248adaca91549ce654c76f39a03f5c8" "checksum either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3be565ca5c557d7f59e7cfcf1844f9e3033650c929c6566f511e8005f205c1d0" @@ -1964,16 +1990,17 @@ dependencies = [ "checksum pathfinder_gfx_utils 0.2.0 (git+https://github.com/pcwalton/pathfinder?branch=webrender)" = "<none>" "checksum pathfinder_partitioner 0.2.0 (git+https://github.com/pcwalton/pathfinder?branch=webrender)" = "<none>" "checksum pathfinder_path_utils 0.2.0 (git+https://github.com/pcwalton/pathfinder?branch=webrender)" = "<none>" "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" "checksum pkg-config 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "110d5ee3593dbb73f56294327fe5668bcc997897097cbc76b51e7aed3f52452f" "checksum plane-split 0.13.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b84b8cf2daa6a829b3e756fb75e0eab8e0d963754de9bfc83a4373a47121323a" "checksum png 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9adebf7fb91ccf5eac9da1a8e00e83cb8ae882c3e8d8e4ad59da73cb8c82a2c9" "checksum proc-macro2 0.4.25 (registry+https://github.com/rust-lang/crates.io-index)" = "d3797b7142c9aa74954e351fc089bbee7958cebbff6bf2815e7ffff0b19f547d" +"checksum procedural-masquerade 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9a1574a51c3fd37b26d2c0032b649d08a7d51d4cca9c41bbc5bf7118fa4509d0" "checksum quick-error 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eda5fe9b71976e62bc81b781206aaa076401769b2143379d3eb2118388babac4" "checksum quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e44651a0dc4cdd99f71c83b561e221f714912d11af1a4dff0631f923d53af035" "checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1" "checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5" "checksum rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e464cd887e869cddcae8792a4ee31d23c7edd516700695608f5b98c67ee0131c" "checksum rand_core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "edecf0f94da5551fc9b492093e30b041a891657db7940ee221f9d2f66e82eef2" "checksum rayon 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "80e811e76f1dbf68abf87a759083d34600017fc4e10b6bd5ad84a700f9dba4b1" "checksum rayon-core 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9d24ad214285a7729b174ed6d3bcfcb80177807f959d95fafd5bfc5c4f201ac8"
--- a/gfx/wr/webrender/Cargo.toml +++ b/gfx/wr/webrender/Cargo.toml @@ -23,16 +23,17 @@ webrender_build = { version = "0.0.1", p [dependencies] app_units = "0.7" base64 = { optional = true, version = "0.10" } bincode = "1.0" bitflags = "1.0" byteorder = "1.0" cfg-if = "0.1.2" +cstr = "0.1.2" fxhash = "0.2.1" gleam = "0.6.8" image = { optional = true, version = "0.21" } lazy_static = "1" log = "0.4" malloc_size_of_derive = "0.1" num-traits = "0.2" plane-split = "0.13.3"
--- a/gfx/wr/webrender/src/frame_builder.rs +++ b/gfx/wr/webrender/src/frame_builder.rs @@ -369,16 +369,18 @@ impl FrameBuilder { &mut self.prim_store.pictures, &frame_context, gpu_cache, &self.clip_store, &data_stores.clip, ); { + profile_marker!("UpdateVisibility"); + let visibility_context = FrameVisibilityContext { device_pixel_scale, clip_scroll_tree, screen_world_rect, surfaces, debug_flags, scene_properties, config: &self.config, @@ -434,25 +436,29 @@ impl FrameBuilder { root_spatial_node_index, ROOT_SURFACE_INDEX, true, &mut frame_state, &frame_context, ) .unwrap(); - self.prim_store.prepare_primitives( - &mut prim_list, - &pic_context, - &mut pic_state, - &frame_context, - &mut frame_state, - data_stores, - scratch, - ); + { + profile_marker!("PreparePrims"); + + self.prim_store.prepare_primitives( + &mut prim_list, + &pic_context, + &mut pic_state, + &frame_context, + &mut frame_state, + data_stores, + scratch, + ); + } let pic = &mut self.prim_store.pictures[self.root_pic_index.0]; pic.restore_context( prim_list, pic_context, pic_state, &mut frame_state, ); @@ -495,16 +501,17 @@ impl FrameBuilder { texture_cache_profile: &mut TextureCacheProfileCounters, gpu_cache_profile: &mut GpuCacheProfileCounters, scene_properties: &SceneProperties, data_stores: &mut DataStores, scratch: &mut PrimitiveScratchBuffer, debug_flags: DebugFlags, ) -> Frame { profile_scope!("build"); + profile_marker!("BuildFrame"); debug_assert!( DeviceIntRect::new(DeviceIntPoint::zero(), self.window_size) .contains_rect(&self.screen_rect) ); let mut profile_counters = FrameProfileCounters::new(); profile_counters .total_primitives @@ -541,89 +548,96 @@ impl FrameBuilder { scene_properties, &mut transform_palette, data_stores, &mut surfaces, scratch, debug_flags, ); - resource_cache.block_until_all_resources_added(gpu_cache, - &mut render_tasks, - texture_cache_profile); + { + profile_marker!("BlockOnResources"); + + resource_cache.block_until_all_resources_added(gpu_cache, + &mut render_tasks, + texture_cache_profile); + } let mut passes = vec![]; - - // Add passes as required for our cached render tasks. - if !render_tasks.cacheable_render_tasks.is_empty() { - passes.push(RenderPass::new_off_screen(screen_size)); - for cacheable_render_task in &render_tasks.cacheable_render_tasks { - render_tasks.assign_to_passes( - *cacheable_render_task, - 0, - screen_size, - &mut passes, - ); - } - passes.reverse(); - } - - if let Some(main_render_task_id) = main_render_task_id { - let passes_start = passes.len(); - passes.push(RenderPass::new_main_framebuffer(screen_size)); - render_tasks.assign_to_passes( - main_render_task_id, - passes_start, - screen_size, - &mut passes, - ); - passes[passes_start..].reverse(); - } - - let mut deferred_resolves = vec![]; let mut has_texture_cache_tasks = false; let mut prim_headers = PrimitiveHeaders::new(); - // Used to generated a unique z-buffer value per primitive. - let mut z_generator = ZBufferIdGenerator::new(); - let use_dual_source_blending = self.config.dual_source_blending_is_enabled && - self.config.dual_source_blending_is_supported; + + { + profile_marker!("Batching"); - for pass in &mut passes { - let mut ctx = RenderTargetContext { - device_pixel_scale, - prim_store: &self.prim_store, - resource_cache, - use_dual_source_blending, - clip_scroll_tree, - data_stores, - surfaces: &surfaces, - scratch, - screen_world_rect, - globals: &self.globals, - }; + // Add passes as required for our cached render tasks. + if !render_tasks.cacheable_render_tasks.is_empty() { + passes.push(RenderPass::new_off_screen(screen_size)); + for cacheable_render_task in &render_tasks.cacheable_render_tasks { + render_tasks.assign_to_passes( + *cacheable_render_task, + 0, + screen_size, + &mut passes, + ); + } + passes.reverse(); + } + + if let Some(main_render_task_id) = main_render_task_id { + let passes_start = passes.len(); + passes.push(RenderPass::new_main_framebuffer(screen_size)); + render_tasks.assign_to_passes( + main_render_task_id, + passes_start, + screen_size, + &mut passes, + ); + passes[passes_start..].reverse(); + } - pass.build( - &mut ctx, - gpu_cache, - &mut render_tasks, - &mut deferred_resolves, - &self.clip_store, - &mut transform_palette, - &mut prim_headers, - &mut z_generator, - ); + // Used to generated a unique z-buffer value per primitive. + let mut z_generator = ZBufferIdGenerator::new(); + let use_dual_source_blending = self.config.dual_source_blending_is_enabled && + self.config.dual_source_blending_is_supported; + + for pass in &mut passes { + let mut ctx = RenderTargetContext { + device_pixel_scale, + prim_store: &self.prim_store, + resource_cache, + use_dual_source_blending, + clip_scroll_tree, + data_stores, + surfaces: &surfaces, + scratch, + screen_world_rect, + globals: &self.globals, + }; - match pass.kind { - RenderPassKind::MainFramebuffer(ref color) => { - has_texture_cache_tasks |= color.must_be_drawn(); - } - RenderPassKind::OffScreen { ref texture_cache, ref color, .. } => { - has_texture_cache_tasks |= !texture_cache.is_empty(); - has_texture_cache_tasks |= color.must_be_drawn(); + pass.build( + &mut ctx, + gpu_cache, + &mut render_tasks, + &mut deferred_resolves, + &self.clip_store, + &mut transform_palette, + &mut prim_headers, + &mut z_generator, + ); + + match pass.kind { + RenderPassKind::MainFramebuffer(ref color) => { + has_texture_cache_tasks |= color.must_be_drawn(); + } + RenderPassKind::OffScreen { ref texture_cache, ref color, .. } => { + has_texture_cache_tasks |= !texture_cache.is_empty(); + has_texture_cache_tasks |= color.must_be_drawn(); + } } } } let gpu_cache_frame_id = gpu_cache.end_frame(gpu_cache_profile).frame_id(); render_tasks.write_task_data(device_pixel_scale);
--- a/gfx/wr/webrender/src/lib.rs +++ b/gfx/wr/webrender/src/lib.rs @@ -51,30 +51,35 @@ macro_rules! matches { } } #[macro_use] extern crate bitflags; #[macro_use] extern crate cfg_if; #[macro_use] +extern crate cstr; +#[macro_use] extern crate lazy_static; #[macro_use] extern crate log; #[macro_use] extern crate malloc_size_of_derive; #[cfg(any(feature = "serde"))] #[macro_use] extern crate serde; #[macro_use] extern crate thread_profiler; extern crate wr_malloc_size_of; use wr_malloc_size_of as malloc_size_of; +#[macro_use] +mod profiler; + mod batch; mod border; mod box_shadow; #[cfg(any(feature = "capture", feature = "replay"))] mod capture; mod clip; mod clip_scroll_tree; mod debug_colors; @@ -98,17 +103,16 @@ mod gpu_types; mod hit_test; mod image; mod intern; mod intern_types; mod internal_types; mod picture; mod prim_store; mod print_tree; -mod profiler; mod record; mod render_backend; mod render_task; mod renderer; mod resource_cache; mod scene; mod scene_builder; mod segment; @@ -202,16 +206,17 @@ pub extern crate webrender_api; extern crate webrender_build; #[doc(hidden)] pub use device::{build_shader_strings, ReadPixelsFormat, UploadMethod, VertexUsageHint}; pub use device::{ProgramBinary, ProgramCache, ProgramCacheObserver}; pub use device::Device; pub use frame_builder::ChasePrimitive; pub use picture::FRAMES_BEFORE_PICTURE_CACHING; +pub use profiler::{ProfilerHooks, set_profiler_hooks}; pub use renderer::{AsyncPropertySampler, CpuProfile, DebugFlags, OutputImageHandler, RendererKind}; pub use renderer::{ExternalImage, ExternalImageHandler, ExternalImageSource, GpuProfile}; pub use renderer::{GraphicsApi, GraphicsApiInfo, PipelineInfo, Renderer, RendererOptions}; pub use renderer::{RenderResults, RendererStats, SceneBuilderHooks, ThreadListener, ShaderPrecacheFlags}; pub use renderer::MAX_VERTEX_TEXTURE_WIDTH; pub use shade::{Shaders, WrShaders}; pub use webrender_api as api; pub use webrender_api::euclid;
--- a/gfx/wr/webrender/src/picture.rs +++ b/gfx/wr/webrender/src/picture.rs @@ -1641,16 +1641,18 @@ impl<'a> PictureUpdateState<'a> { surfaces: &'a mut Vec<SurfaceInfo>, pic_index: PictureIndex, picture_primitives: &mut [PicturePrimitive], frame_context: &FrameBuildingContext, gpu_cache: &mut GpuCache, clip_store: &ClipStore, clip_data_store: &ClipDataStore, ) { + profile_marker!("UpdatePictures"); + let mut state = PictureUpdateState { surfaces, surface_stack: vec![SurfaceIndex(0)], picture_stack: Vec::new(), are_raster_roots_assigned: true, }; state.update(
--- a/gfx/wr/webrender/src/profiler.rs +++ b/gfx/wr/webrender/src/profiler.rs @@ -2,29 +2,92 @@ * 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/. */ use api::{ColorF, ColorU}; use debug_render::DebugRenderer; use device::query::{GpuSampler, GpuTimer, NamedTag}; use euclid::{Point2D, Rect, Size2D, vec2}; use internal_types::FastHashMap; -use renderer::MAX_VERTEX_TEXTURE_WIDTH; +use renderer::{MAX_VERTEX_TEXTURE_WIDTH, wr_has_been_initialized}; use std::collections::vec_deque::VecDeque; use std::{f32, mem}; +use std::ffi::CStr; use time::precise_time_ns; const GRAPH_WIDTH: f32 = 1024.0; const GRAPH_HEIGHT: f32 = 320.0; const GRAPH_PADDING: f32 = 8.0; const GRAPH_FRAME_HEIGHT: f32 = 16.0; const PROFILE_PADDING: f32 = 10.0; const ONE_SECOND_NS: u64 = 1000000000; +/// Defines the interface for hooking up an external profiler to WR. +pub trait ProfilerHooks : Send + Sync { + /// Called at the beginning of a profile scope. The label must + /// be a C string (null terminated). + fn begin_marker(&self, label: &CStr); + + /// Called at the end of a profile scope. The label must + /// be a C string (null terminated). + fn end_marker(&self, label: &CStr); +} + +/// The current global profiler callbacks, if set by embedder. +static mut PROFILER_HOOKS: Option<&'static ProfilerHooks> = None; + +/// Set the profiler callbacks, or None to disable the profiler. +/// This function must only ever be called before any WR instances +/// have been created, or the hooks will not be set. +pub fn set_profiler_hooks(hooks: Option<&'static ProfilerHooks>) { + if !wr_has_been_initialized() { + unsafe { + PROFILER_HOOKS = hooks; + } + } +} + +/// A simple RAII style struct to manage a profile scope. +pub struct ProfileScope { + name: &'static CStr, +} + +impl ProfileScope { + /// Begin a new profile scope + pub fn new(name: &'static CStr) -> Self { + unsafe { + if let Some(ref hooks) = PROFILER_HOOKS { + hooks.begin_marker(name); + } + } + + ProfileScope { + name, + } + } +} + +impl Drop for ProfileScope { + fn drop(&mut self) { + unsafe { + if let Some(ref hooks) = PROFILER_HOOKS { + hooks.end_marker(self.name); + } + } + } +} + +/// A helper macro to define profile scopes. +macro_rules! profile_marker { + ($string:expr) => { + let _scope = $crate::profiler::ProfileScope::new(cstr!($string)); + }; +} + #[derive(Debug, Clone)] pub struct GpuProfileTag { pub label: &'static str, pub color: ColorF, } impl NamedTag for GpuProfileTag { fn get_label(&self) -> &str {
--- a/gfx/wr/webrender/src/render_task.rs +++ b/gfx/wr/webrender/src/render_task.rs @@ -1247,17 +1247,17 @@ impl RenderTaskCache { render_task.uv_rect_kind(), Eviction::Eager, ); // Get the allocation details in the texture cache, and store // this in the render task. The renderer will draw this // task into the appropriate layer and rect of the texture // cache on this frame. - let (texture_id, texture_layer, uv_rect) = + let (texture_id, texture_layer, uv_rect, _) = texture_cache.get_cache_location(&entry.handle); render_task.location = RenderTaskLocation::TextureCache { texture: texture_id, layer: texture_layer, rect: uv_rect.to_i32(), }; }
--- a/gfx/wr/webrender/src/renderer.rs +++ b/gfx/wr/webrender/src/renderer.rs @@ -79,16 +79,17 @@ use std::cmp; use std::collections::VecDeque; use std::collections::hash_map::Entry; use std::f32; use std::mem; use std::os::raw::c_void; use std::path::PathBuf; use std::rc::Rc; use std::sync::Arc; +use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::{channel, Receiver}; use std::thread; use std::cell::RefCell; use texture_cache::TextureCache; use thread_profiler::{register_thread_with_profiler, write_profile}; use tiling::{AlphaRenderTarget, ColorRenderTarget}; use tiling::{BlitJob, BlitJobSource, RenderPass, RenderPassKind, RenderTargetList}; use tiling::{Frame, RenderTarget, RenderTargetKind, TextureCacheRenderTarget}; @@ -101,16 +102,24 @@ cfg_if! { use serde_json; use debug_server::{self, DebugServer}; } else { use api::ApiMsg; use api::channel::MsgSender; } } +/// Is only false if no WR instances have ever been created. +static HAS_BEEN_INITIALIZED: AtomicBool = AtomicBool::new(false); + +/// Returns true if a WR instance has ever been initialized in this process. +pub fn wr_has_been_initialized() -> bool { + HAS_BEEN_INITIALIZED.load(Ordering::SeqCst) +} + pub const MAX_VERTEX_TEXTURE_WIDTH: usize = 1024; /// Enabling this toggle would force the GPU cache scattered texture to /// be resized every frame, which enables GPU debuggers to see if this /// is performed correctly. const GPU_CACHE_RESIZE_TEST: bool = false; /// Number of GPU blocks per UV rectangle provided for an image. pub const BLOCKS_PER_UV_RECT: usize = 2; @@ -1624,16 +1633,18 @@ impl Renderer { /// ``` /// [rendereroptions]: struct.RendererOptions.html pub fn new( gl: Rc<gl::Gl>, notifier: Box<RenderNotifier>, mut options: RendererOptions, shaders: Option<&mut WrShaders> ) -> Result<(Self, RenderApiSender), RendererError> { + HAS_BEEN_INITIALIZED.store(true, Ordering::SeqCst); + let (api_tx, api_rx) = channel::msg_channel()?; let (payload_tx, payload_rx) = channel::payload_channel()?; let (result_tx, result_rx) = channel(); let gl_type = gl.get_type(); let debug_server = DebugServer::new(api_tx.clone()); let mut device = Device::new(
--- a/gfx/wr/webrender/src/texture_cache.rs +++ b/gfx/wr/webrender/src/texture_cache.rs @@ -30,19 +30,19 @@ const TEXTURE_REGION_PIXELS: usize = /// Items in the texture cache can either be standalone textures, /// or a sub-rect inside the shared cache. #[derive(Debug)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] enum EntryDetails { Standalone, Cache { - // Origin within the texture layer where this item exists. + /// Origin within the texture layer where this item exists. origin: DeviceIntPoint, - // The layer index of the texture array. + /// The layer index of the texture array. layer_index: usize, }, } impl EntryDetails { /// Returns the kind associated with the details. fn kind(&self) -> EntryKind { match *self { @@ -116,22 +116,18 @@ impl CacheEntry { // Update the GPU cache for this texture cache entry. // This ensures that the UV rect, and texture layer index // are up to date in the GPU cache for vertex shaders // to fetch from. fn update_gpu_cache(&mut self, gpu_cache: &mut GpuCache) { if let Some(mut request) = gpu_cache.request(&mut self.uv_rect_handle) { let (origin, layer_index) = match self.details { - EntryDetails::Standalone { .. } => (DeviceIntPoint::zero(), 0.0), - EntryDetails::Cache { - origin, - layer_index, - .. - } => (origin, layer_index as f32), + EntryDetails::Standalone => (DeviceIntPoint::zero(), 0.0), + EntryDetails::Cache { origin, layer_index } => (origin, layer_index as f32), }; let image_source = ImageSource { p0: origin.to_f32(), p1: (origin + self.size).to_f32(), texture_layer: layer_index, user_data: self.user_data, uv_rect_kind: self.uv_rect_kind, }; @@ -290,20 +286,19 @@ struct EntryHandles { standalone: Vec<FreeListHandle<CacheEntryMarker>>, /// Handles for each shared texture cache entry. shared: Vec<FreeListHandle<CacheEntryMarker>>, } impl EntryHandles { /// Mutably borrows the requested handle list. fn select(&mut self, kind: EntryKind) -> &mut Vec<FreeListHandle<CacheEntryMarker>> { - if kind == EntryKind::Standalone { - &mut self.standalone - } else { - &mut self.shared + match kind { + EntryKind::Standalone => &mut self.standalone, + EntryKind::Shared => &mut self.shared, } } } /// Container struct for the various parameters used in cache allocation. struct CacheAllocParams { descriptor: ImageDescriptor, filter: TextureFilter, @@ -806,22 +801,18 @@ impl TextureCache { entry.eviction = eviction; // Create an update command, which the render thread processes // to upload the new image data into the correct location // in GPU memory. if let Some(data) = data { let (layer_index, origin) = match entry.details { - EntryDetails::Standalone { .. } => (0, DeviceIntPoint::zero()), - EntryDetails::Cache { - layer_index, - origin, - .. - } => (layer_index, origin), + EntryDetails::Standalone => (0, DeviceIntPoint::zero()), + EntryDetails::Cache { layer_index, origin } => (layer_index, origin), }; let op = TextureCacheUpdate::new_update( data, &descriptor, origin, entry.size, entry.texture_id, @@ -839,63 +830,52 @@ impl TextureCache { } // Retrieve the details of an item in the cache. This is used // during batch creation to provide the resource rect address // to the shaders and texture ID to the batching logic. // This function will assert in debug modes if the caller // tries to get a handle that was not requested this frame. pub fn get(&self, handle: &TextureCacheHandle) -> CacheItem { - let entry = self.entries - .get_opt(handle) - .expect("BUG: was dropped from cache or not updated!"); - debug_assert_eq!(entry.last_access, self.now); - let (layer_index, origin) = match entry.details { - EntryDetails::Standalone { .. } => { - (0, DeviceIntPoint::zero()) - } - EntryDetails::Cache { - layer_index, - origin, - .. - } => (layer_index, origin), - }; + let (texture_id, layer_index, uv_rect, uv_rect_handle) = self.get_cache_location(handle); CacheItem { - uv_rect_handle: entry.uv_rect_handle, - texture_id: TextureSource::TextureCache(entry.texture_id), - uv_rect: DeviceIntRect::new(origin, entry.size), + uv_rect_handle, + texture_id: TextureSource::TextureCache(texture_id), + uv_rect, texture_layer: layer_index as i32, } } /// A more detailed version of get(). This allows access to the actual /// device rect of the cache allocation. /// - /// Returns a tuple identifying the texture, the layer, and the region. + /// Returns a tuple identifying the texture, the layer, the region, + /// and its GPU handle. pub fn get_cache_location( &self, handle: &TextureCacheHandle, - ) -> (CacheTextureId, LayerIndex, DeviceIntRect) { + ) -> (CacheTextureId, LayerIndex, DeviceIntRect, GpuCacheHandle) { let entry = self.entries .get_opt(handle) .expect("BUG: was dropped from cache or not updated!"); debug_assert_eq!(entry.last_access, self.now); let (layer_index, origin) = match entry.details { EntryDetails::Standalone { .. } => { (0, DeviceIntPoint::zero()) } EntryDetails::Cache { layer_index, origin, .. } => (layer_index, origin), }; (entry.texture_id, layer_index as usize, - DeviceIntRect::new(origin, entry.size)) + DeviceIntRect::new(origin, entry.size), + entry.uv_rect_handle) } pub fn mark_unused(&mut self, handle: &TextureCacheHandle) { if let Some(entry) = self.entries.get_opt_mut(handle) { // Set last accessed stamp invalid to ensure it gets cleaned up // next time we expire entries. entry.last_access = FrameStamp::INVALID; entry.eviction = Eviction::Auto; @@ -975,24 +955,21 @@ impl TextureCache { self.doc_data.last_shared_cache_expiration = self.now; } self.doc_data.handles.shared.len() != old_len } // Free a cache entry from the standalone list or shared cache. fn free(&mut self, entry: CacheEntry) { match entry.details { - EntryDetails::Standalone { .. } => { + EntryDetails::Standalone => { // This is a standalone texture allocation. Free it directly. self.pending_updates.push_free(entry.texture_id); } - EntryDetails::Cache { - origin, - layer_index, - } => { + EntryDetails::Cache { origin, layer_index } => { // Free the block in the given region. let texture_array = self.shared_textures.select(entry.format, entry.filter); let region = &mut texture_array.regions[layer_index]; if self.debug_flags.contains( DebugFlags::TEXTURE_CACHE_DBG | DebugFlags::TEXTURE_CACHE_DBG_CLEAR_EVICTED) { self.pending_updates.push_debug_clear( @@ -1197,20 +1174,21 @@ impl TextureCache { // // This is managed with a database style upsert operation. match self.entries.upsert(handle, new_cache_entry) { UpsertResult::Updated(old_entry) => { if new_kind != old_entry.details.kind() { // Handle the rare case than an update moves an entry from // shared to standalone or vice versa. This involves a linear // search, but should be rare enough not to matter. - let (from, to) = if new_kind == EntryKind::Standalone { - (&mut self.doc_data.handles.shared, &mut self.doc_data.handles.standalone) - } else { - (&mut self.doc_data.handles.standalone, &mut self.doc_data.handles.shared) + let (from, to) = match new_kind { + EntryKind::Standalone => + (&mut self.doc_data.handles.shared, &mut self.doc_data.handles.standalone), + EntryKind::Shared => + (&mut self.doc_data.handles.standalone, &mut self.doc_data.handles.shared), }; let idx = from.iter().position(|h| h.weak() == *handle).unwrap(); to.push(from.remove(idx)); } self.free(old_entry); } UpsertResult::Inserted(new_handle) => { *handle = new_handle.weak();
--- a/ipc/glue/BackgroundUtils.cpp +++ b/ipc/glue/BackgroundUtils.cpp @@ -452,16 +452,19 @@ nsresult LoadInfoToLoadInfoArgs(nsILoadI } OptionalIPCServiceWorkerDescriptor ipcController = mozilla::void_t(); const Maybe<ServiceWorkerDescriptor>& controller = aLoadInfo->GetController(); if (controller.isSome()) { ipcController = controller.ref().ToIPC(); } + nsAutoString cspNonce; + Unused << NS_WARN_IF(NS_FAILED(aLoadInfo->GetCspNonce(cspNonce))); + *aOptionalLoadInfoArgs = LoadInfoArgs( loadingPrincipalInfo, triggeringPrincipalInfo, principalToInheritInfo, sandboxedLoadingPrincipalInfo, topLevelPrincipalInfo, topLevelStorageAreaPrincipalInfo, optionalResultPrincipalURI, aLoadInfo->GetSecurityFlags(), aLoadInfo->InternalContentPolicyType(), static_cast<uint32_t>(aLoadInfo->GetTainting()), aLoadInfo->GetUpgradeInsecureRequests(), aLoadInfo->GetBrowserUpgradeInsecureRequests(), @@ -479,17 +482,17 @@ nsresult LoadInfoToLoadInfoArgs(nsILoadI aLoadInfo->GetSendCSPViolationEvents(), aLoadInfo->GetOriginAttributes(), redirectChainIncludingInternalRedirects, redirectChain, ancestorPrincipals, aLoadInfo->AncestorOuterWindowIDs(), ipcClientInfo, ipcReservedClientInfo, ipcInitialClientInfo, ipcController, aLoadInfo->CorsUnsafeHeaders(), aLoadInfo->GetForcePreflight(), aLoadInfo->GetIsPreflight(), aLoadInfo->GetLoadTriggeredFromExternal(), aLoadInfo->GetServiceWorkerTaintingSynthesized(), aLoadInfo->GetDocumentHasUserInteracted(), - aLoadInfo->GetDocumentHasLoaded(), + aLoadInfo->GetDocumentHasLoaded(), cspNonce, aLoadInfo->GetIsFromProcessingFrameAttributes()); return NS_OK; } nsresult LoadInfoArgsToLoadInfo( const OptionalLoadInfoArgs& aOptionalLoadInfoArgs, nsILoadInfo** outLoadInfo) { @@ -635,17 +638,17 @@ nsresult LoadInfoArgsToLoadInfo( loadInfoArgs.isInThirdPartyContext(), loadInfoArgs.isDocshellReload(), loadInfoArgs.sendCSPViolationEvents(), loadInfoArgs.originAttributes(), redirectChainIncludingInternalRedirects, redirectChain, std::move(ancestorPrincipals), loadInfoArgs.ancestorOuterWindowIDs(), loadInfoArgs.corsUnsafeHeaders(), loadInfoArgs.forcePreflight(), loadInfoArgs.isPreflight(), loadInfoArgs.loadTriggeredFromExternal(), loadInfoArgs.serviceWorkerTaintingSynthesized(), loadInfoArgs.documentHasUserInteracted(), - loadInfoArgs.documentHasLoaded()); + loadInfoArgs.documentHasLoaded(), loadInfoArgs.cspNonce()); if (loadInfoArgs.isFromProcessingFrameAttributes()) { loadInfo->SetIsFromProcessingFrameAttributes(); } loadInfo.forget(outLoadInfo); return NS_OK; }
--- a/ipc/glue/MessageChannel.h +++ b/ipc/glue/MessageChannel.h @@ -307,16 +307,21 @@ class MessageChannel : HasResultCodes, M return mLink ? mLink->Unsound_NumQueuedMessages() : 0; } static bool IsPumpingMessages() { return sIsPumpingMessages; } static void SetIsPumpingMessages(bool aIsPumping) { sIsPumpingMessages = aIsPumping; } + /** + * Does this MessageChannel cross process boundaries? + */ + bool IsCrossProcess() const { return mIsCrossProcess; } + #ifdef OS_WIN struct MOZ_STACK_CLASS SyncStackFrame { SyncStackFrame(MessageChannel* channel, bool interrupt); ~SyncStackFrame(); bool mInterrupt; bool mSpinNestedEvents; bool mListenerNotified;
--- a/ipc/ipdl/ipdl/lower.py +++ b/ipc/ipdl/ipdl/lower.py @@ -641,27 +641,19 @@ def _deallocMethod(ptype, side): class _HybridDecl: """A hybrid decl stores both an IPDL type and all the C++ type info needed by later passes, along with a basic name for the decl.""" def __init__(self, ipdltype, name): self.ipdltype = ipdltype self.name = name - def isCopyable(self): - return not _cxxTypeNeedsMove(self.ipdltype) - def var(self): return ExprVar(self.name) - def mayMoveExpr(self): - if self.isCopyable(): - return self.var() - return ExprMove(self.var()) - def bareType(self, side, fq=False): """Return this decl's unqualified C++ type.""" return _cxxBareType(self.ipdltype, side, fq=fq) def refType(self, side): """Return this decl's C++ type as a 'reference' type, which is not necessarily a C++ reference.""" return _cxxRefType(self.ipdltype, side) @@ -1066,17 +1058,23 @@ class MessageDecl(ipdl.ast.MessageDecl): return cxxparams def makeCxxArgs(self, paramsems='in', retsems='out', retcallsems='out', implicit=True): assert not retcallsems or retsems # retcallsems => returnsems cxxargs = [] if paramsems == 'move': - cxxargs.extend([p.mayMoveExpr() for p in self.params]) + # We don't std::move() RefPtr<T> types because current Recv*() + # implementors take these parameters as T*, and + # std::move(RefPtr<T>) doesn't coerce to T*. + cxxargs.extend([ + p.var() if p.ipdltype.isCxx() and p.ipdltype.isRefcounted() else ExprMove(p.var()) + for p in self.params + ]) elif paramsems == 'in': cxxargs.extend([p.var() for p in self.params]) else: assert False for ret in self.returns: if retsems == 'in': if retcallsems == 'in':
--- a/js/xpconnect/src/XPCComponents.cpp +++ b/js/xpconnect/src/XPCComponents.cpp @@ -113,23 +113,19 @@ class nsXPCComponents_Interfaces final : public: nsXPCComponents_Interfaces(); private: virtual ~nsXPCComponents_Interfaces(); }; NS_IMETHODIMP -nsXPCComponents_Interfaces::GetInterfaces(uint32_t* aCount, nsIID*** aArray) { - *aCount = 2; - nsIID** array = static_cast<nsIID**>(moz_xmalloc(2 * sizeof(nsIID*))); - *aArray = array; - - array[0] = NS_GET_IID(nsIXPCComponents_Interfaces).Clone(); - array[1] = NS_GET_IID(nsIXPCScriptable).Clone(); +nsXPCComponents_Interfaces::GetInterfaces(nsTArray<nsIID>& aArray) { + aArray = nsTArray<nsIID>{NS_GET_IID(nsIXPCComponents_Interfaces), + NS_GET_IID(nsIXPCScriptable)}; return NS_OK; } NS_IMETHODIMP nsXPCComponents_Interfaces::GetScriptableHelper(nsIXPCScriptable** retval) { *retval = nullptr; return NS_OK; } @@ -270,23 +266,19 @@ class nsXPCComponents_Classes final : pu nsXPCComponents_Classes(); private: virtual ~nsXPCComponents_Classes(); }; /***************************************************************************/ NS_IMETHODIMP -nsXPCComponents_Classes::GetInterfaces(uint32_t* aCount, nsIID*** aArray) { - *aCount = 2; - nsIID** array = static_cast<nsIID**>(moz_xmalloc(2 * sizeof(nsIID*))); - *aArray = array; - - array[0] = NS_GET_IID(nsIXPCComponents_Classes).Clone(); - array[1] = NS_GET_IID(nsIXPCScriptable).Clone(); +nsXPCComponents_Classes::GetInterfaces(nsTArray<nsIID>& aArray) { + aArray = nsTArray<nsIID>{NS_GET_IID(nsIXPCComponents_Classes), + NS_GET_IID(nsIXPCScriptable)}; return NS_OK; } NS_IMETHODIMP nsXPCComponents_Classes::GetScriptableHelper(nsIXPCScriptable** retval) { *retval = nullptr; return NS_OK; } @@ -427,23 +419,19 @@ class nsXPCComponents_Results final : pu nsXPCComponents_Results(); private: virtual ~nsXPCComponents_Results(); }; /***************************************************************************/ NS_IMETHODIMP -nsXPCComponents_Results::GetInterfaces(uint32_t* aCount, nsIID*** aArray) { - *aCount = 2; - nsIID** array = static_cast<nsIID**>(moz_xmalloc(2 * sizeof(nsIID*))); - *aArray = array; - - array[0] = NS_GET_IID(nsIXPCComponents_Results).Clone(); - array[1] = NS_GET_IID(nsIXPCScriptable).Clone(); +nsXPCComponents_Results::GetInterfaces(nsTArray<nsIID>& aArray) { + aArray = nsTArray<nsIID>{NS_GET_IID(nsIXPCComponents_Results), + NS_GET_IID(nsIXPCScriptable)}; return NS_OK; } NS_IMETHODIMP nsXPCComponents_Results::GetScriptableHelper(nsIXPCScriptable** retval) { *retval = nullptr; return NS_OK; } @@ -572,23 +560,19 @@ class nsXPCComponents_ID final : public virtual ~nsXPCComponents_ID(); static nsresult CallOrConstruct(nsIXPConnectWrappedNative* wrapper, JSContext* cx, HandleObject obj, const CallArgs& args, bool* _retval); }; /***************************************************************************/ NS_IMETHODIMP -nsXPCComponents_ID::GetInterfaces(uint32_t* aCount, nsIID*** aArray) { - *aCount = 2; - nsIID** array = static_cast<nsIID**>(moz_xmalloc(2 * sizeof(nsIID*))); - *aArray = array; - - array[0] = NS_GET_IID(nsIXPCComponents_ID).Clone(); - array[1] = NS_GET_IID(nsIXPCScriptable).Clone(); +nsXPCComponents_ID::GetInterfaces(nsTArray<nsIID>& aArray) { + aArray = nsTArray<nsIID>{NS_GET_IID(nsIXPCComponents_ID), + NS_GET_IID(nsIXPCScriptable)}; return NS_OK; } NS_IMETHODIMP nsXPCComponents_ID::GetScriptableHelper(nsIXPCScriptable** retval) { *retval = nullptr; return NS_OK; } @@ -727,23 +711,19 @@ class nsXPCComponents_Exception final : virtual ~nsXPCComponents_Exception(); static nsresult CallOrConstruct(nsIXPConnectWrappedNative* wrapper, JSContext* cx, HandleObject obj, const CallArgs& args, bool* _retval); }; /***************************************************************************/ NS_IMETHODIMP -nsXPCComponents_Exception::GetInterfaces(uint32_t* aCount, nsIID*** aArray) { - *aCount = 2; - nsIID** array = static_cast<nsIID**>(moz_xmalloc(2 * sizeof(nsIID*))); - *aArray = array; - - array[0] = NS_GET_IID(nsIXPCComponents_Exception).Clone(); - array[1] = NS_GET_IID(nsIXPCScriptable).Clone(); +nsXPCComponents_Exception::GetInterfaces(nsTArray<nsIID>& aArray) { + aArray = nsTArray<nsIID>{NS_GET_IID(nsIXPCComponents_Exception), + NS_GET_IID(nsIXPCScriptable)}; return NS_OK; } NS_IMETHODIMP nsXPCComponents_Exception::GetScriptableHelper(nsIXPCScriptable** retval) { *retval = nullptr; return NS_OK; } @@ -1022,23 +1002,19 @@ class nsXPCComponents_Constructor final static bool InnerConstructor(JSContext* cx, unsigned argc, JS::Value* vp); static nsresult CallOrConstruct(nsIXPConnectWrappedNative* wrapper, JSContext* cx, HandleObject obj, const CallArgs& args, bool* _retval); }; /***************************************************************************/ NS_IMETHODIMP -nsXPCComponents_Constructor::GetInterfaces(uint32_t* aCount, nsIID*** aArray) { - *aCount = 2; - nsIID** array = static_cast<nsIID**>(moz_xmalloc(2 * sizeof(nsIID*))); - *aArray = array; - - array[0] = NS_GET_IID(nsIXPCComponents_Constructor).Clone(); - array[1] = NS_GET_IID(nsIXPCScriptable).Clone(); +nsXPCComponents_Constructor::GetInterfaces(nsTArray<nsIID>& aArray) { + aArray = nsTArray<nsIID>{NS_GET_IID(nsIXPCComponents_Constructor), + NS_GET_IID(nsIXPCScriptable)}; return NS_OK; } NS_IMETHODIMP nsXPCComponents_Constructor::GetScriptableHelper(nsIXPCScriptable** retval) { *retval = nullptr; return NS_OK; }
--- a/js/xpconnect/src/XPCConvert.cpp +++ b/js/xpconnect/src/XPCConvert.cpp @@ -170,26 +170,29 @@ bool XPCConvert::NativeData2JS(MutableHa d.set(*static_cast<const Value*>(s)); return JS_WrapValue(cx, d); } case nsXPTType::T_VOID: XPC_LOG_ERROR(("XPCConvert::NativeData2JS : void* params not supported")); return false; - case nsXPTType::T_IID: { + case nsXPTType::T_NSIDPTR: { nsID* iid2 = *static_cast<nsID* const*>(s); if (!iid2) { d.setNull(); return true; } return xpc::ID2JSValue(cx, *iid2, d); } + case nsXPTType::T_NSID: + return xpc::ID2JSValue(cx, *static_cast<const nsID*>(s), d); + case nsXPTType::T_ASTRING: { const nsAString* p = static_cast<const nsAString*>(s); if (!p || p->IsVoid()) { d.setNull(); return true; } nsStringBuffer* buf; @@ -537,23 +540,30 @@ bool XPCConvert::JSData2Native(JSContext case nsXPTType::T_JSVAL: *((Value*)d) = s; break; case nsXPTType::T_VOID: XPC_LOG_ERROR(("XPCConvert::JSData2Native : void* params not supported")); NS_ERROR("void* params not supported"); return false; - case nsXPTType::T_IID: + case nsXPTType::T_NSIDPTR: if (Maybe<nsID> id = xpc::JSValue2ID(cx, s)) { *((const nsID**)d) = id.ref().Clone(); return true; } return false; + case nsXPTType::T_NSID: + if (Maybe<nsID> id = xpc::JSValue2ID(cx, s)) { + *((nsID*)d) = id.ref(); + return true; + } + return false; + case nsXPTType::T_ASTRING: { nsAString* ws = (nsAString*)d; if (s.isUndefined() || s.isNull()) { ws->SetIsVoid(true); return true; } size_t length = 0; JSString* str = ToString(cx, s); @@ -1610,17 +1620,17 @@ void xpc::InnerCleanupValue(const nsXPTT ((nsAString*)aValue)->Truncate(); break; case nsXPTType::T_UTF8STRING: case nsXPTType::T_CSTRING: ((nsACString*)aValue)->Truncate(); break; // Pointer Types - case nsXPTType::T_IID: + case nsXPTType::T_NSIDPTR: case nsXPTType::T_CHAR_STR: case nsXPTType::T_WCHAR_STR: case nsXPTType::T_PSTRING_SIZE_IS: case nsXPTType::T_PWSTRING_SIZE_IS: free(*(void**)aValue); break; // Legacy Array Type @@ -1642,16 +1652,21 @@ void xpc::InnerCleanupValue(const nsXPTT for (uint32_t i = 0; i < array->Length(); ++i) { DestructValue(elty, elty.ElementPtr(array->Elements(), i)); } array->Clear(); break; } + // Clear nsID& parameters to `0` + case nsXPTType::T_NSID: + ((nsID*)aValue)->Clear(); + break; + // Clear the JS::Value to `undefined` case nsXPTType::T_JSVAL: ((JS::Value*)aValue)->setUndefined(); break; // Non-arithmetic types requiring no cleanup case nsXPTType::T_VOID: break;
--- a/js/xpconnect/src/XPCRuntimeService.cpp +++ b/js/xpconnect/src/XPCRuntimeService.cpp @@ -66,23 +66,19 @@ BackstagePass::NewEnumerate(nsIXPConnect JS::RootedObject obj(cx, objArg); *_retval = WebIDLGlobalNameHash::NewEnumerateSystemGlobal(cx, obj, properties, enumerableOnly); return *_retval ? NS_OK : NS_ERROR_FAILURE; } /***************************************************************************/ NS_IMETHODIMP -BackstagePass::GetInterfaces(uint32_t* aCount, nsIID*** aArray) { - *aCount = 2; - nsIID** array = static_cast<nsIID**>(moz_xmalloc(2 * sizeof(nsIID*))); - *aArray = array; - - array[0] = NS_GET_IID(nsIXPCScriptable).Clone(); - array[1] = NS_GET_IID(nsIScriptObjectPrincipal).Clone(); +BackstagePass::GetInterfaces(nsTArray<nsIID>& aArray) { + aArray = nsTArray<nsIID>{NS_GET_IID(nsIXPCScriptable), + NS_GET_IID(nsIScriptObjectPrincipal)}; return NS_OK; } NS_IMETHODIMP BackstagePass::GetScriptableHelper(nsIXPCScriptable** retval) { nsCOMPtr<nsIXPCScriptable> scriptable = this; scriptable.forget(retval); return NS_OK;
--- a/js/xpconnect/src/XPCVariant.cpp +++ b/js/xpconnect/src/XPCVariant.cpp @@ -226,17 +226,17 @@ bool XPCArrayHomogenizer::GetTypeForArra break; case tBool: *resultType = nsXPTType::MkArrayType(nsXPTType::Idx::BOOL); break; case tStr: *resultType = nsXPTType::MkArrayType(nsXPTType::Idx::PWSTRING); break; case tID: - *resultType = nsXPTType::MkArrayType(nsXPTType::Idx::PNSIID); + *resultType = nsXPTType::MkArrayType(nsXPTType::Idx::NSIDPTR); break; case tISup: *resultType = nsXPTType::MkArrayType(nsXPTType::Idx::INTERFACE_IS_TYPE); *resultID = NS_GET_IID(nsISupports); break; case tNull: // FALL THROUGH case tVar: @@ -461,17 +461,17 @@ bool XPCVariant::VariantDataToJS(nsIVari return XPCConvert::NativeData2JS(pJSVal, (const void*)&wc, {TD_WCHAR}, &iid, 0, pErr); } case nsIDataType::VTYPE_ID: { if (NS_FAILED(variant->GetAsID(&iid))) { return false; } nsID* v = &iid; - return XPCConvert::NativeData2JS(pJSVal, (const void*)&v, {TD_PNSIID}, + return XPCConvert::NativeData2JS(pJSVal, (const void*)&v, {TD_NSIDPTR}, &iid, 0, pErr); } case nsIDataType::VTYPE_ASTRING: { nsAutoString astring; if (NS_FAILED(variant->GetAsAString(astring))) { return false; } return XPCConvert::NativeData2JS(pJSVal, &astring, {TD_ASTRING}, &iid, 0, @@ -607,17 +607,17 @@ bool XPCVariant::VariantDataToJS(nsIVari break; case nsIDataType::VTYPE_CHAR: xptIndex = nsXPTType::Idx::CHAR; break; case nsIDataType::VTYPE_WCHAR: xptIndex = nsXPTType::Idx::WCHAR; break; case nsIDataType::VTYPE_ID: - xptIndex = nsXPTType::Idx::PNSIID; + xptIndex = nsXPTType::Idx::NSIDPTR; break; case nsIDataType::VTYPE_CHAR_STR: xptIndex = nsXPTType::Idx::PSTRING; break; case nsIDataType::VTYPE_WCHAR_STR: xptIndex = nsXPTType::Idx::PWSTRING; break; case nsIDataType::VTYPE_INTERFACE:
--- a/js/xpconnect/src/XPCWrappedJSClass.cpp +++ b/js/xpconnect/src/XPCWrappedJSClass.cpp @@ -554,25 +554,27 @@ bool nsXPCWrappedJSClass::GetInterfaceTy if (!inner.GetInterface()) { return false; } *result = inner.GetInterface()->IID(); } else if (inner.Tag() == nsXPTType::T_INTERFACE_IS) { // Get IID from a passed parameter. const nsXPTParamInfo& param = method->Param(inner.ArgNum()); - if (param.Type().Tag() != nsXPTType::T_IID) { + if (param.Type().Tag() != nsXPTType::T_NSID && + param.Type().Tag() != nsXPTType::T_NSIDPTR) { return false; } void* ptr = nativeParams[inner.ArgNum()].val.p; - // If the IID is passed indirectly (as an outparam), dereference by an - // extra level. - if (ptr && param.IsIndirect()) { + // If our IID is passed as a pointer outparameter, an extra level of + // dereferencing is required. + if (ptr && param.Type().Tag() == nsXPTType::T_NSIDPTR && + param.IsIndirect()) { ptr = *(nsID**)ptr; } if (!ptr) { return false; } *result = *(nsID*)ptr;
--- a/js/xpconnect/src/XPCWrappedNative.cpp +++ b/js/xpconnect/src/XPCWrappedNative.cpp @@ -1257,23 +1257,33 @@ bool CallMethodHelper::GetInterfaceTypeF const nsXPTType& inner = type.InnermostType(); if (inner.Tag() == nsXPTType::T_INTERFACE) { if (!inner.GetInterface()) { return Throw(NS_ERROR_XPC_CANT_GET_PARAM_IFACE_INFO, mCallContext); } *result = inner.GetInterface()->IID(); } else if (inner.Tag() == nsXPTType::T_INTERFACE_IS) { - nsID* id = (nsID*)GetDispatchParam(inner.ArgNum())->val.p; - if (!id) { + const nsXPTCVariant* param = GetDispatchParam(inner.ArgNum()); + if (param->type.Tag() != nsXPTType::T_NSID && + param->type.Tag() != nsXPTType::T_NSIDPTR) { + return Throw(NS_ERROR_UNEXPECTED, mCallContext); + } + + const void* ptr = ¶m->val; + if (param->type.Tag() == nsXPTType::T_NSIDPTR) { + ptr = *static_cast<nsID* const*>(ptr); + } + + if (!ptr) { return ThrowBadParam(NS_ERROR_XPC_CANT_GET_PARAM_IFACE_INFO, inner.ArgNum(), mCallContext); } - *result = *id; + *result = *static_cast<const nsID*>(ptr); } return true; } bool CallMethodHelper::GetOutParamSource(uint8_t paramIndex, MutableHandleValue srcp) const { const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(paramIndex); bool isRetval = ¶mInfo == mMethodInfo->GetRetval(); @@ -1494,19 +1504,19 @@ bool CallMethodHelper::ConvertIndependen if (!paramInfo.IsIn()) { return true; } // Some types usually don't support default values, but we want to handle // the default value if IsOptional is true. if (i >= mArgc) { MOZ_ASSERT(paramInfo.IsOptional(), "missing non-optional argument!"); - if (type.Tag() == nsXPTType::T_IID) { - // NOTE: 'const nsIID&' is supported, so it must be allocated. - dp->val.p = new nsIID(); + if (type.Tag() == nsXPTType::T_NSID) { + // Use a default value of the null ID for optional NSID objects. + dp->ext.nsid.Clear(); return true; } } // We're definitely some variety of 'in' now, so there's something to // convert. The source value for conversion depends on whether we're // dealing with an 'in' or an 'inout' parameter. 'inout' was handled above, // so all that's left is 'in'.
--- a/js/xpconnect/src/XPCWrappedNativeInfo.cpp +++ b/js/xpconnect/src/XPCWrappedNativeInfo.cpp @@ -508,96 +508,70 @@ already_AddRefed<XPCNativeSet> XPCNative } RefPtr<XPCNativeSet> set = map->Find(classInfo); if (set) { return set.forget(); } - nsIID** iidArray = nullptr; - uint32_t iidCount = 0; - - if (NS_FAILED(classInfo->GetInterfaces(&iidCount, &iidArray))) { + AutoTArray<nsIID, 4> iids; + if (NS_FAILED(classInfo->GetInterfaces(iids))) { // Note: I'm making it OK for this call to fail so that one can add // nsIClassInfo to classes implemented in script without requiring this // method to be implemented. // Make sure these are set correctly... - iidArray = nullptr; - iidCount = 0; + iids.Clear(); } - MOZ_ASSERT((iidCount && iidArray) || !(iidCount || iidArray), - "GetInterfaces returned bad array"); - - // !!! from here on we only exit through the 'out' label !!! - - if (iidCount) { - nsTArray<RefPtr<XPCNativeInterface>> interfaceArray(iidCount); - nsIID** currentIID = iidArray; + // Try to look up each IID's XPCNativeInterface object. + nsTArray<RefPtr<XPCNativeInterface>> interfaces(iids.Length()); + for (auto& iid : iids) { + RefPtr<XPCNativeInterface> iface = XPCNativeInterface::GetNewOrUsed(&iid); + if (iface) { + interfaces.AppendElement(iface.forget()); + } + } - for (uint32_t i = 0; i < iidCount; i++) { - nsIID* iid = *(currentIID++); - if (!iid) { - NS_ERROR("Null found in classinfo interface list"); - continue; - } - - RefPtr<XPCNativeInterface> iface = XPCNativeInterface::GetNewOrUsed(iid); - - if (!iface) { - // XXX warn here - continue; + // Build a set from the interfaces specified here. + if (interfaces.Length() > 0) { + set = NewInstance(std::move(interfaces)); + if (set) { + NativeSetMap* map2 = xpcrt->GetNativeSetMap(); + if (!map2) { + return set.forget(); } - interfaceArray.AppendElement(iface.forget()); - } - - if (interfaceArray.Length() > 0) { - set = NewInstance(std::move(interfaceArray)); - if (set) { - NativeSetMap* map2 = xpcrt->GetNativeSetMap(); - if (!map2) { - goto out; - } - - XPCNativeSetKey key(set); + XPCNativeSetKey key(set); + XPCNativeSet* set2 = map2->Add(&key, set); + if (!set2) { + NS_ERROR("failed to add our set"); + return nullptr; + } - XPCNativeSet* set2 = map2->Add(&key, set); - if (!set2) { - NS_ERROR("failed to add our set!"); - set = nullptr; - goto out; - } - // It is okay to find an existing entry here because - // we did not look for one before we called Add(). - if (set2 != set) { - set = set2; - } + // It is okay to find an existing entry here because + // we did not look for one before we called Add(). + if (set2 != set) { + set = set2; } - } else - set = GetNewOrUsed(&NS_GET_IID(nsISupports)); - } else + } + } else { set = GetNewOrUsed(&NS_GET_IID(nsISupports)); + } if (set) { #ifdef DEBUG XPCNativeSet* set2 = #endif map->Add(classInfo, set); MOZ_ASSERT(set2, "failed to add our set!"); MOZ_ASSERT(set2 == set, "hashtables inconsistent!"); } -out: - if (iidArray) { - NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(iidCount, iidArray); - } - return set.forget(); } // static void XPCNativeSet::ClearCacheEntryForClassInfo(nsIClassInfo* classInfo) { XPCJSRuntime* xpcrt = nsXPConnect::GetRuntimeInstance(); ClassInfo2NativeSetMap* map = xpcrt->GetClassInfo2NativeSetMap(); if (map) {
--- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -2891,17 +2891,17 @@ nsresult HasInstance(JSContext* cx, JS:: // Returns the principal associated with |obj|'s realm. The object must not be a // cross-compartment wrapper. nsIPrincipal* GetObjectPrincipal(JSObject* obj); // Attempt to clean up the passed in value pointer. The pointer `value` must be // a pointer to a value described by the type `nsXPTType`. // // This method expects a value of the following types: -// TD_PNSIID +// TD_NSIDPTR // value : nsID* (free) // TD_ASTRING, TD_CSTRING, TD_UTF8STRING // value : ns[C]String* (truncate) // TD_PSTRING, TD_PWSTRING, TD_PSTRING_SIZE_IS, TD_PWSTRING_SIZE_IS // value : char[16_t]** (free) // TD_INTERFACE_TYPE, TD_INTERFACE_IS_TYPE // value : nsISupports** (release) // TD_LEGACY_ARRAY (NOTE: aArrayLen should be passed)
--- a/js/xpconnect/tests/unit/component-blob.js +++ b/js/xpconnect/tests/unit/component-blob.js @@ -54,21 +54,17 @@ BlobComponent.prototype = // nsIClassInfo + information for XPCOM registration code in XPCOMUtils.jsm classDescription: "Blob in components scope code", classID: Components.ID("{06215993-a3c2-41e3-bdfd-0a3a2cc0b65c}"), contractID: "@mozilla.org/tests/component-blob;1", // nsIClassInfo flags: 0, - getInterfaces: function getInterfaces(aCount) { - var interfaces = [Ci.nsIClassInfo]; - aCount.value = interfaces.length; - return interfaces; - }, + interfaces: [Ci.nsIClassInfo], getScriptableHelper: function getScriptableHelper() { return null; }, // nsISupports QueryInterface: ChromeUtils.generateQI([Ci.nsIClassInfo]) };
--- a/js/xpconnect/tests/unit/component-file.js +++ b/js/xpconnect/tests/unit/component-file.js @@ -85,21 +85,17 @@ FileComponent.prototype = // nsIClassInfo + information for XPCOM registration code in XPCOMUtils.jsm classDescription: "File in components scope code", classID: Components.ID("{da332370-91d4-464f-a730-018e14769cab}"), contractID: "@mozilla.org/tests/component-file;1", // nsIClassInfo flags: 0, - getInterfaces: function getInterfaces(aCount) { - var interfaces = [Ci.nsIClassInfo]; - aCount.value = interfaces.length; - return interfaces; - }, + interfaces: [Ci.nsIClassInfo], getScriptableHelper: function getScriptableHelper() { return null; }, // nsISupports QueryInterface: ChromeUtils.generateQI([Ci.nsIClassInfo]) };
--- a/js/xpconnect/tests/unit/component_import.js +++ b/js/xpconnect/tests/unit/component_import.js @@ -12,27 +12,26 @@ FooComponent.prototype = // nsIClassInfo + information for XPCOM registration code in XPCOMUtils.jsm classDescription: "Foo Component", classID: Components.ID("{6b933fe6-6eba-4622-ac86-e4f654f1b474}"), contractID: "@mozilla.org/tests/module-importer;1", // nsIClassInfo flags: 0, - getInterfaces: function getInterfaces(aCount) { + get interfaces() { var interfaces = [Ci.nsIClassInfo]; - aCount.value = interfaces.length; // Guerilla test for line numbers hiding in this method var threw = true; try { thereIsNoSuchIdentifier; threw = false; } catch (ex) { - Assert.ok(ex.lineNumber == 27); + Assert.ok(ex.lineNumber == 26); } Assert.ok(threw); return interfaces; }, getScriptableHelper: function getScriptableHelper() { return null; @@ -55,21 +54,17 @@ BarComponent.prototype = // nsIClassInfo + information for XPCOM registration code in XPCOMUtils.jsm classDescription: "Module importer test 2", classID: Components.ID("{708a896a-b48d-4bff-906e-fc2fd134b296}"), contractID: "@mozilla.org/tests/module-importer;2", // nsIClassInfo flags: 0, - getInterfaces: function getInterfaces(aCount) { - var interfaces = [Ci.nsIClassInfo]; - aCount.value = interfaces.length; - return interfaces; - }, + interfaces: [Ci.nsIClassInfo], getScriptableHelper: function getScriptableHelper() { return null; }, // nsISupports QueryInterface: ChromeUtils.generateQI([Ci.nsIClassInfo]) };
--- a/js/xpconnect/tests/unit/test_import.js +++ b/js/xpconnect/tests/unit/test_import.js @@ -70,20 +70,20 @@ function run_test() { Assert.ok(foo.contractID == contractID + "1"); // XXX the following check succeeds only if the test component wasn't // already registered. Need to figure out a way to force registration // (to manually force it, delete compreg.dat before running the test) // do_check_true(foo.wrappedJSObject.postRegisterCalled); // Call getInterfaces to test line numbers in JS components. But as long as // we're doing that, why not test what it returns too? - // Kind of odd that this is not returning an array containing the - // number... Or for that matter not returning an array containing an object? - var interfaces = foo.getInterfaces({}); - Assert.equal(interfaces, Ci.nsIClassInfo.number); + var interfaces = foo.interfaces; + Assert.ok(Array.isArray(interfaces)); + Assert.equal(interfaces.length, 1); + Assert.ok(interfaces[0].equals(Ci.nsIClassInfo)) // try to create another component which doesn't directly implement QI Assert.ok((contractID + "2") in Cc); var bar = Cc[contractID + "2"] .createInstance(Ci.nsIClassInfo); Assert.ok(Boolean(bar)); Assert.ok(bar.contractID == contractID + "2"); }
--- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -375,19 +375,24 @@ bool nsHTMLScrollFrame::TryLayout(Scroll std::max(aKidMetrics->Height(), vScrollbarMinHeight); aState->mInsideBorderSize = ComputeInsideBorderSize(aState, desiredInsideBorderSize); nsSize layoutSize = mHelper.mIsUsingMinimumScaleSize ? mHelper.mMinimumScaleSize : aState->mInsideBorderSize; - nsSize scrollPortSize = + const nsSize scrollPortSize = nsSize(std::max(0, layoutSize.width - vScrollbarDesiredWidth), std::max(0, layoutSize.height - hScrollbarDesiredHeight)); + if (mHelper.mIsUsingMinimumScaleSize) { + mHelper.mICBSize = + nsSize(std::max(0, aState->mInsideBorderSize.width - vScrollbarDesiredWidth), + std::max(0, aState->mInsideBorderSize.height - hScrollbarDesiredHeight)); + } nsSize visualViewportSize = scrollPortSize; nsIPresShell* presShell = PresShell(); if (mHelper.mIsRoot && presShell->IsVisualViewportSizeSet()) { nsSize compositionSize = nsLayoutUtils::CalculateCompositionSizeForFrame(this, false); float resolution = presShell->GetResolution(); compositionSize.width /= resolution;
--- a/layout/generic/nsGfxScrollFrame.h +++ b/layout/generic/nsGfxScrollFrame.h @@ -190,16 +190,22 @@ class ScrollFrameHelper : public nsIRefl nscoord* aRangeLength); /** * @note This method might destroy the frame, pres shell and other objects. * Update scrollbar curpos attributes to reflect current scroll position */ void UpdateScrollbarPosition(); + nsSize GetLayoutSize() const { + if (mIsUsingMinimumScaleSize) { + return mICBSize; + } + return mScrollPort.Size(); + } nsRect GetScrollPortRect() const { return mScrollPort; } nsPoint GetScrollPosition() const { return mScrollPort.TopLeft() - mScrolledFrame->GetPosition(); } /** * For LTR frames, the logical scroll position is the offset of the top left * corner of the frame from the top left corner of the scroll port (same as * GetScrollPosition). @@ -558,16 +564,20 @@ class ScrollFrameHelper : public nsIRefl nsAtom* mLastSmoothScrollOrigin; Maybe<nsPoint> mApzSmoothScrollDestination; uint32_t mScrollGeneration; // NOTE: On mobile this value might be factoring into overflow:hidden region // in the case of the top level document. nsRect mScrollPort; nsSize mMinimumScaleSize; + // Stores the ICB size for the root document if this frame is using the + // minimum scale size for |mScrollPort|. + nsSize mICBSize; + // Where we're currently scrolling to, if we're scrolling asynchronously. // If we're not in the middle of an asynchronous scroll then this is // just the current scroll position. ScrollBy will choose its // destination based on this value. nsPoint mDestination; // A goal position to try to scroll to as content loads. As long as mLastPos // matches the current logical scroll position, we try to scroll to @@ -883,16 +893,19 @@ class nsHTMLScrollFrame : public nsConta return GetDesiredScrollbarSizes(&bls); } virtual nscoord GetNondisappearingScrollbarWidth( nsPresContext* aPresContext, gfxContext* aRC, mozilla::WritingMode aWM) override { nsBoxLayoutState bls(aPresContext, aRC, 0); return mHelper.GetNondisappearingScrollbarWidth(&bls, aWM); } + virtual nsSize GetLayoutSize() const override { + return mHelper.GetLayoutSize(); + } virtual nsRect GetScrolledRect() const override { return mHelper.GetScrolledRect(); } virtual nsRect GetScrollPortRect() const override { return mHelper.GetScrollPortRect(); } virtual nsPoint GetScrollPosition() const override { return mHelper.GetScrollPosition(); @@ -1357,16 +1370,19 @@ class nsXULScrollFrame final : public ns return GetDesiredScrollbarSizes(&bls); } virtual nscoord GetNondisappearingScrollbarWidth( nsPresContext* aPresContext, gfxContext* aRC, mozilla::WritingMode aWM) override { nsBoxLayoutState bls(aPresContext, aRC, 0); return mHelper.GetNondisappearingScrollbarWidth(&bls, aWM); } + virtual nsSize GetLayoutSize() const override { + return mHelper.GetLayoutSize(); + } virtual nsRect GetScrolledRect() const override { return mHelper.GetScrolledRect(); } virtual nsRect GetScrollPortRect() const override { return mHelper.GetScrollPortRect(); } virtual nsPoint GetScrollPosition() const override { return mHelper.GetScrollPosition();
--- a/layout/generic/nsIScrollableFrame.h +++ b/layout/generic/nsIScrollableFrame.h @@ -106,16 +106,25 @@ class nsIScrollableFrame : public nsIScr gfxContext* aRC) = 0; /** * Return the width for non-disappearing scrollbars. */ virtual nscoord GetNondisappearingScrollbarWidth( nsPresContext* aPresContext, gfxContext* aRC, mozilla::WritingMode aWM) = 0; /** + * Get the layout size of this frame. + * Note that this is a value which is not expanded by the minimum scale size. + * For scroll frames other than the root content document's scroll frame, this + * value will be the same as GetScrollPortRect().Size(). + * + * This value is used for Element.clientWidth and clientHeight. + */ + virtual nsSize GetLayoutSize() const = 0; + /** * GetScrolledRect is designed to encapsulate deciding which * directions of overflow should be reachable by scrolling and which * should not. Callers should NOT depend on it having any particular * behavior (although nsXULScrollFrame currently does). * * This should only be called when the scrolled frame has been * reflowed with the scroll port size given in mScrollPort. *
--- a/layout/style/Loader.cpp +++ b/layout/style/Loader.cpp @@ -853,16 +853,26 @@ nsresult Loader::CheckContentPolicy(nsIP nsContentPolicyType contentPolicyType = aIsPreload ? nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD : nsIContentPolicy::TYPE_INTERNAL_STYLESHEET; nsCOMPtr<nsILoadInfo> secCheckLoadInfo = new net::LoadInfo( aLoadingPrincipal, aTriggeringPrincipal, aRequestingNode, nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK, contentPolicyType); + // snapshot the nonce at load start time for performing CSP checks + if (contentPolicyType == nsIContentPolicy::TYPE_INTERNAL_STYLESHEET) { + nsCOMPtr<Element> element = do_QueryInterface(aRequestingNode); + if (element && element->IsHTMLElement()) { + nsAutoString cspNonce; + element->GetAttribute(NS_LITERAL_STRING("nonce"), cspNonce); + secCheckLoadInfo->SetCspNonce(cspNonce); + } + } + int16_t shouldLoad = nsIContentPolicy::ACCEPT; nsresult rv = NS_CheckContentLoadPolicy( aTargetURI, secCheckLoadInfo, NS_LITERAL_CSTRING("text/css"), &shouldLoad, nsContentUtils::GetContentPolicy()); if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) { return NS_ERROR_CONTENT_BLOCKED; } return NS_OK; @@ -1305,16 +1315,28 @@ nsresult Loader::LoadSheet(SheetLoadData } } if (NS_FAILED(rv)) { LOG_ERROR((" Failed to create channel")); SheetComplete(aLoadData, rv); return rv; } + // snapshot the nonce at load start time for performing CSP checks + if (contentPolicyType == nsIContentPolicy::TYPE_INTERNAL_STYLESHEET) { + nsCOMPtr<Element> element = + do_QueryInterface(aLoadData->mRequestingNode); + if (element && element->IsHTMLElement()) { + nsAutoString cspNonce; + element->GetAttribute(NS_LITERAL_STRING("nonce"), cspNonce); + nsCOMPtr<nsILoadInfo> loadInfo = channel->GetLoadInfo(); + loadInfo->SetCspNonce(cspNonce); + } + } + nsCOMPtr<nsIInputStream> stream; rv = channel->Open(getter_AddRefs(stream)); if (NS_FAILED(rv)) { LOG_ERROR((" Failed to open URI synchronously")); SheetComplete(aLoadData, rv); return rv; } @@ -1433,16 +1455,27 @@ nsresult Loader::LoadSheet(SheetLoadData #ifdef DEBUG mSyncCallback = false; #endif LOG_ERROR((" Failed to create channel")); SheetComplete(aLoadData, rv); return rv; } + // snapshot the nonce at load start time for performing CSP checks + if (contentPolicyType == nsIContentPolicy::TYPE_INTERNAL_STYLESHEET) { + nsCOMPtr<Element> element = do_QueryInterface(aLoadData->mRequestingNode); + if (element && element->IsHTMLElement()) { + nsAutoString cspNonce; + element->GetAttribute(NS_LITERAL_STRING("nonce"), cspNonce); + nsCOMPtr<nsILoadInfo> loadInfo = channel->GetLoadInfo(); + loadInfo->SetCspNonce(cspNonce); + } + } + if (!aLoadData->ShouldDefer()) { nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(channel)); if (cos) { cos->AddClassFlags(nsIClassOfService::Leader); } } nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
--- a/modules/libjar/nsJARURI.cpp +++ b/modules/libjar/nsJARURI.cpp @@ -131,19 +131,18 @@ nsJARURI::Write(nsIObjectOutputStream *a rv = aOutputStream->WriteStringZ(mCharsetHint.get()); return rv; } //////////////////////////////////////////////////////////////////////////////// // nsIClassInfo methods: NS_IMETHODIMP -nsJARURI::GetInterfaces(uint32_t *count, nsIID ***array) { - *count = 0; - *array = nullptr; +nsJARURI::GetInterfaces(nsTArray<nsIID> &array) { + array.Clear(); return NS_OK; } NS_IMETHODIMP nsJARURI::GetScriptableHelper(nsIXPCScriptable **_retval) { *_retval = nullptr; return NS_OK; }
--- a/netwerk/base/LoadInfo.cpp +++ b/netwerk/base/LoadInfo.cpp @@ -473,16 +473,17 @@ LoadInfo::LoadInfo(const LoadInfo& rhs) mForcePreflight(rhs.mForcePreflight), mIsPreflight(rhs.mIsPreflight), mLoadTriggeredFromExternal(rhs.mLoadTriggeredFromExternal), // mServiceWorkerTaintingSynthesized must be handled specially during // redirect mServiceWorkerTaintingSynthesized(false), mDocumentHasUserInteracted(rhs.mDocumentHasUserInteracted), mDocumentHasLoaded(rhs.mDocumentHasLoaded), + mCspNonce(rhs.mCspNonce), mIsFromProcessingFrameAttributes(rhs.mIsFromProcessingFrameAttributes) {} LoadInfo::LoadInfo( nsIPrincipal* aLoadingPrincipal, nsIPrincipal* aTriggeringPrincipal, nsIPrincipal* aPrincipalToInherit, nsIPrincipal* aSandboxedLoadingPrincipal, nsIPrincipal* aTopLevelPrincipal, nsIPrincipal* aTopLevelStorageAreaPrincipal, nsIURI* aResultPrincipalURI, const Maybe<ClientInfo>& aClientInfo, @@ -505,17 +506,17 @@ LoadInfo::LoadInfo( const OriginAttributes& aOriginAttributes, RedirectHistoryArray& aRedirectChainIncludingInternalRedirects, RedirectHistoryArray& aRedirectChain, nsTArray<nsCOMPtr<nsIPrincipal>>&& aAncestorPrincipals, const nsTArray<uint64_t>& aAncestorOuterWindowIDs, const nsTArray<nsCString>& aCorsUnsafeHeaders, bool aForcePreflight, bool aIsPreflight, bool aLoadTriggeredFromExternal, bool aServiceWorkerTaintingSynthesized, bool aDocumentHasUserInteracted, - bool aDocumentHasLoaded) + bool aDocumentHasLoaded, const nsAString& aCspNonce) : mLoadingPrincipal(aLoadingPrincipal), mTriggeringPrincipal(aTriggeringPrincipal), mPrincipalToInherit(aPrincipalToInherit), mTopLevelPrincipal(aTopLevelPrincipal), mTopLevelStorageAreaPrincipal(aTopLevelStorageAreaPrincipal), mResultPrincipalURI(aResultPrincipalURI), mClientInfo(aClientInfo), mReservedClientInfo(aReservedClientInfo), @@ -551,16 +552,17 @@ LoadInfo::LoadInfo( mAncestorOuterWindowIDs(aAncestorOuterWindowIDs), mCorsUnsafeHeaders(aCorsUnsafeHeaders), mForcePreflight(aForcePreflight), mIsPreflight(aIsPreflight), mLoadTriggeredFromExternal(aLoadTriggeredFromExternal), mServiceWorkerTaintingSynthesized(aServiceWorkerTaintingSynthesized), mDocumentHasUserInteracted(aDocumentHasUserInteracted), mDocumentHasLoaded(aDocumentHasLoaded), + mCspNonce(aCspNonce), mIsFromProcessingFrameAttributes(false) { // Only top level TYPE_DOCUMENT loads can have a null loadingPrincipal MOZ_ASSERT(mLoadingPrincipal || aContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT); MOZ_ASSERT(mTriggeringPrincipal); mRedirectChainIncludingInternalRedirects.SwapElements( aRedirectChainIncludingInternalRedirects); @@ -1250,16 +1252,30 @@ LoadInfo::GetDocumentHasLoaded(bool* aDo NS_IMETHODIMP LoadInfo::SetDocumentHasLoaded(bool aDocumentHasLoaded) { mDocumentHasLoaded = aDocumentHasLoaded; return NS_OK; } NS_IMETHODIMP +LoadInfo::GetCspNonce(nsAString& aCspNonce) { + aCspNonce = mCspNonce; + return NS_OK; +} + +NS_IMETHODIMP +LoadInfo::SetCspNonce(const nsAString& aCspNonce) { + MOZ_ASSERT(!mInitialSecurityCheckDone, + "setting the nonce is only allowed before any sec checks"); + mCspNonce = aCspNonce; + return NS_OK; +} + +NS_IMETHODIMP LoadInfo::GetIsTopLevelLoad(bool* aResult) { *aResult = mFrameOuterWindowID ? mFrameOuterWindowID == mOuterWindowID : mParentOuterWindowID == mOuterWindowID; return NS_OK; } void LoadInfo::SetIsFromProcessingFrameAttributes() { mIsFromProcessingFrameAttributes = true;
--- a/netwerk/base/LoadInfo.h +++ b/netwerk/base/LoadInfo.h @@ -7,16 +7,17 @@ #ifndef mozilla_LoadInfo_h #define mozilla_LoadInfo_h #include "nsIContentPolicy.h" #include "nsILoadInfo.h" #include "nsIPrincipal.h" #include "nsIWeakReferenceUtils.h" // for nsWeakPtr #include "nsIURI.h" +#include "nsString.h" #include "nsTArray.h" #include "mozilla/BasePrincipal.h" #include "mozilla/dom/ClientInfo.h" #include "mozilla/dom/ServiceWorkerDescriptor.h" class nsINode; class nsPIDOMWindowOuter; @@ -116,17 +117,18 @@ class LoadInfo final : public nsILoadInf const OriginAttributes& aOriginAttributes, RedirectHistoryArray& aRedirectChainIncludingInternalRedirects, RedirectHistoryArray& aRedirectChain, nsTArray<nsCOMPtr<nsIPrincipal>>&& aAncestorPrincipals, const nsTArray<uint64_t>& aAncestorOuterWindowIDs, const nsTArray<nsCString>& aUnsafeHeaders, bool aForcePreflight, bool aIsPreflight, bool aLoadTriggeredFromExternal, bool aServiceWorkerTaintingSynthesized, - bool aDocumentHasUserInteracted, bool aDocumentHasLoaded); + bool aDocumentHasUserInteracted, bool aDocumentHasLoaded, + const nsAString& aCspNonce); LoadInfo(const LoadInfo& rhs); NS_IMETHOD GetRedirects(JSContext* aCx, JS::MutableHandle<JS::Value> aRedirects, const RedirectHistoryArray& aArra); friend nsresult mozilla::ipc::LoadInfoArgsToLoadInfo( const mozilla::net::OptionalLoadInfoArgs& aLoadInfoArgs, @@ -193,16 +195,17 @@ class LoadInfo final : public nsILoadInf nsTArray<uint64_t> mAncestorOuterWindowIDs; nsTArray<nsCString> mCorsUnsafeHeaders; bool mForcePreflight; bool mIsPreflight; bool mLoadTriggeredFromExternal; bool mServiceWorkerTaintingSynthesized; bool mDocumentHasUserInteracted; bool mDocumentHasLoaded; + nsString mCspNonce; // Is true if this load was triggered by processing the attributes of the // browsing context container. // See nsILoadInfo.isFromProcessingFrameAttributes bool mIsFromProcessingFrameAttributes; }; } // namespace net
--- a/netwerk/base/nsILoadInfo.idl +++ b/netwerk/base/nsILoadInfo.idl @@ -1043,16 +1043,24 @@ interface nsILoadInfo : nsISupports /** * This attribute represents whether the document to which this * load belongs had finished loading when the load was initiated. */ [infallible] attribute boolean documentHasLoaded; /** + * A snapshot of the nonce at load start time which is used for CSP + * checks and only set for: + * * TYPE_SCRIPT and + * * TYPE_STYLESHEET + */ + attribute AString cspNonce; + + /** * The object in charged to receive CSP violation events. It can be null. * This attribute will be merged into the CSP object eventually. * See bug 1500908. */ attribute nsICSPEventListener cspEventListener; /** * This attribute will be true if this is a load triggered by
--- a/netwerk/base/nsSimpleURI.cpp +++ b/netwerk/base/nsSimpleURI.cpp @@ -602,19 +602,18 @@ nsSimpleURI::GetAsciiHost(nsACString &re return NS_OK; } //---------------------------------------------------------------------------- // nsSimpleURI::nsIClassInfo //---------------------------------------------------------------------------- NS_IMETHODIMP -nsSimpleURI::GetInterfaces(uint32_t *count, nsIID ***array) { - *count = 0; - *array = nullptr; +nsSimpleURI::GetInterfaces(nsTArray<nsIID> &array) { + array.Clear(); return NS_OK; } NS_IMETHODIMP nsSimpleURI::GetScriptableHelper(nsIXPCScriptable **_retval) { *_retval = nullptr; return NS_OK; }
--- a/netwerk/base/nsSocketTransport2.cpp +++ b/netwerk/base/nsSocketTransport2.cpp @@ -2939,18 +2939,18 @@ nsSocketTransport::GetInterface(const ns if (iid.Equals(NS_GET_IID(nsIDNSRecord))) { return mDNSRecord ? mDNSRecord->QueryInterface(iid, result) : NS_ERROR_NO_INTERFACE; } return this->QueryInterface(iid, result); } NS_IMETHODIMP -nsSocketTransport::GetInterfaces(uint32_t *count, nsIID ***array) { - return NS_CI_INTERFACE_GETTER_NAME(nsSocketTransport)(count, array); +nsSocketTransport::GetInterfaces(nsTArray<nsIID>& array) { + return NS_CI_INTERFACE_GETTER_NAME(nsSocketTransport)(array); } NS_IMETHODIMP nsSocketTransport::GetScriptableHelper(nsIXPCScriptable **_retval) { *_retval = nullptr; return NS_OK; }
--- a/netwerk/base/nsStandardURL.cpp +++ b/netwerk/base/nsStandardURL.cpp @@ -3442,19 +3442,18 @@ bool nsStandardURL::Deserialize(const UR return true; } //---------------------------------------------------------------------------- // nsStandardURL::nsIClassInfo //---------------------------------------------------------------------------- NS_IMETHODIMP -nsStandardURL::GetInterfaces(uint32_t *count, nsIID ***array) { - *count = 0; - *array = nullptr; +nsStandardURL::GetInterfaces(nsTArray<nsIID> &array) { + array.Clear(); return NS_OK; } NS_IMETHODIMP nsStandardURL::GetScriptableHelper(nsIXPCScriptable **_retval) { *_retval = nullptr; return NS_OK; }
--- a/netwerk/ipc/NeckoChannelParams.ipdlh +++ b/netwerk/ipc/NeckoChannelParams.ipdlh @@ -104,16 +104,17 @@ struct LoadInfoArgs nsCString[] corsUnsafeHeaders; bool forcePreflight; bool isPreflight; bool loadTriggeredFromExternal; bool serviceWorkerTaintingSynthesized; bool documentHasUserInteracted; bool documentHasLoaded; + nsString cspNonce; bool isFromProcessingFrameAttributes; }; /** * Not every channel necessarily has a loadInfo attached. */ union OptionalLoadInfoArgs {
--- a/security/manager/ssl/TransportSecurityInfo.cpp +++ b/security/manager/ssl/TransportSecurityInfo.cpp @@ -511,19 +511,18 @@ TransportSecurityInfo::Read(nsIObjectInp return rv; } mFailedCertChain = do_QueryInterface(failedCertChainSupports); return NS_OK; } NS_IMETHODIMP -TransportSecurityInfo::GetInterfaces(uint32_t* count, nsIID*** array) { - *count = 0; - *array = nullptr; +TransportSecurityInfo::GetInterfaces(nsTArray<nsIID>& array) { + array.Clear(); return NS_OK; } NS_IMETHODIMP TransportSecurityInfo::GetScriptableHelper(nsIXPCScriptable** _retval) { *_retval = nullptr; return NS_OK; }
--- a/security/manager/ssl/nsNSSCertificate.cpp +++ b/security/manager/ssl/nsNSSCertificate.cpp @@ -1222,19 +1222,18 @@ nsNSSCertificate::Read(nsIObjectInputStr if (!InitFromDER(const_cast<char*>(str.get()), len)) { return NS_ERROR_UNEXPECTED; } return NS_OK; } NS_IMETHODIMP -nsNSSCertificate::GetInterfaces(uint32_t* count, nsIID*** array) { - *count = 0; - *array = nullptr; +nsNSSCertificate::GetInterfaces(nsTArray<nsIID>& array) { + array.Clear(); return NS_OK; } NS_IMETHODIMP nsNSSCertificate::GetScriptableHelper(nsIXPCScriptable** _retval) { *_retval = nullptr; return NS_OK; }
--- a/storage/mozStorageAsyncStatement.cpp +++ b/storage/mozStorageAsyncStatement.cpp @@ -40,18 +40,18 @@ NS_IMPL_CI_INTERFACE_GETTER(AsyncStateme class AsyncStatementClassInfo : public nsIClassInfo { public: constexpr AsyncStatementClassInfo() {} NS_DECL_ISUPPORTS_INHERITED NS_IMETHOD - GetInterfaces(uint32_t *_count, nsIID ***_array) override { - return NS_CI_INTERFACE_GETTER_NAME(AsyncStatement)(_count, _array); + GetInterfaces(nsTArray<nsIID> &_array) override { + return NS_CI_INTERFACE_GETTER_NAME(AsyncStatement)(_array); } NS_IMETHOD GetScriptableHelper(nsIXPCScriptable **_helper) override { static AsyncStatementJSHelper sJSHelper; *_helper = &sJSHelper; return NS_OK; }
--- a/storage/mozStorageStatement.cpp +++ b/storage/mozStorageStatement.cpp @@ -42,18 +42,18 @@ NS_IMPL_CI_INTERFACE_GETTER(Statement, m class StatementClassInfo : public nsIClassInfo { public: constexpr StatementClassInfo() {} NS_DECL_ISUPPORTS_INHERITED NS_IMETHOD - GetInterfaces(uint32_t *_count, nsIID ***_array) override { - return NS_CI_INTERFACE_GETTER_NAME(Statement)(_count, _array); + GetInterfaces(nsTArray<nsIID> &_array) override { + return NS_CI_INTERFACE_GETTER_NAME(Statement)(_array); } NS_IMETHOD GetScriptableHelper(nsIXPCScriptable **_helper) override { static StatementJSHelper sJSHelper; *_helper = &sJSHelper; return NS_OK; }
--- a/taskcluster/ci/test/mochitest.yml +++ b/taskcluster/ci/test/mochitest.yml @@ -99,16 +99,17 @@ mochitest-browser-chrome: linux.*/debug: 16 linux64-asan/opt: 16 macosx64/debug: 12 default: 7 max-run-time: by-test-platform: linux64-.*cov/opt: 7200 windows10-64-ccov/debug: 7200 + windows10-aarch64/*: 7200 macosx64-ccov/debug: 10800 linux.*/debug: 5400 default: 3600 mozharness: mochitest-flavor: browser chunked: true # Bug 1281241: migrating to m3.large instances instance-size: default
--- a/taskcluster/ci/test/talos.yml +++ b/taskcluster/ci/test/talos.yml @@ -286,20 +286,22 @@ talos-xperf: description: "Talos xperf" try-name: xperf treeherder-symbol: T(x) virtualization: virtual os-groups: ['Administrators'] run-as-administrator: by-test-platform: windows7-32.*: false + windows10-aarch64.*: false windows10-64.*: true run-on-projects: by-test-platform: windows7-32(-pgo)?/.*: ['mozilla-beta', 'trunk', 'try'] + windows10-aarch64/opt: [] windows10-64.*/opt: ['mozilla-beta', 'trunk', 'try'] default: [] tier: by-test-platform: windows7-32.*: default windows10-64(-pgo)?/.*: default windows10-64-ccov/debug: 3 default: 3 # this should be disabled but might run via try syntax anyway, so explicitly downgrade to tier-3
--- a/taskcluster/ci/test/test-platforms.yml +++ b/taskcluster/ci/test/test-platforms.yml @@ -200,16 +200,28 @@ windows10-64/opt: - desktop-screenshot-capture - windows-talos - marionette-gpu-tests - windows-tests - web-platform-tests - mochitest-headless - raptor-firefox +# windows10-aarch64/opt: +# build-platform: win64-aarch64/opt +# test-sets: +# - awsy +# - desktop-screenshot-capture +# - windows-talos +# - marionette-gpu-tests +# - windows-tests +# - web-platform-tests +# - mochitest-headless +# - raptor-firefox + windows10-64-ux/opt: build-platform: win64-nightly/opt test-sets: - raptor-firefox - talos-ux windows10-64-pgo/opt: build-platform: win64-pgo/opt
--- a/taskcluster/ci/test/web-platform.yml +++ b/taskcluster/ci/test/web-platform.yml @@ -30,30 +30,32 @@ web-platform-tests: treeherder-symbol: W(wpt) chunks: by-test-platform: android.*: 36 linux.*/debug: 18 macosx64.*/opt: 8 macosx64/debug: 16 windows10.*/debug: 18 + windows10-aarch64/opt: 12 macosx64-ccov/debug: 24 default: 12 max-run-time: by-test-platform: linux64-ccov/debug: 10800 windows10-64-ccov/debug: 10800 default: 7200 e10s: by-test-platform: linux32/debug: both default: true run-on-projects: by-test-platform: android.*: ['mozilla-central', 'try'] + windows10-aarch64/opt: ['try', 'mozilla-central'] .*-qr/.*: ['release', 'try'] # skip on integration branches due to high load default: built-projects tier: by-test-platform: android.*: 2 linux64-asan/opt: 2 .*-qr/.*: 2 # can't be tier-1 if it's not running on integration branches default: default @@ -99,16 +101,17 @@ web-platform-tests-reftests: default: 6 e10s: by-test-platform: linux32/debug: both default: true run-on-projects: by-test-platform: android.*: ['mozilla-central', 'try'] + windows10-aarch64/opt: ['try', 'mozilla-central'] linux64-qr/.*: ['release', 'try'] # skip on integration branches due to high load default: built-projects tier: by-test-platform: android.*: 2 linux64-asan/opt: 2 linux64-qr/.*: 2 # can't be tier-1 if it's not running on integration branches default: default @@ -146,16 +149,17 @@ web-platform-tests-wdspec: linux64-ccov/debug: 4 windows10-64-ccov/debug: 4 default: 2 mozharness: extra-options: - --test-type=wdspec run-on-projects: by-test-platform: + windows10-aarch64/opt: ['try', 'mozilla-central'] android.*: ['mozilla-central', 'try'] .*-qr/.*: ['release', 'try'] # skip on integration branches due to high load default: built-projects tier: by-test-platform: android.*: 3 linux64-asan/opt: 2 .*-qr/.*: 2 # can't be tier-1 if it's not running on integration branches
--- a/taskcluster/ci/test/xpcshell.yml +++ b/taskcluster/ci/test/xpcshell.yml @@ -18,34 +18,37 @@ job-defaults: - remove_executables.py macosx.*: - unittests/mac_unittest.py windows.*: - unittests/win_taskcluster_unittest.py requires-signed-builds: by-test-platform: windows10-64-asan/opt: false # No XPCShell on ASAN yet + windows10-aarch64/*: false # No signing on arm64 windows.*: true default: false xpcshell: description: "xpcshell test run" suite: xpcshell treeherder-symbol: X(X) run-on-projects: by-test-platform: windows10-64-asan/opt: [] # No XPCShell on ASAN yet + windows10-aarch64/opt: ['try', 'mozilla-central'] default: built-projects chunks: by-test-platform: linux32/debug: 12 linux64/debug: 10 android-em-4.3-arm7-api-16/debug: 12 macosx.*: 1 windows.*: 1 + windows10-aarch64/opt: 8 windows10-64-ccov/debug: 8 macosx64-ccov/debug: 8 default: 8 instance-size: by-test-platform: android-em.*: xlarge default: default max-run-time:
--- a/taskcluster/taskgraph/transforms/tests.py +++ b/taskcluster/taskgraph/transforms/tests.py @@ -72,16 +72,21 @@ WINDOWS_WORKER_TYPES = { 'virtual-with-gpu': 'aws-provisioner-v1/gecko-t-win7-32-gpu', 'hardware': 'releng-hardware/gecko-t-win10-64-hw', }, 'windows10-64': { 'virtual': 'aws-provisioner-v1/gecko-t-win10-64', 'virtual-with-gpu': 'aws-provisioner-v1/gecko-t-win10-64-gpu', 'hardware': 'releng-hardware/gecko-t-win10-64-hw', }, + 'windows10-aarch64': { + 'virtual': 'test-provisioner/bitbar', + 'virtual-with-gpu': 'test-provisioner/bitbar', + 'hardware': 'test-provisioner/bitbar', + }, 'windows10-64-ccov': { 'virtual': 'aws-provisioner-v1/gecko-t-win10-64', 'virtual-with-gpu': 'aws-provisioner-v1/gecko-t-win10-64-gpu', 'hardware': 'releng-hardware/gecko-t-win10-64-hw', }, 'windows10-64-pgo': { 'virtual': 'aws-provisioner-v1/gecko-t-win10-64', 'virtual-with-gpu': 'aws-provisioner-v1/gecko-t-win10-64-gpu', @@ -578,16 +583,17 @@ def set_treeherder_machine_platform(conf translation = { # Linux64 build platforms for asan and pgo are specified differently to # treeherder. 'linux64-asan/opt': 'linux64/asan', 'linux64-pgo/opt': 'linux64/pgo', 'macosx64/debug': 'osx-10-10/debug', 'macosx64/opt': 'osx-10-10/opt', 'win64-asan/opt': 'windows10-64/asan', + 'win64-aarch64/opt': 'windows10-aarch64/opt', 'win32-pgo/opt': 'windows7-32/pgo', 'win64-pgo/opt': 'windows10-64/pgo', # The build names for Android platforms have partially evolved over the # years and need to be translated. 'android-api-16/debug': 'android-em-4-3-armv7-api16/debug', 'android-api-16-ccov/debug': 'android-em-4-3-armv7-api16-ccov/debug', 'android-api-16/opt': 'android-em-4-3-armv7-api16/opt', 'android-x86/opt': 'android-em-4-2-x86/opt', @@ -639,16 +645,17 @@ def set_tier(config, tests): 'linux64-asan/opt', 'linux64-qr/opt', 'linux64-qr/debug', 'windows7-32/debug', 'windows7-32/opt', 'windows7-32-pgo/opt', 'windows7-32-devedition/opt', 'windows7-32-nightly/opt', + 'windows10-aarch64/opt', 'windows10-64/debug', 'windows10-64/opt', 'windows10-64-pgo/opt', 'windows10-64-devedition/opt', 'windows10-64-nightly/opt', 'windows10-64-asan/opt', 'windows10-64-qr/opt', 'windows10-64-qr/debug',
--- a/taskcluster/taskgraph/util/workertypes.py +++ b/taskcluster/taskgraph/util/workertypes.py @@ -29,16 +29,17 @@ WORKER_TYPES = { 'aws-provisioner-v1/gecko-t-linux-xlarge': ('docker-worker', 'linux'), 'aws-provisioner-v1/gecko-t-win10-64': ('generic-worker', 'windows'), 'aws-provisioner-v1/gecko-t-win10-64-gpu': ('generic-worker', 'windows'), 'releng-hardware/gecko-t-win10-64-hw': ('generic-worker', 'windows'), 'releng-hardware/gecko-t-win10-64-ux': ('generic-worker', 'windows'), 'aws-provisioner-v1/gecko-t-win7-32': ('generic-worker', 'windows'), 'aws-provisioner-v1/gecko-t-win7-32-gpu': ('generic-worker', 'windows'), 'releng-hardware/gecko-t-win7-32-hw': ('generic-worker', 'windows'), + 'test-provisioner/bitbar': ('generic-worker', 'windows'), 'aws-provisioner-v1/taskcluster-generic': ('docker-worker', 'linux'), 'invalid/invalid': ('invalid', None), 'invalid/always-optimized': ('always-optimized', None), 'releng-hardware/gecko-t-linux-talos': ('generic-worker', 'linux'), 'scriptworker-prov-v1/balrog-dev': ('balrog', None), 'scriptworker-prov-v1/balrogworker-v1': ('balrog', None), 'scriptworker-prov-v1/beetmoverworker-v1': ('beetmover', None), 'scriptworker-prov-v1/pushapk-v1': ('push-apk', None),
--- a/testing/mozbase/mozrunner/mozrunner/devices/android_device.py +++ b/testing/mozbase/mozrunner/mozrunner/devices/android_device.py @@ -719,17 +719,17 @@ class AndroidEmulator(object): def _get_avd_type(self, requested): if requested in AVD_DICT.keys(): return requested if self.substs: if not self.substs['TARGET_CPU'].startswith('arm'): return 'x86-7.0' else: - return '7.0' + return '4.3' return 'x86-7.0' def _find_sdk_exe(substs, exe, tools): if tools: subdirs = ['emulator', 'tools'] else: subdirs = ['platform-tools']
--- a/testing/raptor/mach_commands.py +++ b/testing/raptor/mach_commands.py @@ -127,20 +127,16 @@ class RaptorRunner(MozbuildObject): 'base_work_dir': self.mozharness_dir, 'exes': { 'python': self.python_interp, 'virtualenv': [self.python_interp, self.virtualenv_script], }, 'title': socket.gethostname(), 'default_actions': default_actions, 'raptor_cmd_line_args': self.raptor_args, - 'python3_manifest': { - 'win32': 'python3.manifest', - 'win64': 'python3_x64.manifest', - }, 'host': self.host, 'power_test': self.power_test, 'is_release_build': self.is_release_build, } def make_args(self): self.args = { 'config': {},
--- a/testing/raptor/raptor/manifest.py +++ b/testing/raptor/raptor/manifest.py @@ -14,17 +14,17 @@ here = os.path.abspath(os.path.dirname(_ raptor_ini = os.path.join(here, 'raptor.ini') tests_dir = os.path.join(here, 'tests') LOG = get_proxy_logger(component="raptor-manifest") required_settings = ['apps', 'type', 'page_cycles', 'test_url', 'measure', 'unit', 'lower_is_better', 'alert_threshold'] playback_settings = ['playback_binary_manifest', 'playback_pageset_manifest', - 'playback_recordings', 'python3_win_manifest'] + 'playback_recordings'] def filter_app(tests, values): for test in tests: if values["app"] in test['apps']: yield test
new file mode 100644 --- /dev/null +++ b/testing/raptor/raptor/playback/mitmproxy-rel-bin-win.manifest @@ -0,0 +1,10 @@ +[ + { + "size": 13576786, + "visibility": "public", + "digest": "5d471e470369381f130de28a3c54db27c3724e1e9269c510a593d61c4cf41713c9424da62e46ae98fd1decce00ecca876209ed059487f5a02882ba16a50daed1", + "algorithm": "sha512", + "filename": "mitmdump-win-2.0.2.zip", + "unpack": true + } +] \ No newline at end of file
--- a/testing/raptor/raptor/playback/mitmproxy.py +++ b/testing/raptor/raptor/playback/mitmproxy.py @@ -7,36 +7,32 @@ from __future__ import absolute_import import os import subprocess import sys import time import mozinfo from mozlog import get_proxy_logger +from mozprocess import ProcessHandler from .base import Playback here = os.path.dirname(os.path.realpath(__file__)) LOG = get_proxy_logger(component='raptor-mitmproxy') # needed so unit tests can find their imports if os.environ.get('SCRIPTSPATH', None) is not None: # in production it is env SCRIPTS_PATH mozharness_dir = os.environ['SCRIPTSPATH'] else: # locally it's in source tree mozharness_dir = os.path.join(here, '../../../mozharness') sys.path.insert(0, mozharness_dir) -# required for using a python3 virtualenv on win for mitmproxy -from mozharness.base.python import Python3Virtualenv -from mozharness.mozilla.testing.testbase import TestingMixin -from mozharness.base.vcs.vcsbase import MercurialScript - raptor_dir = os.path.join(here, '..') sys.path.insert(0, raptor_dir) from utils import transform_platform, tooltool_download, download_file_from_url # path for mitmproxy certificate, generated auto after mitmdump is started # on local machine it is 'HOME', however it is different on production machines try: @@ -74,17 +70,17 @@ POLICIES_CONTENT_OFF = '''{ "Proxy": { "Mode": "none", "Locked": false } } }''' -class Mitmproxy(Playback, Python3Virtualenv, TestingMixin, MercurialScript): +class Mitmproxy(Playback): def __init__(self, config): self.config = config self.mitmproxy_proc = None self.mitmdump_path = None self.recordings = config.get('playback_recordings', None) self.browser_path = config.get('binary', None) @@ -101,85 +97,41 @@ class Mitmproxy(Playback, Python3Virtual # add raptor to raptor_dir self.raptor_dir = os.path.join(self.raptor_dir, "testing", "raptor") self.recordings_path = self.raptor_dir LOG.info("raptor_dir used for mitmproxy downloads and exe files: %s" % self.raptor_dir) # go ahead and download and setup mitmproxy self.download() - # on windows we must use a python3 virtualen for mitmproxy - if 'win' in self.config['platform']: - self.setup_py3_virtualenv() - # mitmproxy must be started before setup, so that the CA cert is available self.start() self.setup() def download(self): """Download and unpack mitmproxy binary and pageset using tooltool""" if not os.path.exists(self.raptor_dir): os.makedirs(self.raptor_dir) - if 'win' in self.config['platform']: - # on windows we need a python3 environment and use our own package from tooltool - self.py3_path = self.fetch_python3() - LOG.info("python3 path is: %s" % self.py3_path) - else: - # on osx and linux we use pre-built binaries - LOG.info("downloading mitmproxy binary") - _manifest = os.path.join(here, self.config['playback_binary_manifest']) - transformed_manifest = transform_platform(_manifest, self.config['platform']) - tooltool_download(transformed_manifest, self.config['run_local'], self.raptor_dir) + LOG.info("downloading mitmproxy binary") + _manifest = os.path.join(here, self.config['playback_binary_manifest']) + transformed_manifest = transform_platform(_manifest, self.config['platform']) + tooltool_download(transformed_manifest, self.config['run_local'], self.raptor_dir) # we use one pageset for all platforms LOG.info("downloading mitmproxy pageset") _manifest = os.path.join(here, self.config['playback_pageset_manifest']) transformed_manifest = transform_platform(_manifest, self.config['platform']) tooltool_download(transformed_manifest, self.config['run_local'], self.raptor_dir) return - def fetch_python3(self): - """Mitmproxy on windows needs Python 3.x""" - python3_path = os.path.join(self.raptor_dir, 'python3.6', 'python') - if not os.path.exists(os.path.dirname(python3_path)): - _manifest = os.path.join(here, self.config['python3_win_manifest']) - transformed_manifest = transform_platform(_manifest, self.config['platform'], - self.config['processor']) - LOG.info("downloading py3 package for mitmproxy windows: %s" % transformed_manifest) - tooltool_download(transformed_manifest, self.config['run_local'], self.raptor_dir) - cmd = [python3_path, '--version'] - # just want python3 ver printed in production log - subprocess.Popen(cmd, env=os.environ.copy()) - return python3_path + def start(self): + """Start playing back the mitmproxy recording.""" - def setup_py3_virtualenv(self): - """Mitmproxy on windows needs Python 3.x; set up a separate py 3.x env here""" - LOG.info("Setting up python 3.x virtualenv, required for mitmproxy on windows") - # these next two are required for py3_venv_configuration - self.abs_dirs = {'base_work_dir': mozharness_dir} - self.log_obj = None - # now create the py3 venv - venv_path = os.path.join(self.raptor_dir, 'py3venv') - self.py3_venv_configuration(python_path=self.py3_path, venv_path=venv_path) - self.py3_create_venv() - self.py3_install_modules(["cffi==1.10.0"]) - requirements = [os.path.join(here, "mitmproxy_requirements.txt")] - self.py3_install_requirement_files(requirements) - # add py3 executables path to system path - sys.path.insert(1, self.py3_path_to_executables()) - # install mitmproxy itself - self.py3_install_modules(modules=['mitmproxy']) - self.mitmdump_path = os.path.join(self.py3_path_to_executables(), 'mitmdump') - - def start(self): - """Start playing back the mitmproxy recording. If on windows, the mitmdump_path was - already set when creating py3 env""" - if self.mitmdump_path is None: - self.mitmdump_path = os.path.join(self.raptor_dir, 'mitmdump') + self.mitmdump_path = os.path.join(self.raptor_dir, 'mitmdump') recordings_list = self.recordings.split() self.mitmproxy_proc = self.start_mitmproxy_playback(self.mitmdump_path, self.recordings_path, recordings_list, self.browser_path) return @@ -223,34 +175,34 @@ class Mitmproxy(Playback, Python3Virtual env["PATH"] = os.path.dirname(browser_path) + ";" + env["PATH"] command = [mitmdump_path, '-k', '-q', '-s', param2] LOG.info("Starting mitmproxy playback using env path: %s" % env["PATH"]) LOG.info("Starting mitmproxy playback using command: %s" % ' '.join(command)) # to turn off mitmproxy log output, use these params for Popen: # Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env) - mitmproxy_proc = subprocess.Popen(command, env=env) + mitmproxy_proc = ProcessHandler(command, env=env) + mitmproxy_proc.run() + time.sleep(MITMDUMP_SLEEP) data = mitmproxy_proc.poll() if data is None: # None value indicates process hasn't terminated LOG.info("Mitmproxy playback successfully started as pid %d" % mitmproxy_proc.pid) return mitmproxy_proc # cannot continue as we won't be able to playback the pages LOG.error('Aborting: mitmproxy playback process failed to start, poll returned: %s' % data) sys.exit() def stop_mitmproxy_playback(self): """Stop the mitproxy server playback""" mitmproxy_proc = self.mitmproxy_proc LOG.info("Stopping mitmproxy playback, klling process %d" % mitmproxy_proc.pid) - if mozinfo.os == 'win': - mitmproxy_proc.kill() - else: - mitmproxy_proc.terminate() + mitmproxy_proc.kill() + time.sleep(MITMDUMP_SLEEP) status = mitmproxy_proc.poll() if status is None: # None value indicates process hasn't terminated # I *think* we can still continue, as process will be automatically # killed anyway when mozharness is done (?) if not, we won't be able # to startup mitmxproy next time if it is already running LOG.error("Failed to kill the mitmproxy playback process") LOG.info(str(status)) @@ -329,18 +281,18 @@ class MitmproxyDesktop(Mitmproxy): def is_mitmproxy_cert_installed(self): """Verify mitmxproy CA cert was added to Firefox""" try: # read autoconfig file, confirm mitmproxy cert is in there contents = self.read_policies_json(self.policies_dir) LOG.info("Firefox policies file contents:") LOG.info(contents) if (POLICIES_CONTENT_ON % { - 'cert': self.cert_path, - 'host': self.config['host']}) in contents: + 'cert': self.cert_path, + 'host': self.config['host']}) in contents: LOG.info("Verified mitmproxy CA certificate is installed in Firefox") else: return False except Exception as e: LOG.info("failed to read Firefox policies file, exeption: %s" % e) return False return True
deleted file mode 100644 --- a/testing/raptor/raptor/playback/python3.manifest +++ /dev/null @@ -1,10 +0,0 @@ -[ - { - "size": 15380470, - "visibility": "public", - "digest": "cd78b88d95b69bef99d7192b71dd34118700f44db0a0069a13bfd4943b131e8d7fdac83859f8ac15d873d4b329eef69d8d75d0a6746d06fdcfc5d06da0c9784c", - "algorithm": "sha512", - "unpack": true, - "filename": "python3.6.zip" - } -]
deleted file mode 100644 --- a/testing/raptor/raptor/playback/python3_x64.manifest +++ /dev/null @@ -1,10 +0,0 @@ -[ - { - "size": 16026760, - "visibility": "public", - "digest": "379428e3955671213a245ccd9ccf6f9d17d368db68c02da8baed7be629f2691127cd3e3f86807b25e2098d9840083fdc07946ab1bed0c14db4a5b628a47ed9ef", - "algorithm": "sha512", - "unpack": true, - "filename": "python3.6.amd64.zip" - } -]
--- a/testing/raptor/raptor/raptor.py +++ b/testing/raptor/raptor/raptor.py @@ -166,17 +166,16 @@ class Raptor(object): self.log.info("test uses playback tool: %s " % self.config['playback_tool']) self.config['playback_binary_manifest'] = test.get('playback_binary_manifest', None) _key = 'playback_binary_zip_%s' % self.config['platform'] self.config['playback_binary_zip'] = test.get(_key, None) self.config['playback_pageset_manifest'] = test.get('playback_pageset_manifest', None) _key = 'playback_pageset_zip_%s' % self.config['platform'] self.config['playback_pageset_zip'] = test.get(_key, None) self.config['playback_recordings'] = test.get('playback_recordings', None) - self.config['python3_win_manifest'] = test.get('python3_win_manifest', None) def run_test(self, test, timeout=None): self.log.info("starting raptor test: %s" % test['name']) self.log.info("test settings: %s" % str(test)) self.log.info("raptor config: %s" % str(self.config)) # benchmark-type tests require the benchmark test to be served out if test.get('type') == "benchmark":
--- a/testing/raptor/raptor/tests/raptor-tp6-1.ini +++ b/testing/raptor/raptor/tests/raptor-tp6-1.ini @@ -3,17 +3,16 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. # raptor tp6-1 [DEFAULT] type = pageload playback = mitmproxy playback_binary_manifest = mitmproxy-rel-bin-{platform}.manifest -python3_win_manifest = python3{x64}.manifest playback_pageset_manifest = mitmproxy-recordings-raptor-tp6-1.manifest page_cycles = 25 unit = ms lower_is_better = true alert_threshold = 2.0 # TTI/TTFI can take a while on some pages, and requires at least 5 seconds # beyond typical pageload time page_timeout = 30000
--- a/testing/raptor/raptor/tests/raptor-tp6-10.ini +++ b/testing/raptor/raptor/tests/raptor-tp6-10.ini @@ -3,17 +3,16 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. # raptor tp6-10 [DEFAULT] type = pageload playback = mitmproxy playback_binary_manifest = mitmproxy-rel-bin-{platform}.manifest -python3_win_manifest = python3{x64}.manifest page_cycles = 25 unit = ms lower_is_better = true alert_threshold = 2.0 # TTI/TTFI can take a while on some pages, and requires at least 5 seconds # beyond typical pageload time page_timeout = 60000 gecko_profile_interval = 1
--- a/testing/raptor/raptor/tests/raptor-tp6-2.ini +++ b/testing/raptor/raptor/tests/raptor-tp6-2.ini @@ -3,17 +3,16 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. # raptor tp6-2 [DEFAULT] type = pageload playback = mitmproxy playback_binary_manifest = mitmproxy-rel-bin-{platform}.manifest -python3_win_manifest = python3{x64}.manifest playback_pageset_manifest = mitmproxy-recordings-raptor-tp6-2.manifest page_cycles = 25 unit = ms lower_is_better = true alert_threshold = 2.0 page_timeout = 60000 gecko_profile_interval = 1 gecko_profile_entries = 14000000
--- a/testing/raptor/raptor/tests/raptor-tp6-3.ini +++ b/testing/raptor/raptor/tests/raptor-tp6-3.ini @@ -3,17 +3,16 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. # raptor tp6-3 [DEFAULT] type = pageload playback = mitmproxy playback_binary_manifest = mitmproxy-rel-bin-{platform}.manifest -python3_win_manifest = python3{x64}.manifest playback_pageset_manifest = mitmproxy-recordings-raptor-tp6-3.manifest page_cycles = 25 unit = ms lower_is_better = true alert_threshold = 2.0 # TTI/TTFI can take a while on some pages, and requires at least 5 seconds # beyond typical pageload time page_timeout = 30000
--- a/testing/raptor/raptor/tests/raptor-tp6-4.ini +++ b/testing/raptor/raptor/tests/raptor-tp6-4.ini @@ -3,17 +3,16 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. # raptor tp6-4 [DEFAULT] type = pageload playback = mitmproxy playback_binary_manifest = mitmproxy-rel-bin-{platform}.manifest -python3_win_manifest = python3{x64}.manifest playback_pageset_manifest = mitmproxy-recordings-raptor-tp6-4.manifest page_cycles = 25 unit = ms lower_is_better = true alert_threshold = 2.0 # TTI/TTFI can take a while on some pages, and requires at least 5 seconds # beyond typical pageload time page_timeout = 30000
--- a/testing/raptor/raptor/tests/raptor-tp6-5.ini +++ b/testing/raptor/raptor/tests/raptor-tp6-5.ini @@ -3,17 +3,16 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. # raptor tp6-5 [DEFAULT] type = pageload playback = mitmproxy playback_binary_manifest = mitmproxy-rel-bin-{platform}.manifest -python3_win_manifest = python3{x64}.manifest playback_pageset_manifest = mitmproxy-recordings-raptor-tp6-5.manifest page_cycles = 25 unit = ms lower_is_better = true alert_threshold = 2.0 # TTI/TTFI can take a while on some pages, and requires at least 5 seconds # beyond typical pageload time page_timeout = 30000
--- a/testing/raptor/raptor/tests/raptor-tp6-6.ini +++ b/testing/raptor/raptor/tests/raptor-tp6-6.ini @@ -3,17 +3,16 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. # raptor tp6-6 [DEFAULT] type = pageload playback = mitmproxy playback_binary_manifest = mitmproxy-rel-bin-{platform}.manifest -python3_win_manifest = python3{x64}.manifest playback_pageset_manifest = mitmproxy-recordings-raptor-tp6-6.manifest page_cycles = 25 unit = ms lower_is_better = true alert_threshold = 2.0 # TTI/TTFI can take a while on some pages, and requires at least 5 seconds # beyond typical pageload time page_timeout = 30000
--- a/testing/raptor/raptor/tests/raptor-tp6-7.ini +++ b/testing/raptor/raptor/tests/raptor-tp6-7.ini @@ -3,17 +3,16 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. # raptor tp6-7 [DEFAULT] type = pageload playback = mitmproxy playback_binary_manifest = mitmproxy-rel-bin-{platform}.manifest -python3_win_manifest = python3{x64}.manifest playback_pageset_manifest = mitmproxy-recordings-raptor-tp6-7.manifest page_cycles = 25 unit = ms lower_is_better = true alert_threshold = 2.0 # TTI/TTFI can take a while on some pages, and requires at least 5 seconds # beyond typical pageload time page_timeout = 60000
--- a/testing/raptor/raptor/tests/raptor-tp6-8.ini +++ b/testing/raptor/raptor/tests/raptor-tp6-8.ini @@ -3,17 +3,16 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. # raptor tp6-8 [DEFAULT] type = pageload playback = mitmproxy playback_binary_manifest = mitmproxy-rel-bin-{platform}.manifest -python3_win_manifest = python3{x64}.manifest page_cycles = 25 unit = ms lower_is_better = true alert_threshold = 2.0 # TTI/TTFI can take a while on some pages, and requires at least 5 seconds # beyond typical pageload time page_timeout = 30000 gecko_profile_interval = 1
--- a/testing/raptor/raptor/tests/raptor-tp6-9.ini +++ b/testing/raptor/raptor/tests/raptor-tp6-9.ini @@ -3,17 +3,16 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. # raptor tp6-9 [DEFAULT] type = pageload playback = mitmproxy playback_binary_manifest = mitmproxy-rel-bin-{platform}.manifest -python3_win_manifest = python3{x64}.manifest page_cycles = 25 unit = ms lower_is_better = true alert_threshold = 2.0 # TTI/TTFI can take a while on some pages, and requires at least 5 seconds # beyond typical pageload time page_timeout = 30000 gecko_profile_interval = 1
--- a/testing/raptor/raptor/tests/raptor-tp6m-1.ini +++ b/testing/raptor/raptor/tests/raptor-tp6m-1.ini @@ -3,17 +3,16 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. # raptor tp6m-1 [DEFAULT] type = pageload playback = mitmproxy-android playback_binary_manifest = mitmproxy-rel-bin-{platform}.manifest -python3_win_manifest = python3{x64}.manifest page_cycles = 15 unit = ms lower_is_better = true alert_threshold = 2.0 page_timeout = 60000 alert_on = fcp, loadtime [raptor-tp6m-amazon-geckoview]
--- a/testing/raptor/test/test_manifest.py +++ b/testing/raptor/test/test_manifest.py @@ -31,17 +31,16 @@ VALID_MANIFESTS = [{'apps': 'firefox', 'alert_on': 'fcp', 'unit': 'ms', 'lower_is_better': True, 'alert_threshold': 2.0, 'playback': 'mitmproxy', 'playback_binary_manifest': 'binary.manifest', 'playback_pageset_manifest': 'pageset.manifest', 'playback_recordings': 'recorded_site.mp', - 'python3_win_manifest': 'py3.manifest', 'manifest': 'valid_details_1'}, {'apps': 'chrome', 'type': 'benchmark', 'page_cycles': 5, 'test_url': 'http://www.test-url/goes/here', 'measure': 'fcp', 'unit': 'score', 'lower_is_better': False, @@ -54,17 +53,16 @@ INVALID_MANIFESTS = [{'apps': 'firefox', 'test_url': 'http://www.test-url/goes/here', 'unit': 'ms', 'lower_is_better': True, 'alert_threshold': 2.0, 'playback': 'mitmproxy', 'playback_binary_manifest': 'binary.manifest', 'playback_pageset_manifest': 'pageset.manifest', 'playback_recordings': 'recorded_site.mp', - 'python3_win_manifest': 'py3.manifest', 'manifest': 'invalid_details_1'}, {'apps': 'chrome', 'type': 'pageload', 'page_cycles': 25, 'test_url': 'http://www.test-url/goes/here', 'measure': 'fnbpaint, fcp', 'unit': 'ms', 'lower_is_better': True, @@ -79,17 +77,16 @@ INVALID_MANIFESTS = [{'apps': 'firefox', 'alert_on': 'nope', 'unit': 'ms', 'lower_is_better': True, 'alert_threshold': 2.0, 'playback': 'mitmproxy', 'playback_binary_manifest': 'binary.manifest', 'playback_pageset_manifest': 'pageset.manifest', 'playback_recordings': 'recorded_site.mp', - 'python3_win_manifest': 'py3.manifest', 'manifest': 'invalid_details_3'}] @pytest.mark.parametrize('app', ['firefox', 'chrome', 'geckoview']) def test_get_browser_test_list(app): test_list = get_browser_test_list(app) assert len(test_list) > 0
deleted file mode 100644 --- a/testing/web-platform/meta/css/css-device-adapt/documentElement-clientWidth-on-minimum-scale-size.tentative.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[documentElement-clientWidth-on-minimum-scale-size.tentative.html] - [documentElement clientWidth should be equal to device-width even if overflow:hidden region is visible] - expected: - if (os == "android"): FAIL -
--- a/toolkit/components/extensions/ExtensionParent.jsm +++ b/toolkit/components/extensions/ExtensionParent.jsm @@ -1776,18 +1776,19 @@ class CacheStore { let [store] = await this.getStore(); return new Map(store); } async delete(path) { let [store, key] = await this.getStore(path); - store.delete(key); - StartupCache.save(); + if (store.delete(key)) { + StartupCache.save(); + } } } for (let name of StartupCache.STORE_NAMES) { StartupCache[name] = new CacheStore(name); } var ExtensionParent = {
--- a/toolkit/components/extensions/ExtensionPermissions.jsm +++ b/toolkit/components/extensions/ExtensionPermissions.jsm @@ -58,27 +58,36 @@ var ExtensionPermissions = { }, async _get(extensionId) { await lazyInit(); let perms = prefs.data[extensionId]; if (!perms) { perms = emptyPermissions(); - prefs.data[extensionId] = perms; } return perms; }, async _getCached(extensionId) { return StartupCache.permissions.get(extensionId, () => this._get(extensionId)); }, + /** + * Retrieves the optional permissions for the given extension. + * The information may be retrieved from the StartupCache, and otherwise fall + * back to data from the disk (and cache the result in the StartupCache). + * + * @param {string} extensionId The extensionId + * @returns {object} An object with "permissions" and "origins" array. + * The object may be a direct reference to the storage or cache, so its + * value should immediately be used and not be modified by callers. + */ get(extensionId) { return this._getCached(extensionId); }, /** * Add new permissions for the given extension. `permissions` is * in the format that is passed to browser.permissions.request(). * @@ -149,20 +158,20 @@ var ExtensionPermissions = { this._saveSoon(extensionId); if (emitter) { emitter.emit("remove-permissions", removed); } } }, async removeAll(extensionId) { - let perms = await this._getCached(extensionId); - - if (perms.permissions.length || perms.origins.length) { - Object.assign(perms, emptyPermissions()); + await lazyInit(); + StartupCache.permissions.delete(extensionId); + if (prefs.data[extensionId]) { + delete prefs.data[extensionId]; prefs.saveSoon(); } }, // This is meant for tests only async _uninit() { if (!_initPromise) { return;
--- a/toolkit/components/extensions/test/xpcshell/test_ext_permissions_uninstall.js +++ b/toolkit/components/extensions/test/xpcshell/test_ext_permissions_uninstall.js @@ -12,16 +12,50 @@ const observer = { observe(subject, topic, data) { if (topic == "webextension-optional-permission-prompt") { let {resolve} = subject.wrappedJSObject; resolve(true); } }, }; +// Look up the cached permissions, if any. +async function getCachedPermissions(extensionId) { + const NotFound = Symbol("extension ID not found in permissions cache"); + try { + return await ExtensionParent.StartupCache.permissions.get(extensionId, () => { + // Throw error to prevent the key from being created. + throw NotFound; + }); + } catch (e) { + if (e === NotFound) { + return null; + } + throw e; + } +} + +// Look up the permissions from the file. Internal methods are used to avoid +// inadvertently changing the permissions in the cache or JSON file. +async function getStoredPermissions(extensionId) { + // The two _get calls that follow are expected to return the same object if + // the entry exists in the JSON file, otherwise we expect two different + // objects (with the same default properties). + let perms1 = await ExtensionPermissions._get(extensionId); + let perms2 = await ExtensionPermissions._get(extensionId); + if (perms1 === perms2) { + // There is an entry in the file. + return perms1; + } + // Sanity check: The returned object should be empty. + Assert.deepEqual(perms1, perms2, "Expected same permission values"); + Assert.deepEqual(perms1, {origins: [], permissions: []}, "Expected empty permissions"); + return null; +} + add_task(async function setup() { Services.prefs.setBoolPref("extensions.webextOptionalPermissionPrompts", true); Services.obs.addObserver(observer, "webextension-optional-permission-prompt"); await AddonTestUtils.promiseStartupManager(); registerCleanupFunction(async () => { await AddonTestUtils.promiseShutdownManager(); Services.obs.removeObserver(observer, "webextension-optional-permission-prompt"); Services.prefs.clearUserPref("extensions.webextOptionalPermissionPrompts"); @@ -57,14 +91,36 @@ add_task(async function test_permissions let result = await extension.awaitMessage("request.result"); equal(result, true, "request() for optional permissions succeeded"); }); let id = extension.id; let perms = await ExtensionPermissions.get(id); equal(perms.permissions.length, 1, "optional permission added"); + Assert.deepEqual(await getCachedPermissions(id), { + permissions: ["idle"], + origins: [], + }, "Optional permission added to cache"); + Assert.deepEqual(await getStoredPermissions(id), { + permissions: ["idle"], + origins: [], + }, "Optional permission added to persistent file"); + await extension.unload(); + // Directly read from the internals instead of using ExtensionPermissions.get, + // because the latter will lazily cache the extension ID. + Assert.deepEqual(await getCachedPermissions(id), null, "Cached permissions removed"); + Assert.deepEqual(await getStoredPermissions(id), null, "Stored permissions removed"); + perms = await ExtensionPermissions.get(id); equal(perms.permissions.length, 0, "no permissions after uninstall"); equal(perms.origins.length, 0, "no origin permissions after uninstall"); + + // The public ExtensionPermissions.get method should not store (empty) + // permissions in the persistent JSON file. Polluting the cache is not ideal, + // but acceptable since the cache will eventually be cleared, and non-test + // code is not likely to call ExtensionPermissions.get() for non-installed + // extensions anyway. + Assert.deepEqual(await getCachedPermissions(id), perms, "Permissions cached"); + Assert.deepEqual(await getStoredPermissions(id), null, "Permissions not saved"); });
--- a/toolkit/components/telemetry/tests/utils/TelemetryTestUtils.jsm +++ b/toolkit/components/telemetry/tests/utils/TelemetryTestUtils.jsm @@ -127,25 +127,28 @@ var TelemetryTestUtils = { * other indexes are all zero. * * @param {Object} histogram The histogram to check. * @param {Number} index The index to check against the expected value. * @param {Number} expected The expected value of the index. */ assertHistogram(histogram, index, expected) { const snapshot = histogram.snapshot(); + let found = false; for (let [i, val] of Object.entries(snapshot.values)) { if (i == index) { + found = true; Assert.equal(val, expected, `expected counts should match for the histogram index ${i}`); } else { Assert.equal(val, 0, `unexpected counts should be zero for the histogram index ${i}`); } } + Assert.ok(found, `Should have found an entry for histogram index ${index}`); }, /** * Assert that a key within a keyed histogram contains the required sum. * * @param {Object} histogram The keyed histogram to check. * @param {String} key The key to check. * @param {Number} [expected] The expected sum for the key. @@ -167,16 +170,20 @@ var TelemetryTestUtils = { * * @param {Object} histogram The keyed histogram to check. * @param {String} key The key to check. * @param {Number} index The index to check against the expected value. * @param {Number} [expected] The expected values for the key. */ assertKeyedHistogramValue(histogram, key, index, expected) { const snapshot = histogram.snapshot(); + if (!(key in snapshot)) { + Assert.ok(false, `The histogram must contain ${key}`); + return; + } for (let [i, val] of Object.entries(snapshot[key].values)) { if (i == index) { Assert.equal(val, expected, `expected counts should match for the histogram index ${i}`); } else { Assert.equal(val, 0, `unexpected counts should be zero for the histogram index ${i}`); }
--- a/toolkit/mozapps/update/tests/data/xpcshellUtilsAUS.js +++ b/toolkit/mozapps/update/tests/data/xpcshellUtilsAUS.js @@ -3557,21 +3557,17 @@ function UpdatePrompt(aCallback) { Array.prototype.slice.call(arguments)); }; }); } UpdatePrompt.prototype = { flags: Ci.nsIClassInfo.SINGLETON, getScriptableHelper: () => null, - getInterfaces(aCount) { - let interfaces = [Ci.nsISupports, Ci.nsIUpdatePrompt]; - aCount.value = interfaces.length; - return interfaces; - }, + interfaces: [Ci.nsISupports, Ci.nsIUpdatePrompt], QueryInterface: ChromeUtils.generateQI([Ci.nsIClassInfo, Ci.nsIUpdatePrompt]), }; /* Update check listener */ const updateCheckListener = { onProgress: function UCL_onProgress(aRequest, aPosition, aTotalSize) { },
--- a/xpcom/base/nsClassInfoImpl.cpp +++ b/xpcom/base/nsClassInfoImpl.cpp @@ -11,18 +11,18 @@ NS_IMETHODIMP_(MozExternalRefCountType) GenericClassInfo::AddRef() { return 2; } NS_IMETHODIMP_(MozExternalRefCountType) GenericClassInfo::Release() { return 1; } NS_IMPL_QUERY_INTERFACE(GenericClassInfo, nsIClassInfo) NS_IMETHODIMP -GenericClassInfo::GetInterfaces(uint32_t* aCount, nsIID*** aArray) { - return mData->getinterfaces(aCount, aArray); +GenericClassInfo::GetInterfaces(nsTArray<nsIID>& aArray) { + return mData->getinterfaces(aArray); } NS_IMETHODIMP GenericClassInfo::GetScriptableHelper(nsIXPCScriptable** aHelper) { if (mData->getscriptablehelper) { return mData->getscriptablehelper(aHelper); } return NS_ERROR_NOT_IMPLEMENTED;
--- a/xpcom/base/nsIClassInfoImpl.h +++ b/xpcom/base/nsIClassInfoImpl.h @@ -84,18 +84,17 @@ * as a singleton. */ class GenericClassInfo : public nsIClassInfo { public: struct ClassInfoData { // This function pointer uses NS_CALLBACK_ because it's always set to an // NS_IMETHOD function, which uses __stdcall on Win32. - typedef NS_CALLBACK_(nsresult, GetInterfacesProc)(uint32_t* aCountP, - nsIID*** aArray); + typedef NS_CALLBACK_(nsresult, GetInterfacesProc)(nsTArray<nsIID>& aArray); GetInterfacesProc getinterfaces; // This function pointer doesn't use NS_CALLBACK_ because it's always set to // a vanilla function. typedef nsresult (*GetScriptableHelperProc)(nsIXPCScriptable** aHelper); GetScriptableHelperProc getscriptablehelper; uint32_t flags; @@ -108,18 +107,19 @@ class GenericClassInfo : public nsIClass explicit GenericClassInfo(const ClassInfoData* aData) : mData(aData) {} private: const ClassInfoData* mData; }; #define NS_CLASSINFO_NAME(_class) g##_class##_classInfoGlobal #define NS_CI_INTERFACE_GETTER_NAME(_class) _class##_GetInterfacesHelper -#define NS_DECL_CI_INTERFACE_GETTER(_class) \ - extern NS_IMETHODIMP NS_CI_INTERFACE_GETTER_NAME(_class)(uint32_t*, nsIID***); +#define NS_DECL_CI_INTERFACE_GETTER(_class) \ + extern NS_IMETHODIMP NS_CI_INTERFACE_GETTER_NAME(_class)(nsTArray<nsIID> & \ + array); #define NS_IMPL_CLASSINFO(_class, _getscriptablehelper, _flags, _cid) \ NS_DECL_CI_INTERFACE_GETTER(_class) \ static const GenericClassInfo::ClassInfoData k##_class##ClassInfoData = { \ NS_CI_INTERFACE_GETTER_NAME(_class), \ _getscriptablehelper, \ _flags | nsIClassInfo::SINGLETON_CLASSINFO, \ _cid, \ @@ -130,29 +130,27 @@ class GenericClassInfo : public nsIClass #define NS_IMPL_QUERY_CLASSINFO(_class) \ if (aIID.Equals(NS_GET_IID(nsIClassInfo))) { \ if (!NS_CLASSINFO_NAME(_class)) \ NS_CLASSINFO_NAME(_class) = new (k##_class##ClassInfoDataPlace.addr()) \ GenericClassInfo(&k##_class##ClassInfoData); \ foundInterface = NS_CLASSINFO_NAME(_class); \ } else -#define NS_CLASSINFO_HELPER_BEGIN(_class, _c) \ - NS_IMETHODIMP \ - NS_CI_INTERFACE_GETTER_NAME(_class)(uint32_t * count, nsIID * **array) { \ - *count = _c; \ - *array = (nsIID**)moz_xmalloc(sizeof(nsIID*) * _c); \ - uint32_t i = 0; +#define NS_CLASSINFO_HELPER_BEGIN(_class, _c) \ + NS_IMETHODIMP \ + NS_CI_INTERFACE_GETTER_NAME(_class)(nsTArray<nsIID> & array) { \ + array.Clear(); \ + array.SetCapacity(_c); #define NS_CLASSINFO_HELPER_ENTRY(_interface) \ - (*array)[i++] = NS_GET_IID(_interface).Clone(); + array.AppendElement(NS_GET_IID(_interface)); -#define NS_CLASSINFO_HELPER_END \ - MOZ_ASSERT(i == *count, "Incorrent number of entries"); \ - return NS_OK; \ +#define NS_CLASSINFO_HELPER_END \ + return NS_OK; \ } #define NS_IMPL_CI_INTERFACE_GETTER(aClass, ...) \ static_assert(MOZ_ARG_COUNT(__VA_ARGS__) > 0, \ "Need more arguments to NS_IMPL_CI_INTERFACE_GETTER"); \ NS_CLASSINFO_HELPER_BEGIN(aClass, MOZ_ARG_COUNT(__VA_ARGS__)) \ MOZ_FOR_EACH(NS_CLASSINFO_HELPER_ENTRY, (), (__VA_ARGS__)) \ NS_CLASSINFO_HELPER_END
--- a/xpcom/base/nsISupportsImpl.h +++ b/xpcom/base/nsISupportsImpl.h @@ -1242,50 +1242,50 @@ class Runnable; nsISupports* foundInterface; /////////////////////////////////////////////////////////////////////////////// /** * Macro to generate nsIClassInfo methods for classes which do not have * corresponding nsIFactory implementations. */ -#define NS_IMPL_THREADSAFE_CI(_class) \ - NS_IMETHODIMP \ - _class::GetInterfaces(uint32_t* _count, nsIID*** _array) { \ - return NS_CI_INTERFACE_GETTER_NAME(_class)(_count, _array); \ - } \ - \ - NS_IMETHODIMP \ - _class::GetScriptableHelper(nsIXPCScriptable** _retval) { \ - *_retval = nullptr; \ - return NS_OK; \ - } \ - \ - NS_IMETHODIMP \ - _class::GetContractID(nsACString& _contractID) { \ - _contractID.SetIsVoid(true); \ - return NS_OK; \ - } \ - \ - NS_IMETHODIMP \ - _class::GetClassDescription(nsACString& _classDescription) { \ - _classDescription.SetIsVoid(true); \ - return NS_OK; \ - } \ - \ - NS_IMETHODIMP \ - _class::GetClassID(nsCID** _classID) { \ - *_classID = nullptr; \ - return NS_OK; \ - } \ - \ - NS_IMETHODIMP \ - _class::GetFlags(uint32_t* _flags) { \ - *_flags = nsIClassInfo::THREADSAFE; \ - return NS_OK; \ - } \ - \ - NS_IMETHODIMP \ - _class::GetClassIDNoAlloc(nsCID* _classIDNoAlloc) { \ - return NS_ERROR_NOT_AVAILABLE; \ +#define NS_IMPL_THREADSAFE_CI(_class) \ + NS_IMETHODIMP \ + _class::GetInterfaces(nsTArray<nsIID>& _array) { \ + return NS_CI_INTERFACE_GETTER_NAME(_class)(_array); \ + } \ + \ + NS_IMETHODIMP \ + _class::GetScriptableHelper(nsIXPCScriptable** _retval) { \ + *_retval = nullptr; \ + return NS_OK; \ + } \ + \ + NS_IMETHODIMP \ + _class::GetContractID(nsACString& _contractID) { \ + _contractID.SetIsVoid(true); \ + return NS_OK; \ + } \ + \ + NS_IMETHODIMP \ + _class::GetClassDescription(nsACString& _classDescription) { \ + _classDescription.SetIsVoid(true); \ + return NS_OK; \ + } \ + \ + NS_IMETHODIMP \ + _class::GetClassID(nsCID** _classID) { \ + *_classID = nullptr; \ + return NS_OK; \ + } \ + \ + NS_IMETHODIMP \ + _class::GetFlags(uint32_t* _flags) { \ + *_flags = nsIClassInfo::THREADSAFE; \ + return NS_OK; \ + } \ + \ + NS_IMETHODIMP \ + _class::GetClassIDNoAlloc(nsCID* _classIDNoAlloc) { \ + return NS_ERROR_NOT_AVAILABLE; \ } #endif
--- a/xpcom/components/nsIClassInfo.idl +++ b/xpcom/components/nsIClassInfo.idl @@ -13,25 +13,21 @@ interface nsIXPCScriptable; * your class to implement nsIClassInfo, see nsIClassInfoImpl.h for * instructions--you most likely do not want to inherit from nsIClassInfo. */ [scriptable, uuid(a60569d7-d401-4677-ba63-2aa5971af25d)] interface nsIClassInfo : nsISupports { /** - * Get an ordered list of the interface ids that instances of the class - * promise to implement. Note that nsISupports is an implicit member - * of any such list and need not be included. - * - * Should set *count = 0 and *array = null and return NS_OK if getting the - * list is not supported. + * Returns a list of the interfaces which instances of this class promise + * to implement. Note that nsISupports is an implicit member of any such + * list, and need not be included. */ - void getInterfaces(out uint32_t count, - [array, size_is(count), retval] out nsIIDPtr array); + readonly attribute Array<nsIIDRef> interfaces; /** * Return an object to assist XPConnect in supplying JavaScript-specific * behavior to callers of the instance object, or null if not needed. */ nsIXPCScriptable getScriptableHelper(); /**
--- a/xpcom/ds/nsIVariant.idl +++ b/xpcom/ds/nsIVariant.idl @@ -28,17 +28,17 @@ struct nsIDataType VTYPE_UINT32 = TD_UINT32 , VTYPE_UINT64 = TD_UINT64 , VTYPE_FLOAT = TD_FLOAT , VTYPE_DOUBLE = TD_DOUBLE , VTYPE_BOOL = TD_BOOL , VTYPE_CHAR = TD_CHAR , VTYPE_WCHAR = TD_WCHAR , VTYPE_VOID = TD_VOID , - VTYPE_ID = TD_PNSIID , + VTYPE_ID = TD_NSIDPTR , VTYPE_CHAR_STR = TD_PSTRING , VTYPE_WCHAR_STR = TD_PWSTRING , VTYPE_INTERFACE = TD_INTERFACE_TYPE , VTYPE_INTERFACE_IS = TD_INTERFACE_IS_TYPE, VTYPE_ARRAY = TD_LEGACY_ARRAY , VTYPE_STRING_SIZE_IS = TD_PSTRING_SIZE_IS , VTYPE_WSTRING_SIZE_IS = TD_PWSTRING_SIZE_IS , VTYPE_UTF8STRING = TD_UTF8STRING ,
--- a/xpcom/idl-parser/xpidl/jsonxpt.py +++ b/xpcom/idl-parser/xpidl/jsonxpt.py @@ -32,17 +32,17 @@ TypeMap = { 'unsigned long long': 'TD_UINT64', 'float': 'TD_FLOAT', 'double': 'TD_DOUBLE', 'char': 'TD_CHAR', 'string': 'TD_PSTRING', 'wchar': 'TD_WCHAR', 'wstring': 'TD_PWSTRING', # special types - 'nsid': 'TD_PNSIID', + 'nsid': 'TD_NSID', 'astring': 'TD_ASTRING', 'utf8string': 'TD_UTF8STRING', 'cstring': 'TD_CSTRING', 'jsval': 'TD_JSVAL', 'promise': 'TD_PROMISE', } @@ -88,17 +88,19 @@ def get_type(type, calltype, iid_is=None return { 'tag': 'TD_DOMOBJECT', 'name': type.name, 'native': type.native, 'headerFile': type.headerFile, } if isinstance(type, xpidl.Native): - if type.specialtype: + if type.specialtype == 'nsid' and type.isPtr(calltype): + return {'tag': 'TD_NSIDPTR'} + elif type.specialtype: return { 'tag': TypeMap[type.specialtype] } elif iid_is is not None: return { 'tag': 'TD_INTERFACE_IS_TYPE', 'iid_is': iid_is, }
--- a/xpcom/idl-parser/xpidl/xpidl.py +++ b/xpcom/idl-parser/xpidl/xpidl.py @@ -555,30 +555,31 @@ class Native(object): else: return self.nativename[2] + ' ' # 'in' nsid parameters should be made 'const' if self.specialtype == 'nsid' and calltype == 'in': const = True if calltype == 'element': + if self.specialtype == 'nsid': + if self.isPtr(calltype): + raise IDLError("Array<nsIDPtr> not yet supported. " + "File an XPConnect bug if you need it.", self.location) + + # ns[CI]?IDs should be held directly in Array<T>s + return self.nativename + if self.isRef(calltype): raise IDLError("[ref] qualified type unsupported in Array<T>", self.location) # Promises should be held in RefPtr<T> in Array<T>s if self.specialtype == 'promise': return 'RefPtr<mozilla::dom::Promise>' - # We don't support nsIDPtr, in Array<T> currently, although - # this or support for Array<nsID> will be needed to replace - # [array] completely. - if self.specialtype == 'nsid': - raise IDLError("Array<nsIDPtr> not yet supported. " - "File an XPConnect bug if you need it.", self.location) - if self.isRef(calltype): m = '& ' # [ref] is always passed with a single indirection else: m = '* ' if 'out' in calltype else '' if self.isPtr(calltype): m += '* ' return "%s%s %s" % (const and 'const ' or '', self.nativename, m)
--- a/xpcom/reflect/xptcall/xptcall.h +++ b/xpcom/reflect/xptcall/xptcall.h @@ -46,16 +46,17 @@ struct nsXPTCVariant { // ExtendedVal is an extension on nsXPTCMiniVariant. It contains types // unknown to the assembly implementations which must be passed by indirect // semantics. // // nsXPTCVariant contains enough space to store ExtendedVal inline, which // can be used to store these types when IsIndirect() is true. nsXPTCMiniVariant mini; + nsID nsid; nsCString nscstr; nsString nsstr; JS::Value jsval; xpt::detail::UntypedTArray array; // This type contains non-standard-layout types, so needs an explicit // Ctor/Dtor - we'll just delete them. ExtendedVal() = delete;
--- a/xpcom/reflect/xptinfo/xptcodegen.py +++ b/xpcom/reflect/xptinfo/xptcodegen.py @@ -166,17 +166,17 @@ utility_types = [ {'tag': 'TD_UINT32'}, {'tag': 'TD_INT64'}, {'tag': 'TD_UINT64'}, {'tag': 'TD_FLOAT'}, {'tag': 'TD_DOUBLE'}, {'tag': 'TD_BOOL'}, {'tag': 'TD_CHAR'}, {'tag': 'TD_WCHAR'}, - {'tag': 'TD_PNSIID'}, + {'tag': 'TD_NSIDPTR'}, {'tag': 'TD_PSTRING'}, {'tag': 'TD_PWSTRING'}, {'tag': 'TD_INTERFACE_IS_TYPE', 'iid_is': 0}, ] # Core of the code generator. Takes a list of raw JSON XPT interfaces, and # writes out a file containing the necessary static declarations into fd.
--- a/xpcom/reflect/xptinfo/xptinfo.h +++ b/xpcom/reflect/xptinfo/xptinfo.h @@ -160,17 +160,17 @@ enum nsXPTTypeTag : uint8_t { _TD_LAST_ARITHMETIC = TD_WCHAR, // Pointer Types // - Require cleanup unless NULL, // - All-zeros (NULL) bit pattern is valid, // - Outparams may be uninitialized by caller, // - Supported in xptcall as raw pointer. TD_VOID = 13, - TD_PNSIID = 14, + TD_NSIDPTR = 14, TD_PSTRING = 15, TD_PWSTRING = 16, TD_INTERFACE_TYPE = 17, TD_INTERFACE_IS_TYPE = 18, TD_LEGACY_ARRAY = 19, TD_PSTRING_SIZE_IS = 20, TD_PWSTRING_SIZE_IS = 21, TD_DOMOBJECT = 22, @@ -180,18 +180,19 @@ enum nsXPTTypeTag : uint8_t { // Complex Types // - Require cleanup, // - Always passed indirectly, // - Outparams must be initialized by caller, // - Supported in xptcall due to indirection. TD_UTF8STRING = 24, TD_CSTRING = 25, TD_ASTRING = 26, - TD_JSVAL = 27, - TD_ARRAY = 28, + TD_NSID = 27, + TD_JSVAL = 28, + TD_ARRAY = 29, _TD_LAST_COMPLEX = TD_ARRAY }; static_assert(_TD_LAST_COMPLEX < 32, "nsXPTTypeTag must fit in 5 bits"); /* * A nsXPTType is a union used to identify the type of a method argument or * return value. The internal data is stored as an 5-bit tag, and two 8-bit @@ -289,17 +290,17 @@ struct nsXPTType { UINT32, INT64, UINT64, FLOAT, DOUBLE, BOOL, CHAR, WCHAR, - PNSIID, + NSIDPTR, PSTRING, PWSTRING, INTERFACE_IS_TYPE }; // Helper methods for fabricating nsXPTType values used by xpconnect. static nsXPTType MkArrayType(Idx aInner) { MOZ_ASSERT(aInner <= Idx::INTERFACE_IS_TYPE); @@ -330,27 +331,28 @@ struct nsXPTType { TD_ALIAS_(T_U32, TD_UINT32); TD_ALIAS_(T_U64, TD_UINT64); TD_ALIAS_(T_FLOAT, TD_FLOAT); TD_ALIAS_(T_DOUBLE, TD_DOUBLE); TD_ALIAS_(T_BOOL, TD_BOOL); TD_ALIAS_(T_CHAR, TD_CHAR); TD_ALIAS_(T_WCHAR, TD_WCHAR); TD_ALIAS_(T_VOID, TD_VOID); - TD_ALIAS_(T_IID, TD_PNSIID); + TD_ALIAS_(T_NSIDPTR, TD_NSIDPTR); TD_ALIAS_(T_CHAR_STR, TD_PSTRING); TD_ALIAS_(T_WCHAR_STR, TD_PWSTRING); TD_ALIAS_(T_INTERFACE, TD_INTERFACE_TYPE); TD_ALIAS_(T_INTERFACE_IS, TD_INTERFACE_IS_TYPE); TD_ALIAS_(T_LEGACY_ARRAY, TD_LEGACY_ARRAY); TD_ALIAS_(T_PSTRING_SIZE_IS, TD_PSTRING_SIZE_IS); TD_ALIAS_(T_PWSTRING_SIZE_IS, TD_PWSTRING_SIZE_IS); TD_ALIAS_(T_UTF8STRING, TD_UTF8STRING); TD_ALIAS_(T_CSTRING, TD_CSTRING); TD_ALIAS_(T_ASTRING, TD_ASTRING); + TD_ALIAS_(T_NSID, TD_NSID); TD_ALIAS_(T_JSVAL, TD_JSVAL); TD_ALIAS_(T_DOMOBJECT, TD_DOMOBJECT); TD_ALIAS_(T_PROMISE, TD_PROMISE); TD_ALIAS_(T_ARRAY, TD_ARRAY); #undef TD_ALIAS_ //////////////////////////////////////////////////////////////// // Ensure these fields are in the same order as xptcodegen.py // @@ -630,31 +632,32 @@ inline const char* GetString(uint32_t aI MACRO(TD_FLOAT, float) \ MACRO(TD_DOUBLE, double) \ MACRO(TD_BOOL, bool) \ MACRO(TD_CHAR, char) \ MACRO(TD_WCHAR, char16_t) #define XPT_FOR_EACH_POINTER_TYPE(MACRO) \ MACRO(TD_VOID, void*) \ - MACRO(TD_PNSIID, nsID*) \ + MACRO(TD_NSIDPTR, nsID*) \ MACRO(TD_PSTRING, char*) \ MACRO(TD_PWSTRING, wchar_t*) \ MACRO(TD_INTERFACE_TYPE, nsISupports*) \ MACRO(TD_INTERFACE_IS_TYPE, nsISupports*) \ MACRO(TD_LEGACY_ARRAY, void*) \ MACRO(TD_PSTRING_SIZE_IS, char*) \ MACRO(TD_PWSTRING_SIZE_IS, wchar_t*) \ MACRO(TD_DOMOBJECT, void*) \ MACRO(TD_PROMISE, mozilla::dom::Promise*) #define XPT_FOR_EACH_COMPLEX_TYPE(MACRO) \ MACRO(TD_UTF8STRING, nsCString) \ MACRO(TD_CSTRING, nsCString) \ MACRO(TD_ASTRING, nsString) \ + MACRO(TD_NSID, nsID) \ MACRO(TD_JSVAL, JS::Value) \ MACRO(TD_ARRAY, xpt::detail::UntypedTArray) #define XPT_FOR_EACH_TYPE(MACRO) \ XPT_FOR_EACH_ARITHMETIC_TYPE(MACRO) \ XPT_FOR_EACH_POINTER_TYPE(MACRO) \ XPT_FOR_EACH_COMPLEX_TYPE(MACRO)
--- a/xpcom/threads/nsThread.cpp +++ b/xpcom/threads/nsThread.cpp @@ -135,18 +135,18 @@ class nsThreadClassInfo : public nsIClas NS_IMETHODIMP_(MozExternalRefCountType) nsThreadClassInfo::AddRef() { return 2; } NS_IMETHODIMP_(MozExternalRefCountType) nsThreadClassInfo::Release() { return 1; } NS_IMPL_QUERY_INTERFACE(nsThreadClassInfo, nsIClassInfo) NS_IMETHODIMP -nsThreadClassInfo::GetInterfaces(uint32_t* aCount, nsIID*** aArray) { - return NS_CI_INTERFACE_GETTER_NAME(nsThread)(aCount, aArray); +nsThreadClassInfo::GetInterfaces(nsTArray<nsIID>& aArray) { + return NS_CI_INTERFACE_GETTER_NAME(nsThread)(aArray); } NS_IMETHODIMP nsThreadClassInfo::GetScriptableHelper(nsIXPCScriptable** aResult) { *aResult = nullptr; return NS_OK; }