author | Kartikaya Gupta <kgupta@mozilla.com> |
Thu, 23 Feb 2017 09:51:44 -0500 | |
changeset 344482 | 2fcef8b1c4374728d95c3d72cf6e4fba76d69169 |
parent 344481 | 02a1b5cd91dbccf7ad95c2090d620501ff273f28 (current diff) |
parent 344472 | c02dd6a7e9c193b488271eb53e3ea039042c9ed6 (diff) |
child 344483 | 5069348353f8fc1121e632e3208da33900627214 |
child 345773 | 3e6f3f29feeea936f222df376b0d7f706cd33a1f |
push id | 31410 |
push user | kwierso@gmail.com |
push date | Thu, 23 Feb 2017 19:05:01 +0000 |
treeherder | mozilla-central@5069348353f8 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
milestone | 54.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/accessible/generic/Accessible.cpp +++ b/accessible/generic/Accessible.cpp @@ -2158,22 +2158,23 @@ Accessible::RemoveChild(Accessible* aChi } return true; } void Accessible::MoveChild(uint32_t aNewIndex, Accessible* aChild) { - MOZ_ASSERT(aChild, "No child was given"); - MOZ_ASSERT(aChild->mParent == this, "A child from different subtree was given"); - MOZ_ASSERT(aChild->mIndexInParent != -1, "Unbound child was given"); - MOZ_ASSERT(static_cast<uint32_t>(aChild->mIndexInParent) != aNewIndex, + MOZ_DIAGNOSTIC_ASSERT(aChild, "No child was given"); + MOZ_DIAGNOSTIC_ASSERT(aChild->mParent == this, "A child from different subtree was given"); + MOZ_DIAGNOSTIC_ASSERT(aChild->mIndexInParent != -1, "Unbound child was given"); + MOZ_DIAGNOSTIC_ASSERT(aChild->mParent->GetChildAt(aChild->mIndexInParent) == aChild, "Wrong index in parent"); + MOZ_DIAGNOSTIC_ASSERT(static_cast<uint32_t>(aChild->mIndexInParent) != aNewIndex, "No move, same index"); - MOZ_ASSERT(aNewIndex <= mChildren.Length(), "Wrong new index was given"); + MOZ_DIAGNOSTIC_ASSERT(aNewIndex <= mChildren.Length(), "Wrong new index was given"); RefPtr<AccHideEvent> hideEvent = new AccHideEvent(aChild, false); if (mDoc->Controller()->QueueMutationEvent(hideEvent)) { aChild->SetHideEventTarget(true); } mEmbeddedObjCollector = nullptr; mChildren.RemoveElementAt(aChild->mIndexInParent);
--- a/browser/app/nsBrowserApp.cpp +++ b/browser/app/nsBrowserApp.cpp @@ -212,23 +212,26 @@ static int do_main(int argc, char* argv[ // no -app flag so we use the compiled-in app data config.appData = &sAppData; config.appDataPath = kDesktopFolder; } #if defined(XP_WIN) && defined(MOZ_SANDBOX) sandbox::BrokerServices* brokerServices = sandboxing::GetInitializedBrokerServices(); + sandboxing::PermissionsService* permissionsService = + sandboxing::GetPermissionsService(); #if defined(MOZ_CONTENT_SANDBOX) if (!brokerServices) { Output("Couldn't initialize the broker services.\n"); return 255; } #endif config.sandboxBrokerServices = brokerServices; + config.sandboxPermissionsService = permissionsService; #endif #ifdef LIBFUZZER if (getenv("LIBFUZZER")) gBootstrap->XRE_LibFuzzerSetDriver(fuzzer::FuzzerDriver); #endif return gBootstrap->XRE_main(argc, argv, config);
--- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -455,48 +455,49 @@ const gStoragePressureObserver = { const BYTES_IN_GIGABYTE = 1073741824; const USAGE_THRESHOLD_BYTES = BYTES_IN_GIGABYTE * Services.prefs.getIntPref("browser.storageManager.pressureNotification.usageThresholdGB"); let msg = ""; let buttons = []; let usage = parseInt(data); let prefStrBundle = document.getElementById("bundle_preferences"); + let brandShortName = document.getElementById("bundle_brand").getString("brandShortName"); let notificationBox = document.getElementById("high-priority-global-notificationbox"); buttons.push({ label: prefStrBundle.getString("spaceAlert.learnMoreButton.label"), accessKey: prefStrBundle.getString("spaceAlert.learnMoreButton.accesskey"), callback(notificationBar, button) { let learnMoreURL = Services.urlFormatter.formatURLPref("app.support.baseURL") + "storage-permissions"; gBrowser.selectedTab = gBrowser.addTab(learnMoreURL); } }); if (usage < USAGE_THRESHOLD_BYTES) { // The firefox-used space < 5GB, then warn user to free some disk space. // This is because this usage is small and not the main cause for space issue. // In order to avoid the bad and wrong impression among users that // firefox eats disk space a lot, indicate users to clean up other disk space. - msg = prefStrBundle.getString("spaceAlert.under5GB.description"); + msg = prefStrBundle.getFormattedString("spaceAlert.under5GB.message", [brandShortName]); buttons.push({ label: prefStrBundle.getString("spaceAlert.under5GB.okButton.label"), accessKey: prefStrBundle.getString("spaceAlert.under5GB.okButton.accesskey"), callback() {} }); } else { // The firefox-used space >= 5GB, then guide users to about:preferences // to clear some data stored on firefox by websites. - let descriptionStringID = "spaceAlert.over5GB.description"; + let descriptionStringID = "spaceAlert.over5GB.message"; let prefButtonLabelStringID = "spaceAlert.over5GB.prefButton.label"; let prefButtonAccesskeyStringID = "spaceAlert.over5GB.prefButton.accesskey"; if (AppConstants.platform == "win") { - descriptionStringID = "spaceAlert.over5GB.descriptionWin"; + descriptionStringID = "spaceAlert.over5GB.messageWin"; prefButtonLabelStringID = "spaceAlert.over5GB.prefButtonWin.label"; prefButtonAccesskeyStringID = "spaceAlert.over5GB.prefButtonWin.accesskey"; } - msg = prefStrBundle.getString(descriptionStringID); + msg = prefStrBundle.getFormattedString(descriptionStringID, [brandShortName]); buttons.push({ label: prefStrBundle.getString(prefButtonLabelStringID), accessKey: prefStrBundle.getString(prefButtonAccesskeyStringID), callback(notificationBar, button) { gBrowser.ownerGlobal.openPreferences("advanced", { advancedTab: "networkTab" }); } }); } @@ -3430,28 +3431,28 @@ var PrintPreviewListener = { _tabBeforePrintPreview: null, _simplifyPageTab: null, getPrintPreviewBrowser() { if (!this._printPreviewTab) { let browser = gBrowser.selectedBrowser; let preferredRemoteType = browser.remoteType; this._tabBeforePrintPreview = gBrowser.selectedTab; - this._printPreviewTab = gBrowser.loadOneTab("about:blank", { + this._printPreviewTab = gBrowser.loadOneTab("about:printpreview", { inBackground: false, preferredRemoteType, sameProcessAsFrameLoader: browser.frameLoader }); gBrowser.selectedTab = this._printPreviewTab; } return gBrowser.getBrowserForTab(this._printPreviewTab); }, createSimplifiedBrowser() { let browser = this._tabBeforePrintPreview.linkedBrowser; - this._simplifyPageTab = gBrowser.loadOneTab("about:blank", { + this._simplifyPageTab = gBrowser.loadOneTab("about:printpreview", { inBackground: true, sameProcessAsFrameLoader: browser.frameLoader }); return this.getSimplifiedSourceBrowser(); }, getSourceBrowser() { return this._tabBeforePrintPreview ? this._tabBeforePrintPreview.linkedBrowser : gBrowser.selectedBrowser;
--- a/browser/base/content/test/general/browser_audioTabIcon.js +++ b/browser/base/content/test/general/browser_audioTabIcon.js @@ -12,29 +12,42 @@ function* wait_for_tab_playing_event(tab is(tab.hasAttribute("soundplaying"), expectPlaying, "The tab should " + (expectPlaying ? "" : "not ") + "be playing"); is(tab.soundPlaying, expectPlaying, "The tab should " + (expectPlaying ? "" : "not ") + "be playing"); return true; } return false; }); } +function* is_audio_playing(tab) { + let browser = tab.linkedBrowser; + let isPlaying = yield ContentTask.spawn(browser, {}, function* () { + let audio = content.document.querySelector("audio"); + return !audio.paused; + }); + return isPlaying; +} + function* play(tab) { let browser = tab.linkedBrowser; yield ContentTask.spawn(browser, {}, function* () { let audio = content.document.querySelector("audio"); audio.play(); }); + // If the tab has already be muted, it means the tab won't get soundplaying, + // so we don't need to check this attribute. + if (browser.audioMuted) { + return; + } + yield wait_for_tab_playing_event(tab, true); } function* pause(tab, options) { - ok(tab.hasAttribute("soundplaying"), "The tab should have the soundplaying attribute when pause() is called"); - let extendedDelay = options && options.extendedDelay; if (extendedDelay) { // Use 10s to remove possibility of race condition with attr removal. Services.prefs.setIntPref(TABATTR_REMOVAL_PREFNAME, 10000); } try { let browser = tab.linkedBrowser; @@ -42,16 +55,22 @@ function* pause(tab, options) { BrowserTestUtils.waitForEvent(browser, "DOMAudioPlaybackStopped", "DOMAudioPlaybackStopped event should get fired after pause"); let awaitTabPausedAttrModified = wait_for_tab_playing_event(tab, false); yield ContentTask.spawn(browser, {}, function* () { let audio = content.document.querySelector("audio"); audio.pause(); }); + // If the tab has already be muted, it means the tab won't have soundplaying, + // so we don't need to check this attribute. + if (browser.audioMuted) { + return; + } + if (extendedDelay) { ok(tab.hasAttribute("soundplaying"), "The tab should still have the soundplaying attribute immediately after pausing"); yield awaitDOMAudioPlaybackStopped; ok(tab.hasAttribute("soundplaying"), "The tab should still have the soundplaying attribute immediately after DOMAudioPlaybackStopped"); } yield awaitTabPausedAttrModified; @@ -136,16 +155,23 @@ function* test_mute_tab(tab, icon, expec let tooltip = document.getElementById("tabbrowser-tab-tooltip"); yield hover_icon(icon, tooltip); EventUtils.synthesizeMouseAtCenter(icon, {button: 0}); leave_icon(icon); is(gBrowser.selectedTab, activeTab, "Clicking on mute should not change the currently selected tab"); + // If the audio is playing, we should check whether clicking on icon affects + // the media element's playing state. + let isAudioPlaying = yield is_audio_playing(tab); + if (isAudioPlaying) { + yield wait_for_tab_playing_event(tab, !expectMuted); + } + return mutedPromise; } function get_tab_state(tab) { const ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore); return JSON.parse(ss.getTabState(tab)); } @@ -164,17 +190,17 @@ function* test_muting_using_menu(tab, ex is(toggleMute.accessKey, expectedAccessKey, "Correct accessKey expected"); is(toggleMute.hasAttribute("muted"), expectMuted, "Should have the correct state for the muted attribute"); ok(!toggleMute.hasAttribute("soundplaying"), "Should not have the soundplaying attribute"); yield play(tab); is(toggleMute.hasAttribute("muted"), expectMuted, "Should have the correct state for the muted attribute"); - ok(toggleMute.hasAttribute("soundplaying"), "Should have the soundplaying attribute"); + is(!toggleMute.hasAttribute("soundplaying"), expectMuted, "The value of soundplaying attribute is incorrect"); yield pause(tab); is(toggleMute.hasAttribute("muted"), expectMuted, "Should have the correct state for the muted attribute"); ok(!toggleMute.hasAttribute("soundplaying"), "Should not have the soundplaying attribute"); // Click on the menu and wait for the tab to be muted. let mutedPromise = get_wait_for_mute_promise(tab, !expectMuted); @@ -229,32 +255,32 @@ function* test_playing_icon_on_tab(tab, // Make sure it's possible to mute using the context menu. yield test_muting_using_menu(tab, false); // Make sure it's possible to unmute using the context menu. yield test_muting_using_menu(tab, true); } function* test_swapped_browser_while_playing(oldTab, newBrowser) { + // The tab was muted so it won't have soundplaying attribute even it's playing. ok(oldTab.hasAttribute("muted"), "Expected the correct muted attribute on the old tab"); is(oldTab.muteReason, null, "Expected the correct muteReason attribute on the old tab"); - ok(oldTab.hasAttribute("soundplaying"), "Expected the correct soundplaying attribute on the old tab"); + ok(!oldTab.hasAttribute("soundplaying"), "Expected the correct soundplaying attribute on the old tab"); let newTab = gBrowser.getTabForBrowser(newBrowser); let AttrChangePromise = BrowserTestUtils.waitForEvent(newTab, "TabAttrModified", false, event => { - return event.detail.changed.includes("soundplaying") && - event.detail.changed.includes("muted"); + return event.detail.changed.includes("muted"); }); gBrowser.swapBrowsersAndCloseOther(newTab, oldTab); yield AttrChangePromise; ok(newTab.hasAttribute("muted"), "Expected the correct muted attribute on the new tab"); is(newTab.muteReason, null, "Expected the correct muteReason property on the new tab"); - ok(newTab.hasAttribute("soundplaying"), "Expected the correct soundplaying attribute on the new tab"); + ok(!newTab.hasAttribute("soundplaying"), "Expected the correct soundplaying attribute on the new tab"); let icon = document.getAnonymousElementByAttribute(newTab, "anonid", "soundplaying-icon"); yield test_tooltip(icon, "Unmute tab", true); } function* test_swapped_browser_while_not_playing(oldTab, newBrowser) { ok(oldTab.hasAttribute("muted"), "Expected the correct muted attribute on the old tab");
--- a/browser/base/content/test/referrer/head.js +++ b/browser/base/content/test/referrer/head.js @@ -137,17 +137,17 @@ function delayedStartupFinished(aWindow) * Waits for some (any) tab to load. The caller triggers the load. * @param aWindow The window where to wait for a tab to load. * @return {Promise} * @resolves With the tab once it's loaded. */ function someTabLoaded(aWindow) { return new Promise(function(resolve) { aWindow.gBrowser.addEventListener("load", function onLoad(aEvent) { - if (aWindow.location === "about:blank") { + if (aWindow.location.href === "about:blank") { return; } let tab = aWindow.gBrowser._getTabForContentWindow( aEvent.target.defaultView.top); if (tab) { aWindow.gBrowser.removeEventListener("load", onLoad, true); resolve(tab); }
--- a/browser/components/customizableui/CustomizableWidgets.jsm +++ b/browser/components/customizableui/CustomizableWidgets.jsm @@ -304,16 +304,18 @@ const CustomizableWidgets = [ viewId: "PanelUI-remotetabs", defaultArea: CustomizableUI.AREA_PANEL, deckIndices: { DECKINDEX_TABS: 0, DECKINDEX_TABSDISABLED: 1, DECKINDEX_FETCHING: 2, DECKINDEX_NOCLIENTS: 3, }, + TABS_PER_PAGE: 25, + NEXT_PAGE_MIN_TABS: 5, // Minimum number of tabs displayed when we click "Show All" onCreated(aNode) { // Add an observer to the button so we get the animation during sync. // (Note the observer sets many attributes, including label and // tooltiptext, but we only want the 'syncstatus' attribute for the // animation) let doc = aNode.ownerDocument; let obnode = doc.createElementNS(kNSXUL, "observes"); obnode.setAttribute("element", "sync-status"); @@ -396,23 +398,23 @@ const CustomizableWidgets = [ // We call setAttribute instead of relying on the XBL property setter due // to things going wrong when we try and set the index before the XBL // binding has been created - see bug 1241851 for the gory details. deck.setAttribute("selectedIndex", index); }, _showTabsPromise: Promise.resolve(), // Update the tab list after any existing in-flight updates are complete. - _showTabs() { + _showTabs(paginationInfo) { this._showTabsPromise = this._showTabsPromise.then(() => { - return this.__showTabs(); + return this.__showTabs(paginationInfo); }); }, // Return a new promise to update the tab list. - __showTabs() { + __showTabs(paginationInfo) { let doc = this._tabsList.ownerDocument; return SyncedTabs.getTabClients().then(clients => { // The view may have been hidden while the promise was resolving. if (!this._tabsList) { return; } if (clients.length === 0 && !SyncedTabs.hasSyncedThisSession) { // the "fetching tabs" deck is being shown - let's leave it there. @@ -422,26 +424,30 @@ const CustomizableWidgets = [ if (clients.length === 0) { this.setDeckIndex(this.deckIndices.DECKINDEX_NOCLIENTS); return; } this.setDeckIndex(this.deckIndices.DECKINDEX_TABS); this._clearTabList(); - SyncedTabs.sortTabClientsByLastUsed(clients, 50 /* maxTabs */); + SyncedTabs.sortTabClientsByLastUsed(clients); let fragment = doc.createDocumentFragment(); for (let client of clients) { // add a menu separator for all clients other than the first. if (fragment.lastChild) { let separator = doc.createElementNS(kNSXUL, "menuseparator"); fragment.appendChild(separator); } - this._appendClient(client, fragment); + if (paginationInfo && paginationInfo.clientId == client.id) { + this._appendClient(client, fragment, paginationInfo.maxTabs); + } else { + this._appendClient(client, fragment); + } } this._tabsList.appendChild(fragment); }).catch(err => { Cu.reportError(err); }).then(() => { // an observer for tests. Services.obs.notifyObservers(null, "synced-tabs-menu:test:tabs-updated", null); }); @@ -461,36 +467,56 @@ const CustomizableWidgets = [ } let message = this._tabsList.getAttribute(messageAttr); let doc = this._tabsList.ownerDocument; let messageLabel = doc.createElementNS(kNSXUL, "label"); messageLabel.textContent = message; appendTo.appendChild(messageLabel); return messageLabel; }, - _appendClient(client, attachFragment) { + _appendClient(client, attachFragment, maxTabs = this.TABS_PER_PAGE) { let doc = attachFragment.ownerDocument; // Create the element for the remote client. let clientItem = doc.createElementNS(kNSXUL, "label"); clientItem.setAttribute("itemtype", "client"); let window = doc.defaultView; clientItem.setAttribute("tooltiptext", window.gSyncUI.formatLastSyncDate(new Date(client.lastModified))); clientItem.textContent = client.name; attachFragment.appendChild(clientItem); if (client.tabs.length == 0) { let label = this._appendMessageLabel("notabsforclientlabel", attachFragment); label.setAttribute("class", "PanelUI-remotetabs-notabsforclient-label"); } else { + // If this page will display all tabs, show no additional buttons. + // If the next page will display all the remaining tabs, show a "Show All" button + // Otherwise, show a "Shore More" button + let hasNextPage = client.tabs.length > maxTabs; + let nextPageIsLastPage = hasNextPage && maxTabs + this.TABS_PER_PAGE >= client.tabs.length; + if (nextPageIsLastPage) { + // When the user clicks "Show All", try to have at least NEXT_PAGE_MIN_TABS more tabs + // to display in order to avoid user frustration + maxTabs = Math.min(client.tabs.length - this.NEXT_PAGE_MIN_TABS, maxTabs); + } + if (hasNextPage) { + client.tabs = client.tabs.slice(0, maxTabs); + } for (let tab of client.tabs) { let tabEnt = this._createTabElement(doc, tab); attachFragment.appendChild(tabEnt); } + if (hasNextPage) { + let showAllEnt = this._createShowMoreElement(doc, client.id, + nextPageIsLastPage ? + Infinity : + maxTabs + this.TABS_PER_PAGE); + attachFragment.appendChild(showAllEnt); + } } }, _createTabElement(doc, tabInfo) { let item = doc.createElementNS(kNSXUL, "toolbarbutton"); let tooltipText = (tabInfo.title ? tabInfo.title + "\n" : "") + tabInfo.url; item.setAttribute("itemtype", "tab"); item.setAttribute("class", "subviewbutton"); item.setAttribute("targetURI", tabInfo.url); @@ -501,16 +527,39 @@ const CustomizableWidgets = [ // respects different buttons (eg, to open in a new tab). item.addEventListener("click", e => { doc.defaultView.openUILink(tabInfo.url, e); CustomizableUI.hidePanelForNode(item); BrowserUITelemetry.countSyncedTabEvent("open", "toolbarbutton-subview"); }); return item; }, + _createShowMoreElement(doc, clientId, showCount) { + let labelAttr, tooltipAttr; + if (showCount === Infinity) { + labelAttr = "showAllLabel"; + tooltipAttr = "showAllTooltipText"; + } else { + labelAttr = "showMoreLabel"; + tooltipAttr = "showMoreTooltipText"; + } + let showAllItem = doc.createElementNS(kNSXUL, "toolbarbutton"); + showAllItem.setAttribute("itemtype", "showmorebutton"); + showAllItem.setAttribute("class", "subviewbutton"); + let label = this._tabsList.getAttribute(labelAttr); + showAllItem.setAttribute("label", label); + let tooltipText = this._tabsList.getAttribute(tooltipAttr); + showAllItem.setAttribute("tooltiptext", tooltipText); + showAllItem.addEventListener("click", e => { + e.preventDefault(); + e.stopPropagation(); + this._showTabs({ clientId, maxTabs: showCount }); + }); + return showAllItem; + } }, { id: "privatebrowsing-button", shortcutId: "key_privatebrowsing", defaultArea: CustomizableUI.AREA_PANEL, onCommand(e) { let win = e.target.ownerGlobal; win.OpenBrowserWindow({private: true}); }
--- a/browser/components/customizableui/content/panelUI.inc.xul +++ b/browser/components/customizableui/content/panelUI.inc.xul @@ -120,16 +120,20 @@ oncommand="gSyncUI.doSync();" closemenu="none"/> <menuseparator id="PanelUI-remotetabs-separator"/> </vbox> <deck id="PanelUI-remotetabs-deck"> <!-- Sync is ready to Sync and the "tabs" engine is enabled --> <vbox id="PanelUI-remotetabs-tabspane"> <vbox id="PanelUI-remotetabs-tabslist" + showAllLabel="&appMenuRemoteTabs.showAll.label;" + showAllTooltipText="&appMenuRemoteTabs.showAll.tooltip;" + showMoreLabel="&appMenuRemoteTabs.showMore.label;" + showMoreTooltipText="&appMenuRemoteTabs.showMore.tooltip;" notabsforclientlabel="&appMenuRemoteTabs.notabs.label;" /> </vbox> <!-- Sync is ready to Sync but the "tabs" engine isn't enabled--> <hbox id="PanelUI-remotetabs-tabsdisabledpane" pack="center" flex="1"> <vbox class="PanelUI-remotetabs-instruction-box"> <hbox pack="center"> <html:img class="fxaSyncIllustration" src="chrome://browser/skin/fxa/sync-illustration.svg"/>
--- a/browser/components/customizableui/test/browser.ini +++ b/browser/components/customizableui/test/browser.ini @@ -95,17 +95,16 @@ skip-if = os == "linux" # Intermittent f [browser_948985_non_removable_defaultArea.js] [browser_952963_areaType_getter_no_area.js] [browser_956602_remove_special_widget.js] [browser_962069_drag_to_overflow_chevron.js] [browser_962884_opt_in_disable_hyphens.js] [browser_963639_customizing_attribute_non_customizable_toolbar.js] [browser_967000_button_charEncoding.js] [browser_967000_button_feeds.js] -[browser_967000_button_sync.js] [browser_968447_bookmarks_toolbar_items_in_panel.js] skip-if = os == "linux" # Intemittent failures - bug 979207 [browser_968565_insert_before_hidden_items.js] [browser_969427_recreate_destroyed_widget_after_reset.js] [browser_969661_character_encoding_navbar_disabled.js] [browser_970511_undo_restore_default.js] [browser_972267_customizationchange_events.js] [browser_973641_button_addon.js] @@ -146,9 +145,10 @@ skip-if = os == "mac" [browser_1087303_button_preferences.js] [browser_1089591_still_customizable_after_reset.js] [browser_1096763_seen_widgets_post_reset.js] [browser_1161838_inserted_new_default_buttons.js] [browser_bootstrapped_custom_toolbar.js] [browser_customizemode_contextmenu_menubuttonstate.js] [browser_panel_toggle.js] [browser_switch_to_customize_mode.js] +[browser_synced_tabs_menu.js] [browser_check_tooltips_in_navbar.js]
deleted file mode 100644 --- a/browser/components/customizableui/test/browser_967000_button_sync.js +++ /dev/null @@ -1,337 +0,0 @@ -/* 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/. */ - -"use strict"; - -requestLongerTimeout(2); - -let {SyncedTabs} = Cu.import("resource://services-sync/SyncedTabs.jsm", {}); - -XPCOMUtils.defineLazyModuleGetter(this, "UITour", "resource:///modules/UITour.jsm"); - -// These are available on the widget implementation, but it seems impossible -// to grab that impl at runtime. -const DECKINDEX_TABS = 0; -const DECKINDEX_TABSDISABLED = 1; -const DECKINDEX_FETCHING = 2; -const DECKINDEX_NOCLIENTS = 3; - -var initialLocation = gBrowser.currentURI.spec; -var newTab = null; - -// A helper to notify there are new tabs. Returns a promise that is resolved -// once the UI has been updated. -function updateTabsPanel() { - let promiseTabsUpdated = promiseObserverNotified("synced-tabs-menu:test:tabs-updated"); - Services.obs.notifyObservers(null, SyncedTabs.TOPIC_TABS_CHANGED, null); - return promiseTabsUpdated; -} - -// This is the mock we use for SyncedTabs.jsm - tests may override various -// functions. -let mockedInternal = { - get isConfiguredToSyncTabs() { return true; }, - getTabClients() { return []; }, - syncTabs() {}, - hasSyncedThisSession: false, -}; - - -add_task(function* setup() { - let oldInternal = SyncedTabs._internal; - SyncedTabs._internal = mockedInternal; - - // This test hacks some observer states to simulate a user being signed - // in to Sync - restore them when the test completes. - let initialObserverStates = {}; - for (let id of ["sync-reauth-state", "sync-setup-state", "sync-syncnow-state"]) { - initialObserverStates[id] = document.getElementById(id).hidden; - } - - registerCleanupFunction(() => { - SyncedTabs._internal = oldInternal; - for (let [id, initial] of Object.entries(initialObserverStates)) { - document.getElementById(id).hidden = initial; - } - }); -}); - -// The test expects the about:preferences#sync page to open in the current tab -function* openPrefsFromMenuPanel(expectedPanelId, entryPoint) { - info("Check Sync button functionality"); - Services.prefs.setCharPref("identity.fxaccounts.remote.signup.uri", "http://example.com/"); - - // check the button's functionality - yield PanelUI.show(); - - if (entryPoint == "uitour") { - UITour.tourBrowsersByWindow.set(window, new Set()); - UITour.tourBrowsersByWindow.get(window).add(gBrowser.selectedBrowser); - } - - let syncButton = document.getElementById("sync-button"); - ok(syncButton, "The Sync button was added to the Panel Menu"); - - syncButton.click(); - let syncPanel = document.getElementById("PanelUI-remotetabs"); - ok(syncPanel.getAttribute("current"), "Sync Panel is in view"); - - // Sync is not configured - verify that state is reflected. - let subpanel = document.getElementById(expectedPanelId) - ok(!subpanel.hidden, "sync setup element is visible"); - - // Find and click the "setup" button. - let setupButton = subpanel.querySelector(".PanelUI-remotetabs-prefs-button"); - setupButton.click(); - - let deferred = Promise.defer(); - let handler = (e) => { - if (e.originalTarget != gBrowser.selectedBrowser.contentDocument || - e.target.location.href == "about:blank") { - info("Skipping spurious 'load' event for " + e.target.location.href); - return; - } - gBrowser.selectedBrowser.removeEventListener("load", handler, true); - deferred.resolve(); - } - gBrowser.selectedBrowser.addEventListener("load", handler, true); - - yield deferred.promise; - newTab = gBrowser.selectedTab; - - is(gBrowser.currentURI.spec, "about:preferences?entrypoint=" + entryPoint + "#sync", - "Firefox Sync preference page opened with `menupanel` entrypoint"); - ok(!isPanelUIOpen(), "The panel closed"); - - if (isPanelUIOpen()) { - let panelHidePromise = promisePanelHidden(window); - PanelUI.hide(); - yield panelHidePromise; - } -} - -function* asyncCleanup() { - Services.prefs.clearUserPref("identity.fxaccounts.remote.signup.uri"); - // reset the panel UI to the default state - yield resetCustomization(); - ok(CustomizableUI.inDefaultState, "The panel UI is in default state again."); - - // restore the tabs - gBrowser.addTab(initialLocation); - gBrowser.removeTab(newTab); - UITour.tourBrowsersByWindow.delete(window); -} - -// When Sync is not setup. -add_task(() => openPrefsFromMenuPanel("PanelUI-remotetabs-setupsync", "synced-tabs")); -add_task(asyncCleanup); - -// When Sync is configured in a "needs reauthentication" state. -add_task(function* () { - // configure our broadcasters so we are in the right state. - document.getElementById("sync-reauth-state").hidden = false; - document.getElementById("sync-setup-state").hidden = true; - document.getElementById("sync-syncnow-state").hidden = true; - yield openPrefsFromMenuPanel("PanelUI-remotetabs-reauthsync", "synced-tabs") -}); - -// Test the mobile promo links -add_task(function* () { - // change the preferences for the mobile links. - Services.prefs.setCharPref("identity.mobilepromo.android", "http://example.com/?os=android&tail="); - Services.prefs.setCharPref("identity.mobilepromo.ios", "http://example.com/?os=ios&tail="); - - mockedInternal.getTabClients = () => []; - mockedInternal.syncTabs = () => Promise.resolve(); - - document.getElementById("sync-reauth-state").hidden = true; - document.getElementById("sync-setup-state").hidden = true; - document.getElementById("sync-syncnow-state").hidden = false; - - let syncPanel = document.getElementById("PanelUI-remotetabs"); - let links = syncPanel.querySelectorAll(".remotetabs-promo-link"); - - is(links.length, 2, "found 2 links as expected"); - - // test each link and left and middle mouse buttons - for (let link of links) { - for (let button = 0; button < 2; button++) { - yield PanelUI.show(); - EventUtils.sendMouseEvent({ type: "click", button }, link, window); - // the panel should have been closed. - ok(!isPanelUIOpen(), "click closed the panel"); - // should be a new tab - wait for the load. - is(gBrowser.tabs.length, 2, "there's a new tab"); - yield new Promise(resolve => { - if (gBrowser.selectedBrowser.currentURI.spec == "about:blank") { - gBrowser.selectedBrowser.addEventListener("load", function(e) { - resolve(); - }, {capture: true, once: true}); - return; - } - // the new tab has already transitioned away from about:blank so we - // are good to go. - resolve(); - }); - - let os = link.getAttribute("mobile-promo-os"); - let expectedUrl = `http://example.com/?os=${os}&tail=synced-tabs`; - is(gBrowser.selectedBrowser.currentURI.spec, expectedUrl, "correct URL"); - gBrowser.removeTab(gBrowser.selectedTab); - } - } - - // test each link and right mouse button - should be a noop. - yield PanelUI.show(); - for (let link of links) { - EventUtils.sendMouseEvent({ type: "click", button: 2 }, link, window); - // the panel should still be open - ok(isPanelUIOpen(), "panel remains open after right-click"); - is(gBrowser.tabs.length, 1, "no new tab was opened"); - } - PanelUI.hide(); - - Services.prefs.clearUserPref("identity.mobilepromo.android"); - Services.prefs.clearUserPref("identity.mobilepromo.ios"); -}); - -// Test the "Sync Now" button -add_task(function* () { - mockedInternal.getTabClients = () => []; - mockedInternal.syncTabs = () => { - return Promise.resolve(); - } - - // configure our broadcasters so we are in the right state. - document.getElementById("sync-reauth-state").hidden = true; - document.getElementById("sync-setup-state").hidden = true; - document.getElementById("sync-syncnow-state").hidden = false; - - yield PanelUI.show(); - document.getElementById("sync-button").click(); - let syncPanel = document.getElementById("PanelUI-remotetabs"); - ok(syncPanel.getAttribute("current"), "Sync Panel is in view"); - - let subpanel = document.getElementById("PanelUI-remotetabs-main") - ok(!subpanel.hidden, "main pane is visible"); - let deck = document.getElementById("PanelUI-remotetabs-deck"); - - // The widget is still fetching tabs, as we've neutered everything that - // provides them - is(deck.selectedIndex, DECKINDEX_FETCHING, "first deck entry is visible"); - - let syncNowButton = document.getElementById("PanelUI-remotetabs-syncnow"); - - let didSync = false; - let oldDoSync = gSyncUI.doSync; - gSyncUI.doSync = function() { - didSync = true; - mockedInternal.hasSyncedThisSession = true; - gSyncUI.doSync = oldDoSync; - } - syncNowButton.click(); - ok(didSync, "clicking the button called the correct function"); - - // Tell the widget there are tabs available, but with zero clients. - mockedInternal.getTabClients = () => { - return Promise.resolve([]); - } - yield updateTabsPanel(); - // The UI should be showing the "no clients" pane. - is(deck.selectedIndex, DECKINDEX_NOCLIENTS, "no-clients deck entry is visible"); - - // Tell the widget there are tabs available - we have 3 clients, one with no - // tabs. - mockedInternal.getTabClients = () => { - return Promise.resolve([ - { - id: "guid_mobile", - type: "client", - name: "My Phone", - tabs: [], - }, - { - id: "guid_desktop", - type: "client", - name: "My Desktop", - tabs: [ - { - title: "http://example.com/10", - lastUsed: 10, // the most recent - }, - { - title: "http://example.com/1", - lastUsed: 1, // the least recent. - }, - { - title: "http://example.com/5", - lastUsed: 5, - }, - ], - }, - { - id: "guid_second_desktop", - name: "My Other Desktop", - tabs: [ - { - title: "http://example.com/6", - lastUsed: 6, - } - ], - }, - ]); - }; - yield updateTabsPanel(); - - // The UI should be showing tabs! - is(deck.selectedIndex, DECKINDEX_TABS, "no-clients deck entry is visible"); - let tabList = document.getElementById("PanelUI-remotetabs-tabslist"); - let node = tabList.firstChild; - // First entry should be the client with the most-recent tab. - is(node.getAttribute("itemtype"), "client", "node is a client entry"); - is(node.textContent, "My Desktop", "correct client"); - // Next entry is the most-recent tab - node = node.nextSibling; - is(node.getAttribute("itemtype"), "tab", "node is a tab"); - is(node.getAttribute("label"), "http://example.com/10"); - - // Next entry is the next-most-recent tab - node = node.nextSibling; - is(node.getAttribute("itemtype"), "tab", "node is a tab"); - is(node.getAttribute("label"), "http://example.com/5"); - - // Next entry is the least-recent tab from the first client. - node = node.nextSibling; - is(node.getAttribute("itemtype"), "tab", "node is a tab"); - is(node.getAttribute("label"), "http://example.com/1"); - - // Next is a menuseparator between the clients. - node = node.nextSibling; - is(node.nodeName, "menuseparator"); - - // Next is the client with 1 tab. - node = node.nextSibling; - is(node.getAttribute("itemtype"), "client", "node is a client entry"); - is(node.textContent, "My Other Desktop", "correct client"); - // Its single tab - node = node.nextSibling; - is(node.getAttribute("itemtype"), "tab", "node is a tab"); - is(node.getAttribute("label"), "http://example.com/6"); - - // Next is a menuseparator between the clients. - node = node.nextSibling; - is(node.nodeName, "menuseparator"); - - // Next is the client with no tab. - node = node.nextSibling; - is(node.getAttribute("itemtype"), "client", "node is a client entry"); - is(node.textContent, "My Phone", "correct client"); - // There is a single node saying there's no tabs for the client. - node = node.nextSibling; - is(node.nodeName, "label", "node is a label"); - is(node.getAttribute("itemtype"), "", "node is neither a tab nor a client"); - - node = node.nextSibling; - is(node, null, "no more entries"); -});
new file mode 100644 --- /dev/null +++ b/browser/components/customizableui/test/browser_synced_tabs_menu.js @@ -0,0 +1,427 @@ +/* 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/. */ + +"use strict"; + +requestLongerTimeout(2); + +let {SyncedTabs} = Cu.import("resource://services-sync/SyncedTabs.jsm", {}); + +XPCOMUtils.defineLazyModuleGetter(this, "UITour", "resource:///modules/UITour.jsm"); + +// These are available on the widget implementation, but it seems impossible +// to grab that impl at runtime. +const DECKINDEX_TABS = 0; +const DECKINDEX_TABSDISABLED = 1; +const DECKINDEX_FETCHING = 2; +const DECKINDEX_NOCLIENTS = 3; + +var initialLocation = gBrowser.currentURI.spec; +var newTab = null; + +// A helper to notify there are new tabs. Returns a promise that is resolved +// once the UI has been updated. +function updateTabsPanel() { + let promiseTabsUpdated = promiseObserverNotified("synced-tabs-menu:test:tabs-updated"); + Services.obs.notifyObservers(null, SyncedTabs.TOPIC_TABS_CHANGED, null); + return promiseTabsUpdated; +} + +// This is the mock we use for SyncedTabs.jsm - tests may override various +// functions. +let mockedInternal = { + get isConfiguredToSyncTabs() { return true; }, + getTabClients() { return Promise.resolve([]); }, + syncTabs() { return Promise.resolve(); }, + hasSyncedThisSession: false, +}; + + +add_task(function* setup() { + let oldInternal = SyncedTabs._internal; + SyncedTabs._internal = mockedInternal; + + // This test hacks some observer states to simulate a user being signed + // in to Sync - restore them when the test completes. + let initialObserverStates = {}; + for (let id of ["sync-reauth-state", "sync-setup-state", "sync-syncnow-state"]) { + initialObserverStates[id] = document.getElementById(id).hidden; + } + + registerCleanupFunction(() => { + SyncedTabs._internal = oldInternal; + for (let [id, initial] of Object.entries(initialObserverStates)) { + document.getElementById(id).hidden = initial; + } + }); +}); + +// The test expects the about:preferences#sync page to open in the current tab +function* openPrefsFromMenuPanel(expectedPanelId, entryPoint) { + info("Check Sync button functionality"); + Services.prefs.setCharPref("identity.fxaccounts.remote.signup.uri", "http://example.com/"); + + // check the button's functionality + yield PanelUI.show(); + + if (entryPoint == "uitour") { + UITour.tourBrowsersByWindow.set(window, new Set()); + UITour.tourBrowsersByWindow.get(window).add(gBrowser.selectedBrowser); + } + + let syncButton = document.getElementById("sync-button"); + ok(syncButton, "The Sync button was added to the Panel Menu"); + + let tabsUpdatedPromise = promiseObserverNotified("synced-tabs-menu:test:tabs-updated"); + syncButton.click(); + yield tabsUpdatedPromise; + let syncPanel = document.getElementById("PanelUI-remotetabs"); + ok(syncPanel.getAttribute("current"), "Sync Panel is in view"); + + // Sync is not configured - verify that state is reflected. + let subpanel = document.getElementById(expectedPanelId) + ok(!subpanel.hidden, "sync setup element is visible"); + + // Find and click the "setup" button. + let setupButton = subpanel.querySelector(".PanelUI-remotetabs-prefs-button"); + setupButton.click(); + + let deferred = Promise.defer(); + let handler = (e) => { + if (e.originalTarget != gBrowser.selectedBrowser.contentDocument || + e.target.location.href == "about:blank") { + info("Skipping spurious 'load' event for " + e.target.location.href); + return; + } + gBrowser.selectedBrowser.removeEventListener("load", handler, true); + deferred.resolve(); + } + gBrowser.selectedBrowser.addEventListener("load", handler, true); + + yield deferred.promise; + newTab = gBrowser.selectedTab; + + is(gBrowser.currentURI.spec, "about:preferences?entrypoint=" + entryPoint + "#sync", + "Firefox Sync preference page opened with `menupanel` entrypoint"); + ok(!isPanelUIOpen(), "The panel closed"); + + if (isPanelUIOpen()) { + yield panelUIHide(); + } +} + +function panelUIHide() { + let panelHidePromise = promisePanelHidden(window); + PanelUI.hide(); + return panelHidePromise; +} + +function* asyncCleanup() { + Services.prefs.clearUserPref("identity.fxaccounts.remote.signup.uri"); + // reset the panel UI to the default state + yield resetCustomization(); + ok(CustomizableUI.inDefaultState, "The panel UI is in default state again."); + + // restore the tabs + gBrowser.addTab(initialLocation); + gBrowser.removeTab(newTab); + UITour.tourBrowsersByWindow.delete(window); +} + +// When Sync is not setup. +add_task(function* () { + document.getElementById("sync-reauth-state").hidden = true; + document.getElementById("sync-setup-state").hidden = false; + document.getElementById("sync-syncnow-state").hidden = true; + yield openPrefsFromMenuPanel("PanelUI-remotetabs-setupsync", "synced-tabs") +}); +add_task(asyncCleanup); + +// When Sync is configured in a "needs reauthentication" state. +add_task(function* () { + // configure our broadcasters so we are in the right state. + document.getElementById("sync-reauth-state").hidden = false; + document.getElementById("sync-setup-state").hidden = true; + document.getElementById("sync-syncnow-state").hidden = true; + yield openPrefsFromMenuPanel("PanelUI-remotetabs-reauthsync", "synced-tabs") +}); + +// Test the mobile promo links +add_task(function* () { + // change the preferences for the mobile links. + Services.prefs.setCharPref("identity.mobilepromo.android", "http://example.com/?os=android&tail="); + Services.prefs.setCharPref("identity.mobilepromo.ios", "http://example.com/?os=ios&tail="); + + document.getElementById("sync-reauth-state").hidden = true; + document.getElementById("sync-setup-state").hidden = true; + document.getElementById("sync-syncnow-state").hidden = false; + + let syncPanel = document.getElementById("PanelUI-remotetabs"); + let links = syncPanel.querySelectorAll(".remotetabs-promo-link"); + + is(links.length, 2, "found 2 links as expected"); + + // test each link and left and middle mouse buttons + for (let link of links) { + for (let button = 0; button < 2; button++) { + yield PanelUI.show(); + EventUtils.sendMouseEvent({ type: "click", button }, link, window); + // the panel should have been closed. + ok(!isPanelUIOpen(), "click closed the panel"); + // should be a new tab - wait for the load. + is(gBrowser.tabs.length, 2, "there's a new tab"); + yield new Promise(resolve => { + if (gBrowser.selectedBrowser.currentURI.spec == "about:blank") { + gBrowser.selectedBrowser.addEventListener("load", function(e) { + resolve(); + }, {capture: true, once: true}); + return; + } + // the new tab has already transitioned away from about:blank so we + // are good to go. + resolve(); + }); + + let os = link.getAttribute("mobile-promo-os"); + let expectedUrl = `http://example.com/?os=${os}&tail=synced-tabs`; + is(gBrowser.selectedBrowser.currentURI.spec, expectedUrl, "correct URL"); + gBrowser.removeTab(gBrowser.selectedTab); + } + } + + // test each link and right mouse button - should be a noop. + yield PanelUI.show(); + for (let link of links) { + EventUtils.sendMouseEvent({ type: "click", button: 2 }, link, window); + // the panel should still be open + ok(isPanelUIOpen(), "panel remains open after right-click"); + is(gBrowser.tabs.length, 1, "no new tab was opened"); + } + yield panelUIHide(); + + Services.prefs.clearUserPref("identity.mobilepromo.android"); + Services.prefs.clearUserPref("identity.mobilepromo.ios"); +}); + +// Test the "Sync Now" button +add_task(function* () { + // configure our broadcasters so we are in the right state. + document.getElementById("sync-reauth-state").hidden = true; + document.getElementById("sync-setup-state").hidden = true; + document.getElementById("sync-syncnow-state").hidden = false; + + yield PanelUI.show(); + let tabsUpdatedPromise = promiseObserverNotified("synced-tabs-menu:test:tabs-updated"); + document.getElementById("sync-button").click(); + yield tabsUpdatedPromise; + let syncPanel = document.getElementById("PanelUI-remotetabs"); + ok(syncPanel.getAttribute("current"), "Sync Panel is in view"); + + let subpanel = document.getElementById("PanelUI-remotetabs-main") + ok(!subpanel.hidden, "main pane is visible"); + let deck = document.getElementById("PanelUI-remotetabs-deck"); + + // The widget is still fetching tabs, as we've neutered everything that + // provides them + is(deck.selectedIndex, DECKINDEX_FETCHING, "first deck entry is visible"); + + let syncNowButton = document.getElementById("PanelUI-remotetabs-syncnow"); + + let didSync = false; + let oldDoSync = gSyncUI.doSync; + gSyncUI.doSync = function() { + didSync = true; + mockedInternal.hasSyncedThisSession = true; + gSyncUI.doSync = oldDoSync; + } + syncNowButton.click(); + ok(didSync, "clicking the button called the correct function"); + + // Tell the widget there are tabs available, but with zero clients. + mockedInternal.getTabClients = () => { + return Promise.resolve([]); + } + yield updateTabsPanel(); + // The UI should be showing the "no clients" pane. + is(deck.selectedIndex, DECKINDEX_NOCLIENTS, "no-clients deck entry is visible"); + + // Tell the widget there are tabs available - we have 3 clients, one with no + // tabs. + mockedInternal.getTabClients = () => { + return Promise.resolve([ + { + id: "guid_mobile", + type: "client", + name: "My Phone", + tabs: [], + }, + { + id: "guid_desktop", + type: "client", + name: "My Desktop", + tabs: [ + { + title: "http://example.com/10", + lastUsed: 10, // the most recent + }, + { + title: "http://example.com/1", + lastUsed: 1, // the least recent. + }, + { + title: "http://example.com/5", + lastUsed: 5, + }, + ], + }, + { + id: "guid_second_desktop", + name: "My Other Desktop", + tabs: [ + { + title: "http://example.com/6", + lastUsed: 6, + } + ], + }, + ]); + }; + yield updateTabsPanel(); + + // The UI should be showing tabs! + is(deck.selectedIndex, DECKINDEX_TABS, "no-clients deck entry is visible"); + let tabList = document.getElementById("PanelUI-remotetabs-tabslist"); + let node = tabList.firstChild; + // First entry should be the client with the most-recent tab. + is(node.getAttribute("itemtype"), "client", "node is a client entry"); + is(node.textContent, "My Desktop", "correct client"); + // Next entry is the most-recent tab + node = node.nextSibling; + is(node.getAttribute("itemtype"), "tab", "node is a tab"); + is(node.getAttribute("label"), "http://example.com/10"); + + // Next entry is the next-most-recent tab + node = node.nextSibling; + is(node.getAttribute("itemtype"), "tab", "node is a tab"); + is(node.getAttribute("label"), "http://example.com/5"); + + // Next entry is the least-recent tab from the first client. + node = node.nextSibling; + is(node.getAttribute("itemtype"), "tab", "node is a tab"); + is(node.getAttribute("label"), "http://example.com/1"); + + // Next is a menuseparator between the clients. + node = node.nextSibling; + is(node.nodeName, "menuseparator"); + + // Next is the client with 1 tab. + node = node.nextSibling; + is(node.getAttribute("itemtype"), "client", "node is a client entry"); + is(node.textContent, "My Other Desktop", "correct client"); + // Its single tab + node = node.nextSibling; + is(node.getAttribute("itemtype"), "tab", "node is a tab"); + is(node.getAttribute("label"), "http://example.com/6"); + + // Next is a menuseparator between the clients. + node = node.nextSibling; + is(node.nodeName, "menuseparator"); + + // Next is the client with no tab. + node = node.nextSibling; + is(node.getAttribute("itemtype"), "client", "node is a client entry"); + is(node.textContent, "My Phone", "correct client"); + // There is a single node saying there's no tabs for the client. + node = node.nextSibling; + is(node.nodeName, "label", "node is a label"); + is(node.getAttribute("itemtype"), "", "node is neither a tab nor a client"); + + node = node.nextSibling; + is(node, null, "no more entries"); + + yield panelUIHide(); +}); + +// Test the pagination capabilities (Show More/All tabs) +add_task(function* () { + mockedInternal.getTabClients = () => { + return Promise.resolve([ + { + id: "guid_desktop", + type: "client", + name: "My Desktop", + tabs: function() { + let allTabsDesktop = []; + // We choose 77 tabs, because TABS_PER_PAGE is 25, which means + // on the second to last page we should have 22 items shown + // (because we have to show at least NEXT_PAGE_MIN_TABS=5 tabs on the last page) + for (let i = 1; i <= 77; i++) { + allTabsDesktop.push({ title: "Tab #" + i }); + } + return allTabsDesktop; + }(), + } + ]); + }; + + // configure our broadcasters so we are in the right state. + document.getElementById("sync-reauth-state").hidden = true; + document.getElementById("sync-setup-state").hidden = true; + document.getElementById("sync-syncnow-state").hidden = false; + + yield PanelUI.show(); + let tabsUpdatedPromise = promiseObserverNotified("synced-tabs-menu:test:tabs-updated"); + document.getElementById("sync-button").click(); + yield tabsUpdatedPromise; + + // Check pre-conditions + let syncPanel = document.getElementById("PanelUI-remotetabs"); + ok(syncPanel.getAttribute("current"), "Sync Panel is in view"); + let subpanel = document.getElementById("PanelUI-remotetabs-main") + ok(!subpanel.hidden, "main pane is visible"); + let deck = document.getElementById("PanelUI-remotetabs-deck"); + is(deck.selectedIndex, DECKINDEX_TABS, "we should be showing tabs"); + + function checkTabsPage(tabsShownCount, showMoreLabel) { + let tabList = document.getElementById("PanelUI-remotetabs-tabslist"); + let node = tabList.firstChild; + is(node.getAttribute("itemtype"), "client", "node is a client entry"); + is(node.textContent, "My Desktop", "correct client"); + for (let i = 0; i < tabsShownCount; i++) { + node = node.nextSibling; + is(node.getAttribute("itemtype"), "tab", "node is a tab"); + is(node.getAttribute("label"), "Tab #" + (i + 1), "the tab is the correct one"); + } + let showMoreButton; + if (showMoreLabel) { + node = showMoreButton = node.nextSibling; + is(node.getAttribute("itemtype"), "showmorebutton", "node is a show more button"); + is(node.getAttribute("label"), showMoreLabel); + } + node = node.nextSibling; + is(node, null, "no more entries"); + + return showMoreButton; + } + + let showMoreButton; + function clickShowMoreButton() { + let promise = promiseObserverNotified("synced-tabs-menu:test:tabs-updated"); + showMoreButton.click(); + return promise; + } + + showMoreButton = checkTabsPage(25, "Show More"); + yield clickShowMoreButton(); + + showMoreButton = checkTabsPage(50, "Show More"); + yield clickShowMoreButton(); + + showMoreButton = checkTabsPage(72, "Show All"); + yield clickShowMoreButton(); + + checkTabsPage(77, null); + + yield panelUIHide(); +});
--- a/browser/components/extensions/ext-browserAction.js +++ b/browser/components/extensions/ext-browserAction.js @@ -33,17 +33,24 @@ function isAncestorOrSelf(target, node) if (node === target) { return true; } } return false; } // WeakMap[Extension -> BrowserAction] -var browserActionMap = new WeakMap(); +const browserActionMap = new WeakMap(); + +const browserAreas = { + "navbar": CustomizableUI.AREA_NAVBAR, + "menupanel": CustomizableUI.AREA_PANEL, + "tabstrip": CustomizableUI.AREA_TABSTRIP, + "personaltoolbar": CustomizableUI.AREA_BOOKMARKS, +}; // Responsible for the browser_action section of the manifest as well // as the associated popup. function BrowserAction(options, extension) { this.extension = extension; let widgetId = makeWidgetId(extension.id); this.id = `${widgetId}-browser-action`; @@ -57,16 +64,17 @@ function BrowserAction(options, extensio this.defaults = { enabled: true, title: options.default_title || extension.name, badgeText: "", badgeBackgroundColor: null, icon: IconDetails.normalize({path: options.default_icon}, extension), popup: options.default_popup || "", + area: browserAreas[options.default_area || "navbar"], }; this.browserStyle = options.browser_style || false; if (options.browser_style === null) { this.extension.logger.warn("Please specify whether you want browser_style " + "or not in your browser_action options."); } @@ -80,17 +88,17 @@ BrowserAction.prototype = { build() { let widget = CustomizableUI.createWidget({ id: this.id, viewId: this.viewId, type: "view", removable: true, label: this.defaults.title || this.extension.name, tooltiptext: this.defaults.title || "", - defaultArea: CustomizableUI.AREA_NAVBAR, + defaultArea: this.defaults.area, onBeforeCreated: document => { let view = document.createElementNS(XUL_NS, "panelview"); view.id = this.viewId; view.setAttribute("flex", "1"); document.getElementById("PanelUI-multiView").appendChild(view); document.addEventListener("popupshowing", this);
--- a/browser/components/extensions/schemas/browser_action.json +++ b/browser/components/extensions/schemas/browser_action.json @@ -26,16 +26,22 @@ "type": "string", "format": "relativeUrl", "optional": true, "preprocess": "localize" }, "browser_style": { "type": "boolean", "optional": true + }, + "default_area": { + "description": "Defines the location the browserAction will appear by default. The default location is navbar.", + "type": "string", + "enum": ["navbar", "menupanel", "tabstrip", "personaltoolbar"], + "optional": true } }, "optional": true } } } ] },
--- a/browser/components/extensions/test/browser/browser-common.ini +++ b/browser/components/extensions/test/browser/browser-common.ini @@ -19,16 +19,17 @@ support-files = file_dummy.html file_inspectedwindow_reload_target.sjs file_serviceWorker.html serviceWorker.js searchSuggestionEngine.xml searchSuggestionEngine.sjs ../../../../../toolkit/components/extensions/test/mochitest/head_webrequest.js +[browser_ext_browserAction_area.js] [browser_ext_browserAction_context.js] [browser_ext_browserAction_disabled.js] [browser_ext_browserAction_pageAction_icon.js] [browser_ext_browserAction_pageAction_icon_permissions.js] [browser_ext_browserAction_popup.js] [browser_ext_browserAction_popup_preload.js] [browser_ext_browserAction_popup_resize.js] [browser_ext_browserAction_simple.js]
new file mode 100644 --- /dev/null +++ b/browser/components/extensions/test/browser/browser_ext_browserAction_area.js @@ -0,0 +1,49 @@ +/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set sts=2 sw=2 et tw=80: */ +"use strict"; + +var browserAreas = { + "navbar": CustomizableUI.AREA_NAVBAR, + "menupanel": CustomizableUI.AREA_PANEL, + "tabstrip": CustomizableUI.AREA_TABSTRIP, + "personaltoolbar": CustomizableUI.AREA_BOOKMARKS, +}; + +function* testInArea(area) { + let manifest = { + "browser_action": { + "browser_style": true, + }, + }; + if (area) { + manifest.browser_action.default_area = area; + } + let extension = ExtensionTestUtils.loadExtension({ + manifest, + }); + yield extension.startup(); + let widget = getBrowserActionWidget(extension); + let placement = CustomizableUI.getPlacementOfWidget(widget.id); + is(placement && placement.area, browserAreas[area || "navbar"], `widget located in correct area`); + yield extension.unload(); +} + +add_task(function* testBrowserActionDefaultArea() { + yield testInArea(); +}); + +add_task(function* testBrowserActionInToolbar() { + yield testInArea("navbar"); +}); + +add_task(function* testBrowserActionInMenuPanel() { + yield testInArea("menupanel"); +}); + +add_task(function* testBrowserActionInTabStrip() { + yield testInArea("tabstrip"); +}); + +add_task(function* testBrowserActionInPersonalToolbar() { + yield testInArea("personaltoolbar"); +});
--- a/browser/components/sessionstore/SessionStore.jsm +++ b/browser/components/sessionstore/SessionStore.jsm @@ -383,17 +383,17 @@ this.SessionStore = { * The browser state for which we remove worth-saving tabs. * The given object will be modified. */ keepOnlyWorthSavingTabs: function (aState) { for (let i = aState.windows.length - 1; i >= 0; i--) { let win = aState.windows[i]; for (let j = win.tabs.length - 1; j >= 0; j--) { let tab = win.tabs[j]; - if (!SessionStoreInternal._shouldSaveTabState(tab)) { + if (!SessionStoreInternal._shouldSaveTab(tab)) { win.tabs.splice(j, 1); if (win.selected > j) { win.selected--; } } } if (!win.tabs.length) { aState.windows.splice(i, 1); @@ -4193,21 +4193,40 @@ var SessionStoreInternal = { _shouldSaveTabState: function ssi_shouldSaveTabState(aTabState) { // If the tab has only a transient about: history entry, no other // session history, and no userTypedValue, then we don't actually want to // store this tab's data. return aTabState.entries.length && !(aTabState.entries.length == 1 && (aTabState.entries[0].url == "about:blank" || aTabState.entries[0].url == "about:newtab" || + aTabState.entries[0].url == "about:printpreview" || aTabState.entries[0].url == "about:privatebrowsing") && !aTabState.userTypedValue); }, /** + * Determine if the tab state we're passed is something we should keep to be + * reopened at session restore. This is used when we are saving the current + * session state to disk. This method is very similar to _shouldSaveTabState, + * however, "about:blank" and "about:newtab" tabs will still be saved to disk. + * + * @param aTabState + * The current tab state + * @returns boolean + */ + _shouldSaveTab: function ssi_shouldSaveTab(aTabState) { + // If the tab has one of the following transient about: history entry, + // then we don't actually want to write this tab's data to disk. + return aTabState.entries.length && + !(aTabState.entries[0].url == "about:printpreview" || + aTabState.entries[0].url == "about:privatebrowsing"); + }, + + /** * This is going to take a state as provided at startup (via * nsISessionStartup.state) and split it into 2 parts. The first part * (defaultState) will be a state that should still be restored at startup, * while the second part (state) is a state that should be saved for later. * defaultState will be comprised of windows with only pinned tabs, extracted * from state. It will contain the cookies that go along with the history * entries in those tabs. It will also contain window position information. *
--- a/browser/confvars.sh +++ b/browser/confvars.sh @@ -50,17 +50,16 @@ MOZ_OFFICIAL_BRANDING_DIRECTORY=browser/ MOZ_APP_ID={ec8030f7-c20a-464f-9b0e-13a3a9e97384} # This should usually be the same as the value MAR_CHANNEL_ID. # If more than one ID is needed, then you should use a comma separated list # of values. ACCEPTED_MAR_CHANNEL_IDS=firefox-mozilla-central # The MAR_CHANNEL_ID must not contain the following 3 characters: ",\t " MAR_CHANNEL_ID=firefox-mozilla-central MOZ_PROFILE_MIGRATOR=1 -MOZ_WEBGL_CONFORMANT=1 MOZ_JSDOWNLOADS=1 MOZ_RUST_MP4PARSE=1 # Enable checking that add-ons are signed by the trusted root MOZ_ADDON_SIGNING=1 # Include the DevTools client, not just the server (which is the default) MOZ_DEVTOOLS=all
deleted file mode 100644 --- a/browser/extensions/disableSHA1rollout/README.md +++ /dev/null @@ -1,99 +0,0 @@ -This system add-on is a follow-up to the MITM prevalence experiment. The purpose -is to facilitate rolling out the disabling of SHA-1 in signatures on -certificates issued by publicly-trusted roots. When installed, this add-on will -perform a number of checks to determine if it should change the preference that -controls the SHA-1 policy. First, this should only apply to users on the beta -update channel. It should also only apply to users who have not otherwise -changed the policy to always allow or always forbid SHA-1. Additionally, it -must double-check that the user is not affected by a TLS intercepting proxy -using a publicly-trusted root. If these checks pass, the add-on will divide the -population into a test group and a control group (starting on a 10%/90% split). -The test group will have the policy changed. After doing this, a telemetry -payload is reported with the following values: - -* cohortName -- the name of the group this user is in: - 1. "notSafeToDisableSHA1" if the user is behind a MITM proxy using a - publicly-trusted root - 2. "optedOut" if the user already set the SHA-1 policy to always allow or - always forbid - 3. "optedIn" if the user already set the SHA-1 policy to only allow for - non-built-in roots - 4. "test" if the user is in the test cohort (and SHA-1 will be disabled) - 5. "control" if the user is not in the test cohort -* errorCode -- 0 for successful connections, some PR error code otherwise -* error -- a short description of one of four error conditions encountered, if - applicable, and an empty string otherwise: - 1. "timeout" if the connection to telemetry.mozilla.org timed out - 2. "user override" if the user has stored a permanent certificate exception - override for telemetry.mozilla.org (due to technical limitations, we can't - gather much information in this situation) - 3. "certificate reverification" if re-building the certificate chain after - connecting failed for some reason (unfortunately this step is necessary - due to technical limitations) - 4. "connection error" if the connection to telemetry.mozilla.org failed for - another reason -* chain -- a list of dictionaries each corresponding to a certificate in the - verified certificate chain, if it was successfully constructed. The first - entry is the end-entity certificate. The last entry is the root certificate. - This will be empty if the connection failed or if reverification failed. Each - element in the list contains the following values: - * sha256Fingerprint -- a hex string representing the SHA-256 hash of the - certificate - * isBuiltInRoot -- true if the certificate is a trust anchor in the web PKI, - false otherwise - * signatureAlgorithm -- a description of the algorithm used to sign the - certificate. Will be one of "md2WithRSAEncryption", "md5WithRSAEncryption", - "sha1WithRSAEncryption", "sha256WithRSAEncryption", - "sha384WithRSAEncryption", "sha512WithRSAEncryption", "ecdsaWithSHA1", - "ecdsaWithSHA224", "ecdsaWithSHA256", "ecdsaWithSHA384", "ecdsaWithSHA512", - or "unknown". -* disabledSHA1 -- true if SHA-1 was disabled, false otherwise -* didNotDisableSHA1Because -- a short string describing why SHA-1 could not be - disabled, if applicable. Reasons are limited to: - 1. "MITM" if the user is behind a TLS intercepting proxy using a - publicly-trusted root - 2. "connection error" if there was an error connecting to - telemetry.mozilla.org - 3. "code error" if some inconsistent state was detected, and it was - determined that the experiment should not attempt to change the - preference - 4. "preference:userReset" if the user reset the SHA-1 policy after it had - been changed by this add-on - 5. "preference:allow" if the user had already configured Firefox to always - accept SHA-1 signatures - 6. "preference:forbid" if the user had already configured Firefox to always - forbid SHA-1 signatures - -For a connection not intercepted by a TLS proxy and where the user is in the -test cohort, the expected result will be: - - { "cohortName": "test", - "errorCode": 0, - "error": "", - "chain": [ - { "sha256Fingerprint": "197feaf3faa0f0ad637a89c97cb91336bfc114b6b3018203cbd9c3d10c7fa86c", - "isBuiltInRoot": false, - "signatureAlgorithm": "sha256WithRSAEncryption" - }, - { "sha256Fingerprint": "154c433c491929c5ef686e838e323664a00e6a0d822ccc958fb4dab03e49a08f", - "isBuiltInRoot": false, - "signatureAlgorithm": "sha256WithRSAEncryption" - }, - { "sha256Fingerprint": "4348a0e9444c78cb265e058d5e8944b4d84f9662bd26db257f8934a443c70161", - "isBuiltInRoot": true, - "signatureAlgorithm": "sha1WithRSAEncryption" - } - ], - "disabledSHA1": true, - "didNotDisableSHA1Because": "" - } - -When this result is encountered, the user's preferences are updated to disable -SHA-1 in signatures on certificates issued by publicly-trusted roots. -Similarly, if the user is behind a TLS intercepting proxy but the root -certificate is not part of the public web PKI, we can also disable SHA-1 in -signatures on certificates issued by publicly-trusted roots. - -If the user has already indicated in their preferences that they will always -accept SHA-1 in signatures or that they will never accept SHA-1 in signatures, -then the preference is not changed.
deleted file mode 100644 --- a/browser/extensions/disableSHA1rollout/bootstrap.js +++ /dev/null @@ -1,306 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -"use strict"; - -const {classes: Cc, interfaces: Ci, utils: Cu} = Components; - -Cu.import("resource://gre/modules/Preferences.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/UpdateUtils.jsm"); -Cu.import("resource://gre/modules/TelemetryController.jsm"); - - // Percentage of the population to attempt to disable SHA-1 for, by channel. -const TEST_THRESHOLD = { - beta: 0.1, // 10% -}; - -const PREF_COHORT_SAMPLE = "disableSHA1.rollout.cohortSample"; -const PREF_COHORT_NAME = "disableSHA1.rollout.cohort"; -const PREF_SHA1_POLICY = "security.pki.sha1_enforcement_level"; -const PREF_SHA1_POLICY_SET_BY_ADDON = "disableSHA1.rollout.policySetByAddOn"; -const PREF_SHA1_POLICY_RESET_BY_USER = "disableSHA1.rollout.userResetPref"; - -const SHA1_MODE_ALLOW = 0; -const SHA1_MODE_FORBID = 1; -const SHA1_MODE_IMPORTED_ROOTS_ONLY = 3; -const SHA1_MODE_CURRENT_DEFAULT = 4; - -function startup() { - Preferences.observe(PREF_SHA1_POLICY, policyPreferenceChanged); -} - -function install() { - let updateChannel = UpdateUtils.getUpdateChannel(false); - if (updateChannel in TEST_THRESHOLD) { - makeRequest().then(defineCohort).catch((e) => console.error(e)); - } -} - -function policyPreferenceChanged() { - let currentPrefValue = Preferences.get(PREF_SHA1_POLICY, - SHA1_MODE_CURRENT_DEFAULT); - Preferences.reset(PREF_SHA1_POLICY_RESET_BY_USER); - if (currentPrefValue == SHA1_MODE_CURRENT_DEFAULT) { - Preferences.set(PREF_SHA1_POLICY_RESET_BY_USER, true); - } -} - -function defineCohort(result) { - let userOptedOut = optedOut(); - let userOptedIn = optedIn(); - let shouldNotDisableSHA1Because = reasonToNotDisableSHA1(result); - let safeToDisableSHA1 = shouldNotDisableSHA1Because.length == 0; - let updateChannel = UpdateUtils.getUpdateChannel(false); - let testGroup = getUserSample() < TEST_THRESHOLD[updateChannel]; - - let cohortName; - if (!safeToDisableSHA1) { - cohortName = "notSafeToDisableSHA1"; - } else if (userOptedOut) { - cohortName = "optedOut"; - } else if (userOptedIn) { - cohortName = "optedIn"; - } else if (testGroup) { - cohortName = "test"; - Preferences.ignore(PREF_SHA1_POLICY, policyPreferenceChanged); - Preferences.set(PREF_SHA1_POLICY, SHA1_MODE_IMPORTED_ROOTS_ONLY); - Preferences.observe(PREF_SHA1_POLICY, policyPreferenceChanged); - Preferences.set(PREF_SHA1_POLICY_SET_BY_ADDON, true); - } else { - cohortName = "control"; - } - Preferences.set(PREF_COHORT_NAME, cohortName); - reportTelemetry(result, cohortName, shouldNotDisableSHA1Because, - cohortName == "test"); -} - -function shutdown(data, reason) { - Preferences.ignore(PREF_SHA1_POLICY, policyPreferenceChanged); -} - -function uninstall() { -} - -function getUserSample() { - let prefValue = Preferences.get(PREF_COHORT_SAMPLE, undefined); - let value = 0.0; - - if (typeof(prefValue) == "string") { - value = parseFloat(prefValue, 10); - return value; - } - - value = Math.random(); - - Preferences.set(PREF_COHORT_SAMPLE, value.toString().substr(0, 8)); - return value; -} - -function reportTelemetry(result, cohortName, didNotDisableSHA1Because, - disabledSHA1) { - result.cohortName = cohortName; - result.disabledSHA1 = disabledSHA1; - if (cohortName == "optedOut") { - let userResetPref = Preferences.get(PREF_SHA1_POLICY_RESET_BY_USER, false); - let currentPrefValue = Preferences.get(PREF_SHA1_POLICY, - SHA1_MODE_CURRENT_DEFAULT); - if (userResetPref) { - didNotDisableSHA1Because = "preference:userReset"; - } else if (currentPrefValue == SHA1_MODE_ALLOW) { - didNotDisableSHA1Because = "preference:allow"; - } else { - didNotDisableSHA1Because = "preference:forbid"; - } - } - result.didNotDisableSHA1Because = didNotDisableSHA1Because; - return TelemetryController.submitExternalPing("disableSHA1rollout", result, - {}); -} - -function optedIn() { - let policySetByAddOn = Preferences.get(PREF_SHA1_POLICY_SET_BY_ADDON, false); - let currentPrefValue = Preferences.get(PREF_SHA1_POLICY, - SHA1_MODE_CURRENT_DEFAULT); - return currentPrefValue == SHA1_MODE_IMPORTED_ROOTS_ONLY && !policySetByAddOn; -} - -function optedOut() { - // Users can also opt-out by setting the policy to always allow or always - // forbid SHA-1, or by resetting the preference after this add-on has changed - // it (in that case, this will be reported the next time this add-on is - // updated). - let currentPrefValue = Preferences.get(PREF_SHA1_POLICY, - SHA1_MODE_CURRENT_DEFAULT); - let userResetPref = Preferences.get(PREF_SHA1_POLICY_RESET_BY_USER, false); - return currentPrefValue == SHA1_MODE_ALLOW || - currentPrefValue == SHA1_MODE_FORBID || - userResetPref; -} - -function delocalizeAlgorithm(localizedString) { - let bundle = Services.strings.createBundle( - "chrome://pipnss/locale/pipnss.properties"); - let algorithmStringIdsToOIDDescriptionMap = { - "CertDumpMD2WithRSA": "md2WithRSAEncryption", - "CertDumpMD5WithRSA": "md5WithRSAEncryption", - "CertDumpSHA1WithRSA": "sha1WithRSAEncryption", - "CertDumpSHA256WithRSA": "sha256WithRSAEncryption", - "CertDumpSHA384WithRSA": "sha384WithRSAEncryption", - "CertDumpSHA512WithRSA": "sha512WithRSAEncryption", - "CertDumpAnsiX962ECDsaSignatureWithSha1": "ecdsaWithSHA1", - "CertDumpAnsiX962ECDsaSignatureWithSha224": "ecdsaWithSHA224", - "CertDumpAnsiX962ECDsaSignatureWithSha256": "ecdsaWithSHA256", - "CertDumpAnsiX962ECDsaSignatureWithSha384": "ecdsaWithSHA384", - "CertDumpAnsiX962ECDsaSignatureWithSha512": "ecdsaWithSHA512", - }; - - let description; - Object.keys(algorithmStringIdsToOIDDescriptionMap).forEach((l10nID) => { - let candidateLocalizedString = bundle.GetStringFromName(l10nID); - if (localizedString == candidateLocalizedString) { - description = algorithmStringIdsToOIDDescriptionMap[l10nID]; - } - }); - if (!description) { - return "unknown"; - } - return description; -} - -function getSignatureAlgorithm(cert) { - // Certificate ::= SEQUENCE { - // tbsCertificate TBSCertificate, - // signatureAlgorithm AlgorithmIdentifier, - // signatureValue BIT STRING } - let certificate = cert.ASN1Structure.QueryInterface(Ci.nsIASN1Sequence); - let signatureAlgorithm = certificate.ASN1Objects - .queryElementAt(1, Ci.nsIASN1Sequence); - // AlgorithmIdentifier ::= SEQUENCE { - // algorithm OBJECT IDENTIFIER, - // parameters ANY DEFINED BY algorithm OPTIONAL } - - // If parameters is NULL (or empty), signatureAlgorithm won't be a container - // under this implementation. Just get its displayValue. - if (!signatureAlgorithm.isValidContainer) { - return signatureAlgorithm.displayValue; - } - let oid = signatureAlgorithm.ASN1Objects.queryElementAt(0, Ci.nsIASN1Object); - return oid.displayValue; -} - -function processCertChain(chain) { - let output = []; - let enumerator = chain.getEnumerator(); - while (enumerator.hasMoreElements()) { - let cert = enumerator.getNext().QueryInterface(Ci.nsIX509Cert); - output.push({ - sha256Fingerprint: cert.sha256Fingerprint.replace(/:/g, "").toLowerCase(), - isBuiltInRoot: cert.isBuiltInRoot, - signatureAlgorithm: delocalizeAlgorithm(getSignatureAlgorithm(cert)), - }); - } - return output; -} - -class CertificateVerificationResult { - constructor(resolve) { - this.resolve = resolve; - } - - verifyCertFinished(aPRErrorCode, aVerifiedChain, aEVStatus) { - let result = { errorCode: aPRErrorCode, error: "", chain: [] }; - if (aPRErrorCode == 0) { - result.chain = processCertChain(aVerifiedChain); - } else { - result.error = "certificate reverification"; - } - this.resolve(result); - } -} - -function makeRequest() { - return new Promise((resolve) => { - let hostname = "telemetry.mozilla.org"; - let req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"] - .createInstance(Ci.nsIXMLHttpRequest); - req.open("GET", "https://" + hostname); - req.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE; - req.timeout = 30000; - req.addEventListener("error", (evt) => { - // If we can't connect to telemetry.mozilla.org, then how did we even - // download the experiment? In any case, we may still be able to get some - // information. - let result = { error: "connection error" }; - if (evt.target.channel && evt.target.channel.securityInfo) { - let securityInfo = evt.target.channel.securityInfo - .QueryInterface(Ci.nsITransportSecurityInfo); - if (securityInfo) { - result.errorCode = securityInfo.errorCode; - } - if (securityInfo && securityInfo.failedCertChain) { - result.chain = processCertChain(securityInfo.failedCertChain); - } - } - resolve(result); - }); - req.addEventListener("timeout", (evt) => { - resolve({ error: "timeout" }); - }); - req.addEventListener("load", (evt) => { - let securityInfo = evt.target.channel.securityInfo - .QueryInterface(Ci.nsITransportSecurityInfo); - if (securityInfo.securityState & - Ci.nsIWebProgressListener.STATE_CERT_USER_OVERRIDDEN) { - resolve({ error: "user override" }); - return; - } - let sslStatus = securityInfo.QueryInterface(Ci.nsISSLStatusProvider) - .SSLStatus; - let certdb = Cc["@mozilla.org/security/x509certdb;1"] - .getService(Ci.nsIX509CertDB); - let result = new CertificateVerificationResult(resolve); - // Unfortunately, we don't have direct access to the verified certificate - // chain as built by the AuthCertificate hook, so we have to re-build it - // here. In theory we are likely to get the same result. - certdb.asyncVerifyCertAtTime(sslStatus.serverCert, - 2, // certificateUsageSSLServer - 0, // flags - hostname, - Date.now() / 1000, - result); - }); - req.send(); - }); -} - -// As best we know, it is safe to disable SHA1 if the connection was successful -// and either the connection was MITM'd by a root not in the public PKI or the -// chain is part of the public PKI and is the one served by the real -// telemetry.mozilla.org. -// This will return a short string description of why it might not be safe to -// disable SHA1 or an empty string if it is safe to disable SHA1. -function reasonToNotDisableSHA1(result) { - if (!("errorCode" in result) || result.errorCode != 0) { - return "connection error"; - } - if (!("chain" in result)) { - return "code error"; - } - if (!result.chain[result.chain.length - 1].isBuiltInRoot) { - return ""; - } - if (result.chain.length != 3) { - return "MITM"; - } - const kEndEntityFingerprint = "197feaf3faa0f0ad637a89c97cb91336bfc114b6b3018203cbd9c3d10c7fa86c"; - const kIntermediateFingerprint = "154c433c491929c5ef686e838e323664a00e6a0d822ccc958fb4dab03e49a08f"; - const kRootFingerprint = "4348a0e9444c78cb265e058d5e8944b4d84f9662bd26db257f8934a443c70161"; - if (result.chain[0].sha256Fingerprint != kEndEntityFingerprint || - result.chain[1].sha256Fingerprint != kIntermediateFingerprint || - result.chain[2].sha256Fingerprint != kRootFingerprint) { - return "MITM"; - } - return ""; -}
deleted file mode 100644 --- a/browser/extensions/disableSHA1rollout/install.rdf.in +++ /dev/null @@ -1,32 +0,0 @@ -<?xml version="1.0"?> -<!-- 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/. --> - -#filter substitution - -<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:em="http://www.mozilla.org/2004/em-rdf#"> - - <Description about="urn:mozilla:install-manifest"> - <em:id>disableSHA1rollout@mozilla.org</em:id> - <em:version>1.0</em:version> - <em:type>2</em:type> - <em:bootstrap>true</em:bootstrap> - <em:multiprocessCompatible>true</em:multiprocessCompatible> - - <!-- Target Application this theme can install into, - with minimum and maximum supported versions. --> - <em:targetApplication> - <Description> - <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id> - <em:minVersion>@MOZ_APP_VERSION@</em:minVersion> - <em:maxVersion>@MOZ_APP_MAXVERSION@</em:maxVersion> - </Description> - </em:targetApplication> - - <!-- Front End MetaData --> - <em:name>SHA-1 deprecation staged rollout</em:name> - <em:description>Staged rollout deprecating SHA-1 in certificate signatures.</em:description> - </Description> -</RDF>
deleted file mode 100644 --- a/browser/extensions/disableSHA1rollout/moz.build +++ /dev/null @@ -1,16 +0,0 @@ -# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -DEFINES['MOZ_APP_VERSION'] = CONFIG['MOZ_APP_VERSION'] -DEFINES['MOZ_APP_MAXVERSION'] = CONFIG['MOZ_APP_MAXVERSION'] - -FINAL_TARGET_FILES.features['disableSHA1rollout@mozilla.org'] += [ - 'bootstrap.js' -] - -FINAL_TARGET_PP_FILES.features['disableSHA1rollout@mozilla.org'] += [ - 'install.rdf.in' -]
--- a/browser/extensions/formautofill/FormAutofillContent.jsm +++ b/browser/extensions/formautofill/FormAutofillContent.jsm @@ -266,19 +266,20 @@ var FormAutofillContent = { Services.cpmm.addMessageListener("FormAutofill:enabledStatus", (result) => { if (result.data) { ProfileAutocomplete.ensureRegistered(); } else { ProfileAutocomplete.ensureUnregistered(); } }); - Services.cpmm.sendAsyncMessage("FormAutofill:getEnabledStatus"); - // TODO: use initialProcessData: - // Services.cpmm.initialProcessData.autofillEnabled + + if (Services.cpmm.initialProcessData.autofillEnabled) { + ProfileAutocomplete.ensureRegistered(); + } }, /** * Get the input's information from cache which is created after page identified. * * @param {HTMLInputElement} element Focused input which triggered profile searching * @returns {Object|null} * Return target input's information that cloned from content cache
--- a/browser/extensions/formautofill/FormAutofillParent.jsm +++ b/browser/extensions/formautofill/FormAutofillParent.jsm @@ -75,21 +75,20 @@ FormAutofillParent.prototype = { this._profileStore = new ProfileStorage(storePath); this._profileStore.initialize(); Services.obs.addObserver(this, "advanced-pane-loaded", false); // Observing the pref and storage changes Services.prefs.addObserver(ENABLED_PREF, this, false); Services.obs.addObserver(this, "formautofill-storage-changed", false); - this._enabled = this._getStatus(); + // Force to trigger the onStatusChanged function for setting listeners properly // while initizlization - this._onStatusChanged(); - Services.ppmm.addMessageListener("FormAutofill:getEnabledStatus", this); + this._setStatus(this._getStatus()); }, observe(subject, topic, data) { log.debug("observe:", topic, "with data:", data); switch (topic) { case "advanced-pane-loaded": { let formAutofillPreferences = new FormAutofillPreferences(); let document = subject.document; @@ -99,32 +98,30 @@ FormAutofillParent.prototype = { parentNode.insertBefore(prefGroup, insertBeforeNode); break; } case "nsPref:changed": { // Observe pref changes and update _enabled cache if status is changed. let currentStatus = this._getStatus(); if (currentStatus !== this._enabled) { - this._enabled = currentStatus; - this._onStatusChanged(); + this._setStatus(currentStatus); } break; } case "formautofill-storage-changed": { // Early exit if the action is not "add" nor "remove" if (data != "add" && data != "remove") { break; } let currentStatus = this._getStatus(); if (currentStatus !== this._enabled) { - this._enabled = currentStatus; - this._onStatusChanged(); + this._setStatus(currentStatus); } break; } default: { throw new Error(`FormAutofillParent: Unexpected topic observed: ${topic}`); } } @@ -138,16 +135,19 @@ FormAutofillParent.prototype = { log.debug("_onStatusChanged: Status changed to", this._enabled); if (this._enabled) { Services.ppmm.addMessageListener("FormAutofill:GetProfiles", this); } else { Services.ppmm.removeMessageListener("FormAutofill:GetProfiles", this); } Services.ppmm.broadcastAsyncMessage("FormAutofill:enabledStatus", this._enabled); + // Sync process data autofillEnabled to make sure the value up to date + // no matter when the new content process is initialized. + Services.ppmm.initialProcessData.autofillEnabled = this._enabled; }, /** * Query pref and storage status to determine the overall status for * form autofill feature. * * @returns {boolean} status of form autofill feature */ @@ -155,31 +155,37 @@ FormAutofillParent.prototype = { if (!Services.prefs.getBoolPref(ENABLED_PREF)) { return false; } return this._profileStore.getAll().length > 0; }, /** + * Set status and trigger _onStatusChanged. + * + * @param {boolean} newStatus The latest status we want to set for _enabled + */ + _setStatus(newStatus) { + this._enabled = newStatus; + this._onStatusChanged(); + }, + + /** * Handles the message coming from FormAutofillContent. * * @param {string} message.name The name of the message. * @param {object} message.data The data of the message. * @param {nsIFrameMessageManager} message.target Caller's message manager. */ receiveMessage({name, data, target}) { switch (name) { case "FormAutofill:GetProfiles": this._getProfiles(data, target); break; - case "FormAutofill:getEnabledStatus": - Services.ppmm.broadcastAsyncMessage("FormAutofill:enabledStatus", - this._enabled); - break; } }, /** * Returns the instance of ProfileStorage. To avoid syncing issues, anyone * who needs to access the profile should request the instance by this instead * of creating a new one. *
--- a/browser/extensions/formautofill/ProfileAutoCompleteResult.jsm +++ b/browser/extensions/formautofill/ProfileAutoCompleteResult.jsm @@ -64,21 +64,21 @@ ProfileAutoCompleteResult.prototype = { // An array of primary and secondary labels for each profiles. _popupLabels: null, /** * @returns {number} The number of results */ get matchCount() { - return this._matchingProfiles.length; + return this._popupLabels.length; }, _checkIndexBounds(index) { - if (index < 0 || index >= this._matchingProfiles.length) { + if (index < 0 || index >= this._popupLabels.length) { throw Components.Exception("Index out of range.", Cr.NS_ERROR_ILLEGAL_VALUE); } }, /** * Get the secondary label based on the focused field name and related field names * in the same form. * @param {string} focusedFieldName The field name of the focused input @@ -118,17 +118,20 @@ ProfileAutoCompleteResult.prototype = { return profile[currentFieldName]; } } return ""; // Nothing matched. }, _generateLabels(focusedFieldName, allFieldNames, profiles) { - return profiles.map(profile => { + // Skip results without a primary label. + return profiles.filter(profile => { + return !!profile[focusedFieldName]; + }).map(profile => { return { primary: profile[focusedFieldName], secondary: this._getSecondaryLabel(focusedFieldName, allFieldNames, profile), }; }); },
--- a/browser/extensions/formautofill/test/unit/test_enabledStatus.js +++ b/browser/extensions/formautofill/test/unit/test_enabledStatus.js @@ -3,60 +3,62 @@ */ "use strict"; Cu.import("resource://formautofill/FormAutofillParent.jsm"); add_task(function* test_enabledStatus_init() { let formAutofillParent = new FormAutofillParent(); - sinon.spy(formAutofillParent, "_onStatusChanged"); + sinon.spy(formAutofillParent, "_setStatus"); // Default status is false before initialization do_check_eq(formAutofillParent._enabled, false); + do_check_eq(Services.ppmm.initialProcessData.autofillEnabled, undefined); formAutofillParent.init(); - do_check_eq(formAutofillParent._onStatusChanged.called, true); + do_check_eq(formAutofillParent._setStatus.called, true); + do_check_eq(Services.ppmm.initialProcessData.autofillEnabled, false); formAutofillParent._uninit(); }); add_task(function* test_enabledStatus_observe() { let formAutofillParent = new FormAutofillParent(); sinon.stub(formAutofillParent, "_getStatus"); - sinon.spy(formAutofillParent, "_onStatusChanged"); + sinon.spy(formAutofillParent, "_setStatus"); // _enabled = _getStatus() => No need to trigger onStatusChanged formAutofillParent._enabled = true; formAutofillParent._getStatus.returns(true); formAutofillParent.observe(null, "nsPref:changed", "browser.formautofill.enabled"); - do_check_eq(formAutofillParent._onStatusChanged.called, false); + do_check_eq(formAutofillParent._setStatus.called, false); // _enabled != _getStatus() => Need to trigger onStatusChanged formAutofillParent._getStatus.returns(false); formAutofillParent.observe(null, "nsPref:changed", "browser.formautofill.enabled"); - do_check_eq(formAutofillParent._onStatusChanged.called, true); + do_check_eq(formAutofillParent._setStatus.called, true); // profile added => Need to trigger onStatusChanged formAutofillParent._getStatus.returns(!formAutofillParent._enabled); - formAutofillParent._onStatusChanged.reset(); + formAutofillParent._setStatus.reset(); formAutofillParent.observe(null, "formautofill-storage-changed", "add"); - do_check_eq(formAutofillParent._onStatusChanged.called, true); + do_check_eq(formAutofillParent._setStatus.called, true); // profile removed => Need to trigger onStatusChanged formAutofillParent._getStatus.returns(!formAutofillParent._enabled); - formAutofillParent._onStatusChanged.reset(); + formAutofillParent._setStatus.reset(); formAutofillParent.observe(null, "formautofill-storage-changed", "remove"); - do_check_eq(formAutofillParent._onStatusChanged.called, true); + do_check_eq(formAutofillParent._setStatus.called, true); // profile updated => no need to trigger onStatusChanged formAutofillParent._getStatus.returns(!formAutofillParent._enabled); - formAutofillParent._onStatusChanged.reset(); + formAutofillParent._setStatus.reset(); formAutofillParent.observe(null, "formautofill-storage-changed", "update"); - do_check_eq(formAutofillParent._onStatusChanged.called, false); + do_check_eq(formAutofillParent._setStatus.called, false); }); add_task(function* test_enabledStatus_getStatus() { let formAutofillParent = new FormAutofillParent(); do_register_cleanup(function cleanup() { Services.prefs.clearUserPref("browser.formautofill.enabled"); }); @@ -77,8 +79,24 @@ add_task(function* test_enabledStatus_ge // pref is enabled and profile is not empty. Services.prefs.setBoolPref("browser.formautofill.enabled", true); do_check_eq(formAutofillParent._getStatus(), true); // pref is disabled and profile is not empty. Services.prefs.setBoolPref("browser.formautofill.enabled", false); do_check_eq(formAutofillParent._getStatus(), false); }); + +add_task(function* test_enabledStatus_setStatus() { + let formAutofillParent = new FormAutofillParent(); + sinon.spy(formAutofillParent, "_onStatusChanged"); + + formAutofillParent._setStatus(true); + do_check_eq(formAutofillParent._enabled, true); + do_check_eq(Services.ppmm.initialProcessData.autofillEnabled, true); + do_check_eq(formAutofillParent._onStatusChanged.called, true); + + formAutofillParent._onStatusChanged.reset(); + formAutofillParent._setStatus(false); + do_check_eq(formAutofillParent._enabled, false); + do_check_eq(Services.ppmm.initialProcessData.autofillEnabled, false); + do_check_eq(formAutofillParent._onStatusChanged.called, true); +});
--- a/browser/extensions/formautofill/test/unit/test_profileAutocompleteResult.js +++ b/browser/extensions/formautofill/test/unit/test_profileAutocompleteResult.js @@ -7,21 +7,27 @@ let matchingProfiles = [{ organization: "Sesame Street", "street-address": "123 Sesame Street.", tel: "1-345-345-3456.", }, { guid: "test-guid-2", organization: "Mozilla", "street-address": "331 E. Evelyn Avenue", tel: "1-650-903-0800", +}, { + guid: "test-guid-3", + organization: "", + "street-address": "321, No Name St.", + tel: "1-000-000-0000", }]; let allFieldNames = ["street-address", "organization", "tel"]; let testCases = [{ + description: "Focus on an `organization` field", options: {}, matchingProfiles, allFieldNames, searchString: "", fieldName: "organization", expected: { searchResult: Ci.nsIAutoCompleteResult.RESULT_SUCCESS, defaultIndex: 0, @@ -41,16 +47,17 @@ let testCases = [{ label: JSON.stringify({ primary: "Mozilla", secondary: "331 E. Evelyn Avenue", }), image: "", }], }, }, { + description: "Focus on an `tel` field", options: {}, matchingProfiles, allFieldNames, searchString: "", fieldName: "tel", expected: { searchResult: Ci.nsIAutoCompleteResult.RESULT_SUCCESS, defaultIndex: 0, @@ -67,19 +74,29 @@ let testCases = [{ value: "1-650-903-0800", style: "autofill-profile", comment: JSON.stringify(matchingProfiles[1]), label: JSON.stringify({ primary: "1-650-903-0800", secondary: "331 E. Evelyn Avenue", }), image: "", + }, { + value: "1-000-000-0000", + style: "autofill-profile", + comment: JSON.stringify(matchingProfiles[2]), + label: JSON.stringify({ + primary: "1-000-000-0000", + secondary: "321, No Name St.", + }), + image: "", }], }, }, { + description: "Focus on an `street-address` field", options: {}, matchingProfiles, allFieldNames, searchString: "", fieldName: "street-address", expected: { searchResult: Ci.nsIAutoCompleteResult.RESULT_SUCCESS, defaultIndex: 0, @@ -96,50 +113,62 @@ let testCases = [{ value: "331 E. Evelyn Avenue", style: "autofill-profile", comment: JSON.stringify(matchingProfiles[1]), label: JSON.stringify({ primary: "331 E. Evelyn Avenue", secondary: "Mozilla", }), image: "", + }, { + value: "321, No Name St.", + style: "autofill-profile", + comment: JSON.stringify(matchingProfiles[2]), + label: JSON.stringify({ + primary: "321, No Name St.", + secondary: "1-000-000-0000", + }), + image: "", }], }, }, { + description: "No matching profiles", options: {}, matchingProfiles: [], allFieldNames, searchString: "", fieldName: "", expected: { searchResult: Ci.nsIAutoCompleteResult.RESULT_NOMATCH, defaultIndex: 0, items: [], }, }, { + description: "Search with failure", options: {resultCode: Ci.nsIAutoCompleteResult.RESULT_FAILURE}, matchingProfiles: [], allFieldNames, searchString: "", fieldName: "", expected: { searchResult: Ci.nsIAutoCompleteResult.RESULT_FAILURE, defaultIndex: 0, items: [], }, }]; add_task(function* test_all_patterns() { - testCases.forEach(pattern => { - let actual = new ProfileAutoCompleteResult(pattern.searchString, - pattern.fieldName, - pattern.allFieldNames, - pattern.matchingProfiles, - pattern.options); - let expectedValue = pattern.expected; + testCases.forEach(testCase => { + do_print("Starting testcase: " + testCase.description); + let actual = new ProfileAutoCompleteResult(testCase.searchString, + testCase.fieldName, + testCase.allFieldNames, + testCase.matchingProfiles, + testCase.options); + let expectedValue = testCase.expected; equal(actual.searchResult, expectedValue.searchResult); equal(actual.defaultIndex, expectedValue.defaultIndex); equal(actual.matchCount, expectedValue.items.length); expectedValue.items.forEach((item, index) => { equal(actual.getValueAt(index), item.value); equal(actual.getCommentAt(index), item.comment); equal(actual.getLabelAt(index), item.label); equal(actual.getStyleAt(index), item.style);
--- a/browser/extensions/moz.build +++ b/browser/extensions/moz.build @@ -1,17 +1,16 @@ # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- # vim: set filetype=python: # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. DIRS += [ 'aushelper', - 'disableSHA1rollout', 'e10srollout', 'pdfjs', 'pocket', 'webcompat', 'shield-recipe-client', ] # Only include the following system add-ons if building Aurora or Nightly
--- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -631,16 +631,17 @@ ; [Default Preferences] ; All the pref files must be part of base to prevent migration bugs @RESPATH@/browser/@PREF_DIR@/firefox.js @RESPATH@/browser/@PREF_DIR@/firefox-branding.js @RESPATH@/greprefs.js @RESPATH@/defaults/autoconfig/prefcalls.js @RESPATH@/browser/defaults/permissions +@RESPATH@/browser/defaults/blocklists ; Warning: changing the path to channel-prefs.js can cause bugs (Bug 756325) ; Technically this is an app pref file, but we are keeping it in the original ; gre location for now. @RESPATH@/defaults/pref/channel-prefs.js ; Services (gre) prefs @RESPATH@/defaults/pref/services-sync.js
--- a/browser/locales/en-US/chrome/browser/browser.dtd +++ b/browser/locales/en-US/chrome/browser/browser.dtd @@ -353,16 +353,24 @@ These should match what Safari and other <!ENTITY appMenuHistory.restoreSession.label "Restore Previous Session"> <!ENTITY appMenuHistory.viewSidebar.label "View History Sidebar"> <!ENTITY appMenuHelp.tooltip "Open Help Menu"> <!ENTITY appMenuRemoteTabs.label "Synced Tabs"> <!-- LOCALIZATION NOTE (appMenuRemoteTabs.notabs.label): This is shown beneath the name of a device when that device has no open tabs --> <!ENTITY appMenuRemoteTabs.notabs.label "No open tabs"> +<!-- LOCALIZATION NOTE (appMenuRemoteTabs.showMore.label, appMenuRemoteTabs.showMore.tooltip): + This is shown after the tabs list if we can display more tabs by clicking on the button --> +<!ENTITY appMenuRemoteTabs.showMore.label "Show More"> +<!ENTITY appMenuRemoteTabs.showMore.tooltip "Show more tabs from this device"> +<!-- LOCALIZATION NOTE (appMenuRemoteTabs.showAll.label, appMenuRemoteTabs.showAll.tooltip): + This is shown after the tabs list if we can all the remaining tabs by clicking on the button --> +<!ENTITY appMenuRemoteTabs.showAll.label "Show All"> +<!ENTITY appMenuRemoteTabs.showAll.tooltip "Show all tabs from this device"> <!-- LOCALIZATION NOTE (appMenuRemoteTabs.tabsnotsyncing.label): This is shown when Sync is configured but syncing tabs is disabled. --> <!ENTITY appMenuRemoteTabs.tabsnotsyncing.label "Turn on tab syncing to view a list of tabs from your other devices."> <!-- LOCALIZATION NOTE (appMenuRemoteTabs.noclients.label): This is shown when Sync is configured but this appears to be the only device attached to the account. We also show links to download Firefox for android/ios. --> <!ENTITY appMenuRemoteTabs.noclients.title "No synced tabs… yet!"> <!ENTITY appMenuRemoteTabs.noclients.subtitle "Want to see your tabs from other devices here?">
--- a/browser/locales/en-US/chrome/browser/preferences/preferences.properties +++ b/browser/locales/en-US/chrome/browser/preferences/preferences.properties @@ -202,22 +202,26 @@ removeAllSiteDataShown.label=Remove All removeAllSiteDataShown.accesskey=e spaceAlert.learnMoreButton.label=Learn More spaceAlert.learnMoreButton.accesskey=L spaceAlert.over5GB.prefButton.label=Open Preferences spaceAlert.over5GB.prefButton.accesskey=O # LOCALIZATION NOTE (spaceAlert.over5GB.prefButtonWin.label): On Windows Preferences is called Options spaceAlert.over5GB.prefButtonWin.label=Open Options spaceAlert.over5GB.prefButtonWin.accesskey=O -spaceAlert.over5GB.description=Firefox is running out of disk space. Website contents may not display properly. You can clear stored site data in Preferences > Advanced > Site Data. -# LOCALIZATION NOTE (spaceAlert.over5GB.descriptionWin): On Windows Preferences is called Options -spaceAlert.over5GB.descriptionWin=Firefox is running out of disk space. Website contents may not display properly. You can clear stored site data in Options > Advanced > Site Data. +# LOCALIZATION NOTE (spaceAlert.over5GB.message): %S = brandShortName +spaceAlert.over5GB.message=%S is running out of disk space. Website contents may not display properly. You can clear stored site data in Preferences > Advanced > Site Data. +# LOCALIZATION NOTE (spaceAlert.over5GB.messageWin): +# - On Windows Preferences is called Options +# - %S = brandShortName +spaceAlert.over5GB.messageWin=%S is running out of disk space. Website contents may not display properly. You can clear stored site data in Options > Advanced > Site Data. spaceAlert.under5GB.okButton.label=OK, Got it spaceAlert.under5GB.okButton.accesskey=K -spaceAlert.under5GB.description=Firefox is running out of disk space. Website contents may not display properly. Visit “Learn More” to optimize your disk usage for better browsing experience. +# LOCALIZATION NOTE (spaceAlert.under5GB.message): %S = brandShortName +spaceAlert.under5GB.message=%S is running out of disk space. Website contents may not display properly. Visit “Learn More” to optimize your disk usage for better browsing experience. # LOCALIZATION NOTE (featureEnableRequiresRestart, featureDisableRequiresRestart, restartTitle): %S = brandShortName featureEnableRequiresRestart=%S must restart to enable this feature. featureDisableRequiresRestart=%S must restart to disable this feature. shouldRestartTitle=Restart %S okToRestartButton=Restart %S now revertNoRestartButton=Revert
--- a/browser/themes/shared/customizableui/panelUI.inc.css +++ b/browser/themes/shared/customizableui/panelUI.inc.css @@ -1238,29 +1238,29 @@ menuitem.panel-subview-footer@menuStateA .subviewbutton > .menu-accel-container { -moz-box-pack: start; margin-inline-start: 10px; margin-inline-end: auto; color: GrayText; } -#PanelUI-remotetabs-tabslist > toolbarbutton, +#PanelUI-remotetabs-tabslist > toolbarbutton[itemtype="tab"], #PanelUI-historyItems > toolbarbutton { list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png"); } @media (min-resolution: 1.1dppx) { - #PanelUI-remotetabs-tabslist > toolbarbutton, + #PanelUI-remotetabs-tabslist > toolbarbutton[itemtype="tab"], #PanelUI-historyItems > toolbarbutton { list-style-image: url("chrome://mozapps/skin/places/defaultFavicon@2x.png"); } } -#PanelUI-remotetabs-tabslist > toolbarbutton > .toolbarbutton-icon, +#PanelUI-remotetabs-tabslist > toolbarbutton[itemtype="tab"] > .toolbarbutton-icon, #PanelUI-recentlyClosedWindows > toolbarbutton > .toolbarbutton-icon, #PanelUI-recentlyClosedTabs > toolbarbutton > .toolbarbutton-icon, #PanelUI-historyItems > toolbarbutton > .toolbarbutton-icon { width: 16px; height: 16px; } toolbarbutton[panel-multiview-anchor="true"],
--- a/build/moz.configure/warnings.configure +++ b/build/moz.configure/warnings.configure @@ -73,19 +73,16 @@ check_and_add_gcc_warning('-Wimplicit-fa # --enable-warnings-as-errors is specified so that no unexpected fatal # warnings are produced. check_and_add_gcc_warning('-Werror=non-literal-null-conversion', when='--enable-warnings-as-errors') # catches string literals used in boolean expressions check_and_add_gcc_warning('-Wstring-conversion') -# catches inconsistent use of mutexes -check_and_add_gcc_warning('-Wthread-safety') - # we inline 'new' and 'delete' in mozalloc check_and_add_gcc_warning('-Wno-inline-new-delete', cxx_compiler) # Prevent the following GCC warnings from being treated as errors: # too many false positives check_and_add_gcc_warning('-Wno-error=maybe-uninitialized') # we don't want our builds held hostage when a platform-specific API
--- a/caps/moz.build +++ b/caps/moz.build @@ -36,16 +36,17 @@ EXPORTS.mozilla = [ SOURCES += [ # Compile this separately since nsExceptionHandler.h conflicts # with something from nsNullPrincipal.cpp. 'BasePrincipal.cpp', ] UNIFIED_SOURCES += [ 'DomainPolicy.cpp', + 'nsExpandedPrincipal.cpp', 'nsJSPrincipals.cpp', 'nsNullPrincipal.cpp', 'nsNullPrincipalURI.cpp', 'nsPrincipal.cpp', 'nsScriptSecurityManager.cpp', 'nsSystemPrincipal.cpp', ]
copy from caps/nsPrincipal.cpp copy to caps/nsExpandedPrincipal.cpp --- a/caps/nsPrincipal.cpp +++ b/caps/nsExpandedPrincipal.cpp @@ -1,471 +1,19 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 sw=2 et tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "nsPrincipal.h" - -#include "mozIThirdPartyUtil.h" -#include "nscore.h" -#include "nsScriptSecurityManager.h" -#include "nsString.h" -#include "nsReadableUtils.h" -#include "pratom.h" -#include "nsIURI.h" -#include "nsIURL.h" -#include "nsIStandardURL.h" -#include "nsIURIWithPrincipal.h" -#include "nsJSPrincipals.h" -#include "nsIEffectiveTLDService.h" +#include "nsExpandedPrincipal.h" #include "nsIClassInfoImpl.h" -#include "nsIProtocolHandler.h" -#include "nsError.h" -#include "nsIContentSecurityPolicy.h" -#include "nsNetCID.h" -#include "jswrapper.h" - -#include "mozilla/dom/nsCSPContext.h" -#include "mozilla/dom/ScriptSettings.h" -#include "mozilla/Preferences.h" -#include "mozilla/HashFunctions.h" using namespace mozilla; -static bool gCodeBasePrincipalSupport = false; - -static bool URIIsImmutable(nsIURI* aURI) -{ - nsCOMPtr<nsIMutable> mutableObj(do_QueryInterface(aURI)); - bool isMutable; - return - mutableObj && - NS_SUCCEEDED(mutableObj->GetMutable(&isMutable)) && - !isMutable; -} - -NS_IMPL_CLASSINFO(nsPrincipal, nullptr, nsIClassInfo::MAIN_THREAD_ONLY, - NS_PRINCIPAL_CID) -NS_IMPL_QUERY_INTERFACE_CI(nsPrincipal, - nsIPrincipal, - nsISerializable) -NS_IMPL_CI_INTERFACE_GETTER(nsPrincipal, - nsIPrincipal, - nsISerializable) - -// Called at startup: -/* static */ void -nsPrincipal::InitializeStatics() -{ - Preferences::AddBoolVarCache(&gCodeBasePrincipalSupport, - "signed.applets.codebase_principal_support", - false); -} - -nsPrincipal::nsPrincipal() - : mCodebaseImmutable(false) - , mDomainImmutable(false) - , mInitialized(false) -{ } - -nsPrincipal::~nsPrincipal() -{ - // let's clear the principal within the csp to avoid a tangling pointer - if (mCSP) { - static_cast<nsCSPContext*>(mCSP.get())->clearLoadingPrincipal(); - } -} - -nsresult -nsPrincipal::Init(nsIURI *aCodebase, const OriginAttributes& aOriginAttributes) -{ - NS_ENSURE_STATE(!mInitialized); - NS_ENSURE_ARG(aCodebase); - - mInitialized = true; - - mCodebase = NS_TryToMakeImmutable(aCodebase); - mCodebaseImmutable = URIIsImmutable(mCodebase); - mOriginAttributes = aOriginAttributes; - - return NS_OK; -} - -nsresult -nsPrincipal::GetScriptLocation(nsACString &aStr) -{ - return mCodebase->GetSpec(aStr); -} - -nsresult -nsPrincipal::GetOriginInternal(nsACString& aOrigin) -{ - if (!mCodebase) { - return NS_ERROR_FAILURE; - } - - nsCOMPtr<nsIURI> origin = NS_GetInnermostURI(mCodebase); - if (!origin) { - return NS_ERROR_FAILURE; - } - - nsAutoCString hostPort; - - // chrome: URLs don't have a meaningful origin, so make - // sure we just get the full spec for them. - // XXX this should be removed in favor of the solution in - // bug 160042. - bool isChrome; - nsresult rv = origin->SchemeIs("chrome", &isChrome); - if (NS_SUCCEEDED(rv) && !isChrome) { - rv = origin->GetAsciiHostPort(hostPort); - // Some implementations return an empty string, treat it as no support - // for asciiHost by that implementation. - if (hostPort.IsEmpty()) { - rv = NS_ERROR_FAILURE; - } - } - - // We want the invariant that prinA.origin == prinB.origin i.f.f. - // prinA.equals(prinB). However, this requires that we impose certain constraints - // on the behavior and origin semantics of principals, and in particular, forbid - // creating origin strings for principals whose equality constraints are not - // expressible as strings (i.e. object equality). Moreover, we want to forbid URIs - // containing the magic "^" we use as a separating character for origin - // attributes. - // - // These constraints can generally be achieved by restricting .origin to - // nsIStandardURL-based URIs, but there are a few other URI schemes that we need - // to handle. - bool isBehaved; - if ((NS_SUCCEEDED(origin->SchemeIs("about", &isBehaved)) && isBehaved) || - (NS_SUCCEEDED(origin->SchemeIs("moz-safe-about", &isBehaved)) && isBehaved) || - (NS_SUCCEEDED(origin->SchemeIs("indexeddb", &isBehaved)) && isBehaved)) { - rv = origin->GetAsciiSpec(aOrigin); - NS_ENSURE_SUCCESS(rv, rv); - // These URIs could technically contain a '^', but they never should. - if (NS_WARN_IF(aOrigin.FindChar('^', 0) != -1)) { - aOrigin.Truncate(); - return NS_ERROR_FAILURE; - } - return NS_OK; - } - - if (NS_SUCCEEDED(rv) && !isChrome) { - rv = origin->GetScheme(aOrigin); - NS_ENSURE_SUCCESS(rv, rv); - aOrigin.AppendLiteral("://"); - aOrigin.Append(hostPort); - return NS_OK; - } - - // This URL can be a blobURL. In this case, we should use the 'parent' - // principal instead. - nsCOMPtr<nsIURIWithPrincipal> uriWithPrincipal = do_QueryInterface(origin); - if (uriWithPrincipal) { - nsCOMPtr<nsIPrincipal> uriPrincipal; - rv = uriWithPrincipal->GetPrincipal(getter_AddRefs(uriPrincipal)); - NS_ENSURE_SUCCESS(rv, rv); - - if (uriPrincipal) { - return uriPrincipal->GetOriginNoSuffix(aOrigin); - } - } - - // If we reached this branch, we can only create an origin if we have a - // nsIStandardURL. So, we query to a nsIStandardURL, and fail if we aren't - // an instance of an nsIStandardURL nsIStandardURLs have the good property - // of escaping the '^' character in their specs, which means that we can be - // sure that the caret character (which is reserved for delimiting the end - // of the spec, and the beginning of the origin attributes) is not present - // in the origin string - nsCOMPtr<nsIStandardURL> standardURL = do_QueryInterface(origin); - NS_ENSURE_TRUE(standardURL, NS_ERROR_FAILURE); - - rv = origin->GetAsciiSpec(aOrigin); - NS_ENSURE_SUCCESS(rv, rv); - - // The origin, when taken from the spec, should not contain the ref part of - // the URL. - - int32_t pos = aOrigin.FindChar('?'); - int32_t hashPos = aOrigin.FindChar('#'); - - if (hashPos != kNotFound && (pos == kNotFound || hashPos < pos)) { - pos = hashPos; - } - - if (pos != kNotFound) { - aOrigin.Truncate(pos); - } - - return NS_OK; -} - -bool -nsPrincipal::SubsumesInternal(nsIPrincipal* aOther, - BasePrincipal::DocumentDomainConsideration aConsideration) -{ - MOZ_ASSERT(aOther); - - // For nsPrincipal, Subsumes is equivalent to Equals. - if (aOther == this) { - return true; - } - - // If either the subject or the object has changed its principal by - // explicitly setting document.domain then the other must also have - // done so in order to be considered the same origin. This prevents - // DNS spoofing based on document.domain (154930) - nsresult rv; - if (aConsideration == ConsiderDocumentDomain) { - // Get .domain on each principal. - nsCOMPtr<nsIURI> thisDomain, otherDomain; - GetDomain(getter_AddRefs(thisDomain)); - aOther->GetDomain(getter_AddRefs(otherDomain)); - - // If either has .domain set, we have equality i.f.f. the domains match. - // Otherwise, we fall through to the non-document-domain-considering case. - if (thisDomain || otherDomain) { - return nsScriptSecurityManager::SecurityCompareURIs(thisDomain, otherDomain); - } - } - - nsCOMPtr<nsIURI> otherURI; - rv = aOther->GetURI(getter_AddRefs(otherURI)); - NS_ENSURE_SUCCESS(rv, false); - - // Compare codebases. - return nsScriptSecurityManager::SecurityCompareURIs(mCodebase, otherURI); -} - -NS_IMETHODIMP -nsPrincipal::GetURI(nsIURI** aURI) -{ - if (mCodebaseImmutable) { - NS_ADDREF(*aURI = mCodebase); - return NS_OK; - } - - if (!mCodebase) { - *aURI = nullptr; - return NS_OK; - } - - return NS_EnsureSafeToReturn(mCodebase, aURI); -} - -bool -nsPrincipal::MayLoadInternal(nsIURI* aURI) -{ - // See if aURI is something like a Blob URI that is actually associated with - // a principal. - nsCOMPtr<nsIURIWithPrincipal> uriWithPrin = do_QueryInterface(aURI); - nsCOMPtr<nsIPrincipal> uriPrin; - if (uriWithPrin) { - uriWithPrin->GetPrincipal(getter_AddRefs(uriPrin)); - } - if (uriPrin) { - return nsIPrincipal::Subsumes(uriPrin); - } - - // If this principal is associated with an addon, check whether that addon - // has been given permission to load from this domain. - if (AddonAllowsLoad(aURI)) { - return true; - } - - if (nsScriptSecurityManager::SecurityCompareURIs(mCodebase, aURI)) { - return true; - } - - // If strict file origin policy is in effect, local files will always fail - // SecurityCompareURIs unless they are identical. Explicitly check file origin - // policy, in that case. - if (nsScriptSecurityManager::GetStrictFileOriginPolicy() && - NS_URIIsLocalFile(aURI) && - NS_RelaxStrictFileOriginPolicy(aURI, mCodebase)) { - return true; - } - - return false; -} - -void -nsPrincipal::SetURI(nsIURI* aURI) -{ - mCodebase = NS_TryToMakeImmutable(aURI); - mCodebaseImmutable = URIIsImmutable(mCodebase); -} - -NS_IMETHODIMP -nsPrincipal::GetHashValue(uint32_t* aValue) -{ - NS_PRECONDITION(mCodebase, "Need a codebase"); - - *aValue = nsScriptSecurityManager::HashPrincipalByOrigin(this); - return NS_OK; -} - -NS_IMETHODIMP -nsPrincipal::GetDomain(nsIURI** aDomain) -{ - if (!mDomain) { - *aDomain = nullptr; - return NS_OK; - } - - if (mDomainImmutable) { - NS_ADDREF(*aDomain = mDomain); - return NS_OK; - } - - return NS_EnsureSafeToReturn(mDomain, aDomain); -} - -NS_IMETHODIMP -nsPrincipal::SetDomain(nsIURI* aDomain) -{ - mDomain = NS_TryToMakeImmutable(aDomain); - mDomainImmutable = URIIsImmutable(mDomain); - - // Recompute all wrappers between compartments using this principal and other - // non-chrome compartments. - AutoSafeJSContext cx; - JSPrincipals *principals = nsJSPrincipals::get(static_cast<nsIPrincipal*>(this)); - bool success = js::RecomputeWrappers(cx, js::ContentCompartmentsOnly(), - js::CompartmentsWithPrincipals(principals)); - NS_ENSURE_TRUE(success, NS_ERROR_FAILURE); - success = js::RecomputeWrappers(cx, js::CompartmentsWithPrincipals(principals), - js::ContentCompartmentsOnly()); - NS_ENSURE_TRUE(success, NS_ERROR_FAILURE); - - return NS_OK; -} - -NS_IMETHODIMP -nsPrincipal::GetBaseDomain(nsACString& aBaseDomain) -{ - // For a file URI, we return the file path. - if (NS_URIIsLocalFile(mCodebase)) { - nsCOMPtr<nsIURL> url = do_QueryInterface(mCodebase); - - if (url) { - return url->GetFilePath(aBaseDomain); - } - } - - bool hasNoRelativeFlag; - nsresult rv = NS_URIChainHasFlags(mCodebase, - nsIProtocolHandler::URI_NORELATIVE, - &hasNoRelativeFlag); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (hasNoRelativeFlag) { - return mCodebase->GetSpec(aBaseDomain); - } - - // For everything else, we ask the TLD service via - // the ThirdPartyUtil. - nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil = - do_GetService(THIRDPARTYUTIL_CONTRACTID); - if (thirdPartyUtil) { - return thirdPartyUtil->GetBaseDomain(mCodebase, aBaseDomain); - } - - return NS_OK; -} - -NS_IMETHODIMP -nsPrincipal::Read(nsIObjectInputStream* aStream) -{ - nsCOMPtr<nsISupports> supports; - nsCOMPtr<nsIURI> codebase; - nsresult rv = NS_ReadOptionalObject(aStream, true, getter_AddRefs(supports)); - if (NS_FAILED(rv)) { - return rv; - } - - codebase = do_QueryInterface(supports); - - nsCOMPtr<nsIURI> domain; - rv = NS_ReadOptionalObject(aStream, true, getter_AddRefs(supports)); - if (NS_FAILED(rv)) { - return rv; - } - - domain = do_QueryInterface(supports); - - nsAutoCString suffix; - rv = aStream->ReadCString(suffix); - NS_ENSURE_SUCCESS(rv, rv); - - OriginAttributes attrs; - bool ok = attrs.PopulateFromSuffix(suffix); - NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE); - - rv = NS_ReadOptionalObject(aStream, true, getter_AddRefs(supports)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = Init(codebase, attrs); - NS_ENSURE_SUCCESS(rv, rv); - - mCSP = do_QueryInterface(supports, &rv); - // make sure setRequestContext is called after Init(), - // to make sure the principals URI been initalized. - if (mCSP) { - mCSP->SetRequestContext(nullptr, this); - } - - SetDomain(domain); - - return NS_OK; -} - -NS_IMETHODIMP -nsPrincipal::Write(nsIObjectOutputStream* aStream) -{ - NS_ENSURE_STATE(mCodebase); - nsresult rv = NS_WriteOptionalCompoundObject(aStream, mCodebase, NS_GET_IID(nsIURI), - true); - if (NS_FAILED(rv)) { - return rv; - } - - rv = NS_WriteOptionalCompoundObject(aStream, mDomain, NS_GET_IID(nsIURI), - true); - if (NS_FAILED(rv)) { - return rv; - } - - nsAutoCString suffix; - OriginAttributesRef().CreateSuffix(suffix); - - rv = aStream->WriteStringZ(suffix.get()); - NS_ENSURE_SUCCESS(rv, rv); - - rv = NS_WriteOptionalCompoundObject(aStream, mCSP, - NS_GET_IID(nsIContentSecurityPolicy), - true); - if (NS_FAILED(rv)) { - return rv; - } - - // mCodebaseImmutable and mDomainImmutable will be recomputed based - // on the deserialized URIs in Read(). - - return NS_OK; -} - -/************************************************************************************************************************/ - NS_IMPL_CLASSINFO(nsExpandedPrincipal, nullptr, nsIClassInfo::MAIN_THREAD_ONLY, NS_EXPANDEDPRINCIPAL_CID) NS_IMPL_QUERY_INTERFACE_CI(nsExpandedPrincipal, nsIPrincipal, nsIExpandedPrincipal) NS_IMPL_CI_INTERFACE_GETTER(nsExpandedPrincipal, nsIPrincipal, nsIExpandedPrincipal)
copy from caps/nsPrincipal.h copy to caps/nsExpandedPrincipal.h --- a/caps/nsPrincipal.h +++ b/caps/nsExpandedPrincipal.h @@ -1,69 +1,24 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef nsPrincipal_h__ -#define nsPrincipal_h__ +#ifndef nsExpandedPrincipal_h +#define nsExpandedPrincipal_h #include "nsCOMPtr.h" #include "nsJSPrincipals.h" #include "nsTArray.h" -#include "nsIContentSecurityPolicy.h" -#include "nsIProtocolHandler.h" #include "nsNetUtil.h" -#include "nsScriptSecurityManager.h" #include "mozilla/BasePrincipal.h" -class nsPrincipal final : public mozilla::BasePrincipal -{ -public: - NS_DECL_NSISERIALIZABLE - NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) override; - NS_IMETHOD GetHashValue(uint32_t* aHashValue) override; - NS_IMETHOD GetURI(nsIURI** aURI) override; - NS_IMETHOD GetDomain(nsIURI** aDomain) override; - NS_IMETHOD SetDomain(nsIURI* aDomain) override; - NS_IMETHOD GetBaseDomain(nsACString& aBaseDomain) override; - bool IsCodebasePrincipal() const override { return true; } - nsresult GetOriginInternal(nsACString& aOrigin) override; - - nsPrincipal(); - - // Init() must be called before the principal is in a usable state. - nsresult Init(nsIURI* aCodebase, - const mozilla::OriginAttributes& aOriginAttributes); - - virtual nsresult GetScriptLocation(nsACString& aStr) override; - void SetURI(nsIURI* aURI); - - /** - * Called at startup to setup static data, e.g. about:config pref-observers. - */ - static void InitializeStatics(); - - PrincipalKind Kind() override { return eCodebasePrincipal; } - - nsCOMPtr<nsIURI> mDomain; - nsCOMPtr<nsIURI> mCodebase; - // If mCodebaseImmutable is true, mCodebase is non-null and immutable - bool mCodebaseImmutable; - bool mDomainImmutable; - bool mInitialized; - -protected: - virtual ~nsPrincipal(); - - bool SubsumesInternal(nsIPrincipal* aOther, DocumentDomainConsideration aConsideration) override; - bool MayLoadInternal(nsIURI* aURI) override; -}; - -class nsExpandedPrincipal : public nsIExpandedPrincipal, public mozilla::BasePrincipal +class nsExpandedPrincipal : public nsIExpandedPrincipal + , public mozilla::BasePrincipal { public: nsExpandedPrincipal(nsTArray<nsCOMPtr<nsIPrincipal>> &aWhiteList, const mozilla::OriginAttributes& aAttrs); NS_DECL_NSIEXPANDEDPRINCIPAL NS_DECL_NSISERIALIZABLE NS_IMETHOD_(MozExternalRefCountType) AddRef() override { return nsJSPrincipals::AddRef(); }; @@ -85,19 +40,14 @@ protected: bool SubsumesInternal(nsIPrincipal* aOther, DocumentDomainConsideration aConsideration) override; bool MayLoadInternal(nsIURI* aURI) override; private: nsTArray< nsCOMPtr<nsIPrincipal> > mPrincipals; }; -#define NS_PRINCIPAL_CONTRACTID "@mozilla.org/principal;1" -#define NS_PRINCIPAL_CID \ -{ 0x653e0e4d, 0x3ee4, 0x45fa, \ - { 0xb2, 0x72, 0x97, 0xc2, 0x0b, 0xc0, 0x1e, 0xb8 } } - #define NS_EXPANDEDPRINCIPAL_CONTRACTID "@mozilla.org/expandedprincipal;1" #define NS_EXPANDEDPRINCIPAL_CID \ { 0xe8ee88b0, 0x5571, 0x4086, \ { 0xa4, 0x5b, 0x39, 0xa7, 0x16, 0x90, 0x6b, 0xdb } } -#endif // nsPrincipal_h__ +#endif // nsExpandedPrincipal_h
--- a/caps/nsIPrincipal.idl +++ b/caps/nsIPrincipal.idl @@ -6,19 +6,39 @@ /* Defines the abstract interface for a principal. */ #include "nsISerializable.idl" %{C++ struct JSPrincipals; #include "nsCOMPtr.h" #include "nsTArray.h" +#include "mozilla/DebugOnly.h" namespace mozilla { class OriginAttributes; } + +/** + * Some methods have a fast path for the case when we're comparing a principal + * to itself. The situation may happen for example with about:blank documents. + */ + +#define DECL_FAST_INLINE_HELPER(method_) \ + inline bool method_(nsIPrincipal* aOther) \ + { \ + mozilla::DebugOnly<bool> val = false; \ + MOZ_ASSERT_IF(this == aOther, \ + NS_SUCCEEDED(method_(aOther, &val)) && val); \ + \ + bool retVal = false; \ + return \ + this == aOther || \ + (NS_SUCCEEDED(method_(aOther, &retVal)) && retVal); \ + } + %} interface nsIURI; interface nsIContentSecurityPolicy; interface nsIDOMDocument; [ptr] native JSContext(JSContext); [ptr] native JSPrincipals(JSPrincipals); @@ -36,25 +56,18 @@ interface nsIPrincipal : nsISerializable boolean equals(in nsIPrincipal other); /** * Like equals, but takes document.domain changes into account. */ boolean equalsConsideringDomain(in nsIPrincipal other); %{C++ - inline bool Equals(nsIPrincipal* aOther) { - bool equal = false; - return NS_SUCCEEDED(Equals(aOther, &equal)) && equal; - } - - inline bool EqualsConsideringDomain(nsIPrincipal* aOther) { - bool equal = false; - return NS_SUCCEEDED(EqualsConsideringDomain(aOther, &equal)) && equal; - } + DECL_FAST_INLINE_HELPER(Equals) + DECL_FAST_INLINE_HELPER(EqualsConsideringDomain) %} /** * Returns a hash value for the principal. */ [noscript] readonly attribute unsigned long hashValue; /** @@ -96,30 +109,20 @@ interface nsIPrincipal : nsISerializable /** * Same as the subsumesConsideringDomain(), but ignores the first party * domain in its originAttributes. */ boolean subsumesConsideringDomainIgnoringFPD(in nsIPrincipal other); %{C++ - inline bool Subsumes(nsIPrincipal* aOther) { - bool subsumes = false; - return NS_SUCCEEDED(Subsumes(aOther, &subsumes)) && subsumes; - } - - inline bool SubsumesConsideringDomain(nsIPrincipal* aOther) { - bool subsumes = false; - return NS_SUCCEEDED(SubsumesConsideringDomain(aOther, &subsumes)) && subsumes; - } - - inline bool SubsumesConsideringDomainIgnoringFPD(nsIPrincipal* aOther) { - bool subsumes = false; - return NS_SUCCEEDED(SubsumesConsideringDomainIgnoringFPD(aOther, &subsumes)) && subsumes; - } + DECL_FAST_INLINE_HELPER(Subsumes) + DECL_FAST_INLINE_HELPER(SubsumesConsideringDomain) + DECL_FAST_INLINE_HELPER(SubsumesConsideringDomainIgnoringFPD) +#undef DECL_FAST_INLINE_HELPER %} /** * Checks whether this principal is allowed to load the network resource * located at the given URI under the same-origin policy. This means that * codebase principals are only allowed to load resources from the same * domain, the system principal is allowed to load anything, and null * principals can only load URIs where they are the principal. This is
--- a/caps/nsPrincipal.cpp +++ b/caps/nsPrincipal.cpp @@ -453,208 +453,8 @@ nsPrincipal::Write(nsIObjectOutputStream return rv; } // mCodebaseImmutable and mDomainImmutable will be recomputed based // on the deserialized URIs in Read(). return NS_OK; } - -/************************************************************************************************************************/ - -NS_IMPL_CLASSINFO(nsExpandedPrincipal, nullptr, nsIClassInfo::MAIN_THREAD_ONLY, - NS_EXPANDEDPRINCIPAL_CID) -NS_IMPL_QUERY_INTERFACE_CI(nsExpandedPrincipal, - nsIPrincipal, - nsIExpandedPrincipal) -NS_IMPL_CI_INTERFACE_GETTER(nsExpandedPrincipal, - nsIPrincipal, - nsIExpandedPrincipal) - -struct OriginComparator -{ - bool LessThan(nsIPrincipal* a, nsIPrincipal* b) const - { - nsAutoCString originA; - nsresult rv = a->GetOrigin(originA); - NS_ENSURE_SUCCESS(rv, false); - nsAutoCString originB; - rv = b->GetOrigin(originB); - NS_ENSURE_SUCCESS(rv, false); - return originA < originB; - } - - bool Equals(nsIPrincipal* a, nsIPrincipal* b) const - { - nsAutoCString originA; - nsresult rv = a->GetOrigin(originA); - NS_ENSURE_SUCCESS(rv, false); - nsAutoCString originB; - rv = b->GetOrigin(originB); - NS_ENSURE_SUCCESS(rv, false); - return a == b; - } -}; - -nsExpandedPrincipal::nsExpandedPrincipal(nsTArray<nsCOMPtr<nsIPrincipal>> &aWhiteList, - const OriginAttributes& aAttrs) -{ - // We force the principals to be sorted by origin so that nsExpandedPrincipal - // origins can have a canonical form. - OriginComparator c; - for (size_t i = 0; i < aWhiteList.Length(); ++i) { - mPrincipals.InsertElementSorted(aWhiteList[i], c); - } - mOriginAttributes = aAttrs; -} - -nsExpandedPrincipal::~nsExpandedPrincipal() -{ } - -NS_IMETHODIMP -nsExpandedPrincipal::GetDomain(nsIURI** aDomain) -{ - *aDomain = nullptr; - return NS_OK; -} - -NS_IMETHODIMP -nsExpandedPrincipal::SetDomain(nsIURI* aDomain) -{ - return NS_OK; -} - -nsresult -nsExpandedPrincipal::GetOriginInternal(nsACString& aOrigin) -{ - aOrigin.AssignLiteral("[Expanded Principal ["); - for (size_t i = 0; i < mPrincipals.Length(); ++i) { - if (i != 0) { - aOrigin.AppendLiteral(", "); - } - - nsAutoCString subOrigin; - nsresult rv = mPrincipals.ElementAt(i)->GetOrigin(subOrigin); - NS_ENSURE_SUCCESS(rv, rv); - aOrigin.Append(subOrigin); - } - - aOrigin.Append("]]"); - return NS_OK; -} - -bool -nsExpandedPrincipal::SubsumesInternal(nsIPrincipal* aOther, - BasePrincipal::DocumentDomainConsideration aConsideration) -{ - // If aOther is an ExpandedPrincipal too, we break it down into its component - // nsIPrincipals, and check subsumes on each one. - nsCOMPtr<nsIExpandedPrincipal> expanded = do_QueryInterface(aOther); - if (expanded) { - nsTArray< nsCOMPtr<nsIPrincipal> >* otherList; - expanded->GetWhiteList(&otherList); - for (uint32_t i = 0; i < otherList->Length(); ++i){ - // Use SubsumesInternal rather than Subsumes here, since OriginAttribute - // checks are only done between non-expanded sub-principals, and we don't - // need to incur the extra virtual call overhead. - if (!SubsumesInternal((*otherList)[i], aConsideration)) { - return false; - } - } - return true; - } - - // We're dealing with a regular principal. One of our principals must subsume - // it. - for (uint32_t i = 0; i < mPrincipals.Length(); ++i) { - if (Cast(mPrincipals[i])->Subsumes(aOther, aConsideration)) { - return true; - } - } - - return false; -} - -bool -nsExpandedPrincipal::MayLoadInternal(nsIURI* uri) -{ - for (uint32_t i = 0; i < mPrincipals.Length(); ++i){ - if (BasePrincipal::Cast(mPrincipals[i])->MayLoadInternal(uri)) { - return true; - } - } - - return false; -} - -NS_IMETHODIMP -nsExpandedPrincipal::GetHashValue(uint32_t* result) -{ - MOZ_CRASH("extended principal should never be used as key in a hash map"); -} - -NS_IMETHODIMP -nsExpandedPrincipal::GetURI(nsIURI** aURI) -{ - *aURI = nullptr; - return NS_OK; -} - -NS_IMETHODIMP -nsExpandedPrincipal::GetWhiteList(nsTArray<nsCOMPtr<nsIPrincipal> >** aWhiteList) -{ - *aWhiteList = &mPrincipals; - return NS_OK; -} - -NS_IMETHODIMP -nsExpandedPrincipal::GetBaseDomain(nsACString& aBaseDomain) -{ - return NS_ERROR_NOT_AVAILABLE; -} - -bool -nsExpandedPrincipal::AddonHasPermission(const nsAString& aPerm) -{ - for (size_t i = 0; i < mPrincipals.Length(); ++i) { - if (BasePrincipal::Cast(mPrincipals[i])->AddonHasPermission(aPerm)) { - return true; - } - } - return false; -} - -nsresult -nsExpandedPrincipal::GetScriptLocation(nsACString& aStr) -{ - aStr.Assign("[Expanded Principal ["); - for (size_t i = 0; i < mPrincipals.Length(); ++i) { - if (i != 0) { - aStr.AppendLiteral(", "); - } - - nsAutoCString spec; - nsresult rv = - nsJSPrincipals::get(mPrincipals.ElementAt(i))->GetScriptLocation(spec); - NS_ENSURE_SUCCESS(rv, rv); - - aStr.Append(spec); - } - aStr.Append("]]"); - return NS_OK; -} - -////////////////////////////////////////// -// Methods implementing nsISerializable // -////////////////////////////////////////// - -NS_IMETHODIMP -nsExpandedPrincipal::Read(nsIObjectInputStream* aStream) -{ - return NS_ERROR_NOT_IMPLEMENTED; -} - -NS_IMETHODIMP -nsExpandedPrincipal::Write(nsIObjectOutputStream* aStream) -{ - return NS_ERROR_NOT_IMPLEMENTED; -}
--- a/caps/nsPrincipal.h +++ b/caps/nsPrincipal.h @@ -53,51 +53,14 @@ public: protected: virtual ~nsPrincipal(); bool SubsumesInternal(nsIPrincipal* aOther, DocumentDomainConsideration aConsideration) override; bool MayLoadInternal(nsIURI* aURI) override; }; -class nsExpandedPrincipal : public nsIExpandedPrincipal, public mozilla::BasePrincipal -{ -public: - nsExpandedPrincipal(nsTArray<nsCOMPtr<nsIPrincipal>> &aWhiteList, - const mozilla::OriginAttributes& aAttrs); - - NS_DECL_NSIEXPANDEDPRINCIPAL - NS_DECL_NSISERIALIZABLE - NS_IMETHOD_(MozExternalRefCountType) AddRef() override { return nsJSPrincipals::AddRef(); }; - NS_IMETHOD_(MozExternalRefCountType) Release() override { return nsJSPrincipals::Release(); }; - NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) override; - NS_IMETHOD GetHashValue(uint32_t* aHashValue) override; - NS_IMETHOD GetURI(nsIURI** aURI) override; - NS_IMETHOD GetDomain(nsIURI** aDomain) override; - NS_IMETHOD SetDomain(nsIURI* aDomain) override; - NS_IMETHOD GetBaseDomain(nsACString& aBaseDomain) override; - virtual bool AddonHasPermission(const nsAString& aPerm) override; - virtual nsresult GetScriptLocation(nsACString &aStr) override; - nsresult GetOriginInternal(nsACString& aOrigin) override; - - PrincipalKind Kind() override { return eExpandedPrincipal; } - -protected: - virtual ~nsExpandedPrincipal(); - - bool SubsumesInternal(nsIPrincipal* aOther, DocumentDomainConsideration aConsideration) override; - bool MayLoadInternal(nsIURI* aURI) override; - -private: - nsTArray< nsCOMPtr<nsIPrincipal> > mPrincipals; -}; - #define NS_PRINCIPAL_CONTRACTID "@mozilla.org/principal;1" #define NS_PRINCIPAL_CID \ { 0x653e0e4d, 0x3ee4, 0x45fa, \ { 0xb2, 0x72, 0x97, 0xc2, 0x0b, 0xc0, 0x1e, 0xb8 } } -#define NS_EXPANDEDPRINCIPAL_CONTRACTID "@mozilla.org/expandedprincipal;1" -#define NS_EXPANDEDPRINCIPAL_CID \ -{ 0xe8ee88b0, 0x5571, 0x4086, \ - { 0xa4, 0x5b, 0x39, 0xa7, 0x16, 0x90, 0x6b, 0xdb } } - #endif // nsPrincipal_h__
--- a/devtools/client/framework/target-from-url.js +++ b/devtools/client/framework/target-from-url.js @@ -1,45 +1,48 @@ /* 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/. */ "use strict"; -const { Cu, Ci } = require("chrome"); - const { TargetFactory } = require("devtools/client/framework/target"); const { DebuggerServer } = require("devtools/server/main"); const { DebuggerClient } = require("devtools/shared/client/main"); const { Task } = require("devtools/shared/task"); /** * Construct a Target for a given URL object having various query parameters: * * host: * {String} The hostname or IP address to connect to. * port: * {Number} The TCP port to connect to, to use with `host` argument. * ws: * {Boolean} If true, connect via websocket instread of regular TCP connection. * - * type: tab, process - * {String} The type of target to connect to. Currently tabs and processes are supported types. + * type: tab, process, window + * {String} The type of target to connect to. * - * If type="tab": + * If type == "tab": * id: * {Number} the tab outerWindowID * chrome: Optional - * {Boolean} Force the creation of a chrome target. Gives more privileges to the tab - * actor. Allows chrome execution in the webconsole and see chrome files in - * the debugger. (handy when contributing to firefox) + * {Boolean} Force the creation of a chrome target. Gives more privileges to + * the tab actor. Allows chrome execution in the webconsole and see chrome + * files in the debugger. (handy when contributing to firefox) * - * If type="process": + * If type == "process": * id: - * {Number} the process id to debug. Default to 0, which is the parent process. + * {Number} the process id to debug. Default to 0, which is the parent + * process. + * + * If type == "window": + * id: + * {Number} the window outerWindowID * * @param {URL} url * The url to fetch query params from. * * @return A target object */ exports.targetFromURL = Task.async(function* (url) { let params = url.searchParams; @@ -88,16 +91,36 @@ exports.targetFromURL = Task.async(funct isTabActor = false; } } catch (ex) { if (ex.error == "noProcess") { throw new Error("targetFromURL, process with id:'" + id + "' doesn't exist"); } throw ex; } + } else if (type == "window") { + // Fetch target for a remote window actor + DebuggerServer.allowChromeProcess = true; + try { + id = parseInt(id, 10); + if (isNaN(id)) { + throw new Error("targetFromURL, window requires id parameter"); + } + let response = yield client.mainRoot.getWindow({ + outerWindowID: id, + }); + form = response.window; + chrome = true; + } catch (ex) { + if (ex.error == "notFound") { + throw new Error(`targetFromURL, window with id:'${id}' ` + + "doesn't exist"); + } + throw ex; + } } else { throw new Error("targetFromURL, unsupported type='" + type + "' parameter"); } return TargetFactory.forRemoteTab({ client, form, chrome, isTabActor }); }); function* createClient(params) {
--- a/devtools/client/framework/test/browser_target_from_url.js +++ b/devtools/client/framework/test/browser_target_from_url.js @@ -31,18 +31,29 @@ add_task(function* () { info("Test invalid type"); try { yield targetFromURL(new URL("http://foo?type=x")); ok(false, "Shouldn't pass"); } catch (e) { is(e.message, "targetFromURL, unsupported type='x' parameter"); } + info("Test browser window"); + let windowId = window.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils) + .outerWindowID; + target = yield targetFromURL(new URL("http://foo?type=window&id=" + windowId)); + is(target.url, window.location.href); + is(target.isLocalTab, false); + is(target.chrome, true); + is(target.isTabActor, true); + is(target.isRemote, true); + info("Test tab"); - let windowId = browser.outerWindowID; + windowId = browser.outerWindowID; target = yield targetFromURL(new URL("http://foo?type=tab&id=" + windowId)); assertIsTabTarget(target, TEST_URI); info("Test tab with chrome privileges"); target = yield targetFromURL(new URL("http://foo?type=tab&id=" + windowId + "&chrome")); assertIsTabTarget(target, TEST_URI, true); info("Test invalid tab id");
--- a/devtools/client/framework/test/shared-head.js +++ b/devtools/client/framework/test/shared-head.js @@ -107,26 +107,28 @@ registerCleanupFunction(function* cleanu /** * Add a new test tab in the browser and load the given url. * @param {String} url The url to be loaded in the new tab * @param {Object} options Object with various optional fields: * - {Boolean} background If true, open the tab in background * - {ChromeWindow} window Firefox top level window we should use to open the tab * - {Number} userContextId The userContextId of the tab. + * - {String} preferredRemoteType * @return a promise that resolves to the tab object when the url is loaded */ var addTab = Task.async(function* (url, options = { background: false, window: window }) { info("Adding a new tab with URL: " + url); let { background } = options; let { gBrowser } = options.window ? options.window : window; let { userContextId } = options; - let tab = gBrowser.addTab(url, {userContextId}); + let tab = gBrowser.addTab(url, + {userContextId, preferredRemoteType: options.preferredRemoteType}); if (!background) { gBrowser.selectedTab = tab; } yield BrowserTestUtils.browserLoaded(tab.linkedBrowser); info("Tab added and finished loading"); return tab;
--- a/devtools/client/framework/toolbox-init.js +++ b/devtools/client/framework/toolbox-init.js @@ -1,12 +1,14 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/* eslint-env browser */ + "use strict"; // URL constructor doesn't support about: scheme let href = window.location.href.replace("about:", "http://"); let url = new window.URL(href); // Only use this method to attach the toolbox if some query parameters are given if (url.search.length > 1) { @@ -21,16 +23,33 @@ if (url.search.length > 1) { const { DebuggerClient } = require("devtools/shared/client/main"); const { Task } = require("devtools/shared/task"); // `host` is the frame element loading the toolbox. let host = window.QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIDOMWindowUtils) .containerElement; + // If there's no containerElement (which happens when loading about:devtools-toolbox as + // a top level document), use the current window. + if (!host) { + host = { + contentWindow: window, + contentDocument: document, + // toolbox-host-manager.js wants to set attributes on the frame that contains it, + // but that is fine to skip and doesn't make sense when using the current window. + setAttribute() {}, + ownerDocument: document, + // toolbox-host-manager.js wants to listen for unload events from outside the frame, + // but this is fine to skip since the toolbox code listens inside the frame as well, + // and there is no outer document in this case. + addEventListener() {}, + }; + } + // Specify the default tool to open let tool = url.searchParams.get("tool"); Task.spawn(function* () { let target; if (url.searchParams.has("target")) { // Attach toolbox to a given browser iframe (<xul:browser> or <html:iframe // mozbrowser>) whose reference is set on the host iframe.
--- a/devtools/client/inspector/inspector.js +++ b/devtools/client/inspector/inspector.js @@ -1285,33 +1285,33 @@ Inspector.prototype = { label: INSPECTOR_L10N.getStr("inspectorAddAttribute.label"), accesskey: INSPECTOR_L10N.getStr("inspectorAddAttribute.accesskey"), disabled: !isEditableElement, click: () => this.onAddAttribute(), })); attributesSubmenu.append(new MenuItem({ id: "node-menu-copy-attribute", label: INSPECTOR_L10N.getFormatStr("inspectorCopyAttributeValue.label", - isAttributeClicked ? `"${nodeInfo.value}"` : ""), + isAttributeClicked ? `${nodeInfo.value}` : ""), accesskey: INSPECTOR_L10N.getStr("inspectorCopyAttributeValue.accesskey"), disabled: !isAttributeClicked, click: () => this.onCopyAttributeValue(), })); attributesSubmenu.append(new MenuItem({ id: "node-menu-edit-attribute", label: INSPECTOR_L10N.getFormatStr("inspectorEditAttribute.label", - isAttributeClicked ? `"${nodeInfo.name}"` : ""), + isAttributeClicked ? `${nodeInfo.name}` : ""), accesskey: INSPECTOR_L10N.getStr("inspectorEditAttribute.accesskey"), disabled: !isAttributeClicked, click: () => this.onEditAttribute(), })); attributesSubmenu.append(new MenuItem({ id: "node-menu-remove-attribute", label: INSPECTOR_L10N.getFormatStr("inspectorRemoveAttribute.label", - isAttributeClicked ? `"${nodeInfo.name}"` : ""), + isAttributeClicked ? `${nodeInfo.name}` : ""), accesskey: INSPECTOR_L10N.getStr("inspectorRemoveAttribute.accesskey"), disabled: !isAttributeClicked, click: () => this.onRemoveAttribute(), })); return attributesSubmenu; },
--- a/devtools/client/inspector/layout/actions/grids.js +++ b/devtools/client/inspector/layout/actions/grids.js @@ -1,34 +1,51 @@ /* 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/. */ "use strict"; const { + UPDATE_GRID_COLOR, UPDATE_GRID_HIGHLIGHTED, UPDATE_GRIDS, } = require("./index"); module.exports = { /** + * Update the color used for the grid's highlighter. + * + * @param {NodeFront} nodeFront + * The NodeFront of the DOM node to toggle the grid highlighter. + * @param {String} color + * The color to use for thie nodeFront's grid highlighter. + */ + updateGridColor(nodeFront, color) { + return { + type: UPDATE_GRID_COLOR, + color, + nodeFront, + }; + }, + + /** * Update the grid highlighted state. * * @param {NodeFront} nodeFront * The NodeFront of the DOM node to toggle the grid highlighter. * @param {Boolean} highlighted * Whether or not the grid highlighter is highlighting the grid. */ updateGridHighlighted(nodeFront, highlighted) { return { type: UPDATE_GRID_HIGHLIGHTED, + highlighted, nodeFront, - highlighted, }; }, /** * Update the grid state with the new list of grids. */ updateGrids(grids) { return {
--- a/devtools/client/inspector/layout/actions/index.js +++ b/devtools/client/inspector/layout/actions/index.js @@ -3,16 +3,19 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; const { createEnum } = require("devtools/client/shared/enum"); createEnum([ + // Update the color used for the overlay of a grid. + "UPDATE_GRID_COLOR", + // Update the grid highlighted state. "UPDATE_GRID_HIGHLIGHTED", // Update the entire grids state with the new list of grids. "UPDATE_GRIDS", // Update the layout state with the latest layout properties. "UPDATE_LAYOUT",
--- a/devtools/client/inspector/layout/components/Accordion.css +++ b/devtools/client/inspector/layout/components/Accordion.css @@ -19,17 +19,17 @@ font-size: 11px; padding: 5px; transition: all 0.25s ease; width: 100%; -moz-user-select: none; } .accordion ._header:hover { - background-color: var(--theme-selection-color); + background-color: var(--theme-toolbar-hover); } .accordion ._header:hover svg { fill: var(--theme-comment-alt); } .accordion ._content { border-bottom: 1px solid var(--theme-splitter-color);
--- a/devtools/client/inspector/layout/components/App.js +++ b/devtools/client/inspector/layout/components/App.js @@ -21,21 +21,23 @@ const BOXMODEL_STRINGS_URI = "devtools/c const BOXMODEL_L10N = new LocalizationHelper(BOXMODEL_STRINGS_URI); const App = createClass({ displayName: "App", propTypes: { boxModel: PropTypes.shape(Types.boxModel).isRequired, + getSwatchColorPickerTooltip: PropTypes.func.isRequired, grids: PropTypes.arrayOf(PropTypes.shape(Types.grid)).isRequired, highlighterSettings: PropTypes.shape(Types.highlighterSettings).isRequired, showBoxModelProperties: PropTypes.bool.isRequired, + onHideBoxModelHighlighter: PropTypes.func.isRequired, + onSetGridOverlayColor: PropTypes.func.isRequired, onShowBoxModelEditor: PropTypes.func.isRequired, - onHideBoxModelHighlighter: PropTypes.func.isRequired, onShowBoxModelHighlighter: PropTypes.func.isRequired, onToggleGridHighlighter: PropTypes.func.isRequired, onToggleShowGridLineNumbers: PropTypes.func.isRequired, onToggleShowInfiniteLines: PropTypes.func.isRequired, }, mixins: [ addons.PureRenderMixin ],
--- a/devtools/client/inspector/layout/components/Grid.js +++ b/devtools/client/inspector/layout/components/Grid.js @@ -13,41 +13,47 @@ const GridList = createFactory(require(" const Types = require("../types"); const { getStr } = require("../utils/l10n"); module.exports = createClass({ displayName: "Grid", propTypes: { + getSwatchColorPickerTooltip: PropTypes.func.isRequired, grids: PropTypes.arrayOf(PropTypes.shape(Types.grid)).isRequired, highlighterSettings: PropTypes.shape(Types.highlighterSettings).isRequired, + onSetGridOverlayColor: PropTypes.func.isRequired, onToggleGridHighlighter: PropTypes.func.isRequired, onToggleShowGridLineNumbers: PropTypes.func.isRequired, onToggleShowInfiniteLines: PropTypes.func.isRequired, }, mixins: [ addons.PureRenderMixin ], render() { let { + getSwatchColorPickerTooltip, grids, highlighterSettings, + onSetGridOverlayColor, onToggleGridHighlighter, onToggleShowGridLineNumbers, onToggleShowInfiniteLines, } = this.props; return grids.length ? dom.div( { id: "layout-grid-container", }, GridList({ + getSwatchColorPickerTooltip, grids, + onSetGridOverlayColor, onToggleGridHighlighter, }), GridDisplaySettings({ highlighterSettings, onToggleShowGridLineNumbers, onToggleShowInfiniteLines, }) )
new file mode 100644 --- /dev/null +++ b/devtools/client/inspector/layout/components/GridItem.js @@ -0,0 +1,118 @@ +/* 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/. */ + +"use strict"; + +const { addons, createClass, DOM: dom, PropTypes } = require("devtools/client/shared/vendor/react"); +const { findDOMNode } = require("devtools/client/shared/vendor/react-dom"); + +const Types = require("../types"); + +module.exports = createClass({ + + displayName: "GridItem", + + propTypes: { + getSwatchColorPickerTooltip: PropTypes.func.isRequired, + grid: PropTypes.shape(Types.grid).isRequired, + onSetGridOverlayColor: PropTypes.func.isRequired, + onToggleGridHighlighter: PropTypes.func.isRequired, + }, + + mixins: [ addons.PureRenderMixin ], + + componentDidMount() { + let tooltip = this.props.getSwatchColorPickerTooltip(); + let swatchEl = findDOMNode(this).querySelector(".grid-color-swatch"); + + let previousColor; + tooltip.addSwatch(swatchEl, { + onCommit: this.setGridColor, + onPreview: this.setGridColor, + onRevert: () => { + this.props.onSetGridOverlayColor(this.props.grid.nodeFront, previousColor); + }, + onShow: () => { + previousColor = this.props.grid.color; + }, + }); + }, + + componentWillUnmount() { + let tooltip = this.props.getSwatchColorPickerTooltip(); + let swatchEl = findDOMNode(this).querySelector(".grid-color-swatch"); + tooltip.removeSwatch(swatchEl); + }, + + setGridColor() { + let color = findDOMNode(this).querySelector(".grid-color-value").textContent; + this.props.onSetGridOverlayColor(this.props.grid.nodeFront, color); + }, + + onGridCheckboxClick() { + let { + grid, + onToggleGridHighlighter, + } = this.props; + + onToggleGridHighlighter(grid.nodeFront); + }, + + render() { + let { grid } = this.props; + let { nodeFront } = grid; + let { displayName, attributes } = nodeFront; + + let gridName = displayName; + + let idIndex = attributes.findIndex(({ name }) => name === "id"); + if (idIndex > -1 && attributes[idIndex].value) { + gridName += "#" + attributes[idIndex].value; + } + + let classIndex = attributes.findIndex(({name}) => name === "class"); + if (classIndex > -1 && attributes[classIndex].value) { + gridName += "." + attributes[classIndex].value.split(" ").join("."); + } + + return dom.li( + { + key: grid.id, + className: "grid-item", + }, + dom.label( + {}, + dom.input( + { + type: "checkbox", + value: grid.id, + checked: grid.highlighted, + onChange: this.onGridCheckboxClick, + } + ), + gridName + ), + dom.div( + { + className: "grid-color-swatch", + style: { + backgroundColor: grid.color, + }, + title: grid.color, + } + ), + // The SwatchColorPicker relies on the nextSibling of the swatch element to apply + // the selected color. This is why we use a span in display: none for now. + // Ideally we should modify the SwatchColorPickerTooltip to bypass this requirement. + // See https://bugzilla.mozilla.org/show_bug.cgi?id=1341578 + dom.span( + { + className: "grid-color-value" + }, + grid.color + ) + ); + }, + +});
--- a/devtools/client/inspector/layout/components/GridList.js +++ b/devtools/client/inspector/layout/components/GridList.js @@ -1,87 +1,56 @@ /* 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/. */ "use strict"; -const { addons, createClass, DOM: dom, PropTypes } = +const { addons, createClass, createFactory, DOM: dom, PropTypes } = require("devtools/client/shared/vendor/react"); +const GridItem = createFactory(require("./GridItem")); + const Types = require("../types"); const { getStr } = require("../utils/l10n"); module.exports = createClass({ displayName: "GridList", propTypes: { + getSwatchColorPickerTooltip: PropTypes.func.isRequired, grids: PropTypes.arrayOf(PropTypes.shape(Types.grid)).isRequired, + onSetGridOverlayColor: PropTypes.func.isRequired, onToggleGridHighlighter: PropTypes.func.isRequired, }, mixins: [ addons.PureRenderMixin ], - onGridCheckboxClick({ target }) { - let { - grids, - onToggleGridHighlighter, - } = this.props; - - onToggleGridHighlighter(grids[target.value].nodeFront); - }, - render() { let { + getSwatchColorPickerTooltip, grids, + onSetGridOverlayColor, + onToggleGridHighlighter, } = this.props; return dom.div( { className: "grid-container", }, dom.span( {}, getStr("layout.overlayGrid") ), dom.ul( {}, - grids.map(grid => { - let { nodeFront } = grid; - let { displayName, attributes } = nodeFront; - - let gridName = displayName; - - let idIndex = attributes.findIndex(({ name }) => name === "id"); - if (idIndex > -1 && attributes[idIndex].value) { - gridName += "#" + attributes[idIndex].value; - } - - let classIndex = attributes.findIndex(({name}) => name === "class"); - if (classIndex > -1 && attributes[classIndex].value) { - gridName += "." + attributes[classIndex].value.split(" ").join("."); - } - - return dom.li( - { - key: grid.id, - }, - dom.label( - {}, - dom.input( - { - type: "checkbox", - value: grid.id, - checked: grid.highlighted, - onChange: this.onGridCheckboxClick, - } - ), - gridName - ) - ); - }) + grids.map(grid => GridItem({ + getSwatchColorPickerTooltip, + grid, + onSetGridOverlayColor, + onToggleGridHighlighter, + })) ) ); }, }); -
--- a/devtools/client/inspector/layout/components/moz.build +++ b/devtools/client/inspector/layout/components/moz.build @@ -11,10 +11,11 @@ DevToolsModules( 'BoxModel.js', 'BoxModelEditable.js', 'BoxModelInfo.js', 'BoxModelMain.js', 'BoxModelProperties.js', 'ComputedProperty.js', 'Grid.js', 'GridDisplaySettings.js', + 'GridItem.js', 'GridList.js', )
--- a/devtools/client/inspector/layout/layout.js +++ b/devtools/client/inspector/layout/layout.js @@ -8,20 +8,23 @@ const Services = require("Services"); const { Task } = require("devtools/shared/task"); const { getCssProperties } = require("devtools/shared/fronts/css-properties"); const { ReflowFront } = require("devtools/shared/fronts/reflow"); const { InplaceEditor } = require("devtools/client/shared/inplace-editor"); const { createFactory, createElement } = require("devtools/client/shared/vendor/react"); const { Provider } = require("devtools/client/shared/vendor/react-redux"); +const SwatchColorPickerTooltip = require("devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip"); + const { updateLayout, } = require("./actions/box-model"); const { + updateGridColor, updateGridHighlighted, updateGrids, } = require("./actions/grids"); const { updateShowGridLineNumbers, updateShowInfiniteLines, } = require("./actions/highlighter-settings"); @@ -32,36 +35,43 @@ const EditingSession = require("./utils/ const { LocalizationHelper } = require("devtools/shared/l10n"); const INSPECTOR_L10N = new LocalizationHelper("devtools/client/locales/inspector.properties"); const NUMERIC = /^-?[\d\.]+$/; const SHOW_GRID_LINE_NUMBERS = "devtools.gridinspector.showGridLineNumbers"; const SHOW_INFINITE_LINES_PREF = "devtools.gridinspector.showInfiniteLines"; +// Default grid colors. +const GRID_COLORS = [ + "#05E4EE", + "#BB9DFF", + "#FFB53B", + "#71F362", + "#FF90FF", + "#FF90FF", + "#1B80FF", + "#FF2647" +]; + function LayoutView(inspector, window) { this.document = window.document; this.highlighters = inspector.highlighters; this.inspector = inspector; this.store = inspector.store; this.walker = this.inspector.walker; this.updateBoxModel = this.updateBoxModel.bind(this); this.onGridLayoutChange = this.onGridLayoutChange.bind(this); this.onHighlighterChange = this.onHighlighterChange.bind(this); this.onNewSelection = this.onNewSelection.bind(this); this.onSidebarSelect = this.onSidebarSelect.bind(this); this.init(); - - this.highlighters.on("grid-highlighter-hidden", this.onHighlighterChange); - this.highlighters.on("grid-highlighter-shown", this.onHighlighterChange); - this.inspector.selection.on("new-node-front", this.onNewSelection); - this.inspector.sidebar.on("select", this.onSidebarSelect); } LayoutView.prototype = { /** * Initializes the layout view by fetching the LayoutFront from the walker, creating * the redux store and adding the view into the inspector sidebar. */ @@ -69,32 +79,76 @@ LayoutView.prototype = { if (!this.inspector) { return; } this.layoutInspector = yield this.inspector.walker.getLayoutInspector(); this.loadHighlighterSettings(); + this.highlighters.on("grid-highlighter-hidden", this.onHighlighterChange); + this.highlighters.on("grid-highlighter-shown", this.onHighlighterChange); + this.inspector.selection.on("new-node-front", this.onNewSelection); + this.inspector.sidebar.on("select", this.onSidebarSelect); + + // Create a shared SwatchColorPicker instance to be reused by all GridItem components. + this.swatchColorPickerTooltip = new SwatchColorPickerTooltip( + this.inspector.toolbox.doc, + this.inspector, + { + supportsCssColor4ColorFunction: () => false + } + ); + let app = App({ /** + * Retrieve the shared SwatchColorPicker instance. + */ + getSwatchColorPickerTooltip: () => { + return this.swatchColorPickerTooltip; + }, + + /** * Shows the box model properties under the box model if true, otherwise, hidden by * default. */ showBoxModelProperties: true, /** * Hides the box-model highlighter on the currently selected element. */ onHideBoxModelHighlighter: () => { let toolbox = this.inspector.toolbox; toolbox.highlighterUtils.unhighlight(); }, /** + * Handler for a change in the grid overlay color picker for a grid container. + * + * @param {NodeFront} node + * The NodeFront of the grid container element for which the grid color is + * being updated. + * @param {String} color + * A hex string representing the color to use. + */ + onSetGridOverlayColor: (node, color) => { + this.store.dispatch(updateGridColor(node, color)); + let { grids } = this.store.getState(); + + // If the grid for which the color was updated currently has a highlighter, update + // the color. + for (let grid of grids) { + if (grid.nodeFront === node && grid.highlighted) { + let highlighterSettings = this.getGridHighlighterSettings(node); + this.highlighters.showGridHighlighter(node, highlighterSettings); + } + } + }, + + /** * Shows the inplace editor when a box model editable value is clicked on the * box model panel. * * @param {DOMNode} element * The element that was clicked. * @param {Event} event * The event object. * @param {String} property @@ -175,37 +229,38 @@ LayoutView.prototype = { * Handler for a change in the input checkboxes in the GridList component. * Toggles on/off the grid highlighter for the provided grid container element. * * @param {NodeFront} node * The NodeFront of the grid container element for which the grid * highlighter is toggled on/off for. */ onToggleGridHighlighter: node => { - let { highlighterSettings } = this.store.getState(); + let highlighterSettings = this.getGridHighlighterSettings(node); this.highlighters.toggleGridHighlighter(node, highlighterSettings); }, /** * Handler for a change in the show grid line numbers checkbox in the - * GridDisplaySettings component. TOggles on/off the option to show the grid line + * GridDisplaySettings component. Toggles on/off the option to show the grid line * numbers in the grid highlighter. Refreshes the shown grid highlighter for the * grids currently highlighted. * * @param {Boolean} enabled * Whether or not the grid highlighter should show the grid line numbers. */ onToggleShowGridLineNumbers: enabled => { this.store.dispatch(updateShowGridLineNumbers(enabled)); Services.prefs.setBoolPref(SHOW_GRID_LINE_NUMBERS, enabled); - let { grids, highlighterSettings } = this.store.getState(); + let { grids } = this.store.getState(); for (let grid of grids) { if (grid.highlighted) { + let highlighterSettings = this.getGridHighlighterSettings(grid.nodeFront); this.highlighters.showGridHighlighter(grid.nodeFront, highlighterSettings); } } }, /** * Handler for a change in the extend grid lines infinitely checkbox in the * GridDisplaySettings component. Toggles on/off the option to extend the grid @@ -214,24 +269,25 @@ LayoutView.prototype = { * * @param {Boolean} enabled * Whether or not the grid highlighter should extend grid lines infinitely. */ onToggleShowInfiniteLines: enabled => { this.store.dispatch(updateShowInfiniteLines(enabled)); Services.prefs.setBoolPref(SHOW_INFINITE_LINES_PREF, enabled); - let { grids, highlighterSettings } = this.store.getState(); + let { grids } = this.store.getState(); for (let grid of grids) { if (grid.highlighted) { + let highlighterSettings = this.getGridHighlighterSettings(grid.nodeFront); this.highlighters.showGridHighlighter(grid.nodeFront, highlighterSettings); } } - }, + } }); let provider = createElement(Provider, { store: this.store, id: "layoutview", title: INSPECTOR_L10N.getStr("inspector.sidebar.layoutViewTitle2"), key: "layoutview", }, app); @@ -266,16 +322,53 @@ LayoutView.prototype = { this.document = null; this.inspector = null; this.layoutInspector = null; this.store = null; this.walker = null; }, /** + * Returns the color set for the grid highlighter associated with the provided + * nodeFront. + * + * @param {NodeFront} nodeFront + * The NodeFront for which we need the color. + */ + getGridColorForNodeFront(nodeFront) { + let { grids } = this.store.getState(); + + for (let grid of grids) { + if (grid.nodeFront === nodeFront) { + return grid.color; + } + } + + return null; + }, + + /** + * Create a highlighter settings object for the provided nodeFront. + * + * @param {NodeFront} nodeFront + * The NodeFront for which we need highlighter settings. + */ + getGridHighlighterSettings(nodeFront) { + let { highlighterSettings } = this.store.getState(); + + // Get the grid color for the provided nodeFront. + let color = this.getGridColorForNodeFront(nodeFront); + + // Merge the grid color to the generic highlighter settings. + return Object.assign({}, highlighterSettings, { + color + }); + }, + + /** * Returns true if the layout panel is visible, and false otherwise. */ isPanelVisible() { return this.inspector.toolbox.currentToolId === "inspector" && this.inspector.sidebar && this.inspector.sidebar.getCurrentTabID() === "layoutview"; }, @@ -386,18 +479,22 @@ LayoutView.prototype = { gridFronts = yield this.layoutInspector.getAllGrids(this.walker.rootNode); } let grids = []; for (let i = 0; i < gridFronts.length; i++) { let grid = gridFronts[i]; let nodeFront = yield this.walker.getNodeFromActor(grid.actorID, ["containerEl"]); + let fallbackColor = GRID_COLORS[i % GRID_COLORS.length]; + let color = this.getGridColorForNodeFront(nodeFront) || fallbackColor; + grids.push({ id: i, + color, gridFragments: grid.gridFragments, highlighted: nodeFront == this.highlighters.gridHighlighterShown, nodeFront, }); } this.store.dispatch(updateGrids(grids)); }),
--- a/devtools/client/inspector/layout/reducers/grids.js +++ b/devtools/client/inspector/layout/reducers/grids.js @@ -1,37 +1,44 @@ /* 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/. */ "use strict"; const { + UPDATE_GRID_COLOR, UPDATE_GRID_HIGHLIGHTED, UPDATE_GRIDS, } = require("../actions/index"); const INITIAL_GRIDS = []; let reducers = { - [UPDATE_GRID_HIGHLIGHTED](grids, { nodeFront, highlighted }) { + [UPDATE_GRID_COLOR](grids, { nodeFront, color }) { let newGrids = grids.map(g => { if (g.nodeFront == nodeFront) { - g.highlighted = highlighted; - } else { - g.highlighted = false; + g.color = color; } return g; }); return newGrids; }, + [UPDATE_GRID_HIGHLIGHTED](grids, { nodeFront, highlighted }) { + return grids.map(g => { + return Object.assign({}, g, { + highlighted: g.nodeFront === nodeFront ? highlighted : false + }); + }); + }, + [UPDATE_GRIDS](_, { grids }) { return grids; }, }; module.exports = function (grids = INITIAL_GRIDS, action) { let reducer = reducers[action.type];
--- a/devtools/client/inspector/layout/types.js +++ b/devtools/client/inspector/layout/types.js @@ -19,16 +19,19 @@ exports.boxModel = { /** * A single grid container in the document. */ exports.grid = { // The id of the grid id: PropTypes.number, + // The color for the grid overlay highlighter + color: PropTypes.string, + // The grid fragment object of the grid container gridFragments: PropTypes.array, // Whether or not the grid highlighter is highlighting the grid highlighted: PropTypes.bool, // The node front of the grid container nodeFront: PropTypes.object,
--- a/devtools/client/locales/en-US/inspector.properties +++ b/devtools/client/locales/en-US/inspector.properties @@ -102,31 +102,31 @@ inspector.menu.copyUrlToClipboard.label= # element in the DOM (like with <label for="input-id">), and that allows to # select that element in the inspector. inspector.menu.selectElement.label=Select Element #%S # LOCALIZATION NOTE (inspectorEditAttribute.label): This is the label of a # sub-menu "Attribute" in the inspector contextual-menu that appears # when the user right-clicks on the node in the inspector, and that allows # to edit an attribute on this node. -inspectorEditAttribute.label=Edit Attribute %S +inspectorEditAttribute.label=Edit Attribute “%S” inspectorEditAttribute.accesskey=E # LOCALIZATION NOTE (inspectorRemoveAttribute.label): This is the label of a # sub-menu "Attribute" in the inspector contextual-menu that appears # when the user right-clicks on the attribute of a node in the inspector, # and that allows to remove this attribute. -inspectorRemoveAttribute.label=Remove Attribute %S +inspectorRemoveAttribute.label=Remove Attribute “%S” inspectorRemoveAttribute.accesskey=R # LOCALIZATION NOTE (inspectorCopyAttributeValue.label): This is the label of a # sub-menu "Attribute" in the inspector contextual-menu that appears # when the user right-clicks on the attribute of a node in the inspector, # and that allows to copy the attribute value to clipboard. -inspectorCopyAttributeValue.label=Copy Attribute Value %S +inspectorCopyAttributeValue.label=Copy Attribute Value “%S” inspectorCopyAttributeValue.accesskey=V # LOCALIZATION NOTE (inspector.nodePreview.selectNodeLabel): # This string is displayed in a tooltip that is shown when hovering over a DOM # node preview (e.g. something like "div#foo.bar"). # DOM node previews can be displayed in places like the animation-inspector, the # console or the object inspector. # The tooltip invites the user to click on the node in order to select it in the
--- a/devtools/client/responsive.html/index.css +++ b/devtools/client/responsive.html/index.css @@ -16,17 +16,17 @@ --viewport-selection-arrow-hovered: url("./images/select-arrow.svg#light-hovered"); --viewport-selection-arrow-selected: url("./images/select-arrow.svg#light-selected"); } .theme-dark { --rdm-box-shadow: 0 4px 4px 0 rgba(105, 105, 105, 0.26); - --submit-button-active-background-color: var(--toolbar-tab-hover-active); + --submit-button-active-background-color: var(--theme-toolbar-hover-active); --submit-button-active-color: var(--theme-selection-color); --viewport-color: #c6ccd0; --viewport-hover-color: #dde1e4; --viewport-active-color: #fcfcfc; --viewport-selection-arrow: url("./images/select-arrow.svg#dark"); --viewport-selection-arrow-hovered: url("./images/select-arrow.svg#dark-hovered"); --viewport-selection-arrow-selected: @@ -531,17 +531,17 @@ select > option.divider { color: var(--theme-body-color); width: 100%; height: 20px; position: absolute; bottom: 0; } #device-submit-button:hover { - background-color: var(--toolbar-tab-hover); + background-color: var(--theme-toolbar-hover); } #device-submit-button:hover:active { background-color: var(--submit-button-active-background-color); color: var(--submit-button-active-color); } /**
--- a/devtools/client/shared/components/tabs/tabs.css +++ b/devtools/client/shared/components/tabs/tabs.css @@ -90,22 +90,22 @@ .theme-dark .tabs .tabs-menu-item a, .theme-light .tabs .tabs-menu-item a { color: inherit; padding: 3px 15px; } .theme-dark .tabs .tabs-menu-item:hover:not(.is-active), .theme-light .tabs .tabs-menu-item:hover:not(.is-active) { - background-color: var(--toolbar-tab-hover); + background-color: var(--theme-toolbar-hover); } .theme-dark .tabs .tabs-menu-item:hover:active:not(.is-active), .theme-light .tabs .tabs-menu-item:hover:active:not(.is-active) { - background-color: var(--toolbar-tab-hover-active); + background-color: var(--theme-toolbar-hover-active); } .theme-dark .tabs .tabs-menu-item.is-active, .theme-light .tabs .tabs-menu-item.is-active { background-color: var(--theme-selection-background); color: var(--theme-selection-color); } @@ -160,17 +160,17 @@ .theme-firebug .tabs .tabs-menu-item.is-active a { background-color: var(--theme-toolbar-tab-selected-background); border: 1px solid var(--theme-splitter-color); border-bottom-color: transparent; color: var(--theme-body-color); } .theme-firebug .tabs .tabs-menu-item:hover:active a { - background-color: var(--toolbar-tab-hover-active); + background-color: var(--theme-toolbar-hover-active); } .theme-firebug .tabs .tabs-menu-item.is-active:hover:active a { background-color: var(--theme-selection-background); color: var(--theme-selection-color); } .theme-firebug .tabs .tabs-menu-item a {
--- a/devtools/client/themes/layout.css +++ b/devtools/client/themes/layout.css @@ -47,8 +47,34 @@ * Container when no grids are present */ .layout-no-grids { font-style: italic; text-align: center; padding: 0.5em; } + +/** + * Grid Item + */ + +.grid-item { + display: flex; + align-items: center; +} + +.grid-item input { + margin: 0 5px; +} + +.grid-color-swatch { + width: 12px; + height: 12px; + margin-left: 5px; + border: 1px solid var(--theme-highlight-gray); + border-radius: 50%; + cursor: pointer; +} + +.grid-color-value { + display: none; +}
--- a/devtools/client/themes/toolbars.css +++ b/devtools/client/themes/toolbars.css @@ -1,31 +1,27 @@ /* vim:set ts=2 sw=2 sts=2 et: */ /* 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/. */ /* CSS Variables specific to the devtools toolbar that aren't defined by the themes */ .theme-light { - --toolbar-tab-hover: rgba(170, 170, 170, .2); - --toolbar-tab-hover-active: rgba(170, 170, 170, .4); --searchbox-background-color: #ffee99; --searchbox-border-color: #ffbf00; --searcbox-no-match-background-color: #ffe5e5; --searcbox-no-match-border-color: #e52e2e; --magnifying-glass-image: url(chrome://devtools/skin/images/search.svg); --filter-image: url(chrome://devtools/skin/images/filter.svg); --tool-options-image: url(chrome://devtools/skin/images/tool-options.svg); --icon-filter: none; --checked-icon-filter: url(chrome://devtools/skin/images/filters.svg#checked-icon-state); } .theme-dark { - --toolbar-tab-hover: rgba(110,120,130,0.1); - --toolbar-tab-hover-active: rgba(110,120,130,0.2); --searchbox-background-color: #4d4222; --searchbox-border-color: #d99f2b; --searcbox-no-match-background-color: #402325; --searcbox-no-match-border-color: #cc3d3d; --magnifying-glass-image: url(chrome://devtools/skin/images/search.svg); --filter-image: url(chrome://devtools/skin/images/filter.svg); --tool-options-image: url(chrome://devtools/skin/images/tool-options.svg); --icon-filter: invert(1);
--- a/devtools/client/themes/toolbox.css +++ b/devtools/client/themes/toolbox.css @@ -129,25 +129,25 @@ border-color: var(--theme-splitter-color); } .theme-dark .devtools-tab:hover { color: #ced3d9; } .devtools-tab:hover { - background-color: var(--toolbar-tab-hover); + background-color: var(--theme-toolbar-hover); } .theme-dark .devtools-tab:hover:active { color: var(--theme-selection-color); } .devtools-tab:hover:active { - background-color: var(--toolbar-tab-hover-active); + background-color: var(--theme-toolbar-hover-active); } .theme-dark .devtools-tab:not(.selected).highlighted { background-color: hsla(99, 100%, 14%, .3); } .theme-light .devtools-tab:not(.selected).highlighted { background-color: rgba(44, 187, 15, .2);
--- a/devtools/client/themes/variables.css +++ b/devtools/client/themes/variables.css @@ -16,16 +16,18 @@ :root.theme-light { --theme-body-background: white; --theme-sidebar-background: white; --theme-contrast-background: #e6b064; --theme-tab-toolbar-background: #fcfcfc; --theme-toolbar-background: #fcfcfc; + --theme-toolbar-hover: rgba(170, 170, 170, .2); + --theme-toolbar-hover-active: rgba(170, 170, 170, .4); --theme-selection-background: #4c9ed9; --theme-selection-background-semitransparent: rgba(76, 158, 217, 0.15); --theme-selection-color: #f5f7fa; --theme-splitter-color: #dde1e4; --theme-comment: #696969; --theme-body-color: #393f4c; --theme-body-color-alt: #585959; @@ -76,16 +78,18 @@ :root.theme-dark { --theme-body-background: #393f4c; --theme-sidebar-background: #393f4c; --theme-contrast-background: #ffb35b; --theme-tab-toolbar-background: #272b35; --theme-toolbar-background: #272b35; + --theme-toolbar-hover: rgba(110, 120, 130, 0.1); + --theme-toolbar-hover-active: rgba(110, 120, 130, 0.2); --theme-selection-background: #5675B9; --theme-selection-background-semitransparent: rgba(86, 117, 185, 0.5); --theme-selection-color: #f5f7fa; --theme-splitter-color: #454d5d; --theme-comment: #757873; --theme-body-color: #8fa1b2; --theme-body-color-alt: #b6babf;
--- a/devtools/client/webconsole/test/head.js +++ b/devtools/client/webconsole/test/head.js @@ -42,44 +42,27 @@ var WCUL10n = new WebConsoleUtils.L10n(W const DOCS_GA_PARAMS = "?utm_source=mozilla" + "&utm_medium=firefox-console-errors" + "&utm_campaign=default"; flags.testing = true; function loadTab(url, preferredRemoteType) { - let deferred = promise.defer(); - - let tab = gBrowser.selectedTab = gBrowser.addTab(url, { preferredRemoteType }); - let browser = gBrowser.getBrowserForTab(tab); - - browser.addEventListener("load", function () { - deferred.resolve({tab: tab, browser: browser}); - }, {capture: true, once: true}); - - return deferred.promise; + return addTab(url, { preferredRemoteType }).then( tab => { + return { tab, browser: tab.linkedBrowser }; + }); } function loadBrowser(browser) { return BrowserTestUtils.browserLoaded(browser); } function closeTab(tab) { - let deferred = promise.defer(); - - let container = gBrowser.tabContainer; - - container.addEventListener("TabClose", function () { - deferred.resolve(null); - }, {capture: true, once: true}); - - gBrowser.removeTab(tab); - - return deferred.promise; + return removeTab(tab); } /** * Load the page and return the associated HUD. * * @param string uri * The URI of the page to load. * @param string consoleType [optional]
--- a/devtools/server/actors/highlighters/css-grid.js +++ b/devtools/server/actors/highlighters/css-grid.js @@ -16,38 +16,40 @@ const { const { getCurrentZoom, setIgnoreLayoutChanges, getWindowDimensions } = require("devtools/shared/layout/utils"); const { stringifyGridFragments } = require("devtools/server/actors/utils/css-grid-utils"); const CSS_GRID_ENABLED_PREF = "layout.css.grid.enabled"; +const DEFAULT_GRID_COLOR = "#4B0082"; const ROWS = "rows"; const COLUMNS = "cols"; + const GRID_LINES_PROPERTIES = { "edge": { lineDash: [0, 0], - strokeStyle: "#4B0082" + alpha: 1, }, "explicit": { lineDash: [5, 3], - strokeStyle: "#8A2BE2" + alpha: 0.75, }, "implicit": { lineDash: [2, 2], - strokeStyle: "#9370DB" + alpha: 0.5, } }; // px const GRID_GAP_PATTERN_WIDTH = 14; const GRID_GAP_PATTERN_HEIGHT = 14; const GRID_GAP_PATTERN_LINE_DASH = [5, 3]; -const GRID_GAP_PATTERN_STROKE_STYLE = "#9370DB"; +const GRID_GAP_ALPHA = 0.5; /** * Cached used by `CssGridHighlighter.getGridGapPattern`. */ const gCachedGridPattern = new WeakMap(); // WeakMap key for the Row grid pattern. const ROW_KEY = {}; // WeakMap key for the Column grid pattern. @@ -59,16 +61,19 @@ const COLUMN_KEY = {}; * * Usage example: * let h = new CssGridHighlighter(env); * h.show(node, options); * h.hide(); * h.destroy(); * * Available Options: + * - color(colorValue) + * @param {String} colorValue + * The color that should be used to draw the highlighter for this grid. * - showGridArea(areaName) * @param {String} areaName * Shows the grid area highlight for the given area name. * - showAllGridAreas * Shows all the grid area highlights for the current grid. * - showGridLineNumbers(isShown) * @param {Boolean} * Displays the grid line numbers on the grid lines if isShown is true. @@ -246,16 +251,20 @@ CssGridHighlighter.prototype = extend(Au get ctx() { return this.canvas.getCanvasContext("2d"); }, get canvas() { return this.getElement("canvas"); }, + get color() { + return this.options.color || DEFAULT_GRID_COLOR; + }, + /** * Gets the grid gap pattern used to render the gap regions. * * @param {Object} dimension * Refers to the WeakMap key for the grid dimension type which is either the * constant COLUMN or ROW. * @return {CanvasPattern} grid gap pattern. */ @@ -265,60 +274,70 @@ CssGridHighlighter.prototype = extend(Au } // Create the diagonal lines pattern for the rendering the grid gaps. let canvas = createNode(this.win, { nodeType: "canvas" }); canvas.width = GRID_GAP_PATTERN_WIDTH; canvas.height = GRID_GAP_PATTERN_HEIGHT; let ctx = canvas.getContext("2d"); + ctx.save(); ctx.setLineDash(GRID_GAP_PATTERN_LINE_DASH); ctx.beginPath(); ctx.translate(.5, .5); if (dimension === COLUMN_KEY) { ctx.moveTo(0, 0); ctx.lineTo(GRID_GAP_PATTERN_WIDTH, GRID_GAP_PATTERN_HEIGHT); } else { ctx.moveTo(GRID_GAP_PATTERN_WIDTH, 0); ctx.lineTo(0, GRID_GAP_PATTERN_HEIGHT); } - ctx.strokeStyle = GRID_GAP_PATTERN_STROKE_STYLE; + ctx.strokeStyle = this.color; + ctx.globalAlpha = GRID_GAP_ALPHA; ctx.stroke(); + ctx.restore(); let pattern = ctx.createPattern(canvas, "repeat"); gCachedGridPattern.set(dimension, pattern); return pattern; }, /** * Called when the page navigates. Used to clear the cached gap patterns and avoid * using DeadWrapper objects as gap patterns the next time. */ onNavigate() { - gCachedGridPattern.delete(ROW_KEY); - gCachedGridPattern.delete(COLUMN_KEY); + this._clearCache(); }, onWillNavigate({ isTopLevel }) { if (isTopLevel) { this.hide(); } }, _show() { if (Services.prefs.getBoolPref(CSS_GRID_ENABLED_PREF) && !this.isGrid()) { this.hide(); return false; } + // The grid pattern cache should be cleared in case the color changed. + this._clearCache(); + return this._update(); }, + _clearCache() { + gCachedGridPattern.delete(ROW_KEY); + gCachedGridPattern.delete(COLUMN_KEY); + }, + /** * Shows the grid area highlight for the given area name. * * @param {String} areaName * Grid area name. */ showGridArea(areaName) { this.renderGridArea(areaName); @@ -605,17 +624,19 @@ CssGridHighlighter.prototype = extend(Au if (dimensionType === COLUMNS) { this.ctx.moveTo(linePos, startPos); this.ctx.lineTo(linePos, endPos); } else { this.ctx.moveTo(startPos, linePos); this.ctx.lineTo(endPos, linePos); } - this.ctx.strokeStyle = GRID_LINES_PROPERTIES[lineType].strokeStyle; + this.ctx.strokeStyle = this.color; + this.ctx.globalAlpha = GRID_LINES_PROPERTIES[lineType].alpha; + this.ctx.stroke(); this.ctx.restore(); }, /** * Render the grid line number on the css grid highlighter canvas. * * @param {Number} lineNumber @@ -626,21 +647,26 @@ CssGridHighlighter.prototype = extend(Au * @param {Number} startPos * The start position of the cross side of the grid line. * @param {String} dimensionType * The grid dimension type which is either the constant COLUMNS or ROWS. */ renderGridLineNumber(lineNumber, linePos, startPos, dimensionType) { this.ctx.save(); + let textWidth = this.ctx.measureText(lineNumber).width; + // Guess the font height based on the measured width + let textHeight = textWidth * 2; + if (dimensionType === COLUMNS) { - this.ctx.fillText(lineNumber, linePos, startPos); + let yPos = Math.max(startPos, textHeight); + this.ctx.fillText(lineNumber, linePos, yPos); } else { - let textWidth = this.ctx.measureText(lineNumber).width; - this.ctx.fillText(lineNumber, startPos - textWidth, linePos); + let xPos = Math.max(startPos, textWidth); + this.ctx.fillText(lineNumber, xPos - textWidth, linePos); } this.ctx.restore(); }, /** * Render the grid gap area on the css grid highlighter canvas. *
--- a/devtools/server/actors/moz.build +++ b/devtools/server/actors/moz.build @@ -61,16 +61,17 @@ DevToolsModules( 'tab.js', 'timeline.js', 'webaudio.js', 'webbrowser.js', 'webconsole.js', 'webextension-inspected-window.js', 'webextension.js', 'webgl.js', + 'window.js', 'worker-list.js', 'worker.js', ) with Files('animation.js'): BUG_COMPONENT = ('Firefox', 'Developer Tools: Animation Inspector') with Files('breakpoint.js'):
--- a/devtools/server/actors/root.js +++ b/devtools/server/actors/root.js @@ -2,23 +2,26 @@ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ /* 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/. */ "use strict"; const { Cc, Ci, Cu } = require("chrome"); +const Services = require("Services"); const { ActorPool, appendExtraActors, createExtraActors } = require("devtools/server/actors/common"); const { DebuggerServer } = require("devtools/server/main"); loader.lazyGetter(this, "ppmm", () => { return Cc["@mozilla.org/parentprocessmessagemanager;1"].getService( Ci.nsIMessageBroadcaster); }); +loader.lazyRequireGetter(this, "WindowActor", + "devtools/server/actors/window", true); /* Root actor for the remote debugging protocol. */ /** * Create a remote debugging protocol root actor. * * @param connection * The DebuggerServerConnection whose root actor we are constructing. @@ -162,22 +165,25 @@ RootActor.prototype = { // grabbing allocations from the MemoryActor are available for the performance tool memoryActorAllocations: true, // Added in Gecko 40, indicating that the backend isn't stupid about // sending resumption packets on tab navigation. noNeedToFakeResumptionOnNavigation: true, // Added in Firefox 40. Indicates that the backend supports registering custom // commands through the WebConsoleCommands API. webConsoleCommands: true, - // Whether root actor exposes tab actors - // if allowChromeProcess is true, you can fetch a ChromeActor instance - // to debug chrome and any non-content ressource via getProcess request - // if allocChromeProcess is defined, but not true, it means that root actor - // no longer expose tab actors, but also that getProcess forbids - // exposing actors for security reasons + // Whether root actor exposes tab actors and access to any window. + // If allowChromeProcess is true, you can: + // * get a ChromeActor instance to debug chrome and any non-content + // resource via getProcess requests + // * get a WindowActor instance to debug windows which could be chrome, + // like browser windows via getWindow requests + // If allowChromeProcess is defined, but not true, it means that root actor + // no longer expose tab actors, but also that the above requests are + // forbidden for security reasons. get allowChromeProcess() { return DebuggerServer.allowChromeProcess; }, // Whether or not `getProfile()` supports specifying a `startTime` // and `endTime` to filter out samples. Fx40+ profilerDataFilterable: true, // Whether or not the MemoryActor's heap snapshot abilities are // fully equipped to handle heap snapshots for the memory tool. Fx44+ @@ -227,16 +233,17 @@ RootActor.prototype = { } if (typeof this._parameters.onShutdown === "function") { this._parameters.onShutdown(); } this._extraActors = null; this.conn = null; this._tabActorPool = null; this._globalActorPool = null; + this._windowActorPool = null; this._parameters = null; this._chromeActor = null; this._processActors.clear(); }, /* The 'listTabs' request and the 'tabListChanged' notification. */ /** @@ -333,16 +340,48 @@ RootActor.prototype = { } return { error: "noTab", message: "Unexpected error while calling getTab(): " + error }; }); }, + onGetWindow: function ({ outerWindowID }) { + if (!DebuggerServer.allowChromeProcess) { + return { + from: this.actorID, + error: "forbidden", + message: "You are not allowed to debug windows." + }; + } + let window = Services.wm.getOuterWindowWithId(outerWindowID); + if (!window) { + return { + from: this.actorID, + error: "notFound", + message: `No window found with outerWindowID ${outerWindowID}`, + }; + } + + if (!this._windowActorPool) { + this._windowActorPool = new ActorPool(this.conn); + this.conn.addActorPool(this._windowActorPool); + } + + let actor = new WindowActor(this.conn, window); + actor.parentID = this.actorID; + this._windowActorPool.addActor(actor); + + return { + from: this.actorID, + window: actor.form(), + }; + }, + onTabListChanged: function () { this.conn.send({ from: this.actorID, type: "tabListChanged" }); /* It's a one-shot notification; no need to watch any more. */ this._parameters.tabList.onListChanged = null; }, onListAddons: function () { let addonList = this._parameters.addonList; @@ -534,16 +573,17 @@ RootActor.prototype = { delete this._extraActors[name]; } } }; RootActor.prototype.requestTypes = { "listTabs": RootActor.prototype.onListTabs, "getTab": RootActor.prototype.onGetTab, + "getWindow": RootActor.prototype.onGetWindow, "listAddons": RootActor.prototype.onListAddons, "listWorkers": RootActor.prototype.onListWorkers, "listServiceWorkerRegistrations": RootActor.prototype.onListServiceWorkerRegistrations, "listProcesses": RootActor.prototype.onListProcesses, "getProcess": RootActor.prototype.onGetProcess, "echo": RootActor.prototype.onEcho, "protocolDescription": RootActor.prototype.onProtocolDescription };
new file mode 100644 --- /dev/null +++ b/devtools/server/actors/window.js @@ -0,0 +1,83 @@ +/* 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/. */ + +"use strict"; + +const { Ci } = require("chrome"); +const Services = require("Services"); +const { TabActor } = require("./tab"); + +/** + * Creates a WindowActor for debugging a single window, like a browser window in Firefox, + * but it can be used to reach any window in the process. (Currently this is parent + * process only because the root actor's `onGetWindow` doesn't try to cross process + * boundaries.) Both chrome and content windows are supported. + * + * Most of the implementation is inherited from TabActor. WindowActor exposes all tab + * actors via its form() request, like TabActor. + * + * You can request a specific window's actor via RootActor.getWindow(). + * + * @param connection DebuggerServerConnection + * The connection to the client. + * @param window DOMWindow + * The window. + */ +function WindowActor(connection, window) { + TabActor.call(this, connection); + + let docShell = window.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDocShell); + Object.defineProperty(this, "docShell", { + value: docShell, + configurable: true + }); +} + +WindowActor.prototype = Object.create(TabActor.prototype); + +// Bug 1266561: This setting is mysteriously named, we should split up the +// functionality that is triggered by it. +WindowActor.prototype.isRootActor = true; + +WindowActor.prototype.observe = function (subject, topic, data) { + TabActor.prototype.observe.call(this, subject, topic, data); + if (!this.attached) { + return; + } + if (topic == "chrome-webnavigation-destroy") { + this._onDocShellDestroy(subject); + } +}; + +WindowActor.prototype._attach = function () { + if (this.attached) { + return false; + } + + TabActor.prototype._attach.call(this); + + // Listen for chrome docshells in addition to content docshells + if (this.docShell.itemType == Ci.nsIDocShellTreeItem.typeChrome) { + Services.obs.addObserver(this, "chrome-webnavigation-destroy", false); + } + + return true; +}; + +WindowActor.prototype._detach = function () { + if (!this.attached) { + return false; + } + + if (this.docShell.itemType == Ci.nsIDocShellTreeItem.typeChrome) { + Services.obs.removeObserver(this, "chrome-webnavigation-destroy"); + } + + TabActor.prototype._detach.call(this); + + return true; +}; + +exports.WindowActor = WindowActor;
--- a/devtools/server/docs/actor-hierarchy.md +++ b/devtools/server/docs/actor-hierarchy.md @@ -2,17 +2,18 @@ To start with, actors are living within /devtools/server/actors/ folder. They are organized in a hierarchy for easier lifecycle/memory management: once a parent is removed from the pool, its children are removed as well. (See actor-registration.md for more information about how to implement one) The overall hierarchy of actors looks like this: - RootActor: First one, automatically instantiated when we start connecting. +``` +RootActor: First one, automatically instantiated when we start connecting. | Mostly meant to instantiate new actors. | |--> Global-scoped actors: | Actors exposing features related to the main process, | that are not specific to any particular context (document, tab, app, | add-on, or worker). | A good example is the preference actor. | @@ -22,26 +23,28 @@ The overall hierarchy of actors looks li | one of these for each thing you can point a toolbox at. | \--> Tab-scoped actors: Actors exposing one particular feature set, this time, specific to a given context (document, tab, app, add-on, or worker). Examples include the console and inspector actors. These actors may extend this hierarchy by having their own children, like LongStringActor, WalkerActor, etc. +``` ## RootActor The root actor is special. It is automatically created when a client connects. It has a special `actorID` which is unique and is "root". All other actors have an `actorID` which is computed dynamically, so that you need to ask an existing actor to create an Actor and returns its `actorID`. That's the main role of RootActor. - RootActor (root.js) +``` +RootActor (root.js) | |-- BrowserTabActor (webbrowser.js) | Targets tabs living in the parent or child process. Note that this is | just a proxy for ContentActor, which is loaded via the tab's message | manager as a frame script in the process containing the tab. This proxy | via message manager is always used, even when e10s is disabled. | Returned by "listTabs" or "getTab" requests. | | @@ -55,29 +58,35 @@ and returns its `actorID`. That's the ma | Targets a worker (applies to various kinds like web worker, service | worker, etc.). | Returned by "listWorkers" request to the root actor to get all workers. | Returned by "listWorkers" request to a BrowserTabActor to get workers for | a specific tab. | Returned by "listWorkers" request to a ChildProcessActor to get workers | for the chrome of the child process. | + |-- WindowActor (window.js) + | Targets a single window, such as a browser window in Firefox, but it can + | be used to reach any window in the parent process. + | Returned by "getWindow" request to the root actor. + | |-- ChromeActor (chrome.js) | Targets all resources in the parent process of firefox | (chrome documents, JSM, JS XPCOM, etc.). | Returned by "getProcess" request without any argument. | |-- ChildProcessActor (child-process.js) | Targets the chrome of the child process (e10s). | Returned by "getProcess" request with a id argument, | matching the targeted process. | \-- BrowserAddonActor (addon.js) Targets the javascript of add-ons. Returned by "listAddons" request. +``` ## "TabActor" Those are the actors exposed by the root actors which are meant to track the lifetime of a given context: tab, app, process, add-on, or worker. It also allows to fetch the tab-scoped actors connected to this context. Actors like console, inspector, thread (for debugger), styleinspector, etc. Most of them inherit from TabActor (defined in tab.js) which is document centric. It
--- a/devtools/server/tests/browser/animation.html +++ b/devtools/server/tests/browser/animation.html @@ -7,18 +7,18 @@ height: 50px; border-radius: 50%; background: #eee; } .simple-animation { display: inline-block; - width: 50px; - height: 50px; + width: 64px; + height: 64px; border-radius: 50%; background: red; animation: move 200s infinite; } .multiple-animations { display: inline-block;
--- a/devtools/shared/client/main.js +++ b/devtools/shared/client/main.js @@ -1737,16 +1737,37 @@ RootClient.prototype = { throw new Error("Unsupported argument given to getTab request"); } } return this.request(packet); }, /** + * Fetch the WindowActor for a specific window, like a browser window in + * Firefox, but it can be used to reach any window in the process. + * + * @param number outerWindowID + * The outerWindowID of the top level window you are looking for. + */ + getWindow: function ({ outerWindowID }) { + if (!outerWindowID) { + throw new Error("Must specify outerWindowID"); + } + + let packet = { + to: this.actor, + type: "getWindow", + outerWindowID, + }; + + return this.request(packet); + }, + + /** * Description of protocol's actors and methods. * * @param function onResponse * Called with the response packet. */ protocolDescription: DebuggerClient.requester({ type: "protocolDescription" }), /*
--- a/docshell/base/LoadContext.cpp +++ b/docshell/base/LoadContext.cpp @@ -41,27 +41,29 @@ namespace mozilla { NS_IMPL_ISUPPORTS(LoadContext, nsILoadContext, nsIInterfaceRequestor) LoadContext::LoadContext(nsIPrincipal* aPrincipal, nsILoadContext* aOptionalBase) : mTopFrameElement(nullptr) , mNestedFrameId(0) , mIsContent(true) , mUseRemoteTabs(false) + , mUseTrackingProtection(false) #ifdef DEBUG , mIsNotNull(true) #endif { mOriginAttributes.Inherit(aPrincipal->OriginAttributesRef()); if (!aOptionalBase) { return; } MOZ_ALWAYS_SUCCEEDS(aOptionalBase->GetIsContent(&mIsContent)); MOZ_ALWAYS_SUCCEEDS(aOptionalBase->GetUseRemoteTabs(&mUseRemoteTabs)); + MOZ_ALWAYS_SUCCEEDS(aOptionalBase->GetUseTrackingProtection(&mUseTrackingProtection)); } //----------------------------------------------------------------------------- // LoadContext::nsILoadContext //----------------------------------------------------------------------------- NS_IMETHODIMP LoadContext::GetAssociatedWindow(mozIDOMWindowProxy**) @@ -175,30 +177,32 @@ LoadContext::GetOriginAttributes(JS::Mut MOZ_ASSERT(cx); bool ok = ToJSValue(cx, mOriginAttributes, aAttrs); NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE); return NS_OK; } NS_IMETHODIMP -LoadContext::IsTrackingProtectionOn(bool* aIsTrackingProtectionOn) +LoadContext::GetUseTrackingProtection(bool* aUseTrackingProtection) { MOZ_ASSERT(mIsNotNull); - if (Preferences::GetBool("privacy.trackingprotection.enabled", false)) { - *aIsTrackingProtectionOn = true; - } else if ((mOriginAttributes.mPrivateBrowsingId > 0) && - Preferences::GetBool("privacy.trackingprotection.pbmode.enabled", false)) { - *aIsTrackingProtectionOn = true; - } else { - *aIsTrackingProtectionOn = false; - } + NS_ENSURE_ARG_POINTER(aUseTrackingProtection); + + *aUseTrackingProtection = mUseTrackingProtection; + return NS_OK; +} - return NS_OK; +NS_IMETHODIMP +LoadContext::SetUseTrackingProtection(bool aUseTrackingProtection) +{ + MOZ_ASSERT_UNREACHABLE("Should only be set through nsDocShell"); + + return NS_ERROR_UNEXPECTED; } //----------------------------------------------------------------------------- // LoadContext::nsIInterfaceRequestor //----------------------------------------------------------------------------- NS_IMETHODIMP LoadContext::GetInterface(const nsIID& aIID, void** aResult) {
--- a/docshell/base/LoadContext.h +++ b/docshell/base/LoadContext.h @@ -40,62 +40,67 @@ public: // provided by child process. LoadContext(const IPC::SerializedLoadContext& aToCopy, dom::Element* aTopFrameElement, OriginAttributes& aAttrs) : mTopFrameElement(do_GetWeakReference(aTopFrameElement)) , mNestedFrameId(0) , mIsContent(aToCopy.mIsContent) , mUseRemoteTabs(aToCopy.mUseRemoteTabs) + , mUseTrackingProtection(aToCopy.mUseTrackingProtection) , mOriginAttributes(aAttrs) #ifdef DEBUG , mIsNotNull(aToCopy.mIsNotNull) #endif { } // appId/inIsolatedMozBrowser arguments override those in SerializedLoadContext // provided by child process. LoadContext(const IPC::SerializedLoadContext& aToCopy, uint64_t aNestedFrameId, OriginAttributes& aAttrs) : mTopFrameElement(nullptr) , mNestedFrameId(aNestedFrameId) , mIsContent(aToCopy.mIsContent) , mUseRemoteTabs(aToCopy.mUseRemoteTabs) + , mUseTrackingProtection(aToCopy.mUseTrackingProtection) , mOriginAttributes(aAttrs) #ifdef DEBUG , mIsNotNull(aToCopy.mIsNotNull) #endif { } LoadContext(dom::Element* aTopFrameElement, bool aIsContent, bool aUsePrivateBrowsing, bool aUseRemoteTabs, + bool aUseTrackingProtection, const OriginAttributes& aAttrs) : mTopFrameElement(do_GetWeakReference(aTopFrameElement)) , mNestedFrameId(0) , mIsContent(aIsContent) , mUseRemoteTabs(aUseRemoteTabs) + , mUseTrackingProtection(aUseTrackingProtection) , mOriginAttributes(aAttrs) #ifdef DEBUG , mIsNotNull(true) #endif { MOZ_DIAGNOSTIC_ASSERT(aUsePrivateBrowsing == (aAttrs.mPrivateBrowsingId > 0)); } // Constructor taking reserved origin attributes. explicit LoadContext(OriginAttributes& aAttrs) : mTopFrameElement(nullptr) , mNestedFrameId(0) , mIsContent(false) , mUseRemoteTabs(false) + , mUseTrackingProtection(false) , mOriginAttributes(aAttrs) #ifdef DEBUG , mIsNotNull(true) #endif { } // Constructor for creating a LoadContext with a given principal's appId and @@ -105,16 +110,17 @@ public: private: ~LoadContext() {} nsWeakPtr mTopFrameElement; uint64_t mNestedFrameId; bool mIsContent; bool mUseRemoteTabs; + bool mUseTrackingProtection; OriginAttributes mOriginAttributes; #ifdef DEBUG bool mIsNotNull; #endif }; } // namespace mozilla
--- a/docshell/base/SerializedLoadContext.cpp +++ b/docshell/base/SerializedLoadContext.cpp @@ -56,22 +56,24 @@ SerializedLoadContext::SerializedLoadCon void SerializedLoadContext::Init(nsILoadContext* aLoadContext) { if (aLoadContext) { mIsNotNull = true; mIsPrivateBitValid = true; aLoadContext->GetIsContent(&mIsContent); aLoadContext->GetUseRemoteTabs(&mUseRemoteTabs); + aLoadContext->GetUseTrackingProtection(&mUseTrackingProtection); if (!aLoadContext->GetOriginAttributes(mOriginAttributes)) { NS_WARNING("GetOriginAttributes failed"); } } else { mIsNotNull = false; mIsPrivateBitValid = false; // none of below values really matter when mIsNotNull == false: // we won't be GetInterfaced to nsILoadContext mIsContent = true; mUseRemoteTabs = false; + mUseTrackingProtection = false; } } } // namespace IPC
--- a/docshell/base/SerializedLoadContext.h +++ b/docshell/base/SerializedLoadContext.h @@ -27,16 +27,17 @@ namespace IPC { class SerializedLoadContext { public: SerializedLoadContext() : mIsNotNull(false) , mIsPrivateBitValid(false) , mIsContent(false) , mUseRemoteTabs(false) + , mUseTrackingProtection(false) { Init(nullptr); } explicit SerializedLoadContext(nsILoadContext* aLoadContext); explicit SerializedLoadContext(nsIChannel* aChannel); explicit SerializedLoadContext(nsIWebSocketChannel* aChannel); @@ -47,16 +48,17 @@ public: // used to indicate if child-side LoadContext * was null. bool mIsNotNull; // used to indicate if child-side mUsePrivateBrowsing flag is valid, even if // mIsNotNull is false, i.e., child LoadContext was null. bool mIsPrivateBitValid; bool mIsContent; bool mUseRemoteTabs; + bool mUseTrackingProtection; mozilla::OriginAttributes mOriginAttributes; }; // Function to serialize over IPDL template<> struct ParamTraits<SerializedLoadContext> { typedef SerializedLoadContext paramType; @@ -65,26 +67,28 @@ struct ParamTraits<SerializedLoadContext { nsAutoCString suffix; aParam.mOriginAttributes.CreateSuffix(suffix); WriteParam(aMsg, aParam.mIsNotNull); WriteParam(aMsg, aParam.mIsContent); WriteParam(aMsg, aParam.mIsPrivateBitValid); WriteParam(aMsg, aParam.mUseRemoteTabs); + WriteParam(aMsg, aParam.mUseTrackingProtection); WriteParam(aMsg, suffix); } static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) { nsAutoCString suffix; if (!ReadParam(aMsg, aIter, &aResult->mIsNotNull) || !ReadParam(aMsg, aIter, &aResult->mIsContent) || !ReadParam(aMsg, aIter, &aResult->mIsPrivateBitValid) || !ReadParam(aMsg, aIter, &aResult->mUseRemoteTabs) || + !ReadParam(aMsg, aIter, &aResult->mUseTrackingProtection) || !ReadParam(aMsg, aIter, &suffix)) { return false; } return aResult->mOriginAttributes.PopulateFromSuffix(suffix); } }; } // namespace IPC
--- a/docshell/base/nsAboutRedirector.cpp +++ b/docshell/base/nsAboutRedirector.cpp @@ -133,16 +133,22 @@ static const RedirEntry kRedirMap[] = { }, { "telemetry", "chrome://global/content/aboutTelemetry.xhtml", nsIAboutModule::ALLOW_SCRIPT }, { "webrtc", "chrome://global/content/aboutwebrtc/aboutWebrtc.html", nsIAboutModule::ALLOW_SCRIPT + }, + { + "printpreview", "about:blank", + nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | + nsIAboutModule::HIDE_FROM_ABOUTABOUT | + nsIAboutModule::URI_CAN_LOAD_IN_CHILD } }; static const int kRedirTotal = mozilla::ArrayLength(kRedirMap); NS_IMETHODIMP nsAboutRedirector::NewChannel(nsIURI* aURI, nsILoadInfo* aLoadInfo, nsIChannel** aResult) @@ -168,19 +174,21 @@ nsAboutRedirector::NewChannel(nsIURI* aU // chrome:// or resource://) then set the LOAD_REPLACE flag on the // channel which forces the channel owner to reflect the displayed // URL rather then being the systemPrincipal. bool isUIResource = false; rv = NS_URIChainHasFlags(tempURI, nsIProtocolHandler::URI_IS_UI_RESOURCE, &isUIResource); NS_ENSURE_SUCCESS(rv, rv); - nsLoadFlags loadFlags = - isUIResource ? static_cast<nsLoadFlags>(nsIChannel::LOAD_NORMAL) - : static_cast<nsLoadFlags>(nsIChannel::LOAD_REPLACE); + bool isAboutBlank = NS_IsAboutBlank(tempURI); + + nsLoadFlags loadFlags = isUIResource || isAboutBlank + ? static_cast<nsLoadFlags>(nsIChannel::LOAD_NORMAL) + : static_cast<nsLoadFlags>(nsIChannel::LOAD_REPLACE); rv = NS_NewChannelInternal(getter_AddRefs(tempChannel), tempURI, aLoadInfo, nullptr, // aLoadGroup nullptr, // aCallbacks loadFlags); NS_ENSURE_SUCCESS(rv, rv);
--- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -790,16 +790,17 @@ nsDocShell::nsDocShell() , mAllowKeywordFixup(false) , mIsOffScreenBrowser(false) , mIsActive(true) , mDisableMetaRefreshWhenInactive(false) , mIsPrerendered(false) , mIsAppTab(false) , mUseGlobalHistory(false) , mUseRemoteTabs(false) + , mUseTrackingProtection(false) , mDeviceSizeIsPageSize(false) , mWindowDraggingAllowed(false) , mInFrameSwap(false) , mInheritPrivateBrowsingId(true) , mCanExecuteScripts(false) , mFiredUnloadEvent(false) , mEODForCurrentDocument(false) , mURIResultedInDocument(false) @@ -8847,24 +8848,17 @@ nsDocShell::RestoreFromHistory() nsCOMPtr<nsIDocument> document = do_QueryInterface(domDoc); if (document) { RefPtr<nsDocShell> parent = GetParentDocshell(); if (parent) { nsCOMPtr<nsIDocument> d = parent->GetDocument(); if (d) { if (d->EventHandlingSuppressed()) { - document->SuppressEventHandling(nsIDocument::eEvents, - d->EventHandlingSuppressed()); - } - - // Ick, it'd be nicer to not rewalk all of the subdocs here. - if (d->AnimationsPaused()) { - document->SuppressEventHandling(nsIDocument::eAnimationsOnly, - d->AnimationsPaused()); + document->SuppressEventHandling(d->EventHandlingSuppressed()); } } } // Use the uri from the mLSHE we had when we entered this function // (which need not match the document's URI if anchors are involved), // since that's the history entry we're loading. Note that if we use // origLSHE we don't have to worry about whether the entry in question @@ -13681,27 +13675,50 @@ nsDocShell::GetTopFrameElement(nsIDOMEle NS_IMETHODIMP nsDocShell::GetNestedFrameId(uint64_t* aId) { *aId = 0; return NS_OK; } NS_IMETHODIMP -nsDocShell::IsTrackingProtectionOn(bool* aIsTrackingProtectionOn) -{ - if (Preferences::GetBool("privacy.trackingprotection.enabled", false)) { - *aIsTrackingProtectionOn = true; - } else if (UsePrivateBrowsing() && - Preferences::GetBool("privacy.trackingprotection.pbmode.enabled", false)) { - *aIsTrackingProtectionOn = true; - } else { - *aIsTrackingProtectionOn = false; - } - +nsDocShell::GetUseTrackingProtection(bool* aUseTrackingProtection) +{ + *aUseTrackingProtection = false; + + static bool sTPEnabled = false; + static bool sTPInPBEnabled = false; + static bool sPrefsInit = false; + + if (!sPrefsInit) { + sPrefsInit = true; + Preferences::AddBoolVarCache(&sTPEnabled, + "privacy.trackingprotection.enabled", false); + Preferences::AddBoolVarCache(&sTPInPBEnabled, + "privacy.trackingprotection.pbmode.enabled", false); + } + + if (mUseTrackingProtection || sTPEnabled || + (UsePrivateBrowsing() && sTPInPBEnabled)) { + *aUseTrackingProtection = true; + return NS_OK; + } + + RefPtr<nsDocShell> parent = GetParentDocshell(); + if (parent) { + return parent->GetUseTrackingProtection(aUseTrackingProtection); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsDocShell::SetUseTrackingProtection(bool aUseTrackingProtection) +{ + mUseTrackingProtection = aUseTrackingProtection; return NS_OK; } NS_IMETHODIMP nsDocShell::GetIsContent(bool* aIsContent) { *aIsContent = (mItemType == typeContent); return NS_OK; @@ -14342,20 +14359,29 @@ nsDocShell::StopDocumentLoad(void) NS_IMETHODIMP nsDocShell::GetPrintPreview(nsIWebBrowserPrint** aPrintPreview) { *aPrintPreview = nullptr; #if NS_PRINT_PREVIEW nsCOMPtr<nsIDocumentViewerPrint> print = do_QueryInterface(mContentViewer); if (!print || !print->IsInitializedForPrintPreview()) { + // XXX: Creating a brand new content viewer to host preview every + // time we enter here seems overwork. We could skip ahead to where + // we QI the mContentViewer if the current URI is either about:blank + // or about:printpreview. Stop(nsIWebNavigation::STOP_ALL); nsCOMPtr<nsIPrincipal> principal = nsNullPrincipal::CreateWithInheritedAttributes(this); - nsresult rv = CreateAboutBlankContentViewer(principal, nullptr); + nsCOMPtr<nsIURI> uri; + NS_NewURI(getter_AddRefs(uri), NS_LITERAL_CSTRING("about:printpreview")); + nsresult rv = CreateAboutBlankContentViewer(principal, uri); NS_ENSURE_SUCCESS(rv, rv); + // Here we manually set current URI since we have just created a + // brand new content viewer (about:blank) to host preview. + SetCurrentURI(uri, nullptr, true, 0); print = do_QueryInterface(mContentViewer); NS_ENSURE_STATE(print); print->InitializeForPrintPreview(); } nsCOMPtr<nsIWebBrowserPrint> result = do_QueryInterface(print); result.forget(aPrintPreview); return NS_OK; #else
--- a/docshell/base/nsDocShell.h +++ b/docshell/base/nsDocShell.h @@ -229,17 +229,16 @@ public: NS_IMETHOD GetNestedFrameId(uint64_t*) override; NS_IMETHOD GetIsContent(bool*) override; NS_IMETHOD GetUsePrivateBrowsing(bool*) override; NS_IMETHOD SetUsePrivateBrowsing(bool) override; NS_IMETHOD SetPrivateBrowsing(bool) override; NS_IMETHOD GetUseRemoteTabs(bool*) override; NS_IMETHOD SetRemoteTabs(bool) override; NS_IMETHOD GetOriginAttributes(JS::MutableHandle<JS::Value>) override; - NS_IMETHOD IsTrackingProtectionOn(bool*) override; // Restores a cached presentation from history (mLSHE). // This method swaps out the content viewer and simulates loads for // subframes. It then simulates the completion of the toplevel load. nsresult RestoreFromHistory(); // Perform a URI load from a refresh timer. This is just like the // ForceRefreshURI method on nsIRefreshURI, but makes sure to take @@ -956,16 +955,17 @@ protected: bool mAllowKeywordFixup : 1; bool mIsOffScreenBrowser : 1; bool mIsActive : 1; bool mDisableMetaRefreshWhenInactive : 1; bool mIsPrerendered : 1; bool mIsAppTab : 1; bool mUseGlobalHistory : 1; bool mUseRemoteTabs : 1; + bool mUseTrackingProtection : 1; bool mDeviceSizeIsPageSize : 1; bool mWindowDraggingAllowed : 1; bool mInFrameSwap : 1; bool mInheritPrivateBrowsingId : 1; // Because scriptability depends on the mAllowJavascript values of our // ancestors, we cache the effective scriptability and recompute it when // it might have changed;
--- a/docshell/base/nsIDocShell.idl +++ b/docshell/base/nsIDocShell.idl @@ -1126,9 +1126,14 @@ interface nsIDocShell : nsIDocShellTreeI */ [infallible] readonly attribute boolean isOnlyToplevelInTabGroup; /** * Returns `true` if this docshell was created due to a Large-Allocation * header, and has not seen the initiating load yet. */ [infallible] readonly attribute boolean awaitingLargeAlloc; + + /** + * Attribute that determines whether tracking protection is enabled. + */ + attribute boolean useTrackingProtection; };
--- a/docshell/base/nsILoadContext.idl +++ b/docshell/base/nsILoadContext.idl @@ -73,31 +73,46 @@ interface nsILoadContext : nsISupports */ attribute boolean usePrivateBrowsing; /** * Attribute that determines if remote (out-of-process) tabs should be used. */ readonly attribute boolean useRemoteTabs; + /* + * Attribute that determines if tracking protection should be used. May not be + * changed after a document has been loaded in this context. + */ + attribute boolean useTrackingProtection; + %{C++ /** * De-XPCOMed getter to make call-sites cleaner. */ - bool UsePrivateBrowsing() { - bool usingPB; + bool UsePrivateBrowsing() + { + bool usingPB = false; GetUsePrivateBrowsing(&usingPB); return usingPB; } - bool UseRemoteTabs() { - bool usingRT; + bool UseRemoteTabs() + { + bool usingRT = false; GetUseRemoteTabs(&usingRT); return usingRT; } + + bool UseTrackingProtection() + { + bool usingTP = false; + GetUseTrackingProtection(&usingTP); + return usingTP; + } %} /** * Set the private browsing state of the load context, meant to be used internally. */ [noscript] void SetPrivateBrowsing(in boolean aInPrivateBrowsing); /** @@ -125,25 +140,9 @@ interface nsILoadContext : nsISupports /** * The C++ getter for origin attributes. * * Defined in LoadContext.cpp */ bool GetOriginAttributes(mozilla::OriginAttributes& aAttrs); #endif %} - - /** - * Returns true if tracking protection is enabled for the load context. - */ - boolean IsTrackingProtectionOn(); - -%{C++ - /** - * De-XPCOMed getter to make call-sites cleaner. - */ - bool UseTrackingProtection() { - bool usingTP; - IsTrackingProtectionOn(&usingTP); - return usingTP; - } -%} };
--- a/docshell/build/nsDocShellModule.cpp +++ b/docshell/build/nsDocShellModule.cpp @@ -184,16 +184,17 @@ const mozilla::Module::ContractIDEntry k { NS_ABOUT_MODULE_CONTRACTID_PREFIX "serviceworkers", &kNS_ABOUT_REDIRECTOR_MODULE_CID }, #ifndef ANDROID { NS_ABOUT_MODULE_CONTRACTID_PREFIX "profiles", &kNS_ABOUT_REDIRECTOR_MODULE_CID }, #endif { NS_ABOUT_MODULE_CONTRACTID_PREFIX "srcdoc", &kNS_ABOUT_REDIRECTOR_MODULE_CID }, { NS_ABOUT_MODULE_CONTRACTID_PREFIX "support", &kNS_ABOUT_REDIRECTOR_MODULE_CID }, { NS_ABOUT_MODULE_CONTRACTID_PREFIX "telemetry", &kNS_ABOUT_REDIRECTOR_MODULE_CID }, { NS_ABOUT_MODULE_CONTRACTID_PREFIX "webrtc", &kNS_ABOUT_REDIRECTOR_MODULE_CID }, + { NS_ABOUT_MODULE_CONTRACTID_PREFIX "printpreview", &kNS_ABOUT_REDIRECTOR_MODULE_CID }, { NS_URI_LOADER_CONTRACTID, &kNS_URI_LOADER_CID }, { NS_DOCUMENTLOADER_SERVICE_CONTRACTID, &kNS_DOCUMENTLOADER_SERVICE_CID }, { NS_HANDLERSERVICE_CONTRACTID, &kNS_CONTENTHANDLERSERVICE_CID, mozilla::Module::CONTENT_PROCESS_ONLY }, { NS_EXTERNALHELPERAPPSERVICE_CONTRACTID, &kNS_EXTERNALHELPERAPPSERVICE_CID }, { NS_EXTERNALPROTOCOLSERVICE_CONTRACTID, &kNS_EXTERNALHELPERAPPSERVICE_CID }, { NS_MIMESERVICE_CONTRACTID, &kNS_EXTERNALHELPERAPPSERVICE_CID }, { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX"default", &kNS_EXTERNALPROTOCOLHANDLER_CID }, { NS_PREFETCHSERVICE_CONTRACTID, &kNS_PREFETCHSERVICE_CID },
--- a/dom/animation/AnimationPerformanceWarning.cpp +++ b/dom/animation/AnimationPerformanceWarning.cpp @@ -28,23 +28,16 @@ AnimationPerformanceWarning::ToLocalized bool AnimationPerformanceWarning::ToLocalizedString( nsXPIDLString& aLocalizedString) const { const char* key = nullptr; switch (mType) { - case Type::ContentTooSmall: - MOZ_ASSERT(mParams && mParams->Length() == 2, - "Parameter's length should be 2 for ContentTooSmall"); - - return NS_SUCCEEDED( - ToLocalizedStringWithIntParams<2>( - "CompositorAnimationWarningContentTooSmall", aLocalizedString)); case Type::ContentTooLarge: MOZ_ASSERT(mParams && mParams->Length() == 6, "Parameter's length should be 6 for ContentTooLarge"); return NS_SUCCEEDED( ToLocalizedStringWithIntParams<7>( "CompositorAnimationWarningContentTooLarge2", aLocalizedString)); case Type::TransformBackfaceVisibilityHidden:
--- a/dom/animation/AnimationPerformanceWarning.h +++ b/dom/animation/AnimationPerformanceWarning.h @@ -15,17 +15,16 @@ class nsXPIDLString; namespace mozilla { // Represents the reason why we can't run the CSS property on the compositor. struct AnimationPerformanceWarning { enum class Type : uint8_t { - ContentTooSmall, ContentTooLarge, TransformBackfaceVisibilityHidden, TransformPreserve3D, TransformSVG, TransformWithGeometricProperties, TransformWithSyncGeometricAnimations, TransformFrameInactive, OpacityFrameInactive,
--- a/dom/animation/test/chrome/file_animation_performance_warning.html +++ b/dom/animation/test/chrome/file_animation_performance_warning.html @@ -835,47 +835,45 @@ function testMultipleAnimationsWithGeome }); }, 'Multiple async animations and geometric animation: ' + subtest.desc); }); } function testSmallElements() { [ { - desc: 'opacity on too small element', + desc: 'opacity on small element', frames: { opacity: [0, 1] }, style: { style: 'width: 8px; height: 8px; background-color: red;' + // We need to set transform here to try creating an // individual frame for this opacity element. // Without this, this small element is created on the same // nsIFrame of mochitest iframe, i.e. the document which are // running this test, as a result the layer corresponding // to the frame is sent to compositor. 'transform: translateX(100px);' }, expected: [ { property: 'opacity', - runningOnCompositor: false, - warning: /Animation cannot be run on the compositor because frame size \(8, 8\) is smaller than \(16, 16\)/ + runningOnCompositor: true } ] }, { - desc: 'transform on too small element', + desc: 'transform on small element', frames: { transform: ['translate(0px)', 'translate(100px)'] }, style: { style: 'width: 8px; height: 8px; background-color: red;' }, expected: [ { property: 'transform', - runningOnCompositor: false, - warning: /Animation cannot be run on the compositor because frame size \(8, 8\) is smaller than \(16, 16\)/ + runningOnCompositor: true } ] }, ].forEach(subtest => { promise_test(function(t) { var div = addDiv(t, subtest.style); var animation = div.animate(subtest.frames, 100 * MS_PER_SEC); return animation.ready.then(function() {
--- a/dom/base/GroupedSHistory.cpp +++ b/dom/base/GroupedSHistory.cpp @@ -1,15 +1,17 @@ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "GroupedSHistory.h" + +#include "mozilla/dom/Promise.h" #include "TabParent.h" #include "PartialSHistory.h" namespace mozilla { namespace dom { NS_IMPL_CYCLE_COLLECTION_CLASS(GroupedSHistory)
--- a/dom/base/GroupedSHistory.h +++ b/dom/base/GroupedSHistory.h @@ -6,16 +6,19 @@ #ifndef GroupedSHistory_h #define GroupedSHistory_h #include "nsIFrameLoader.h" #include "nsIGroupedSHistory.h" #include "nsIPartialSHistory.h" #include "nsTArray.h" +#include "nsCOMArray.h" +#include "nsCOMPtr.h" +#include "nsCycleCollectionParticipant.h" #include "nsWeakReference.h" namespace mozilla { namespace dom { /** * GroupedSHistory connects session histories across multiple frameloaders.
--- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -3439,22 +3439,22 @@ nsContentUtils::IsDraggableImage(nsICont bool nsContentUtils::IsDraggableLink(const nsIContent* aContent) { nsCOMPtr<nsIURI> absURI; return aContent->IsLink(getter_AddRefs(absURI)); } // static nsresult -nsContentUtils::NameChanged(mozilla::dom::NodeInfo* aNodeInfo, nsIAtom* aName, - mozilla::dom::NodeInfo** aResult) +nsContentUtils::QNameChanged(mozilla::dom::NodeInfo* aNodeInfo, nsIAtom* aName, + mozilla::dom::NodeInfo** aResult) { nsNodeInfoManager *niMgr = aNodeInfo->NodeInfoManager(); - *aResult = niMgr->GetNodeInfo(aName, aNodeInfo->GetPrefixAtom(), + *aResult = niMgr->GetNodeInfo(aName, nullptr, aNodeInfo->NamespaceID(), aNodeInfo->NodeType(), aNodeInfo->GetExtraName()).take(); return NS_OK; } static bool
--- a/dom/base/nsContentUtils.h +++ b/dom/base/nsContentUtils.h @@ -791,21 +791,23 @@ public: * Method that decides whether a content node is a draggable link * * @param aContent The content node to test. * @return whether it's a draggable link */ static bool IsDraggableLink(const nsIContent* aContent); /** - * Convenience method to create a new nodeinfo that differs only by name - * from aNodeInfo. + * Convenience method to create a new nodeinfo that differs only by prefix and + * name from aNodeInfo. The new nodeinfo's name is set to aName, and prefix is + * set to null. */ - static nsresult NameChanged(mozilla::dom::NodeInfo* aNodeInfo, nsIAtom* aName, - mozilla::dom::NodeInfo** aResult); + static nsresult QNameChanged(mozilla::dom::NodeInfo* aNodeInfo, + nsIAtom* aName, + mozilla::dom::NodeInfo** aResult); /** * Returns the appropriate event argument names for the specified * namespace and event name. Added because we need to switch between * SVG's "evt" and the rest of the world's "event", and because onerror * on window takes 5 args. */ static void GetEventArgNames(int32_t aNameSpaceID, nsIAtom *aEventName,
--- a/dom/base/nsCopySupport.cpp +++ b/dom/base/nsCopySupport.cpp @@ -750,18 +750,23 @@ nsCopySupport::FireClipboardEvent(EventM nsIPresShell* aPresShell, nsISelection* aSelection, bool* aActionTaken) { if (aActionTaken) { *aActionTaken = false; } - NS_ASSERTION(aEventMessage == eCut || aEventMessage == eCopy || - aEventMessage == ePaste, + EventMessage originalEventMessage = aEventMessage; + if (originalEventMessage == ePasteNoFormatting) { + originalEventMessage = ePaste; + } + + NS_ASSERTION(originalEventMessage == eCut || originalEventMessage == eCopy || + originalEventMessage == ePaste, "Invalid clipboard event type"); nsCOMPtr<nsIPresShell> presShell = aPresShell; if (!presShell) return false; nsCOMPtr<nsIDocument> doc = presShell->GetDocument(); if (!doc) @@ -808,31 +813,31 @@ nsCopySupport::FireClipboardEvent(EventM docShell && docShell->ItemType() == nsIDocShellTreeItem::typeChrome; // next, fire the cut, copy or paste event bool doDefault = true; RefPtr<DataTransfer> clipboardData; if (chromeShell || Preferences::GetBool("dom.event.clipboardevents.enabled", true)) { clipboardData = new DataTransfer(doc->GetScopeObject(), aEventMessage, - aEventMessage == ePaste, aClipboardType); + originalEventMessage == ePaste, aClipboardType); nsEventStatus status = nsEventStatus_eIgnore; - InternalClipboardEvent evt(true, aEventMessage); + InternalClipboardEvent evt(true, originalEventMessage); evt.mClipboardData = clipboardData; EventDispatcher::Dispatch(content, presShell->GetPresContext(), &evt, nullptr, &status); // If the event was cancelled, don't do the clipboard operation doDefault = (status != nsEventStatus_eConsumeNoDefault); } // No need to do anything special during a paste. Either an event listener // took care of it and cancelled the event, or the caller will handle it. // Return true to indicate that the event wasn't cancelled. - if (aEventMessage == ePaste) { + if (originalEventMessage == ePaste) { // Clear and mark the clipboardData as readonly. This prevents someone // from reading the clipboard contents after the paste event has fired. if (clipboardData) { clipboardData->ClearAll(); clipboardData->SetReadOnly(); } if (aActionTaken) { @@ -862,17 +867,17 @@ nsCopySupport::FireClipboardEvent(EventM if (formControl) { if (formControl->GetType() == NS_FORM_INPUT_PASSWORD) { return false; } } // when cutting non-editable content, do nothing // XXX this is probably the wrong editable flag to check - if (aEventMessage != eCut || content->IsEditable()) { + if (originalEventMessage != eCut || content->IsEditable()) { // get the data from the selection if any bool isCollapsed; sel->GetIsCollapsed(&isCollapsed); if (isCollapsed) { if (aActionTaken) { *aActionTaken = true; } return false;
--- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -1645,19 +1645,19 @@ nsDOMWindowUtils::DisableNonTestMouseEve NS_IMETHODIMP nsDOMWindowUtils::SuppressEventHandling(bool aSuppress) { nsCOMPtr<nsIDocument> doc = GetDocument(); NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE); if (aSuppress) { - doc->SuppressEventHandling(nsIDocument::eEvents); + doc->SuppressEventHandling(); } else { - doc->UnsuppressEventHandlingAndFireEvents(nsIDocument::eEvents, true); + doc->UnsuppressEventHandlingAndFireEvents(true); } return NS_OK; } static nsresult getScrollXYAppUnits(const nsWeakPtr& aWindow, bool aFlushLayout, nsPoint& aScrollPos) { nsCOMPtr<nsPIDOMWindowOuter> window = do_QueryReferent(aWindow);
--- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -1353,17 +1353,16 @@ nsIDocument::nsIDocument() #endif mBidiOptions(IBMBIDI_DEFAULT_BIDI_OPTIONS), mSandboxFlags(0), mPartID(0), mMarkedCCGeneration(0), mPresShell(nullptr), mSubtreeModifiedDepth(0), mEventsSuppressed(0), - mAnimationsPaused(0), mExternalScriptsBeingEvaluated(0), mFrameRequestCallbackCounter(0), mStaticCloneCount(0), mWindow(nullptr), mBFCacheEntry(nullptr), mInSyncOperationCount(0), mBlockDOMContentLoaded(0), mDidFireDOMContentLoaded(true), @@ -3806,17 +3805,17 @@ nsDocument::CreateShell(nsPresContext* a RebuildUserFontSet(); return shell.forget(); } void nsDocument::MaybeRescheduleAnimationFrameNotifications() { - if (!mPresShell || !IsEventHandlingEnabled() || AnimationsPaused()) { + if (!mPresShell || !IsEventHandlingEnabled()) { // bail out for now, until one of those conditions changes return; } nsRefreshDriver* rd = mPresShell->GetPresContext()->RefreshDriver(); if (!mFrameRequestCallbacks.IsEmpty()) { rd->ScheduleFrameRequestCallbacks(this); } @@ -3869,17 +3868,17 @@ nsIDocument::ShouldThrottleFrameRequests // We got painted during the last paint, so run at full speed. return false; } void nsDocument::DeleteShell() { mExternalResourceMap.HideViewers(); - if (IsEventHandlingEnabled() && !AnimationsPaused()) { + if (IsEventHandlingEnabled()) { RevokeAnimationFrameNotifications(); } if (nsPresContext* presContext = mPresShell->GetPresContext()) { presContext->RefreshDriver()->CancelPendingEvents(this); } // When our shell goes away, request that all our images be immediately // discarded, so we don't carry around decoded image data for a document we @@ -4651,17 +4650,17 @@ nsDocument::SetScriptGlobalObject(nsIScr nsSMILTimeContainer::PAUSE_BEGIN), "Clearing window pointer while animations are unpaused"); if (mScriptGlobalObject && !aScriptGlobalObject) { // We're detaching from the window. We need to grab a pointer to // our layout history state now. mLayoutHistoryState = GetLayoutHistoryState(); - if (mPresShell && !EventHandlingSuppressed() && !AnimationsPaused()) { + if (mPresShell && !EventHandlingSuppressed()) { RevokeAnimationFrameNotifications(); } // Also make sure to remove our onload blocker now if we haven't done it yet if (mOnloadBlockCount != 0) { nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup(); if (loadGroup) { loadGroup->RemoveRequest(mOnloadBlocker, nullptr, NS_OK); @@ -9320,54 +9319,38 @@ nsIDocument::GetReadyState(nsAString& aR case READYSTATE_COMPLETE : aReadyState.AssignLiteral(u"complete"); break; default: aReadyState.AssignLiteral(u"uninitialized"); } } -namespace { - -struct SuppressArgs -{ - nsIDocument::SuppressionType mWhat; - uint32_t mIncrease; -}; - -} // namespace - static bool SuppressEventHandlingInDocument(nsIDocument* aDocument, void* aData) { - SuppressArgs* args = static_cast<SuppressArgs*>(aData); - aDocument->SuppressEventHandling(args->mWhat, args->mIncrease); + aDocument->SuppressEventHandling(*static_cast<uint32_t*>(aData)); + return true; } void -nsDocument::SuppressEventHandling(nsIDocument::SuppressionType aWhat, - uint32_t aIncrease) -{ - if (mEventsSuppressed == 0 && mAnimationsPaused == 0 && - aIncrease != 0 && mPresShell && mScriptGlobalObject) { +nsDocument::SuppressEventHandling(uint32_t aIncrease) +{ + if (mEventsSuppressed == 0 && aIncrease != 0 && mPresShell && + mScriptGlobalObject) { RevokeAnimationFrameNotifications(); } - if (aWhat == eAnimationsOnly) { - mAnimationsPaused += aIncrease; - } else { - mEventsSuppressed += aIncrease; - for (uint32_t i = 0; i < aIncrease; ++i) { - ScriptLoader()->AddExecuteBlocker(); - } - } - - SuppressArgs args = { aWhat, aIncrease }; - EnumerateSubDocuments(SuppressEventHandlingInDocument, &args); + mEventsSuppressed += aIncrease; + for (uint32_t i = 0; i < aIncrease; ++i) { + ScriptLoader()->AddExecuteBlocker(); + } + + EnumerateSubDocuments(SuppressEventHandlingInDocument, &aIncrease); } static void FireOrClearDelayedEvents(nsTArray<nsCOMPtr<nsIDocument> >& aDocuments, bool aFireEvents) { nsIFocusManager* fm = nsFocusManager::GetFocusManager(); if (!fm) @@ -9659,72 +9642,45 @@ public: FireOrClearDelayedEvents(mDocuments, true); return NS_OK; } private: nsTArray<nsCOMPtr<nsIDocument> > mDocuments; }; -namespace { - -struct UnsuppressArgs -{ - explicit UnsuppressArgs(nsIDocument::SuppressionType aWhat) - : mWhat(aWhat) - { - } - - nsIDocument::SuppressionType mWhat; - nsTArray<nsCOMPtr<nsIDocument>> mDocs; -}; - -} // namespace - static bool GetAndUnsuppressSubDocuments(nsIDocument* aDocument, void* aData) { - UnsuppressArgs* args = static_cast<UnsuppressArgs*>(aData); - if (args->mWhat != nsIDocument::eAnimationsOnly && - aDocument->EventHandlingSuppressed() > 0) { + if (aDocument->EventHandlingSuppressed() > 0) { static_cast<nsDocument*>(aDocument)->DecreaseEventSuppression(); aDocument->ScriptLoader()->RemoveExecuteBlocker(); - } else if (args->mWhat == nsIDocument::eAnimationsOnly && - aDocument->AnimationsPaused()) { - static_cast<nsDocument*>(aDocument)->ResumeAnimations(); - } - - if (args->mWhat != nsIDocument::eAnimationsOnly) { - // No need to remember documents if we only care about animation frames. - args->mDocs.AppendElement(aDocument); - } - + } + + nsTArray<nsCOMPtr<nsIDocument> >* docs = + static_cast<nsTArray<nsCOMPtr<nsIDocument> >* >(aData); + + docs->AppendElement(aDocument); aDocument->EnumerateSubDocuments(GetAndUnsuppressSubDocuments, aData); return true; } void -nsDocument::UnsuppressEventHandlingAndFireEvents(nsIDocument::SuppressionType aWhat, - bool aFireEvents) -{ - UnsuppressArgs args(aWhat); - GetAndUnsuppressSubDocuments(this, &args); - - if (aWhat == nsIDocument::eAnimationsOnly) { - // No need to fire events if we only care about animations here. - return; - } +nsDocument::UnsuppressEventHandlingAndFireEvents(bool aFireEvents) +{ + nsTArray<nsCOMPtr<nsIDocument>> documents; + GetAndUnsuppressSubDocuments(this, &documents); if (aFireEvents) { MOZ_RELEASE_ASSERT(NS_IsMainThread()); - nsCOMPtr<nsIRunnable> ded = new nsDelayedEventDispatcher(args.mDocs); + nsCOMPtr<nsIRunnable> ded = new nsDelayedEventDispatcher(documents); Dispatch("nsDelayedEventDispatcher", TaskCategory::Other, ded.forget()); } else { - FireOrClearDelayedEvents(args.mDocs, false); + FireOrClearDelayedEvents(documents, false); } } nsISupports* nsDocument::GetCurrentContentSink() { return mParser ? mParser->GetContentSink() : nullptr; } @@ -10039,33 +9995,32 @@ nsIDocument::ScheduleFrameRequestCallbac return NS_ERROR_NOT_AVAILABLE; } int32_t newHandle = ++mFrameRequestCallbackCounter; bool alreadyRegistered = !mFrameRequestCallbacks.IsEmpty(); DebugOnly<FrameRequest*> request = mFrameRequestCallbacks.AppendElement(FrameRequest(aCallback, newHandle)); NS_ASSERTION(request, "This is supposed to be infallible!"); - if (!alreadyRegistered && mPresShell && IsEventHandlingEnabled() && - !AnimationsPaused()) { + if (!alreadyRegistered && mPresShell && IsEventHandlingEnabled()) { mPresShell->GetPresContext()->RefreshDriver()-> ScheduleFrameRequestCallbacks(this); } *aHandle = newHandle; return NS_OK; } void nsIDocument::CancelFrameRequestCallback(int32_t aHandle) { // mFrameRequestCallbacks is stored sorted by handle if (mFrameRequestCallbacks.RemoveElementSorted(aHandle) && mFrameRequestCallbacks.IsEmpty() && - mPresShell && IsEventHandlingEnabled() && !AnimationsPaused()) { + mPresShell && IsEventHandlingEnabled()) { mPresShell->GetPresContext()->RefreshDriver()-> RevokeFrameRequestCallbacks(this); } } nsresult nsDocument::GetStateObject(nsIVariant** aState) {
--- a/dom/base/nsDocument.h +++ b/dom/base/nsDocument.h @@ -902,34 +902,26 @@ public: GetPendingAnimationTracker() final override { return mPendingAnimationTracker; } virtual mozilla::PendingAnimationTracker* GetOrCreatePendingAnimationTracker() override; - virtual void SuppressEventHandling(SuppressionType aWhat, - uint32_t aIncrease) override; + virtual void SuppressEventHandling(uint32_t aIncrease) override; - virtual void UnsuppressEventHandlingAndFireEvents(SuppressionType aWhat, - bool aFireEvents) override; + virtual void UnsuppressEventHandlingAndFireEvents(bool aFireEvents) override; void DecreaseEventSuppression() { MOZ_ASSERT(mEventsSuppressed); --mEventsSuppressed; MaybeRescheduleAnimationFrameNotifications(); } - void ResumeAnimations() { - MOZ_ASSERT(mAnimationsPaused); - --mAnimationsPaused; - MaybeRescheduleAnimationFrameNotifications(); - } - virtual nsIDocument* GetTemplateContentsOwner() override; NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsDocument, nsIDocument) void DoNotifyPossibleTitleChange(); nsExternalResourceMap& ExternalResourceMap()
--- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -1999,21 +1999,18 @@ nsGlobalWindow::FreeInnerObjects() if (mDoc) { // Remember the document's principal and URI. mDocumentPrincipal = mDoc->NodePrincipal(); mDocumentURI = mDoc->GetDocumentURI(); mDocBaseURI = mDoc->GetDocBaseURI(); while (mDoc->EventHandlingSuppressed()) { - mDoc->UnsuppressEventHandlingAndFireEvents(nsIDocument::eEvents, false); - } - - // Note: we don't have to worry about eAnimationsOnly suppressions because - // they won't leak. + mDoc->UnsuppressEventHandlingAndFireEvents(false); + } } // Remove our reference to the document and the document principal. mFocusedNode = nullptr; if (mApplicationCache) { static_cast<nsDOMOfflineResourceList*>(mApplicationCache.get())->Disconnect(); mApplicationCache = nullptr; @@ -9261,17 +9258,17 @@ nsGlobalWindow::EnterModalState() nsIPresShell::SetCapturingContent(nullptr, 0); } if (topWin->mModalStateDepth == 0) { NS_ASSERTION(!topWin->mSuspendedDoc, "Shouldn't have mSuspendedDoc here!"); topWin->mSuspendedDoc = topDoc; if (topDoc) { - topDoc->SuppressEventHandling(nsIDocument::eEvents); + topDoc->SuppressEventHandling(); } nsGlobalWindow* inner = topWin->GetCurrentInnerWindowInternal(); if (inner) { topWin->GetCurrentInnerWindowInternal()->Suspend(); } } topWin->mModalStateDepth++; @@ -9298,18 +9295,17 @@ nsGlobalWindow::LeaveModalState() if (topWin->mModalStateDepth == 0) { if (inner) { inner->Resume(); } if (topWin->mSuspendedDoc) { nsCOMPtr<nsIDocument> currentDoc = topWin->GetExtantDoc(); - topWin->mSuspendedDoc->UnsuppressEventHandlingAndFireEvents(nsIDocument::eEvents, - currentDoc == topWin->mSuspendedDoc); + topWin->mSuspendedDoc->UnsuppressEventHandlingAndFireEvents(currentDoc == topWin->mSuspendedDoc); topWin->mSuspendedDoc = nullptr; } } // Remember the time of the last dialog quit. if (inner) { inner->mLastDialogQuitTime = TimeStamp::Now(); }
--- a/dom/base/nsIDocument.h +++ b/dom/base/nsIDocument.h @@ -2153,41 +2153,30 @@ public: virtual mozilla::PendingAnimationTracker* GetPendingAnimationTracker() = 0; // Gets the tracker for animations that are waiting to start and // creates it if it doesn't already exist. As a result, the return value // will never be nullptr. virtual mozilla::PendingAnimationTracker* GetOrCreatePendingAnimationTracker() = 0; - enum SuppressionType { - eAnimationsOnly = 0x1, - - // Note that suppressing events also suppresses animation frames, so - // there's no need to split out events in its own bitmask. - eEvents = 0x3, - }; - /** * Prevents user initiated events from being dispatched to the document and * subdocuments. */ - virtual void SuppressEventHandling(SuppressionType aWhat, - uint32_t aIncrease = 1) = 0; + virtual void SuppressEventHandling(uint32_t aIncrease = 1) = 0; /** * Unsuppress event handling. * @param aFireEvents If true, delayed events (focus/blur) will be fired * asynchronously. */ - virtual void UnsuppressEventHandlingAndFireEvents(SuppressionType aWhat, - bool aFireEvents) = 0; + virtual void UnsuppressEventHandlingAndFireEvents(bool aFireEvents) = 0; uint32_t EventHandlingSuppressed() const { return mEventsSuppressed; } - uint32_t AnimationsPaused() const { return mAnimationsPaused; } bool IsEventHandlingEnabled() { return !EventHandlingSuppressed() && mScriptGlobalObject; } /** * Increment the number of external scripts being evaluated. */ @@ -3277,18 +3266,16 @@ protected: // If we're an external resource document, this will be non-null and will // point to our "display document": the one that all resource lookups should // go to. nsCOMPtr<nsIDocument> mDisplayDocument; uint32_t mEventsSuppressed; - uint32_t mAnimationsPaused; - /** * The number number of external scripts (ones with the src attribute) that * have this document as their owner and that are being evaluated right now. */ uint32_t mExternalScriptsBeingEvaluated; /** * The current frame request callback handle
--- a/dom/bindings/BindingUtils.cpp +++ b/dom/bindings/BindingUtils.cpp @@ -2782,17 +2782,17 @@ void HandlePrerenderingViolation(nsPIDOMWindowInner* aWindow) { // Freeze the window and its workers, and its children too. aWindow->Freeze(); // Suspend event handling on the document nsCOMPtr<nsIDocument> doc = aWindow->GetExtantDoc(); if (doc) { - doc->SuppressEventHandling(nsIDocument::eEvents); + doc->SuppressEventHandling(); } } bool EnforceNotInPrerendering(JSContext* aCx, JSObject* aObj) { JS::Rooted<JSObject*> thisObj(aCx, js::CheckedUnwrap(aObj)); if (!thisObj) {
--- a/dom/bindings/UnionMember.h +++ b/dom/bindings/UnionMember.h @@ -16,17 +16,24 @@ namespace dom { // The union type has an enum to keep track of which of its UnionMembers has // been constructed. template<class T> class UnionMember { AlignedStorage2<T> mStorage; + // Copy construction can't be supported because C++ requires that any enclosed + // T be initialized in a way C++ knows about -- that is, by |new| or similar. + UnionMember(const UnionMember&) = delete; + public: + UnionMember() = default; + ~UnionMember() = default; + T& SetValue() { new (mStorage.addr()) T(); return *mStorage.addr(); } template <typename T1> T& SetValue(const T1& aValue) {
--- a/dom/canvas/CanvasUtils.cpp +++ b/dom/canvas/CanvasUtils.cpp @@ -31,32 +31,23 @@ namespace CanvasUtils { bool GetCanvasContextType(const nsAString& str, dom::CanvasContextType* const out_type) { if (str.EqualsLiteral("2d")) { *out_type = dom::CanvasContextType::Canvas2D; return true; } - if (str.EqualsLiteral("experimental-webgl")) { + if (str.EqualsLiteral("webgl") || + str.EqualsLiteral("experimental-webgl")) + { *out_type = dom::CanvasContextType::WebGL1; return true; } -#ifdef MOZ_WEBGL_CONFORMANT - if (str.EqualsLiteral("webgl")) { - /* WebGL 1.0, $2.1 "Context Creation": - * If the user agent supports both the webgl and experimental-webgl - * canvas context types, they shall be treated as aliases. - */ - *out_type = dom::CanvasContextType::WebGL1; - return true; - } -#endif - if (WebGL2Context::IsSupported()) { if (str.EqualsLiteral("webgl2")) { *out_type = dom::CanvasContextType::WebGL2; return true; } } if (str.EqualsLiteral("bitmaprenderer")) {
--- a/dom/canvas/test/reftest/filters/reftest-stylo.list +++ b/dom/canvas/test/reftest/filters/reftest-stylo.list @@ -1,13 +1,13 @@ # DO NOT EDIT! This is a auto-generated temporary list for Stylo testing default-preferences pref(canvas.filters.enabled,true) fails asserts-if(stylo,2) == default-color.html default-color.html # bug 1324700 -# == drop-shadow.html drop-shadow.html +fails asserts-if(stylo,2) == drop-shadow.html drop-shadow.html # bug 1324700 fails asserts-if(stylo,2) == drop-shadow-transformed.html drop-shadow-transformed.html # bug 1324700 fails asserts-if(stylo,2) == global-alpha.html global-alpha.html # bug 1324700 fails asserts-if(stylo,2) == global-composite-operation.html global-composite-operation.html # bug 1324700 fails asserts-if(stylo,1) == liveness.html liveness.html # bug 1324700 fails asserts-if(stylo,2) == multiple-drop-shadows.html multiple-drop-shadows.html # bug 1324700 fails asserts-if(stylo,2) == shadow.html shadow.html # bug 1324700 fails asserts-if(stylo,2) == subregion-fill-paint.html subregion-fill-paint.html # bug 1324700 fails asserts-if(stylo,2) == subregion-stroke-paint.html subregion-stroke-paint.html # bug 1324700
--- a/dom/canvas/test/reftest/reftest-stylo.list +++ b/dom/canvas/test/reftest/reftest-stylo.list @@ -54,17 +54,17 @@ fuzzy(1,30000) fails-if(winWidget&&layer fuzzy(1,30000) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) skip-if(Android) == webgl-color-test.html?frame=1&aa&________&_______&alpha webgl-color-test.html?frame=1&aa&________&_______&alpha fuzzy(1,30000) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) skip-if(Android) == webgl-color-test.html?frame=1&__&preserve&_______&alpha webgl-color-test.html?frame=1&__&preserve&_______&alpha fuzzy(1,30000) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) skip-if(Android) == webgl-color-test.html?frame=1&aa&preserve&_______&alpha webgl-color-test.html?frame=1&aa&preserve&_______&alpha skip-if(Android) == webgl-color-test.html?frame=1&__&________&premult&alpha webgl-color-test.html?frame=1&__&________&premult&alpha skip-if(Android) == webgl-color-test.html?frame=1&aa&________&premult&alpha webgl-color-test.html?frame=1&aa&________&premult&alpha skip-if(Android) == webgl-color-test.html?frame=1&__&preserve&premult&alpha webgl-color-test.html?frame=1&__&preserve&premult&alpha skip-if(Android) == webgl-color-test.html?frame=1&aa&preserve&premult&alpha webgl-color-test.html?frame=1&aa&preserve&premult&alpha -# == webgl-color-test.html?frame=6&__&________&_______&_____ webgl-color-test.html?frame=6&__&________&_______&_____ +== webgl-color-test.html?frame=6&__&________&_______&_____ webgl-color-test.html?frame=6&__&________&_______&_____ skip-if(Android) == webgl-color-test.html?frame=6&aa&________&_______&_____ webgl-color-test.html?frame=6&aa&________&_______&_____ skip-if(Android) == webgl-color-test.html?frame=6&__&preserve&_______&_____ webgl-color-test.html?frame=6&__&preserve&_______&_____ skip-if(Android) == webgl-color-test.html?frame=6&aa&preserve&_______&_____ webgl-color-test.html?frame=6&aa&preserve&_______&_____ == webgl-color-test.html?frame=6&__&________&premult&_____ webgl-color-test.html?frame=6&__&________&premult&_____ skip-if(Android) == webgl-color-test.html?frame=6&aa&________&premult&_____ webgl-color-test.html?frame=6&aa&________&premult&_____ skip-if(Android) == webgl-color-test.html?frame=6&__&preserve&premult&_____ webgl-color-test.html?frame=6&__&preserve&premult&_____ skip-if(Android) == webgl-color-test.html?frame=6&aa&preserve&premult&_____ webgl-color-test.html?frame=6&aa&preserve&premult&_____ fuzzy(1,30000) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) skip-if(Android) == webgl-color-test.html?frame=6&__&________&_______&alpha webgl-color-test.html?frame=6&__&________&_______&alpha
--- a/dom/encoding/test/reftest/reftest-stylo.list +++ b/dom/encoding/test/reftest/reftest-stylo.list @@ -1,6 +1,6 @@ # DO NOT EDIT! This is a auto-generated temporary list for Stylo testing -# == bug863728-1.html bug863728-1.html +== bug863728-1.html bug863728-1.html == bug863728-2.html bug863728-2.html == bug863728-3.html bug863728-3.html -# == bug945215-1.html bug945215-1.html +== bug945215-1.html bug945215-1.html == bug945215-2.html bug945215-2.html
--- a/dom/events/DataTransfer.cpp +++ b/dom/events/DataTransfer.cpp @@ -100,18 +100,21 @@ DataTransfer::DataTransfer(nsISupports* // For these events, we want to be able to add data to the data transfer, so // clear the readonly state. Otherwise, the data is already present. For // external usage, cache the data from the native clipboard or drag. if (aEventMessage == eCut || aEventMessage == eCopy || aEventMessage == eDragStart) { mReadOnly = false; } else if (mIsExternal) { - if (aEventMessage == ePaste) { - CacheExternalClipboardFormats(); + if (aEventMessage == ePasteNoFormatting) { + mEventMessage = ePaste; + CacheExternalClipboardFormats(true); + } else if (aEventMessage == ePaste) { + CacheExternalClipboardFormats(false); } else if (aEventMessage >= eDragDropEventFirst && aEventMessage <= eDragDropEventLast) { CacheExternalDragFormats(); } } } DataTransfer::DataTransfer(nsISupports* aParent, @@ -1351,17 +1354,17 @@ DataTransfer::CacheExternalDragFormats() if (supported) { CacheExternalData(formats[f], c, sysPrincipal, /* hidden = */ f && hasFileData); } } } } void -DataTransfer::CacheExternalClipboardFormats() +DataTransfer::CacheExternalClipboardFormats(bool aPlainTextOnly) { NS_ASSERTION(mEventMessage == ePaste, "caching clipboard data for invalid event"); // Called during the constructor for paste events to cache the formats // available on the clipboard. As with CacheExternalDragFormats, the // data will only be retrieved when needed. @@ -1370,16 +1373,27 @@ DataTransfer::CacheExternalClipboardForm if (!clipboard || mClipboardType < 0) { return; } nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); nsCOMPtr<nsIPrincipal> sysPrincipal; ssm->GetSystemPrincipal(getter_AddRefs(sysPrincipal)); + if (aPlainTextOnly) { + bool supported; + const char* unicodeMime[] = { kUnicodeMime }; + clipboard->HasDataMatchingFlavors(unicodeMime, 1, mClipboardType, + &supported); + if (supported) { + CacheExternalData(kUnicodeMime, 0, sysPrincipal, false); + } + return; + } + // Check if the clipboard has any files bool hasFileData = false; const char *fileMime[] = { kFileMime }; clipboard->HasDataMatchingFlavors(fileMime, 1, mClipboardType, &hasFileData); // We will be ignoring any application/x-moz-file files found in the paste // datatransfer within e10s, as they will fail to be sent over IPC. Because of // that, we will unset hasFileData, whether or not it would have been set.
--- a/dom/events/DataTransfer.h +++ b/dom/events/DataTransfer.h @@ -302,17 +302,17 @@ protected: nsresult CacheExternalData(const char* aFormat, uint32_t aIndex, nsIPrincipal* aPrincipal, bool aHidden); // caches the formats that exist in the drag service that were added by an // external drag void CacheExternalDragFormats(); // caches the formats that exist in the clipboard - void CacheExternalClipboardFormats(); + void CacheExternalClipboardFormats(bool aPlainTextOnly); FileList* GetFilesInternal(ErrorResult& aRv, nsIPrincipal* aSubjectPrincipal); nsresult GetDataAtInternal(const nsAString& aFormat, uint32_t aIndex, nsIPrincipal* aSubjectPrincipal, nsIVariant** aData); nsresult SetDataAtInternal(const nsAString& aFormat, nsIVariant* aData, uint32_t aIndex, nsIPrincipal* aSubjectPrincipal);
--- a/dom/events/test/mochitest.ini +++ b/dom/events/test/mochitest.ini @@ -134,16 +134,18 @@ support-files = bug1017086_inner.html [test_bug1017086_enable.html] support-files = bug1017086_inner.html [test_bug1079236.html] [test_bug1145910.html] [test_bug1150308.html] [test_bug1248459.html] [test_bug1264380.html] run-if = (e10s && os != "win") # Bug 1270043, crash at windows platforms; Bug1264380 comment 20, nsDragService::InvokeDragSessionImpl behaves differently among platform implementations in non-e10s mode which prevents us to check the validity of nsIDragService::getCurrentSession() consistently via synthesize mouse clicks in non-e10s mode. +[test_bug1327798.html] +subsuite = clipboard [test_clickevent_on_input.html] skip-if = toolkit == 'android' #CRASH_DUMP, RANDOM [test_continuous_wheel_events.html] [test_dblclick_explicit_original_target.html] [test_dom_activate_event.html] [test_dom_keyboard_event.html] skip-if = toolkit == 'android' #CRASH_DUMP, RANDOM [test_dom_mouse_event.html]
new file mode 100644 --- /dev/null +++ b/dom/events/test/test_bug1327798.html @@ -0,0 +1,47 @@ +<!DOCTYPE html> +<html> +<head> +<title>Test for bug 1327798</title> +<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> +<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script> +<link rel="stylesheet" href="/tests/SimpleTest/test.css"> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?=id=1327798">Mozilla Bug 1327798</a> +<p id="display"></p> +<div id="content" style="display: none;"></div> + +<div contenteditable="true" id="editable1"><b>Formatted Text</b><br></div> +<pre> +<script class="testbody" type="application/javascript"> +SimpleTest.waitForExplicitFinish(); +SimpleTest.waitForFocus(() => { + var editable = document.getElementById("editable1"); + editable.focus(); + + window.getSelection().selectAllChildren(editable1); + + SpecialPowers.doCommand(window, "cmd_copy"); + + //--------- now check the content of the clipboard + var clipboard = SpecialPowers.Cc["@mozilla.org/widget/clipboard;1"] + .getService(SpecialPowers.Ci.nsIClipboard); + // does the clipboard contain text/unicode data ? + ok(clipboard.hasDataMatchingFlavors(["text/unicode"], 1, clipboard.kGlobalClipboard), + "clipboard contains unicode text"); + // does the clipboard contain text/html data ? + ok(clipboard.hasDataMatchingFlavors(["text/html"], 1, clipboard.kGlobalClipboard), + "clipboard contains html text"); + + window.addEventListener("paste", (e) => { + ok(e.clipboardData.types.indexOf('text/html'), -1, "clipboardData shouldn't have text/html"); + ok(e.clipboardData.getData('text/plain'), "Formatted Text", "getData(text/plain) should return plain text"); + SimpleTest.finish(); + }); + + SpecialPowers.doCommand(window, "cmd_pasteNoFormatting"); +}); +</script> +</pre> +</body> +</html>
--- a/dom/file/File.cpp +++ b/dom/file/File.cpp @@ -8,16 +8,17 @@ #include "FileBlobImpl.h" #include "MemoryBlobImpl.h" #include "MultipartBlobImpl.h" #include "mozilla/dom/BlobBinding.h" #include "mozilla/dom/FileBinding.h" #include "mozilla/dom/FileCreatorHelper.h" #include "mozilla/dom/FileSystemUtils.h" #include "mozilla/dom/Promise.h" +#include "nsXULAppAPI.h" namespace mozilla { namespace dom { File::File(nsISupports* aParent, BlobImpl* aImpl) : Blob(aParent, aImpl) { MOZ_ASSERT(aImpl->IsFile()); @@ -55,24 +56,26 @@ File::CreateMemoryFile(nsISupports* aPar new MemoryBlobImpl(aMemoryBuffer, aLength, aName, aContentType, aLastModifiedDate)); return file.forget(); } /* static */ already_AddRefed<File> File::CreateFromFile(nsISupports* aParent, nsIFile* aFile) { + MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess()); RefPtr<File> file = new File(aParent, new FileBlobImpl(aFile)); return file.forget(); } /* static */ already_AddRefed<File> File::CreateFromFile(nsISupports* aParent, nsIFile* aFile, const nsAString& aName, const nsAString& aContentType) { + MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess()); RefPtr<File> file = new File(aParent, new FileBlobImpl(aFile, aName, aContentType)); return file.forget(); } JSObject* File::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) { @@ -161,18 +164,16 @@ File::Constructor(const GlobalObject& aG /* static */ already_AddRefed<Promise> File::CreateFromNsIFile(const GlobalObject& aGlobal, nsIFile* aData, const ChromeFilePropertyBag& aBag, SystemCallerGuarantee aGuarantee, ErrorResult& aRv) { - MOZ_ASSERT(NS_IsMainThread()); - nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports()); RefPtr<Promise> promise = FileCreatorHelper::CreateFile(global, aData, aBag, true, aRv); return promise.forget(); } /* static */ already_AddRefed<Promise>
--- a/dom/file/FileBlobImpl.cpp +++ b/dom/file/FileBlobImpl.cpp @@ -23,62 +23,67 @@ using namespace workers; NS_IMPL_ISUPPORTS_INHERITED0(FileBlobImpl, BlobImpl) FileBlobImpl::FileBlobImpl(nsIFile* aFile) : BaseBlobImpl(EmptyString(), EmptyString(), UINT64_MAX, INT64_MAX) , mFile(aFile) , mWholeFile(true) { MOZ_ASSERT(mFile, "must have file"); + MOZ_ASSERT(XRE_IsParentProcess()); // Lazily get the content type and size mContentType.SetIsVoid(true); mFile->GetLeafName(mName); } FileBlobImpl::FileBlobImpl(const nsAString& aName, const nsAString& aContentType, uint64_t aLength, nsIFile* aFile) : BaseBlobImpl(aName, aContentType, aLength, UINT64_MAX) , mFile(aFile) , mWholeFile(true) { MOZ_ASSERT(mFile, "must have file"); + MOZ_ASSERT(XRE_IsParentProcess()); } FileBlobImpl::FileBlobImpl(const nsAString& aName, const nsAString& aContentType, uint64_t aLength, nsIFile* aFile, int64_t aLastModificationDate) : BaseBlobImpl(aName, aContentType, aLength, aLastModificationDate) , mFile(aFile) , mWholeFile(true) { MOZ_ASSERT(mFile, "must have file"); + MOZ_ASSERT(XRE_IsParentProcess()); } FileBlobImpl::FileBlobImpl(nsIFile* aFile, const nsAString& aName, const nsAString& aContentType) : BaseBlobImpl(aName, aContentType, UINT64_MAX, INT64_MAX) , mFile(aFile) , mWholeFile(true) { MOZ_ASSERT(mFile, "must have file"); + MOZ_ASSERT(XRE_IsParentProcess()); if (aContentType.IsEmpty()) { // Lazily get the content type and size mContentType.SetIsVoid(true); } } FileBlobImpl::FileBlobImpl(const FileBlobImpl* aOther, uint64_t aStart, uint64_t aLength, const nsAString& aContentType) : BaseBlobImpl(aContentType, aOther->mStart + aStart, aLength) , mFile(aOther->mFile) , mWholeFile(false) { MOZ_ASSERT(mFile, "must have file"); + MOZ_ASSERT(XRE_IsParentProcess()); mImmutable = aOther->mImmutable; } already_AddRefed<BlobImpl> FileBlobImpl::CreateSlice(uint64_t aStart, uint64_t aLength, const nsAString& aContentType, ErrorResult& aRv) {
--- a/dom/file/FileCreatorHelper.cpp +++ b/dom/file/FileCreatorHelper.cpp @@ -26,17 +26,17 @@ namespace dom { /* static */ already_AddRefed<Promise> FileCreatorHelper::CreateFile(nsIGlobalObject* aGlobalObject, nsIFile* aFile, const ChromeFilePropertyBag& aBag, bool aIsFromNsIFile, ErrorResult& aRv) { - MOZ_ASSERT(NS_IsMainThread()); + MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread()); RefPtr<Promise> promise = Promise::Create(aGlobalObject, aRv); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobalObject);
--- a/dom/file/tests/fileutils.js +++ b/dom/file/tests/fileutils.js @@ -1,14 +1,13 @@ // Utility functions var testRanCounter = 0; var expectedTestCount = 0; function testHasRun() { - //alert(testRanCounter); ++testRanCounter; if (testRanCounter == expectedTestCount) { SimpleTest.finish(); } } function testFile(file, contents, test) { @@ -113,24 +112,16 @@ function getXHRLoadHandler(expectedResul function convertXHRBinary(s) { var res = ""; for (var i = 0; i < s.length; ++i) { res += String.fromCharCode(s.charCodeAt(i) & 255); } return res; } -function testHasRun() { - //alert(testRanCounter); - ++testRanCounter; - if (testRanCounter == expectedTestCount) { - SimpleTest.finish(); - } -} - function gc() { window.QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor) .getInterface(SpecialPowers.Ci.nsIDOMWindowUtils) .garbageCollect(); } function checkMPSubmission(sub, expected) { function getPropCount(o) {
--- a/dom/html/HTMLMediaElement.cpp +++ b/dom/html/HTMLMediaElement.cpp @@ -991,17 +991,17 @@ private: mSuspended == nsISuspendedTypes::SUSPENDED_PAUSE_DISPOSABLE || mSuspended == nsISuspendedTypes::SUSPENDED_BLOCK); } AudibleState IsOwnerAudible() const { // Muted or the volume should not be ~0 - if (mOwner->Muted() || (std::fabs(mOwner->Volume()) <= 1e-7)) { + if (mOwner->mMuted || (std::fabs(mOwner->Volume()) <= 1e-7)) { return AudioChannelService::AudibleState::eNotAudible; } // No audio track. if (!mOwner->HasAudio()) { return AudioChannelService::AudibleState::eNotAudible; } @@ -2034,25 +2034,29 @@ void HTMLMediaElement::NotifyMediaTrackD this, aTrack->AsAudioTrack() ? "Audio" : "Video", NS_ConvertUTF16toUTF8(id).get())); #endif MOZ_ASSERT((!aTrack->AsAudioTrack() || !aTrack->AsAudioTrack()->Enabled()) && (!aTrack->AsVideoTrack() || !aTrack->AsVideoTrack()->Selected())); if (aTrack->AsAudioTrack()) { - bool shouldMute = true; - for (uint32_t i = 0; i < AudioTracks()->Length(); ++i) { - if ((*AudioTracks())[i]->Enabled()) { - shouldMute = false; - break; + // If we don't have any alive track , we don't need to mute MediaElement. + if (AudioTracks()->Length() > 0) { + bool shouldMute = true; + for (uint32_t i = 0; i < AudioTracks()->Length(); ++i) { + if ((*AudioTracks())[i]->Enabled()) { + shouldMute = false; + break; + } } - } - if (shouldMute) { - SetMutedInternal(mMuted | MUTED_BY_AUDIO_TRACK); + + if (shouldMute) { + SetMutedInternal(mMuted | MUTED_BY_AUDIO_TRACK); + } } } else if (aTrack->AsVideoTrack()) { if (mSrcStream) { MOZ_ASSERT(mSelectedVideoStreamTrack); if (mSelectedVideoStreamTrack && mMediaStreamSizeListener) { mSelectedVideoStreamTrack->RemoveDirectListener(mMediaStreamSizeListener); mMediaStreamSizeListener->Forget(); mMediaStreamSizeListener = nullptr;
--- a/dom/html/HTMLSelectElement.cpp +++ b/dom/html/HTMLSelectElement.cpp @@ -712,18 +712,18 @@ HTMLSelectElement::SetLength(uint32_t aL } else if (aLength > curlen) { if (aLength > MAX_DYNAMIC_SELECT_LENGTH) { aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); return; } RefPtr<mozilla::dom::NodeInfo> nodeInfo; - nsContentUtils::NameChanged(mNodeInfo, nsGkAtoms::option, - getter_AddRefs(nodeInfo)); + nsContentUtils::QNameChanged(mNodeInfo, nsGkAtoms::option, + getter_AddRefs(nodeInfo)); nsCOMPtr<nsINode> node = NS_NewHTMLOptionElement(nodeInfo.forget()); RefPtr<nsTextNode> text = new nsTextNode(mNodeInfo->NodeInfoManager()); aRv = node->AppendChildTo(text, false); if (aRv.Failed()) { return;
--- a/dom/html/HTMLTableElement.cpp +++ b/dom/html/HTMLTableElement.cpp @@ -398,26 +398,37 @@ HTMLTableElement::TBodies() already_AddRefed<nsGenericHTMLElement> HTMLTableElement::CreateTHead() { RefPtr<nsGenericHTMLElement> head = GetTHead(); if (!head) { // Create a new head rowgroup. RefPtr<mozilla::dom::NodeInfo> nodeInfo; - nsContentUtils::NameChanged(mNodeInfo, nsGkAtoms::thead, - getter_AddRefs(nodeInfo)); + nsContentUtils::QNameChanged(mNodeInfo, nsGkAtoms::thead, + getter_AddRefs(nodeInfo)); head = NS_NewHTMLTableSectionElement(nodeInfo.forget()); if (!head) { return nullptr; } - ErrorResult rv; - nsCOMPtr<nsINode> refNode = nsINode::GetFirstChild(); + nsCOMPtr<nsIContent> refNode = nullptr; + for (refNode = nsINode::GetFirstChild(); + refNode; + refNode = refNode->GetNextSibling()) { + + if (refNode->IsHTMLElement() && + !refNode->IsHTMLElement(nsGkAtoms::caption) && + !refNode->IsHTMLElement(nsGkAtoms::colgroup)) { + break; + } + } + + IgnoredErrorResult rv; nsINode::InsertBefore(*head, refNode, rv); } return head.forget(); } void HTMLTableElement::DeleteTHead() { @@ -431,18 +442,18 @@ HTMLTableElement::DeleteTHead() already_AddRefed<nsGenericHTMLElement> HTMLTableElement::CreateTFoot() { RefPtr<nsGenericHTMLElement> foot = GetTFoot(); if (!foot) { // create a new foot rowgroup RefPtr<mozilla::dom::NodeInfo> nodeInfo; - nsContentUtils::NameChanged(mNodeInfo, nsGkAtoms::tfoot, - getter_AddRefs(nodeInfo)); + nsContentUtils::QNameChanged(mNodeInfo, nsGkAtoms::tfoot, + getter_AddRefs(nodeInfo)); foot = NS_NewHTMLTableSectionElement(nodeInfo.forget()); if (!foot) { return nullptr; } AppendChildTo(foot, true); } @@ -462,25 +473,27 @@ HTMLTableElement::DeleteTFoot() already_AddRefed<nsGenericHTMLElement> HTMLTableElement::CreateCaption() { RefPtr<nsGenericHTMLElement> caption = GetCaption(); if (!caption) { // Create a new caption. RefPtr<mozilla::dom::NodeInfo> nodeInfo; - nsContentUtils::NameChanged(mNodeInfo, nsGkAtoms::caption, - getter_AddRefs(nodeInfo)); + nsContentUtils::QNameChanged(mNodeInfo, nsGkAtoms::caption, + getter_AddRefs(nodeInfo)); caption = NS_NewHTMLTableCaptionElement(nodeInfo.forget()); if (!caption) { return nullptr; } - AppendChildTo(caption, true); + IgnoredErrorResult rv; + nsCOMPtr<nsINode> firsChild = nsINode::GetFirstChild(); + nsINode::InsertBefore(*caption, firsChild, rv); } return caption.forget(); } void HTMLTableElement::DeleteCaption() { HTMLTableCaptionElement* caption = GetCaption(); @@ -509,17 +522,17 @@ HTMLTableElement::CreateTBody() child; child = child->GetPreviousSibling()) { if (child->IsHTMLElement(nsGkAtoms::tbody)) { referenceNode = child->GetNextSibling(); break; } } - ErrorResult rv; + IgnoredErrorResult rv; nsINode::InsertBefore(*newBody, referenceNode, rv); return newBody.forget(); } already_AddRefed<nsGenericHTMLElement> HTMLTableElement::InsertRow(int32_t aIndex, ErrorResult& aError) { @@ -555,18 +568,18 @@ HTMLTableElement::InsertRow(int32_t aInd refIndex = rowCount - 1; } RefPtr<Element> refRow = rows->Item(refIndex); nsCOMPtr<nsINode> parent = refRow->GetParentNode(); // create the row RefPtr<mozilla::dom::NodeInfo> nodeInfo; - nsContentUtils::NameChanged(mNodeInfo, nsGkAtoms::tr, - getter_AddRefs(nodeInfo)); + nsContentUtils::QNameChanged(mNodeInfo, nsGkAtoms::tr, + getter_AddRefs(nodeInfo)); newRow = NS_NewHTMLTableRowElement(nodeInfo.forget()); if (newRow) { // If aIndex is -1 or equal to the number of rows, the new row // is appended. if (aIndex == -1 || uint32_t(aIndex) == rowCount) { parent->AppendChild(*newRow, aError); @@ -589,32 +602,32 @@ HTMLTableElement::InsertRow(int32_t aInd if (child->IsHTMLElement(nsGkAtoms::tbody)) { rowGroup = child; break; } } if (!rowGroup) { // need to create a TBODY RefPtr<mozilla::dom::NodeInfo> nodeInfo; - nsContentUtils::NameChanged(mNodeInfo, nsGkAtoms::tbody, - getter_AddRefs(nodeInfo)); + nsContentUtils::QNameChanged(mNodeInfo, nsGkAtoms::tbody, + getter_AddRefs(nodeInfo)); rowGroup = NS_NewHTMLTableSectionElement(nodeInfo.forget()); if (rowGroup) { aError = AppendChildTo(rowGroup, true); if (aError.Failed()) { return nullptr; } } } if (rowGroup) { RefPtr<mozilla::dom::NodeInfo> nodeInfo; - nsContentUtils::NameChanged(mNodeInfo, nsGkAtoms::tr, - getter_AddRefs(nodeInfo)); + nsContentUtils::QNameChanged(mNodeInfo, nsGkAtoms::tr, + getter_AddRefs(nodeInfo)); newRow = NS_NewHTMLTableRowElement(nodeInfo.forget()); if (newRow) { HTMLTableSectionElement* section = static_cast<HTMLTableSectionElement*>(rowGroup.get()); nsIHTMLCollection* rows = section->Rows(); nsCOMPtr<nsINode> refNode = rows->Item(0); rowGroup->InsertBefore(*newRow, refNode, aError);
--- a/dom/html/HTMLTableElement.h +++ b/dom/html/HTMLTableElement.h @@ -31,17 +31,18 @@ public: HTMLTableCaptionElement* GetCaption() const { return static_cast<HTMLTableCaptionElement*>(GetChild(nsGkAtoms::caption)); } void SetCaption(HTMLTableCaptionElement* aCaption, ErrorResult& aError) { DeleteCaption(); if (aCaption) { - nsINode::AppendChild(*aCaption, aError); + nsCOMPtr<nsINode> firstChild = nsINode::GetFirstChild(); + nsINode::InsertBefore(*aCaption, firstChild, aError); } } void DeleteTFoot(); already_AddRefed<nsGenericHTMLElement> CreateCaption(); void DeleteCaption(); @@ -54,17 +55,28 @@ public: { if (aTHead && !aTHead->IsHTMLElement(nsGkAtoms::thead)) { aError.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR); return; } DeleteTHead(); if (aTHead) { - nsCOMPtr<nsINode> refNode = nsINode::GetFirstChild(); + + nsCOMPtr<nsIContent> refNode = nullptr; + for (refNode = nsINode::GetFirstChild(); + refNode; + refNode = refNode->GetNextSibling()) { + if (refNode->IsHTMLElement() && + !refNode->IsHTMLElement(nsGkAtoms::caption) && + !refNode->IsHTMLElement(nsGkAtoms::colgroup)) { + break; + } + } + nsINode::InsertBefore(*aTHead, refNode, aError); } } already_AddRefed<nsGenericHTMLElement> CreateTHead(); void DeleteTHead(); HTMLTableSectionElement* GetTFoot() const
--- a/dom/html/HTMLTableRowElement.cpp +++ b/dom/html/HTMLTableRowElement.cpp @@ -170,18 +170,18 @@ HTMLTableRowElement::InsertCell(int32_t aError.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); return nullptr; } } } // create the cell RefPtr<mozilla::dom::NodeInfo> nodeInfo; - nsContentUtils::NameChanged(mNodeInfo, nsGkAtoms::td, - getter_AddRefs(nodeInfo)); + nsContentUtils::QNameChanged(mNodeInfo, nsGkAtoms::td, + getter_AddRefs(nodeInfo)); RefPtr<nsGenericHTMLElement> cell = NS_NewHTMLTableCellElement(nodeInfo.forget()); if (!cell) { aError.Throw(NS_ERROR_OUT_OF_MEMORY); return nullptr; }
--- a/dom/html/HTMLTableSectionElement.cpp +++ b/dom/html/HTMLTableSectionElement.cpp @@ -75,18 +75,18 @@ HTMLTableSectionElement::InsertRow(int32 aError.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); return nullptr; } bool doInsert = (aIndex < int32_t(rowCount)) && (aIndex != -1); // create the row RefPtr<mozilla::dom::NodeInfo> nodeInfo; - nsContentUtils::NameChanged(mNodeInfo, nsGkAtoms::tr, - getter_AddRefs(nodeInfo)); + nsContentUtils::QNameChanged(mNodeInfo, nsGkAtoms::tr, + getter_AddRefs(nodeInfo)); RefPtr<nsGenericHTMLElement> rowContent = NS_NewHTMLTableRowElement(nodeInfo.forget()); if (!rowContent) { aError.Throw(NS_ERROR_OUT_OF_MEMORY); return nullptr; }
--- a/dom/html/reftests/reftest-stylo.list +++ b/dom/html/reftests/reftest-stylo.list @@ -1,66 +1,65 @@ # DO NOT EDIT! This is a auto-generated temporary list for Stylo testing # autofocus attribute (we can't test with mochitests) include autofocus/reftest-stylo.list include toblob-todataurl/reftest-stylo.list fails == 41464-1a.html 41464-1a.html fails == 41464-1b.html 41464-1b.html fails == 52019-1.html 52019-1.html -fails == 82711-1.html 82711-1.html +fails == 82711-1.html 82711-1.html # Bug 1341637 fails == 82711-2.html 82711-2.html -# == 82711-1-ref.html 82711-1-ref.html +fails == 82711-1-ref.html 82711-1-ref.html == 468263-1a.html 468263-1a.html == 468263-1b.html 468263-1b.html == 468263-1c.html 468263-1c.html -# == 468263-1d.html 468263-1d.html -# == 468263-2.html 468263-2.html -# == 468263-2.html 468263-2.html +== 468263-1d.html 468263-1d.html +fails == 468263-2.html 468263-2.html # Bug 1341642 fails == 484200-1.html 484200-1.html -# == 485377.html 485377.html +== 485377.html 485377.html == 557840.html 557840.html == 560059-video-dimensions.html 560059-video-dimensions.html fails == 573322-quirks.html 573322-quirks.html fails == 573322-no-quirks.html 573322-no-quirks.html fails == 596455-1a.html 596455-1a.html fails == 596455-1b.html 596455-1b.html fails == 596455-2a.html 596455-2a.html fails == 596455-2b.html 596455-2b.html fails == 610935.html 610935.html == 649134-1.html 649134-1.html -# == 649134-2.html 649134-2.html +== 649134-2.html 649134-2.html fails == 741776-1.vtt 741776-1.vtt -# == bug448564-1_malformed.html bug448564-1_malformed.html -# == bug448564-1_malformed.html bug448564-1_malformed.html +== bug448564-1_malformed.html bug448564-1_malformed.html +== bug448564-1_malformed.html bug448564-1_malformed.html -# == bug448564-4a.html bug448564-4a.html -# == bug502168-1_malformed.html bug502168-1_malformed.html +== bug448564-4a.html bug448564-4a.html +== bug502168-1_malformed.html bug502168-1_malformed.html -# == responsive-image-load-shortcircuit.html responsive-image-load-shortcircuit.html +fails == responsive-image-load-shortcircuit.html responsive-image-load-shortcircuit.html == image-load-shortcircuit-1.html image-load-shortcircuit-1.html == image-load-shortcircuit-2.html image-load-shortcircuit-2.html # Test that image documents taken into account CSS properties like # image-orientation when determining the size of the image. # (Fuzzy necessary due to pixel-wise comparison of different JPEGs. # The vast majority of the fuzziness comes from Linux and WinXP.) -# == bug917595-iframe-1.html bug917595-iframe-1.html +fails == bug917595-iframe-1.html bug917595-iframe-1.html # Bug 1341647 fails == bug917595-exif-rotated.jpg bug917595-exif-rotated.jpg # Test support for SVG-as-image in <picture> elements. == bug1106522-1.html bug1106522-1.html == bug1106522-2.html bug1106522-2.html fails == href-attr-change-restyles.html href-attr-change-restyles.html -# == figure.html figure.html +== figure.html figure.html fails == pre-1.html pre-1.html -# == table-border-1.html table-border-1.html -# == table-border-2.html table-border-2.html -# == table-border-2.html table-border-2.html +fails == table-border-1.html table-border-1.html # Bug 1341651 +== table-border-2.html table-border-2.html +== table-border-2.html table-border-2.html # Test imageset is using permissions.default.image # pref(permissions.default.image,1) HTTP == bug1196784-with-srcset.html bug1196784-with-srcset.html # pref(permissions.default.image,2) HTTP == bug1196784-with-srcset.html bug1196784-with-srcset.html # Test video with rotation information can be rotated. == bug1228601-video-rotation-90.html bug1228601-video-rotation-90.html
--- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -83,19 +83,17 @@ #include "mozilla/LookAndFeel.h" #include "mozilla/media/MediaParent.h" #include "mozilla/Move.h" #include "mozilla/net/NeckoParent.h" #include "mozilla/plugins/PluginBridge.h" #include "mozilla/Preferences.h" #include "mozilla/ProcessHangMonitor.h" #include "mozilla/ProcessHangMonitorIPC.h" -#ifdef MOZ_GECKO_PROFILER -#include "mozilla/ProfileGatherer.h" -#endif +#include "GeckoProfiler.h" #include "mozilla/ScopeExit.h" #include "mozilla/Services.h" #include "mozilla/StaticPtr.h" #include "mozilla/Telemetry.h" #include "mozilla/TelemetryIPC.h" #include "mozilla/WebBrowserPersistDocumentParent.h" #include "mozilla/Unused.h" #include "nsAnonymousTemporaryFile.h" @@ -262,19 +260,16 @@ static NS_DEFINE_CID(kCClipboardCID, NS_ #if defined(XP_WIN) // e10s forced enable pref, defined in nsAppRunner.cpp extern const char* kForceEnableE10sPref; #endif using base::ChildPrivileges; using base::KillProcess; -#ifdef MOZ_GECKO_PROFILER -using mozilla::ProfileGatherer; -#endif #ifdef MOZ_CRASHREPORTER using namespace CrashReporter; #endif using namespace mozilla::dom::devicestorage; using namespace mozilla::dom::power; using namespace mozilla::media; using namespace mozilla::embedding; @@ -1196,20 +1191,17 @@ ContentParent::Init() DebugOnly<nsresult> rv = profiler->IsActive(&profilerActive); MOZ_ASSERT(NS_SUCCEEDED(rv)); if (profilerActive) { nsCOMPtr<nsIProfilerStartParams> currentProfilerParams; rv = profiler->GetStartParams(getter_AddRefs(currentProfilerParams)); MOZ_ASSERT(NS_SUCCEEDED(rv)); - nsCOMPtr<nsISupports> gatherer; - rv = profiler->GetProfileGatherer(getter_AddRefs(gatherer)); - MOZ_ASSERT(NS_SUCCEEDED(rv)); - mGatherer = static_cast<ProfileGatherer*>(gatherer.get()); + mIsProfilerActive = true; StartProfiler(currentProfilerParams); } #endif RefPtr<GeckoMediaPluginServiceParent> gmps(GeckoMediaPluginServiceParent::GetSingleton()); gmps->UpdateContentProcessGMPCapabilities(); } @@ -1584,18 +1576,18 @@ ContentParent::ActorDestroy(ActorDestroy gpu->RemoveListener(this); } RecvRemoveGeolocationListener(); mConsoleService = nullptr; #ifdef MOZ_GECKO_PROFILER - if (mGatherer && !mProfile.IsEmpty()) { - mGatherer->OOPExitProfile(mProfile); + if (mIsProfilerActive && !mProfile.IsEmpty()) { + profiler_OOP_exit_profile(mProfile); } #endif if (obs) { RefPtr<nsHashPropertyBag> props = new nsHashPropertyBag(); props->SetPropertyAsUint64(NS_LITERAL_STRING("childID"), mChildID); @@ -1823,34 +1815,16 @@ ContentParent::DestroyTestShell(TestShel TestShellParent* ContentParent::GetTestShellSingleton() { PTestShellParent* p = LoneManagedOrNullAsserts(ManagedPTestShellParent()); return static_cast<TestShellParent*>(p); } -void -ContentParent::InitializeMembers() -{ - mSubprocess = nullptr; - mChildID = gContentChildID++; - mGeolocationWatchID = -1; - mNumDestroyingTabs = 0; - mIsAvailable = true; - mIsAlive = true; - mSendPermissionUpdates = false; - mCalledClose = false; - mCalledKillHard = false; - mCreatedPairedMinidumps = false; - mShutdownPending = false; - mIPCOpen = true; - mHangMonitorActor = nullptr; -} - bool ContentParent::LaunchSubprocess(ProcessPriority aInitialPriority /* = PROCESS_PRIORITY_FOREGROUND */) { PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER); std::vector<std::string> extraArgs; extraArgs.push_back("-childID"); char idStr[21]; @@ -1929,23 +1903,37 @@ ContentParent::LaunchSubprocess(ProcessP } return true; } ContentParent::ContentParent(ContentParent* aOpener, const nsAString& aRemoteType) : nsIContentParent() + , mSubprocess(nullptr) , mLaunchTS(TimeStamp::Now()) , mOpener(aOpener) , mRemoteType(aRemoteType) + , mChildID(gContentChildID++) + , mGeolocationWatchID(-1) + , mNumDestroyingTabs(0) + , mIsAvailable(true) + , mIsAlive(true) + , mSendPermissionUpdates(false) , mIsForBrowser(!mRemoteType.IsEmpty()) -{ - InitializeMembers(); // Perform common initialization. - + , mCalledClose(false) + , mCalledKillHard(false) + , mCreatedPairedMinidumps(false) + , mShutdownPending(false) + , mIPCOpen(true) + , mHangMonitorActor(nullptr) +#ifdef MOZ_GECKO_PROFILER + , mIsProfilerActive(false) +#endif +{ // Insert ourselves into the global linked list of ContentParent objects. if (!sContentParents) { sContentParents = new LinkedList<ContentParent>(); } sContentParents->insertBack(this); // From this point on, NS_WARNING, NS_ASSERTION, etc. should print out the // PID along with the warning. @@ -2573,18 +2561,18 @@ ContentParent::Observe(nsISupports* aSub NS_ProcessNextEvent(nullptr, true); } NS_ASSERTION(!mSubprocess, "Close should have nulled mSubprocess"); } #ifdef MOZ_GECKO_PROFILER // Need to do this before the mIsAlive check to avoid missing profiles. if (!strcmp(aTopic, "profiler-subprocess-gather")) { - if (mGatherer) { - mGatherer->WillGatherOOPProfile(); + if (mIsProfilerActive) { + profiler_will_gather_OOP_profile(); if (mIsAlive && mSubprocess) { Unused << SendGatherProfile(); } } } else if (!strcmp(aTopic, "profiler-subprocess")) { nsCOMPtr<nsIProfileSaveEvent> pse = do_QueryInterface(aSubject); if (pse) { @@ -2686,17 +2674,17 @@ ContentParent::Observe(nsISupports* aSub } #endif #ifdef MOZ_GECKO_PROFILER else if (!strcmp(aTopic, "profiler-started")) { nsCOMPtr<nsIProfilerStartParams> params(do_QueryInterface(aSubject)); StartProfiler(params); } else if (!strcmp(aTopic, "profiler-stopped")) { - mGatherer = nullptr; + mIsProfilerActive = false; Unused << SendStopProfiler(); } else if (!strcmp(aTopic, "profiler-paused")) { Unused << SendPauseProfiler(true); } else if (!strcmp(aTopic, "profiler-resumed")) { Unused << SendPauseProfiler(false); } @@ -4538,21 +4526,21 @@ ContentParent::RecvCreateWindowInDiffere return IPC_OK(); } mozilla::ipc::IPCResult ContentParent::RecvProfile(const nsCString& aProfile) { #ifdef MOZ_GECKO_PROFILER - if (NS_WARN_IF(!mGatherer)) { + if (NS_WARN_IF(!mIsProfilerActive)) { return IPC_OK(); } mProfile = aProfile; - mGatherer->GatheredOOPProfile(); + profiler_gathered_OOP_profile(); #endif return IPC_OK(); } mozilla::ipc::IPCResult ContentParent::RecvGetGraphicsDeviceInitData(ContentDeviceData* aOut) { gfxPlatform::GetPlatform()->BuildContentDeviceData(aOut); @@ -4674,19 +4662,17 @@ ContentParent::StartProfiler(nsIProfiler ipcParams.threadFilters() = aParams->GetThreadFilterNames(); Unused << SendStartProfiler(ipcParams); nsCOMPtr<nsIProfiler> profiler(do_GetService("@mozilla.org/tools/profiler;1")); if (NS_WARN_IF(!profiler)) { return; } - nsCOMPtr<nsISupports> gatherer; - profiler->GetProfileGatherer(getter_AddRefs(gatherer)); - mGatherer = static_cast<ProfileGatherer*>(gatherer.get()); + mIsProfilerActive = true; #endif } mozilla::ipc::IPCResult ContentParent::RecvNotifyPushObservers(const nsCString& aScope, const IPC::Principal& aPrincipal, const nsString& aMessageId) {
--- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -49,19 +49,16 @@ class nsICycleCollectorLogSink; class nsIDumpGCAndCCLogsCallback; class nsITabParent; class nsITimer; class ParentIdleListener; class nsIWidget; namespace mozilla { class PRemoteSpellcheckEngineParent; -#ifdef MOZ_GECKO_PROFILER -class ProfileGatherer; -#endif #if defined(XP_LINUX) && defined(MOZ_CONTENT_SANDBOX) class SandboxBroker; class SandboxBrokerPolicyFactory; #endif class PreallocatedProcessManagerImpl; @@ -670,19 +667,16 @@ private: nsCOMPtr<nsITabParent>& aNewTabParent, bool* aWindowIsNew); FORWARD_SHMEM_ALLOCATOR_TO(PContentParent) ContentParent(ContentParent* aOpener, const nsAString& aRemoteType); - // The common initialization for the constructors. - void InitializeMembers(); - // Launch the subprocess and associated initialization. // Returns false if the process fails to start. bool LaunchSubprocess(hal::ProcessPriority aInitialPriority = hal::PROCESS_PRIORITY_FOREGROUND); // Common initialization after sub process launch or adoption. void InitInternal(ProcessPriority aPriority, bool aSetupOffMainThreadCompositing, bool aSendRegisteredChrome); @@ -1186,17 +1180,17 @@ private: // Dup of child's X socket, used to scope its resources to this // object instead of the child process's lifetime. ScopedClose mChildXSocketFdDup; #endif PProcessHangMonitorParent* mHangMonitorActor; #ifdef MOZ_GECKO_PROFILER - RefPtr<mozilla::ProfileGatherer> mGatherer; + bool mIsProfilerActive; #endif nsCString mProfile; UniquePtr<gfx::DriverCrashGuard> mDriverCrashGuard; UniquePtr<MemoryReportRequestHost> mMemoryReportRequest; #if defined(XP_LINUX) && defined(MOZ_CONTENT_SANDBOX) mozilla::UniquePtr<SandboxBroker> mSandboxBroker;
--- a/dom/ipc/ContentPrefs.cpp +++ b/dom/ipc/ContentPrefs.cpp @@ -1,278 +1,279 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "ContentPrefs.h" - -const char* mozilla::dom::ContentPrefs::gInitPrefs[] = { - "accessibility.monoaudio.enable", - "accessibility.mouse_focuses_formcontrol", - "accessibility.tabfocus_applies_to_xul", - "app.update.channel", - "browser.dom.window.dump.enabled", - "browser.sessionhistory.max_entries", - "browser.sessionhistory.max_total_viewers", - "content.cors.disable", - "content.cors.no_private_data", - "content.notify.backoffcount", - "content.notify.interval", - "content.notify.ontimer", - "content.sink.enable_perf_mode", - "content.sink.event_probe_rate", - "content.sink.initial_perf_time", - "content.sink.interactive_deflect_count", - "content.sink.interactive_parse_time", - "content.sink.interactive_time", - "content.sink.pending_event_mode", - "content.sink.perf_deflect_count", - "content.sink.perf_parse_time", - "device.storage.prompt.testing", - "device.storage.writable.name", - "dom.allow_XUL_XBL_for_file", - "dom.allow_cut_copy", - "dom.enable_frame_timing", - "dom.enable_performance", - "dom.enable_resource_timing", - "dom.event.handling-user-input-time-limit", - "dom.event.touch.coalescing.enabled", - "dom.forms.autocomplete.experimental", - "dom.ipc.processPriorityManager.backgroundGracePeriodMS", - "dom.ipc.processPriorityManager.backgroundPerceivableGracePeriodMS", - "dom.max_chrome_script_run_time", - "dom.max_script_run_time", - "dom.mozBrowserFramesEnabled", - "dom.performance.enable_notify_performance_timing", - "dom.performance.enable_user_timing_logging", - "dom.storage.testing", - "dom.url.encode_decode_hash", - "dom.url.getters_decode_hash", - "dom.use_watchdog", - "dom.vibrator.enabled", - "dom.vibrator.max_vibrate_list_len", - "dom.vibrator.max_vibrate_ms", - "focusmanager.testmode", - "font.size.inflation.disabledInMasterProcess", - "font.size.inflation.emPerLine", - "font.size.inflation.forceEnabled", - "font.size.inflation.lineThreshold", - "font.size.inflation.mappingIntercept", - "font.size.inflation.maxRatio", - "font.size.inflation.minTwips", - "full-screen-api.allow-trusted-requests-only", - "full-screen-api.enabled", - "full-screen-api.unprefix.enabled", - "gfx.font_rendering.opentype_svg.enabled", - "hangmonitor.timeout", - "html5.flushtimer.initialdelay", - "html5.flushtimer.subsequentdelay", - "html5.offmainthread", - "intl.charset.fallback.tld", - "intl.ime.hack.on_ime_unaware_apps.fire_key_events_for_composition", - "javascript.enabled", - "javascript.options.asmjs", - "javascript.options.asyncstack", - "javascript.options.baselinejit", - "javascript.options.baselinejit.threshold", - "javascript.options.baselinejit.unsafe_eager_compilation", - "javascript.options.discardSystemSource", - "javascript.options.dump_stack_on_debuggee_would_run", - "javascript.options.gczeal", - "javascript.options.gczeal.frequency", - "javascript.options.ion", - "javascript.options.ion.offthread_compilation", - "javascript.options.ion.threshold", - "javascript.options.ion.unsafe_eager_compilation", - "javascript.options.jit.full_debug_checks", - "javascript.options.native_regexp", - "javascript.options.parallel_parsing", - "javascript.options.shared_memory", - "javascript.options.strict", - "javascript.options.strict.debug", - "javascript.options.throw_on_asmjs_validation_failure", - "javascript.options.throw_on_debuggee_would_run", - "javascript.options.wasm", - "javascript.options.wasm_baselinejit", - "javascript.options.werror", - "javascript.use_us_english_locale", - "jsloader.reuseGlobal", - "layout.css.all-shorthand.enabled", - "layout.css.background-blend-mode.enabled", - "layout.css.background-clip-text.enabled", - "layout.css.box-decoration-break.enabled", - "layout.css.color-adjust.enabled", - "layout.css.contain.enabled", - "layout.css.control-characters.visible", - "layout.css.display-flow-root.enabled", - "layout.css.expensive-style-struct-assertions.enabled", - "layout.css.float-logical-values.enabled", - "layout.css.font-variations.enabled", - "layout.css.grid.enabled", - "layout.css.image-orientation.enabled", - "layout.css.initial-letter.enabled", - "layout.css.isolation.enabled", - "layout.css.mix-blend-mode.enabled", - "layout.css.object-fit-and-position.enabled", - "layout.css.osx-font-smoothing.enabled", - "layout.css.overflow-clip-box.enabled", - "layout.css.prefixes.animations", - "layout.css.prefixes.border-image", - "layout.css.prefixes.box-sizing", - "layout.css.prefixes.device-pixel-ratio-webkit", - "layout.css.prefixes.font-features", - "layout.css.prefixes.gradients", - "layout.css.prefixes.transforms", - "layout.css.prefixes.transitions", - "layout.css.prefixes.webkit", - "layout.css.scope-pseudo.enabled", - "layout.css.scroll-behavior.property-enabled", - "layout.css.scroll-snap.enabled", - "layout.css.servo.enabled", - "layout.css.shape-outside.enabled", - "layout.css.text-align-unsafe-value.enabled", - "layout.css.text-combine-upright-digits.enabled", - "layout.css.text-combine-upright.enabled", - "layout.css.touch_action.enabled", - "layout.css.unprefixing-service.enabled", - "layout.css.unprefixing-service.globally-whitelisted", - "layout.css.unprefixing-service.include-test-domains", - "layout.css.variables.enabled", - "layout.css.visited_links_enabled", - "layout.idle_period.required_quiescent_frames", - "layout.idle_period.time_limit", - "layout.interruptible-reflow.enabled", - "mathml.disabled", - "media.apple.forcevda", - "media.clearkey.persistent-license.enabled", - "media.cubeb_latency_msg_frames", - "media.cubeb_latency_playback_ms", - "media.decoder-doctor.wmf-disabled-is-failure", - "media.decoder.fuzzing.dont-delay-inputexhausted", - "media.decoder.fuzzing.enabled", - "media.decoder.fuzzing.video-output-minimum-interval-ms", - "media.decoder.limit", - "media.decoder.recycle.enabled", - "media.dormant-on-pause-timeout-ms", - "media.eme.audio.blank", - "media.eme.enabled", - "media.eme.video.blank", - "media.ffmpeg.enabled", - "media.ffvpx.enabled", - "media.flac.enabled", - "media.forcestereo.enabled", - "media.gmp.async-shutdown-timeout", - "media.gmp.decoder.aac", - "media.gmp.decoder.enabled", - "media.gmp.decoder.h264", - "media.gmp.insecure.allow", - "media.gpu-process-decoder", - "media.libavcodec.allow-obsolete", - "media.num-decode-threads", - "media.ogg.enabled", - "media.ogg.flac.enabled", - "media.resampling.enabled", - "media.resampling.rate", - "media.ruin-av-sync.enabled", - "media.rust.test_mode", - "media.suspend-bkgnd-video.delay-ms", - "media.suspend-bkgnd-video.enabled", - "media.use-blank-decoder", - "media.video_stats.enabled", - "media.volume_scale", - "media.webspeech.recognition.enable", - "media.webspeech.recognition.force_enable", - "media.webspeech.synth.force_global_queue", - "media.webspeech.test.enable", - "media.webspeech.test.fake_fsm_events", - "media.webspeech.test.fake_recognition_service", - "media.wmf.allow-unsupported-resolutions", - "media.wmf.decoder.thread-count", - "media.wmf.enabled", - "media.wmf.skip-blacklist", - "media.wmf.vp9.enabled", - "memory.free_dirty_pages", - "memory.low_commit_space_threshold_mb", - "memory.low_memory_notification_interval_ms", - "memory.low_physical_memory_threshold_mb", - "memory.low_virtual_mem_threshold_mb", - "network.IDN.blacklist_chars", - "network.IDN.restriction_profile", - "network.IDN.use_whitelist", - "network.IDN_show_punycode", - "network.buffer.cache.count", - "network.buffer.cache.size", - "network.captive-portal-service.enabled", - "network.cookie.cookieBehavior", - "network.cookie.lifetimePolicy", - "network.dns.disablePrefetch", - "network.dns.disablePrefetchFromHTTPS", - "network.jar.block-remote-files", - "network.loadinfo.skip_type_assertion", - "network.notify.changed", - "network.offline-mirrors-connectivity", - "network.protocol-handler.external.jar", - "network.proxy.type", - "network.security.ports.banned", - "network.security.ports.banned.override", - "network.standard-url.enable-rust", - "network.standard-url.max-length", - "network.sts.max_time_for_events_between_two_polls", - "network.sts.max_time_for_pr_close_during_shutdown", - "network.tcp.keepalive.enabled", - "network.tcp.keepalive.idle_time", - "network.tcp.keepalive.probe_count", - "network.tcp.keepalive.retry_interval", - "network.tcp.sendbuffer", - "nglayout.debug.invalidation", - "privacy.donottrackheader.enabled", - "privacy.firstparty.isolate", - "privacy.firstparty.isolate.restrict_opener_access", - "privacy.resistFingerprinting", - "security.data_uri.inherit_security_context", - "security.fileuri.strict_origin_policy", - "security.sandbox.content.level", - "security.sandbox.content.tempDirSuffix", - "security.sandbox.logging.enabled", - "security.sandbox.mac.track.violations", - "security.sandbox.windows.log", - "security.sandbox.windows.log.stackTraceDepth", - "shutdown.watchdog.timeoutSecs", - "signed.applets.codebase_principal_support", - "svg.disabled", - "svg.display-lists.hit-testing.enabled", - "svg.display-lists.painting.enabled", - "svg.new-getBBox.enabled", - "svg.paint-order.enabled", - "svg.path-caching.enabled", - "svg.transform-box.enabled", - "toolkit.asyncshutdown.crash_timeout", - "toolkit.asyncshutdown.log", - "toolkit.osfile.log", - "toolkit.osfile.log.redirect", - "toolkit.telemetry.enabled", - "toolkit.telemetry.idleTimeout", - "toolkit.telemetry.initDelay", - "toolkit.telemetry.log.dump", - "toolkit.telemetry.log.level", - "toolkit.telemetry.minSubsessionLength", - "toolkit.telemetry.scheduler.idleTickInterval", - "toolkit.telemetry.scheduler.tickInterval", - "toolkit.telemetry.unified", - "ui.key.menuAccessKeyFocuses", - "ui.popup.disable_autohide", - "ui.use_activity_cursor", - "view_source.editor.external"}; - -const char** mozilla::dom::ContentPrefs::GetContentPrefs(size_t* aCount) -{ - *aCount = ArrayLength(ContentPrefs::gInitPrefs); - return gInitPrefs; -} - -const char* mozilla::dom::ContentPrefs::GetContentPref(size_t aIndex) -{ - MOZ_ASSERT(aIndex < ArrayLength(ContentPrefs::gInitPrefs)); - return gInitPrefs[aIndex]; -} - +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ContentPrefs.h" + +const char* mozilla::dom::ContentPrefs::gInitPrefs[] = { + "accessibility.monoaudio.enable", + "accessibility.mouse_focuses_formcontrol", + "accessibility.tabfocus_applies_to_xul", + "app.update.channel", + "browser.dom.window.dump.enabled", + "browser.sessionhistory.max_entries", + "browser.sessionhistory.max_total_viewers", + "content.cors.disable", + "content.cors.no_private_data", + "content.notify.backoffcount", + "content.notify.interval", + "content.notify.ontimer", + "content.sink.enable_perf_mode", + "content.sink.event_probe_rate", + "content.sink.initial_perf_time", + "content.sink.interactive_deflect_count", + "content.sink.interactive_parse_time", + "content.sink.interactive_time", + "content.sink.pending_event_mode", + "content.sink.perf_deflect_count", + "content.sink.perf_parse_time", + "device.storage.prompt.testing", + "device.storage.writable.name", + "dom.allow_XUL_XBL_for_file", + "dom.allow_cut_copy", + "dom.enable_frame_timing", + "dom.enable_performance", + "dom.enable_resource_timing", + "dom.event.handling-user-input-time-limit", + "dom.event.touch.coalescing.enabled", + "dom.forms.autocomplete.experimental", + "dom.ipc.processPriorityManager.backgroundGracePeriodMS", + "dom.ipc.processPriorityManager.backgroundPerceivableGracePeriodMS", + "dom.max_chrome_script_run_time", + "dom.max_script_run_time", + "dom.mozBrowserFramesEnabled", + "dom.performance.enable_notify_performance_timing", + "dom.performance.enable_user_timing_logging", + "dom.storage.testing", + "dom.url.encode_decode_hash", + "dom.url.getters_decode_hash", + "dom.use_watchdog", + "dom.vibrator.enabled", + "dom.vibrator.max_vibrate_list_len", + "dom.vibrator.max_vibrate_ms", + "focusmanager.testmode", + "font.size.inflation.disabledInMasterProcess", + "font.size.inflation.emPerLine", + "font.size.inflation.forceEnabled", + "font.size.inflation.lineThreshold", + "font.size.inflation.mappingIntercept", + "font.size.inflation.maxRatio", + "font.size.inflation.minTwips", + "full-screen-api.allow-trusted-requests-only", + "full-screen-api.enabled", + "full-screen-api.unprefix.enabled", + "gfx.font_rendering.opentype_svg.enabled", + "hangmonitor.timeout", + "html5.flushtimer.initialdelay", + "html5.flushtimer.subsequentdelay", + "html5.offmainthread", + "intl.charset.fallback.tld", + "intl.ime.hack.on_ime_unaware_apps.fire_key_events_for_composition", + "javascript.enabled", + "javascript.options.asmjs", + "javascript.options.asyncstack", + "javascript.options.baselinejit", + "javascript.options.baselinejit.threshold", + "javascript.options.baselinejit.unsafe_eager_compilation", + "javascript.options.discardSystemSource", + "javascript.options.dump_stack_on_debuggee_would_run", + "javascript.options.gczeal", + "javascript.options.gczeal.frequency", + "javascript.options.ion", + "javascript.options.ion.offthread_compilation", + "javascript.options.ion.threshold", + "javascript.options.ion.unsafe_eager_compilation", + "javascript.options.jit.full_debug_checks", + "javascript.options.native_regexp", + "javascript.options.parallel_parsing", + "javascript.options.shared_memory", + "javascript.options.strict", + "javascript.options.strict.debug", + "javascript.options.throw_on_asmjs_validation_failure", + "javascript.options.throw_on_debuggee_would_run", + "javascript.options.wasm", + "javascript.options.wasm_baselinejit", + "javascript.options.werror", + "javascript.use_us_english_locale", + "jsloader.reuseGlobal", + "layout.css.all-shorthand.enabled", + "layout.css.background-blend-mode.enabled", + "layout.css.background-clip-text.enabled", + "layout.css.box-decoration-break.enabled", + "layout.css.color-adjust.enabled", + "layout.css.contain.enabled", + "layout.css.control-characters.visible", + "layout.css.display-flow-root.enabled", + "layout.css.expensive-style-struct-assertions.enabled", + "layout.css.float-logical-values.enabled", + "layout.css.font-variations.enabled", + "layout.css.grid.enabled", + "layout.css.image-orientation.enabled", + "layout.css.initial-letter.enabled", + "layout.css.isolation.enabled", + "layout.css.mix-blend-mode.enabled", + "layout.css.object-fit-and-position.enabled", + "layout.css.osx-font-smoothing.enabled", + "layout.css.overflow-clip-box.enabled", + "layout.css.prefixes.animations", + "layout.css.prefixes.border-image", + "layout.css.prefixes.box-sizing", + "layout.css.prefixes.device-pixel-ratio-webkit", + "layout.css.prefixes.font-features", + "layout.css.prefixes.gradients", + "layout.css.prefixes.transforms", + "layout.css.prefixes.transitions", + "layout.css.prefixes.webkit", + "layout.css.scope-pseudo.enabled", + "layout.css.scroll-behavior.property-enabled", + "layout.css.scroll-snap.enabled", + "layout.css.servo.enabled", + "layout.css.shape-outside.enabled", + "layout.css.text-align-unsafe-value.enabled", + "layout.css.text-combine-upright-digits.enabled", + "layout.css.text-combine-upright.enabled", + "layout.css.touch_action.enabled", + "layout.css.unprefixing-service.enabled", + "layout.css.unprefixing-service.globally-whitelisted", + "layout.css.unprefixing-service.include-test-domains", + "layout.css.variables.enabled", + "layout.css.visited_links_enabled", + "layout.idle_period.required_quiescent_frames", + "layout.idle_period.time_limit", + "layout.interruptible-reflow.enabled", + "mathml.disabled", + "media.apple.forcevda", + "media.clearkey.persistent-license.enabled", + "media.cubeb_latency_msg_frames", + "media.cubeb_latency_playback_ms", + "media.decoder-doctor.wmf-disabled-is-failure", + "media.decoder.fuzzing.dont-delay-inputexhausted", + "media.decoder.fuzzing.enabled", + "media.decoder.fuzzing.video-output-minimum-interval-ms", + "media.decoder.limit", + "media.decoder.recycle.enabled", + "media.dormant-on-pause-timeout-ms", + "media.eme.audio.blank", + "media.eme.enabled", + "media.eme.video.blank", + "media.ffmpeg.enabled", + "media.ffvpx.enabled", + "media.ffvpx.low-latency.enabled", + "media.flac.enabled", + "media.forcestereo.enabled", + "media.gmp.async-shutdown-timeout", + "media.gmp.decoder.aac", + "media.gmp.decoder.enabled", + "media.gmp.decoder.h264", + "media.gmp.insecure.allow", + "media.gpu-process-decoder", + "media.libavcodec.allow-obsolete", + "media.num-decode-threads", + "media.ogg.enabled", + "media.ogg.flac.enabled", + "media.resampling.enabled", + "media.resampling.rate", + "media.ruin-av-sync.enabled", + "media.rust.test_mode", + "media.suspend-bkgnd-video.delay-ms", + "media.suspend-bkgnd-video.enabled", + "media.use-blank-decoder", + "media.video_stats.enabled", + "media.volume_scale", + "media.webspeech.recognition.enable", + "media.webspeech.recognition.force_enable", + "media.webspeech.synth.force_global_queue", + "media.webspeech.test.enable", + "media.webspeech.test.fake_fsm_events", + "media.webspeech.test.fake_recognition_service", + "media.wmf.allow-unsupported-resolutions", + "media.wmf.decoder.thread-count", + "media.wmf.enabled", + "media.wmf.skip-blacklist", + "media.wmf.vp9.enabled", + "memory.free_dirty_pages", + "memory.low_commit_space_threshold_mb", + "memory.low_memory_notification_interval_ms", + "memory.low_physical_memory_threshold_mb", + "memory.low_virtual_mem_threshold_mb", + "network.IDN.blacklist_chars", + "network.IDN.restriction_profile", + "network.IDN.use_whitelist", + "network.IDN_show_punycode", + "network.buffer.cache.count", + "network.buffer.cache.size", + "network.captive-portal-service.enabled", + "network.cookie.cookieBehavior", + "network.cookie.lifetimePolicy", + "network.dns.disablePrefetch", + "network.dns.disablePrefetchFromHTTPS", + "network.jar.block-remote-files", + "network.loadinfo.skip_type_assertion", + "network.notify.changed", + "network.offline-mirrors-connectivity", + "network.protocol-handler.external.jar", + "network.proxy.type", + "network.security.ports.banned", + "network.security.ports.banned.override", + "network.standard-url.enable-rust", + "network.standard-url.max-length", + "network.sts.max_time_for_events_between_two_polls", + "network.sts.max_time_for_pr_close_during_shutdown", + "network.tcp.keepalive.enabled", + "network.tcp.keepalive.idle_time", + "network.tcp.keepalive.probe_count", + "network.tcp.keepalive.retry_interval", + "network.tcp.sendbuffer", + "nglayout.debug.invalidation", + "privacy.donottrackheader.enabled", + "privacy.firstparty.isolate", + "privacy.firstparty.isolate.restrict_opener_access", + "privacy.resistFingerprinting", + "security.data_uri.inherit_security_context", + "security.fileuri.strict_origin_policy", + "security.sandbox.content.level", + "security.sandbox.content.tempDirSuffix", + "security.sandbox.logging.enabled", + "security.sandbox.mac.track.violations", + "security.sandbox.windows.log", + "security.sandbox.windows.log.stackTraceDepth", + "shutdown.watchdog.timeoutSecs", + "signed.applets.codebase_principal_support", + "svg.disabled", + "svg.display-lists.hit-testing.enabled", + "svg.display-lists.painting.enabled", + "svg.new-getBBox.enabled", + "svg.paint-order.enabled", + "svg.path-caching.enabled", + "svg.transform-box.enabled", + "toolkit.asyncshutdown.crash_timeout", + "toolkit.asyncshutdown.log", + "toolkit.osfile.log", + "toolkit.osfile.log.redirect", + "toolkit.telemetry.enabled", + "toolkit.telemetry.idleTimeout", + "toolkit.telemetry.initDelay", + "toolkit.telemetry.log.dump", + "toolkit.telemetry.log.level", + "toolkit.telemetry.minSubsessionLength", + "toolkit.telemetry.scheduler.idleTickInterval", + "toolkit.telemetry.scheduler.tickInterval", + "toolkit.telemetry.unified", + "ui.key.menuAccessKeyFocuses", + "ui.popup.disable_autohide", + "ui.use_activity_cursor", + "view_source.editor.external"}; + +const char** mozilla::dom::ContentPrefs::GetContentPrefs(size_t* aCount) +{ + *aCount = ArrayLength(ContentPrefs::gInitPrefs); + return gInitPrefs; +} + +const char* mozilla::dom::ContentPrefs::GetContentPref(size_t aIndex) +{ + MOZ_ASSERT(aIndex < ArrayLength(ContentPrefs::gInitPrefs)); + return gInitPrefs[aIndex]; +} +
--- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -2676,20 +2676,26 @@ already_AddRefed<nsILoadContext> TabParent::GetLoadContext() { nsCOMPtr<nsILoadContext> loadContext; if (mLoadContext) { loadContext = mLoadContext; } else { bool isPrivate = mChromeFlags & nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW; SetPrivateBrowsingAttributes(isPrivate); + bool useTrackingProtection = false; + nsCOMPtr<nsIDocShell> docShell = mFrameElement->OwnerDoc()->GetDocShell(); + if (docShell) { + docShell->GetUseTrackingProtection(&useTrackingProtection); + } loadContext = new LoadContext(GetOwnerElement(), true /* aIsContent */, isPrivate, mChromeFlags & nsIWebBrowserChrome::CHROME_REMOTE_WINDOW, + useTrackingProtection, OriginAttributesRef()); mLoadContext = loadContext; } return loadContext.forget(); } NS_IMETHODIMP TabParent::GetUseAsyncPanZoom(bool* useAsyncPanZoom) @@ -2989,17 +2995,18 @@ public: NS_IMETHOD GetIsContent(bool*) NO_IMPL NS_IMETHOD GetUsePrivateBrowsing(bool*) NO_IMPL NS_IMETHOD SetUsePrivateBrowsing(bool) NO_IMPL NS_IMETHOD SetPrivateBrowsing(bool) NO_IMPL NS_IMETHOD GetIsInIsolatedMozBrowserElement(bool*) NO_IMPL NS_IMETHOD GetOriginAttributes(JS::MutableHandleValue) NO_IMPL NS_IMETHOD GetUseRemoteTabs(bool*) NO_IMPL NS_IMETHOD SetRemoteTabs(bool) NO_IMPL - NS_IMETHOD IsTrackingProtectionOn(bool*) NO_IMPL + NS_IMETHOD GetUseTrackingProtection(bool*) NO_IMPL + NS_IMETHOD SetUseTrackingProtection(bool) NO_IMPL #undef NO_IMPL protected: ~FakeChannel() {} nsCOMPtr<nsIURI> mUri; uint64_t mCallbackId; nsCOMPtr<Element> mElement;
--- a/dom/locales/en-US/chrome/layout/layout_errors.properties +++ b/dom/locales/en-US/chrome/layout/layout_errors.properties @@ -6,19 +6,16 @@ ImageMapRectBoundsError=The “coords” attribute of the <area shape="rect"> tag is not in the “left,top,right,bottom” format. ImageMapCircleWrongNumberOfCoords=The “coords” attribute of the <area shape="circle"> tag is not in the “center-x,center-y,radius” format. ImageMapCircleNegativeRadius=The “coords” attribute of the <area shape="circle"> tag has a negative radius. ImageMapPolyWrongNumberOfCoords=The “coords” attribute of the <area shape="poly"> tag is not in the “x1,y1,x2,y2 …” format. ImageMapPolyOddNumberOfCoords=The “coords” attribute of the <area shape="poly"> tag is missing the last “y” coordinate (the correct format is “x1,y1,x2,y2 …”). TablePartRelPosWarning=Relative positioning of table rows and row groups is now supported. This site may need to be updated because it may depend on this feature having no effect. ScrollLinkedEffectFound2=This site appears to use a scroll-linked positioning effect. This may not work well with asynchronous panning; see https://developer.mozilla.org/docs/Mozilla/Performance/ScrollLinkedEffects for further details and to join the discussion on related tools and features! -## LOCALIZATION NOTE(CompositorAnimationWarningContentTooSmall): -## (%1$S, %2$S) is a pair of integer values of the frame size -CompositorAnimationWarningContentTooSmall=Animation cannot be run on the compositor because frame size (%1$S, %2$S) is smaller than (16, 16) ## LOCALIZATION NOTE(CompositorAnimationWarningContentTooLarge2): ## (%1$S, %2$S) is a pair of integer values of the frame size ## (%3$S, %4$S) is a pair of integer values of a limit based on the viewport size ## (%5$S, %6$S) is a pair of integer values of an absolute limit CompositorAnimationWarningContentTooLarge2=Animation cannot be run on the compositor because the frame size (%1$S, %2$S) is too large relative to the viewport (larger than (%3$S, %4$S)) or larger than the maximum allowed value (%5$S, %6$S) ## LOCALIZATION NOTE(CompositorAnimationWarningTransformBackfaceVisibilityHidden): ## 'backface-visibility: hidden' is a CSS property, don't translate it. CompositorAnimationWarningTransformBackfaceVisibilityHidden=Animations of ‘backface-visibility: hidden’ transforms cannot be run on the compositor
--- a/dom/media/MediaDecoderStateMachine.cpp +++ b/dom/media/MediaDecoderStateMachine.cpp @@ -16,24 +16,26 @@ #include "gfx2DGlue.h" #include "mediasink/AudioSinkWrapper.h" #include "mediasink/DecodedAudioDataSink.h" #include "mediasink/DecodedStream.h" #include "mediasink/OutputStreamManager.h" #include "mediasink/VideoSink.h" #include "mozilla/DebugOnly.h" +#include "mozilla/IndexSequence.h" #include "mozilla/Logging.h" #include "mozilla/mozalloc.h" #include "mozilla/MathAlgorithms.h" #include "mozilla/Preferences.h" #include "mozilla/SharedThreadPool.h" #include "mozilla/SizePrintfMacros.h" #include "mozilla/Sprintf.h" #include "mozilla/TaskQueue.h" +#include "mozilla/Tuple.h" #include "nsComponentManagerUtils.h" #include "nsContentUtils.h" #include "nsIEventTarget.h" #include "nsITimer.h" #include "nsPrintfCString.h" #include "nsTArray.h" #include "nsDeque.h" @@ -279,37 +281,56 @@ protected: // have a waiting promise pending (such as with non-MSE EME). return Resource()->IsExpectingMoreData() || mMaster->IsWaitingAudioData() || mMaster->IsWaitingVideoData(); } MediaQueue<MediaData>& AudioQueue() const { return mMaster->mAudioQueue; } MediaQueue<MediaData>& VideoQueue() const { return mMaster->mVideoQueue; } + template <class S, typename... Args, size_t... Indexes> + auto + CallEnterMemberFunction(S* aS, + Tuple<Args...>& aTuple, + IndexSequence<Indexes...>) + -> decltype(ReturnTypeHelper(&S::Enter)) + { + return aS->Enter(Move(Get<Indexes>(aTuple))...); + } + // Note this function will delete the current state object. // Don't access members to avoid UAF after this call. template <class S, typename... Ts> - auto SetState(Ts... aArgs) + auto SetState(Ts&&... aArgs) -> decltype(ReturnTypeHelper(&S::Enter)) { + // |aArgs| must be passed by reference to avoid passing MOZ_NON_PARAM class + // SeekJob by value. See bug 1287006 and bug 1338374. But we still *must* + // copy the parameters, because |Exit()| can modify them. See bug 1312321. + // So we 1) pass the parameters by reference, but then 2) immediately copy + // them into a Tuple to be safe against modification, and finally 3) move + // the elements of the Tuple into the final function call. + auto copiedArgs = MakeTuple(Forward<Ts>(aArgs)...); + // keep mMaster in a local object because mMaster will become invalid after // the current state object is deleted. auto master = mMaster; - auto s = new S(master); + auto* s = new S(master); MOZ_ASSERT(GetState() != s->GetState() || GetState() == DECODER_STATE_SEEKING); SLOG("change state to: %s", ToStateStr(s->GetState())); Exit(); master->mStateObj.reset(s); - return s->Enter(Move(aArgs)...); + return CallEnterMemberFunction(s, copiedArgs, + typename IndexSequenceFor<Ts...>::Type()); } RefPtr<MediaDecoder::SeekPromise> SetSeekingState(SeekJob&& aSeekJob, EventVisibility aVisibility); // Take a raw pointer in order not to change the life cycle of MDSM. // It is guaranteed to be valid by MDSM. Master* mMaster; @@ -899,17 +920,17 @@ private: * DECODING otherwise. */ class MediaDecoderStateMachine::SeekingState : public MediaDecoderStateMachine::StateObject { public: explicit SeekingState(Master* aPtr) : StateObject(aPtr) { } - RefPtr<MediaDecoder::SeekPromise> Enter(SeekJob aSeekJob, + RefPtr<MediaDecoder::SeekPromise> Enter(SeekJob&& aSeekJob, EventVisibility aVisibility) { mSeekJob = Move(aSeekJob); // Always switch off the blank decoder otherwise we might become visible // in the middle of seeking and won't have a valid video frame to show // when seek is done. if (mMaster->mVideoDecodeSuspended) { @@ -977,17 +998,17 @@ private: class MediaDecoderStateMachine::AccurateSeekingState : public MediaDecoderStateMachine::SeekingState { public: explicit AccurateSeekingState(Master* aPtr) : SeekingState(aPtr) { } - RefPtr<MediaDecoder::SeekPromise> Enter(SeekJob aSeekJob, + RefPtr<MediaDecoder::SeekPromise> Enter(SeekJob&& aSeekJob, EventVisibility aVisibility) { MOZ_ASSERT(aSeekJob.mTarget->IsAccurate() || aSeekJob.mTarget->IsFast()); mCurrentTimeBeforeSeek = TimeUnit::FromMicroseconds(mMaster->GetMediaTime()); return SeekingState::Enter(Move(aSeekJob), aVisibility); } @@ -1436,17 +1457,17 @@ DiscardFrames(MediaQueue<MediaData>& aQu class MediaDecoderStateMachine::NextFrameSeekingState : public MediaDecoderStateMachine::SeekingState { public: explicit NextFrameSeekingState(Master* aPtr) : SeekingState(aPtr) { } - RefPtr<MediaDecoder::SeekPromise> Enter(SeekJob aSeekJob, + RefPtr<MediaDecoder::SeekPromise> Enter(SeekJob&& aSeekJob, EventVisibility aVisibility) { MOZ_ASSERT(aSeekJob.mTarget->IsNextFrame()); mCurrentTime = mMaster->GetMediaTime(); mDuration = mMaster->Duration(); return SeekingState::Enter(Move(aSeekJob), aVisibility); }
--- a/dom/media/MediaPrefs.h +++ b/dom/media/MediaPrefs.h @@ -85,20 +85,21 @@ private: }; // This is where DECL_MEDIA_PREF for each of the preferences should go. // AudioSink DECL_MEDIA_PREF("accessibility.monoaudio.enable", MonoAudio, bool, false); DECL_MEDIA_PREF("media.resampling.enabled", AudioSinkResampling, bool, false); DECL_MEDIA_PREF("media.resampling.rate", AudioSinkResampleRate, uint32_t, 48000); -#if defined(ANDROID) +#if defined(XP_WIN) || defined(XP_DARWIN) || defined(MOZ_PULSEAUDIO) + // libcubeb backend implement .get_preferred_channel_layout + DECL_MEDIA_PREF("media.forcestereo.enabled", AudioSinkForceStereo, bool, false); +#else DECL_MEDIA_PREF("media.forcestereo.enabled", AudioSinkForceStereo, bool, true); -#else - DECL_MEDIA_PREF("media.forcestereo.enabled", AudioSinkForceStereo, bool, false); #endif // VideoSink DECL_MEDIA_PREF("media.ruin-av-sync.enabled", RuinAvSync, bool, false); // Encrypted Media Extensions DECL_MEDIA_PREF("media.clearkey.persistent-license.enabled", ClearKeyPersistentLicenseEnabled, bool, false); // PlatformDecoderModule @@ -115,16 +116,17 @@ private: DECL_MEDIA_PREF("media.android-media-codec.preferred", PDMAndroidMediaCodecPreferred, bool, false); #endif #ifdef MOZ_FFMPEG DECL_MEDIA_PREF("media.ffmpeg.enabled", PDMFFmpegEnabled, bool, true); DECL_MEDIA_PREF("media.libavcodec.allow-obsolete", LibavcodecAllowObsolete, bool, false); #endif #ifdef MOZ_FFVPX DECL_MEDIA_PREF("media.ffvpx.enabled", PDMFFVPXEnabled, bool, true); + DECL_MEDIA_PREF("media.ffvpx.low-latency.enabled", PDMFFVPXLowLatencyEnabled, bool, false); #endif #ifdef XP_WIN DECL_MEDIA_PREF("media.wmf.enabled", PDMWMFEnabled, bool, true); DECL_MEDIA_PREF("media.wmf.skip-blacklist", PDMWMFSkipBlacklist, bool, false); DECL_MEDIA_PREF("media.decoder-doctor.wmf-disabled-is-failure", DecoderDoctorWMFDisabledIsFailure, bool, false); DECL_MEDIA_PREF("media.wmf.vp9.enabled", PDMWMFVP9DecoderEnabled, bool, true); DECL_MEDIA_PREF("media.wmf.decoder.thread-count", PDMWMFThreadCount, int32_t, -1); DECL_MEDIA_PREF("media.wmf.allow-unsupported-resolutions", PDMWMFAllowUnsupportedResolutions, bool, false);
--- a/dom/media/MediaResult.h +++ b/dom/media/MediaResult.h @@ -2,16 +2,18 @@ /* vim:set ts=2 sw=2 sts=2 et cindent: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef MediaResult_h_ #define MediaResult_h_ +#include "nsString.h" // Required before 'mozilla/ErrorNames.h'!? +#include "mozilla/ErrorNames.h" #include "nsError.h" #include "nsPrintfCString.h" // MediaResult can be used interchangeably with nsresult. // It allows to store extra information such as where the error occurred. // While nsresult is typically passed by value; due to its potential size, using // MediaResult const references is recommended. namespace mozilla { @@ -46,20 +48,30 @@ public: bool operator!=(nsresult aResult) const { return aResult != mCode; } operator nsresult () const { return mCode; } nsCString Description() const { if (NS_SUCCEEDED(mCode)) { return nsCString(); } - return nsPrintfCString("0x%08" PRIx32 ": %s", static_cast<uint32_t>(mCode), mMessage.get()); + nsCString name; + GetErrorName(mCode, static_cast<nsACString&>(name)); + return nsPrintfCString("%s (0x%08" PRIx32 ")%s%s", + name.get(), + static_cast<uint32_t>(mCode), + mMessage.IsEmpty() ? "" : " - ", + mMessage.get()); } private: nsresult mCode; nsCString mMessage; }; -#define RESULT_DETAIL(arg, ...) nsPrintfCString("%s: " arg, __func__, ##__VA_ARGS__) +#ifdef _MSC_VER +#define RESULT_DETAIL(arg, ...) nsPrintfCString("%s: " arg, __FUNCSIG__, ##__VA_ARGS__) +#else +#define RESULT_DETAIL(arg, ...) nsPrintfCString("%s: " arg, __PRETTY_FUNCTION__, ##__VA_ARGS__) +#endif } // namespace mozilla #endif // MediaResult_h_
--- a/dom/media/gmp/GMPCrashHelper.h +++ b/dom/media/gmp/GMPCrashHelper.h @@ -2,18 +2,19 @@ /* vim:set ts=2 sw=2 sts=2 et cindent: */ /* 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/. */ #if !defined(GMPCrashHelper_h_) #define GMPCrashHelper_h_ +#include "MainThreadUtils.h" +#include "nsISupportsImpl.h" #include "nsPIDOMWindow.h" -#include "nsISupportsImpl.h" namespace mozilla { // For every GMP actor requested, the caller can specify a crash helper, // which is an object which supplies the nsPIDOMWindowInner to which we'll // dispatch the PluginCrashed event if the GMP crashes. // GMPCrashHelper has threadsafe refcounting. Its release method ensures // that instances are destroyed on the main thread.
--- a/dom/media/gmp/GMPVideoDecoderParent.cpp +++ b/dom/media/gmp/GMPVideoDecoderParent.cpp @@ -10,16 +10,17 @@ #include "nsAutoRef.h" #include "nsThreadUtils.h" #include "GMPUtils.h" #include "GMPVideoEncodedFrameImpl.h" #include "GMPVideoi420FrameImpl.h" #include "GMPContentParent.h" #include "GMPMessageUtils.h" #include "mozilla/gmp/GMPTypes.h" +#include "nsPrintfCString.h" namespace mozilla { #ifdef LOG #undef LOG #endif extern LogModule* GetGMPLog();
--- a/dom/media/gmp/GMPVideoEncoderParent.cpp +++ b/dom/media/gmp/GMPVideoEncoderParent.cpp @@ -252,20 +252,19 @@ GMPVideoEncoderParent::ActorDestroy(Acto if (mCallback) { // May call Close() (and Shutdown()) immediately or with a delay mCallback->Terminated(); mCallback = nullptr; } // Must be shut down before VideoEncoderDestroyed(), since this can recurse // the GMPThread event loop. See bug 1049501 if (mEncodedThread) { - // Can't get it to allow me to use WrapRunnable with a nsCOMPtr<nsIThread>() NS_DispatchToMainThread( - WrapRunnableNM<decltype(&ShutdownEncodedThread), - nsCOMPtr<nsIThread> >(&ShutdownEncodedThread, mEncodedThread)); + WrapRunnableNM(&ShutdownEncodedThread, nsCOMPtr<nsIThread>(mEncodedThread)) + ); mEncodedThread = nullptr; } if (mPlugin) { // Ignore any return code. It is OK for this to fail without killing the process. mPlugin->VideoEncoderDestroyed(this); mPlugin = nullptr; } mVideoHost.ActorDestroyed(); // same as DoneWithAPI
--- a/dom/media/gtest/TestMozPromise.cpp +++ b/dom/media/gtest/TestMozPromise.cpp @@ -34,17 +34,17 @@ private: RefPtr<TaskQueue> mTaskQueue; }; class DelayedResolveOrReject : public Runnable { public: DelayedResolveOrReject(TaskQueue* aTaskQueue, TestPromise::Private* aPromise, - TestPromise::ResolveOrRejectValue aValue, + const TestPromise::ResolveOrRejectValue& aValue, int aIterations) : mTaskQueue(aTaskQueue) , mPromise(aPromise) , mValue(aValue) , mIterations(aIterations) {} NS_IMETHOD Run() override
--- a/dom/media/ipc/PVideoDecoder.ipdl +++ b/dom/media/ipc/PVideoDecoder.ipdl @@ -21,16 +21,17 @@ struct MediaDataIPDL uint32_t frames; bool keyframe; }; struct VideoDataIPDL { MediaDataIPDL base; IntSize display; + IntSize frameSize; SurfaceDescriptorGPUVideo sd; int32_t frameID; }; struct MediaRawDataIPDL { MediaDataIPDL base; Shmem buffer;
--- a/dom/media/ipc/VideoDecoderChild.cpp +++ b/dom/media/ipc/VideoDecoderChild.cpp @@ -39,17 +39,17 @@ mozilla::ipc::IPCResult VideoDecoderChild::RecvOutput(const VideoDataIPDL& aData) { AssertOnManagerThread(); VideoInfo info(aData.display().width, aData.display().height); // The Image here creates a TextureData object that takes ownership // of the SurfaceDescriptor, and is responsible for making sure that // it gets deallocated. - RefPtr<Image> image = new GPUVideoImage(GetManager(), aData.sd(), aData.display()); + RefPtr<Image> image = new GPUVideoImage(GetManager(), aData.sd(), aData.frameSize()); RefPtr<VideoData> video = VideoData::CreateFromImage(info, aData.base().offset(), aData.base().time(), aData.base().duration(), image, aData.base().keyframe(), aData.base().timecode(),
--- a/dom/media/ipc/VideoDecoderParent.cpp +++ b/dom/media/ipc/VideoDecoderParent.cpp @@ -184,16 +184,17 @@ VideoDecoderParent::ProcessDecodedData( texture->InitIPDLActor(mKnowsCompositor); texture->SetAddedToCompositableClient(); } VideoDataIPDL output( MediaDataIPDL(data->mOffset, data->mTime, data->mTimecode, data->mDuration, data->mFrames, data->mKeyframe), video->mDisplay, + texture ? texture->GetSize() : IntSize(), texture ? mParent->StoreImage(video->mImage, texture) : SurfaceDescriptorGPUVideo(0), video->mFrameID); Unused << SendOutput(output); } } mozilla::ipc::IPCResult
--- a/dom/media/mediasource/MediaSource.cpp +++ b/dom/media/mediasource/MediaSource.cpp @@ -110,16 +110,18 @@ MediaSource::IsTypeSupported(const nsASt mimeType == MEDIAMIMETYPE("audio/mp4")) { if (!Preferences::GetBool("media.mediasource.mp4.enabled", false)) { return NS_ERROR_DOM_NOT_SUPPORTED_ERR; } return NS_OK; } if (mimeType == MEDIAMIMETYPE("video/webm")) { if (!(Preferences::GetBool("media.mediasource.webm.enabled", false) || + containerType->ExtendedType().Codecs().Contains( + NS_LITERAL_STRING("vp8")) || IsWebMForced(aDiagnostics))) { return NS_ERROR_DOM_NOT_SUPPORTED_ERR; } return NS_OK; } if (mimeType == MEDIAMIMETYPE("audio/webm")) { if (!(Preferences::GetBool("media.mediasource.webm.enabled", false) || Preferences::GetBool("media.mediasource.webm.audio.enabled", true))) {
--- a/dom/media/mediasource/SourceBuffer.cpp +++ b/dom/media/mediasource/SourceBuffer.cpp @@ -420,17 +420,17 @@ SourceBuffer::AppendData(const uint8_t* mTrackBuffersManager->AppendData(data, mCurrentAttributes) ->Then(mAbstractMainThread, __func__, this, &SourceBuffer::AppendDataCompletedWithSuccess, &SourceBuffer::AppendDataErrored) ->Track(mPendingAppend); } void -SourceBuffer::AppendDataCompletedWithSuccess(SourceBufferTask::AppendBufferResult aResult) +SourceBuffer::AppendDataCompletedWithSuccess(const SourceBufferTask::AppendBufferResult& aResult) { MOZ_ASSERT(mUpdating); mPendingAppend.Complete(); if (aResult.first()) { if (!mActive) { mActive = true; mMediaSource->SourceBufferIsActive(this);
--- a/dom/media/mediasource/SourceBuffer.h +++ b/dom/media/mediasource/SourceBuffer.h @@ -159,17 +159,17 @@ private: void AppendError(const MediaResult& aDecodeError); // Implements the "Prepare Append Algorithm". Returns MediaByteBuffer object // on success or nullptr (with aRv set) on error. already_AddRefed<MediaByteBuffer> PrepareAppend(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv); - void AppendDataCompletedWithSuccess(SourceBufferTask::AppendBufferResult aResult); + void AppendDataCompletedWithSuccess(const SourceBufferTask::AppendBufferResult& aResult); void AppendDataErrored(const MediaResult& aError); RefPtr<MediaSource> mMediaSource; const RefPtr<AbstractThread> mAbstractMainThread; RefPtr<TrackBuffersManager> mTrackBuffersManager; SourceBufferAttributes mCurrentAttributes;
--- a/dom/media/mediasource/test/test_MediaSource.html +++ b/dom/media/mediasource/test/test_MediaSource.html @@ -35,16 +35,19 @@ runWithMSE(function () { v.src = o; document.body.appendChild(v); var loadedmetadataCount = 0; var updatestartCount = 0; var updateendCount = 0; var updateCount = 0; + ok(MediaSource.isTypeSupported("video/webm; codecs=vp8"), "VP8 MSE is always supported"); + ok(MediaSource.isTypeSupported("audio/webm", "Audio MSE is always supported")); + ms.addEventListener("sourceopen", function () { ok(true, "Receive a sourceopen event"); is(ms.readyState, "open", "MediaSource must be in open state after sourceopen"); var sb = ms.addSourceBuffer("video/webm"); ok(sb, "Create a SourceBuffer"); is(ms.sourceBuffers.length, 1, "MediaSource.sourceBuffers is expected length"); is(ms.sourceBuffers[0], sb, "SourceBuffer in list matches our SourceBuffer"); is(ms.activeSourceBuffers.length, 0, "MediaSource.activeSourceBuffers is expected length");
--- a/dom/media/platforms/PlatformDecoderModule.h +++ b/dom/media/platforms/PlatformDecoderModule.h @@ -2,26 +2,27 @@ /* vim:set ts=2 sw=2 sts=2 et cindent: */ /* 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/. */ #if !defined(PlatformDecoderModule_h_) #define PlatformDecoderModule_h_ +#include "GMPCrashHelper.h" #include "MediaDecoderReader.h" #include "MediaInfo.h" +#include "MediaResult.h" +#include "mozilla/EnumSet.h" #include "mozilla/MozPromise.h" +#include "mozilla/RefPtr.h" +#include "mozilla/layers/KnowsCompositor.h" #include "mozilla/layers/LayersTypes.h" -#include "mozilla/layers/KnowsCompositor.h" #include "nsTArray.h" -#include "mozilla/RefPtr.h" -#include "GMPCrashHelper.h" #include <queue> -#include "MediaResult.h" namespace mozilla { class TrackInfo; class AudioInfo; class VideoInfo; class MediaRawData; class DecoderDoctorDiagnostics; @@ -36,20 +37,24 @@ class RemoteDecoderModule; class MediaDataDecoder; class TaskQueue; class CDMProxy; static LazyLogModule sPDMLog("PlatformDecoderModule"); struct MOZ_STACK_CLASS CreateDecoderParams final { - explicit CreateDecoderParams(const TrackInfo& aConfig) - : mConfig(aConfig) + explicit CreateDecoderParams(const TrackInfo& aConfig) : mConfig(aConfig) { } + + enum class Option { - } + Default, + LowLatency, + }; + using OptionSet = EnumSet<Option>; template <typename T1, typename... Ts> CreateDecoderParams(const TrackInfo& aConfig, T1&& a1, Ts&&... args) : mConfig(aConfig) { Set(mozilla::Forward<T1>(a1), mozilla::Forward<Ts>(args)...); } @@ -78,30 +83,32 @@ struct MOZ_STACK_CLASS CreateDecoderPara DecoderDoctorDiagnostics* mDiagnostics = nullptr; layers::ImageContainer* mImageContainer = nullptr; MediaResult* mError = nullptr; RefPtr<layers::KnowsCompositor> mKnowsCompositor; RefPtr<GMPCrashHelper> mCrashHelper; bool mUseBlankDecoder = false; TrackInfo::TrackType mType = TrackInfo::kUndefinedTrack; MediaEventProducer<TrackInfo::TrackType>* mOnWaitingForKeyEvent = nullptr; + OptionSet mOptions = OptionSet(Option::Default); private: void Set(TaskQueue* aTaskQueue) { mTaskQueue = aTaskQueue; } void Set(DecoderDoctorDiagnostics* aDiagnostics) { mDiagnostics = aDiagnostics; } void Set(layers::ImageContainer* aImageContainer) { mImageContainer = aImageContainer; } void Set(MediaResult* aError) { mError = aError; } void Set(GMPCrashHelper* aCrashHelper) { mCrashHelper = aCrashHelper; } void Set(bool aUseBlankDecoder) { mUseBlankDecoder = aUseBlankDecoder; } + void Set(OptionSet aOptions) { mOptions = aOptions; } void Set(layers::KnowsCompositor* aKnowsCompositor) { mKnowsCompositor = aKnowsCompositor; } void Set(TrackInfo::TrackType aType) { mType = aType; } @@ -133,24 +140,25 @@ private: class PlatformDecoderModule { public: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PlatformDecoderModule) // Perform any per-instance initialization. // This is called on the decode task queue. - virtual nsresult Startup() { return NS_OK; }; + virtual nsresult Startup() { return NS_OK; } // Indicates if the PlatformDecoderModule supports decoding of aMimeType. - virtual bool SupportsMimeType( - const nsACString& aMimeType, - DecoderDoctorDiagnostics* aDiagnostics) const = 0; - virtual bool Supports(const TrackInfo& aTrackInfo, - DecoderDoctorDiagnostics* aDiagnostics) const + virtual bool + SupportsMimeType(const nsACString& aMimeType, + DecoderDoctorDiagnostics* aDiagnostics) const = 0; + virtual bool + Supports(const TrackInfo& aTrackInfo, + DecoderDoctorDiagnostics* aDiagnostics) const { // By default, fall back to SupportsMimeType with just the MIME string. // (So PDMs do not need to override this method -- yet.) return SupportsMimeType(aTrackInfo.mMimeType, aDiagnostics); } protected: PlatformDecoderModule() { } @@ -202,17 +210,17 @@ protected: // // Decoding is done asynchronously. Any async work can be done on the // TaskQueue passed into the PlatformDecoderModules's Create*Decoder() // function. This may not be necessary for platforms with async APIs // for decoding. class MediaDataDecoder { protected: - virtual ~MediaDataDecoder() {}; + virtual ~MediaDataDecoder() { } public: typedef TrackInfo::TrackType TrackType; typedef nsTArray<RefPtr<MediaData>> DecodedData; typedef MozPromise<TrackType, MediaResult, /* IsExclusive = */ true> InitPromise; typedef MozPromise<DecodedData, MediaResult, /* IsExclusive = */ true> DecodePromise; @@ -276,17 +284,17 @@ public: virtual const char* GetDescriptionName() const = 0; // Set a hint of seek target time to decoder. Decoder will drop any decoded // data which pts is smaller than this value. This threshold needs to be clear // after reset decoder. // Decoder may not honor this value. However, it'd be better that // video decoder implements this API to improve seek performance. // Note: it should be called before Input() or after Flush(). - virtual void SetSeekThreshold(const media::TimeUnit& aTime) {} + virtual void SetSeekThreshold(const media::TimeUnit& aTime) { } // When playing adaptive playback, recreating an Android video decoder will // cause the transition not smooth during resolution change. // Reuse the decoder if the decoder support recycling. // Currently, only Android video decoder will return true. virtual bool SupportDecoderRecycling() const { return false; } // ConfigurationChanged will be called to inform the video or audio decoder
--- a/dom/media/platforms/agnostic/OpusDecoder.cpp +++ b/dom/media/platforms/agnostic/OpusDecoder.cpp @@ -169,38 +169,47 @@ OpusDataDecoder::ProcessDecode(MediaRawD if (!mLastFrameTime || mLastFrameTime.ref() != aSample->mTime) { // We are starting a new block. mFrames = 0; mLastFrameTime = Some(aSample->mTime); } // Maximum value is 63*2880, so there's no chance of overflow. - uint32_t frames_number = opus_packet_get_nb_frames(aSample->Data(), - aSample->Size()); + int frames_number = + opus_packet_get_nb_frames(aSample->Data(), aSample->Size()); if (frames_number <= 0) { - OPUS_DEBUG("Invalid packet header: r=%" PRIu32 " length=%" PRIuSIZE, frames_number, + OPUS_DEBUG("Invalid packet header: r=%d length=%" PRIuSIZE, frames_number, aSample->Size()); return DecodePromise::CreateAndReject( MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR, RESULT_DETAIL("Invalid packet header: r=%d length=%u", frames_number, uint32_t(aSample->Size()))), __func__); } - uint32_t samples = opus_packet_get_samples_per_frame( + int samples = opus_packet_get_samples_per_frame( aSample->Data(), opus_int32(mOpusParser->mRate)); // A valid Opus packet must be between 2.5 and 120 ms long (48kHz). - uint32_t frames = frames_number*samples; - if (frames < 120 || frames > 5760) { - OPUS_DEBUG("Invalid packet frames: %u", frames); + CheckedInt32 totalFrames = + CheckedInt32(frames_number) * CheckedInt32(samples); + if (!totalFrames.isValid()) { return DecodePromise::CreateAndReject( MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR, - RESULT_DETAIL("Invalid packet frames:%u", frames)), + RESULT_DETAIL("Frames count overflow")), + __func__); + } + + int frames = totalFrames.value(); + if (frames < 120 || frames > 5760) { + OPUS_DEBUG("Invalid packet frames: %d", frames); + return DecodePromise::CreateAndReject( + MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR, + RESULT_DETAIL("Invalid packet frames:%d", frames)), __func__); } AlignedAudioBuffer buffer(frames * channels); if (!buffer) { return DecodePromise::CreateAndReject( MediaResult(NS_ERROR_OUT_OF_MEMORY, __func__), __func__); } @@ -216,39 +225,40 @@ OpusDataDecoder::ProcessDecode(MediaRawD buffer.get(), frames, false); #endif if (ret < 0) { return DecodePromise::CreateAndReject( MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR, RESULT_DETAIL("Opus decoding error:%d", ret)), __func__); } - NS_ASSERTION(uint32_t(ret) == frames, "Opus decoded too few audio samples"); + NS_ASSERTION(ret == frames, "Opus decoded too few audio samples"); CheckedInt64 startTime = aSample->mTime; // Trim the initial frames while the decoder is settling. if (mSkip > 0) { int32_t skipFrames = std::min<int32_t>(mSkip, frames); int32_t keepFrames = frames - skipFrames; - OPUS_DEBUG("Opus decoder skipping %d of %d frames", skipFrames, frames); + OPUS_DEBUG( + "Opus decoder skipping %d of %d frames", skipFrames, frames); PodMove(buffer.get(), buffer.get() + skipFrames * channels, keepFrames * channels); startTime = startTime + FramesToUsecs(skipFrames, mOpusParser->mRate); frames = keepFrames; mSkip -= skipFrames; } if (aSample->mDiscardPadding > 0) { - OPUS_DEBUG("Opus decoder discarding %u of %u frames", + OPUS_DEBUG("Opus decoder discarding %u of %d frames", aSample->mDiscardPadding, frames); // Padding discard is only supposed to happen on the final packet. // Record the discard so we can return an error if another packet is // decoded. - if (aSample->mDiscardPadding > frames) { + if (aSample->mDiscardPadding > uint32_t(frames)) { // Discarding more than the entire packet is invalid. OPUS_DEBUG("Opus error, discard padding larger than packet"); return DecodePromise::CreateAndReject( MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, RESULT_DETAIL("Discard padding larger than packet")), __func__); }
--- a/dom/media/platforms/ffmpeg/FFmpegDecoderModule.h +++ b/dom/media/platforms/ffmpeg/FFmpegDecoderModule.h @@ -6,16 +6,17 @@ #ifndef __FFmpegDecoderModule_h__ #define __FFmpegDecoderModule_h__ #include "PlatformDecoderModule.h" #include "FFmpegLibWrapper.h" #include "FFmpegAudioDecoder.h" #include "FFmpegVideoDecoder.h" +#include "MediaPrefs.h" namespace mozilla { template <int V> class FFmpegDecoderModule : public PlatformDecoderModule { public: static already_AddRefed<PlatformDecoderModule> @@ -34,21 +35,27 @@ public: {