author | Coroiu Cristina <ccoroiu@mozilla.com> |
Sat, 17 Nov 2018 23:37:07 +0200 | |
changeset 446922 | 77223bb2fac278373dfcdde11fcda74b4c80aa61 |
parent 446917 | a92c330bac8bce0c51099d630349aecbb71d7c35 (current diff) |
parent 446921 | f77121fb76fc00afa520b03f847c20e8baa88715 (diff) |
child 446924 | 2d8c5d46389336d6c4620e947f757e302d3f224c |
child 446933 | 572f525f1afe920d092830673f843066425d4494 |
push id | 35057 |
push user | ccoroiu@mozilla.com |
push date | Sat, 17 Nov 2018 21:37:33 +0000 |
treeherder | mozilla-central@77223bb2fac2 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 65.0a1 |
first release with | nightly linux32
77223bb2fac2
/
65.0a1
/
20181117220111
/
files
nightly linux64
77223bb2fac2
/
65.0a1
/
20181117220111
/
files
nightly mac
77223bb2fac2
/
65.0a1
/
20181117220111
/
files
nightly win32
77223bb2fac2
/
65.0a1
/
20181117220111
/
files
nightly win64
77223bb2fac2
/
65.0a1
/
20181117220111
/
files
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
releases | nightly linux32
65.0a1
/
20181117220111
/
pushlog to previous
nightly linux64
65.0a1
/
20181117220111
/
pushlog to previous
nightly mac
65.0a1
/
20181117220111
/
pushlog to previous
nightly win32
65.0a1
/
20181117220111
/
pushlog to previous
nightly win64
65.0a1
/
20181117220111
/
pushlog to previous
|
--- a/browser/base/content/browser-contentblocking.js +++ b/browser/base/content/browser-contentblocking.js @@ -3,32 +3,46 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var TrackingProtection = { reportBreakageLabel: "trackingprotection", telemetryIdentifier: "tp", PREF_ENABLED_GLOBALLY: "privacy.trackingprotection.enabled", PREF_ENABLED_IN_PRIVATE_WINDOWS: "privacy.trackingprotection.pbmode.enabled", PREF_UI_ENABLED: "browser.contentblocking.trackingprotection.control-center.ui.enabled", + PREF_TRACKING_TABLE: "urlclassifier.trackingTable", + PREF_TRACKING_ANNOTATION_TABLE: "urlclassifier.trackingAnnotationTable", enabledGlobally: false, enabledInPrivateWindows: false, get categoryItem() { delete this.categoryItem; return this.categoryItem = document.getElementById("identity-popup-content-blocking-category-tracking-protection"); }, + get subViewList() { + delete this.subViewList; + return this.subViewList = document.getElementById("identity-popup-trackersView-list"); + }, + + get strictInfo() { + delete this.strictInfo; + return this.strictInfo = document.getElementById("identity-popup-trackersView-strict-info"); + }, + init() { this.updateEnabled(); Services.prefs.addObserver(this.PREF_ENABLED_GLOBALLY, this); Services.prefs.addObserver(this.PREF_ENABLED_IN_PRIVATE_WINDOWS, this); XPCOMUtils.defineLazyPreferenceGetter(this, "visible", this.PREF_UI_ENABLED, false); + XPCOMUtils.defineLazyPreferenceGetter(this, "trackingTable", this.PREF_TRACKING_TABLE, false); + XPCOMUtils.defineLazyPreferenceGetter(this, "trackingAnnotationTable", this.PREF_TRACKING_ANNOTATION_TABLE, false); }, uninit() { Services.prefs.removeObserver(this.PREF_ENABLED_GLOBALLY, this); Services.prefs.removeObserver(this.PREF_ENABLED_IN_PRIVATE_WINDOWS, this); }, observe() { @@ -46,16 +60,96 @@ var TrackingProtection = { Services.prefs.getBoolPref(this.PREF_ENABLED_GLOBALLY); this.enabledInPrivateWindows = Services.prefs.getBoolPref(this.PREF_ENABLED_IN_PRIVATE_WINDOWS); }, isBlockerActivated(state) { return state & Ci.nsIWebProgressListener.STATE_BLOCKED_TRACKING_CONTENT; }, + + isAllowing(state) { + return state & Ci.nsIWebProgressListener.STATE_LOADED_TRACKING_CONTENT; + }, + + async updateSubView() { + let previousURI = gBrowser.currentURI.spec; + let previousWindow = gBrowser.selectedBrowser.innerWindowID; + + let contentBlockingLogJSON = await gBrowser.selectedBrowser.getContentBlockingLog(); + let contentBlockingLog = JSON.parse(contentBlockingLogJSON); + + // Don't tell the user to turn on TP if they are already blocking trackers. + this.strictInfo.hidden = this.enabled; + + let fragment = document.createDocumentFragment(); + for (let [origin, actions] of Object.entries(contentBlockingLog)) { + let listItem = await this._createListItem(origin, actions); + if (listItem) { + fragment.appendChild(listItem); + } + } + + // This might have taken a while. Only update the list if we're still on the same page. + if (previousURI == gBrowser.currentURI.spec && + previousWindow == gBrowser.selectedBrowser.innerWindowID) { + this.subViewList.textContent = ""; + this.subViewList.append(fragment); + } + }, + + // Given a URI from a source that was tracking-annotated, figure out + // if it's really on the tracking table or just on the annotation table. + _isOnTrackingTable(uri) { + if (this.trackingTable == this.trackingAnnotationTable) { + return true; + } + return new Promise(resolve => { + classifierService.asyncClassifyLocalWithTables(uri, this.trackingTable, [], [], + (code, list) => resolve(!!list)); + }); + }, + + async _createListItem(origin, actions) { + // Figure out if this list entry was actually detected by TP or something else. + let isDetected = false; + let isAllowed = false; + for (let [state] of actions) { + isAllowed = isAllowed || this.isAllowing(state); + isDetected = isDetected || isAllowed || this.isBlockerActivated(state); + } + + if (!isDetected) { + return null; + } + + let uri = Services.io.newURI(origin); + + // Because we might use different lists for annotation vs. blocking, we + // need to make sure that this is a tracker that we would actually have blocked + // before showing it to the user. + let isTracker = await this._isOnTrackingTable(uri); + if (!isTracker) { + return null; + } + + let listItem = document.createXULElement("hbox"); + listItem.className = "identity-popup-trackersView-list-item"; + listItem.classList.toggle("allowed", isAllowed); + + let image = document.createXULElement("image"); + listItem.append(image); + + let label = document.createXULElement("label"); + label.value = uri.host; + label.setAttribute("crop", "end"); + listItem.append(label); + + return listItem; + }, }; var ThirdPartyCookies = { telemetryIdentifier: "cr", PREF_ENABLED: "network.cookie.cookieBehavior", PREF_REPORT_BREAKAGE_ENABLED: "browser.contentblocking.rejecttrackers.reportBreakage.enabled", PREF_ENABLED_VALUES: [ // These values match the ones exposed under the Content Blocking section @@ -142,16 +236,21 @@ var ContentBlocking = { return this.PREF_INTRO_COUNT_CB; }, get appMenuLabel() { delete this.appMenuLabel; return this.appMenuLabel = document.getElementById("appMenu-tp-label"); }, + get identityPopup() { + delete this.identityPopup; + return this.identityPopup = document.getElementById("identity-popup"); + }, + strings: { get appMenuTitle() { delete this.appMenuTitle; return this.appMenuTitle = gNavigatorBundle.getString("contentBlocking.title"); }, get appMenuTooltip() { @@ -233,30 +332,30 @@ var ContentBlocking = { blocker.uninit(); } } Services.prefs.removeObserver(this.PREF_ANIMATIONS_ENABLED, this.updateAnimationsEnabled); }, hideIdentityPopupAndReload() { - document.getElementById("identity-popup").hidePopup(); + this.identityPopup.hidePopup(); BrowserReload(); }, openPreferences(origin) { openPreferences("privacy-trackingprotection", { origin }); }, backToMainView() { this.identityPopupMultiView.goBack(); }, submitBreakageReport() { - document.getElementById("identity-popup").hidePopup(); + this.identityPopup.hidePopup(); let reportEndpoint = Services.prefs.getStringPref(this.PREF_REPORT_BREAKAGE_URL); if (!reportEndpoint) { return; } let formData = new FormData(); formData.set("title", this.reportURI.host); @@ -309,16 +408,21 @@ var ContentBlocking = { // Save this URI to make sure that the user really only submits the location // they see in the report breakage dialog. this.reportURI = gBrowser.currentURI; let urlWithoutQuery = this.reportURI.asciiSpec.replace("?" + this.reportURI.query, ""); this.reportBreakageURL.textContent = urlWithoutQuery; this.identityPopupMultiView.showSubView("identity-popup-breakageReportView"); }, + async showTrackersSubview() { + await TrackingProtection.updateSubView(); + this.identityPopupMultiView.showSubView("identity-popup-trackersView"); + }, + shieldHistogramAdd(value) { if (PrivateBrowsingUtils.isWindowPrivate(window)) { return; } Services.telemetry.getHistogramById("TRACKING_PROTECTION_SHIELD").add(value); }, onSecurityChange(oldState, state, webProgress, isSimulated,
--- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -138,16 +138,17 @@ XPCOMUtils.defineLazyScriptGetter(this, if (AppConstants.NIGHTLY_BUILD) { XPCOMUtils.defineLazyScriptGetter(this, "gWebRender", "chrome://browser/content/browser-webrender.js"); } // lazy service getters XPCOMUtils.defineLazyServiceGetters(this, { + classifierService: ["@mozilla.org/url-classifier/dbservice;1", "nsIURIClassifier"], Favicons: ["@mozilla.org/browser/favicon-service;1", "nsIFaviconService"], gAboutNewTabService: ["@mozilla.org/browser/aboutnewtab-service;1", "nsIAboutNewTabService"], gDNSService: ["@mozilla.org/network/dns-service;1", "nsIDNSService"], gSerializationHelper: ["@mozilla.org/network/serialization-helper;1", "nsISerializationHelper"], Marionette: ["@mozilla.org/remote/marionette;1", "nsIMarionette"], WindowsUIUtils: ["@mozilla.org/windows-ui-utils;1", "nsIWindowsUIUtils"], });
--- a/browser/base/content/test/trackingUI/browser.ini +++ b/browser/base/content/test/trackingUI/browser.ini @@ -18,8 +18,9 @@ support-files = file_trackingUI_fetch.js file_trackingUI_fetch.js^headers^ [browser_trackingUI_open_preferences.js] [browser_trackingUI_pbmode_exceptions.js] [browser_trackingUI_report_breakage.js] [browser_trackingUI_state.js] [browser_trackingUI_state_all_disabled.js] [browser_trackingUI_telemetry.js] +[browser_trackingUI_trackers_subview.js]
--- a/browser/base/content/test/trackingUI/browser_trackingUI_pbmode_exceptions.js +++ b/browser/base/content/test/trackingUI/browser_trackingUI_pbmode_exceptions.js @@ -54,19 +54,19 @@ function testTrackingPage(window) { ok(!hidden("#tracking-action-unblock"), "unblockButton is visible"); ok(hidden("#tracking-action-unblock-private"), "unblockButtonPrivate is hidden"); } ok(hidden("#identity-popup-content-blocking-not-detected"), "blocking not detected label is hidden"); ok(!hidden("#identity-popup-content-blocking-detected"), "blocking detected label is visible"); ok(!hidden("#identity-popup-content-blocking-category-list"), "category list is visible"); - ok(hidden("#identity-popup-content-blocking-category-tracking-protection > .identity-popup-content-blocking-category-add-blocking"), - "TP category item is not showing add blocking"); - ok(!hidden("#identity-popup-content-blocking-category-tracking-protection > .identity-popup-content-blocking-category-state-label"), + ok(hidden("#identity-popup-content-blocking-category-tracking-protection > #identity-popup-content-blocking-tracking-protection-label-allowed"), + "TP category item is not showing the allowed label"); + ok(!hidden("#identity-popup-content-blocking-category-tracking-protection > #identity-popup-content-blocking-tracking-protection-label-blocked"), "TP category item is set to blocked"); } function testTrackingPageUnblocked() { info("Tracking content must be white-listed and not blocked"); ok(ContentBlocking.content.hasAttribute("detected"), "trackers are detected"); ok(ContentBlocking.content.hasAttribute("hasException"), "content shows exception"); @@ -79,19 +79,19 @@ function testTrackingPageUnblocked() { ok(!hidden("#tracking-action-block"), "blockButton is visible"); ok(hidden("#tracking-action-unblock"), "unblockButton is hidden"); ok(!hidden("#identity-popup-content-blocking-disabled-label"), "disabled label is visible"); ok(hidden("#identity-popup-content-blocking-not-detected"), "blocking not detected label is hidden"); ok(!hidden("#identity-popup-content-blocking-detected"), "blocking detected label is visible"); ok(!hidden("#identity-popup-content-blocking-category-list"), "category list is visible"); - ok(hidden("#identity-popup-content-blocking-category-tracking-protection > .identity-popup-content-blocking-category-add-blocking"), - "TP category item is not showing add blocking"); - ok(hidden("#identity-popup-content-blocking-category-tracking-protection > .identity-popup-content-blocking-category-state-label"), + ok(!hidden("#identity-popup-content-blocking-category-tracking-protection > #identity-popup-content-blocking-tracking-protection-label-allowed"), + "TP category item is showing the allowed label"); + ok(hidden("#identity-popup-content-blocking-category-tracking-protection > #identity-popup-content-blocking-tracking-protection-label-blocked"), "TP category item is not set to blocked"); } add_task(async function testExceptionAddition() { await UrlClassifierTestUtils.addTestTrackers(); let privateWin = await BrowserTestUtils.openNewBrowserWindow({private: true}); browser = privateWin.gBrowser; let tab = await BrowserTestUtils.openNewForegroundTab({ gBrowser: browser, waitForLoad: true, waitForStateStop: true });
--- a/browser/base/content/test/trackingUI/browser_trackingUI_state.js +++ b/browser/base/content/test/trackingUI/browser_trackingUI_state.js @@ -129,30 +129,36 @@ function testTrackingPage(window) { is(!hidden("#tracking-action-unblock"), blockedByTP, "unblockButton is" + (blockedByTP ? "" : " not") + " visible"); } ok(hidden("#identity-popup-content-blocking-not-detected"), "blocking not detected label is hidden"); ok(!hidden("#identity-popup-content-blocking-detected"), "blocking detected label is visible"); ok(!hidden("#identity-popup-content-blocking-category-list"), "category list is visible"); - let category = Services.prefs.getIntPref(TPC_PREF) == Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER ? - "#identity-popup-content-blocking-category-3rdpartycookies" : - "#identity-popup-content-blocking-category-tracking-protection"; - is(hidden(category + " > .identity-popup-content-blocking-category-add-blocking"), blockedByTP, - "Category item is" + (blockedByTP ? " not" : "") + " showing add blocking"); - is(hidden(category + " > .identity-popup-content-blocking-category-state-label"), !blockedByTP, - "Category item is" + (blockedByTP ? "" : " not") + " set to blocked"); - if (Services.prefs.getIntPref(TPC_PREF) == Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER) { + let cookiesBlocked = Services.prefs.getIntPref(TPC_PREF) == Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER; + if (cookiesBlocked) { + let category = "#identity-popup-content-blocking-category-3rdpartycookies"; + is(hidden(category + " > .identity-popup-content-blocking-category-add-blocking"), blockedByTP, + "Category item is" + (blockedByTP ? " not" : "") + " showing add blocking"); + is(hidden(category + " > .identity-popup-content-blocking-category-state-label"), !blockedByTP, + "Category item is" + (blockedByTP ? "" : " not") + " set to blocked"); + ok(hidden("#identity-popup-content-blocking-category-label-default"), "Not showing default cookie restrictions label."); ok(!hidden("#identity-popup-content-blocking-category-label-trackers"), "Showing trackers cookie restrictions label."); } else { + let category = "#identity-popup-content-blocking-category-tracking-protection"; + is(hidden(category + " > #identity-popup-content-blocking-tracking-protection-label-allowed"), blockedByTP, + "Category item is" + (blockedByTP ? " not" : "") + " showing the allowed label"); + is(!hidden(category + " > #identity-popup-content-blocking-tracking-protection-label-blocked"), blockedByTP, + "Category item is" + (blockedByTP ? "" : " not") + " set to blocked"); + ok(hidden("#identity-popup-content-blocking-category-label-trackers"), "Not showing trackers cookie restrictions label."); ok(!hidden("#identity-popup-content-blocking-category-label-default"), "Showing default cookie restrictions label."); } } function testTrackingPageUnblocked(blockedByTP, window) { @@ -170,24 +176,32 @@ function testTrackingPageUnblocked(block ok(!hidden("#tracking-action-block"), "blockButton is visible"); ok(hidden("#tracking-action-unblock"), "unblockButton is hidden"); ok(!hidden("#identity-popup-content-blocking-disabled-label"), "disabled label is visible"); ok(hidden("#identity-popup-content-blocking-not-detected"), "blocking not detected label is hidden"); ok(!hidden("#identity-popup-content-blocking-detected"), "blocking detected label is visible"); ok(!hidden("#identity-popup-content-blocking-category-list"), "category list is visible"); - let category = Services.prefs.getIntPref(TPC_PREF) == Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER ? - "#identity-popup-content-blocking-category-3rdpartycookies" : - "#identity-popup-content-blocking-category-tracking-protection"; - is(hidden(category + " > .identity-popup-content-blocking-category-add-blocking"), blockedByTP, - "Category item is" + (blockedByTP ? " not" : "") + " showing add blocking"); - // Always hidden no matter if blockedByTP or not, since we have an exception. - ok(hidden("#identity-popup-content-blocking-category-tracking-protection > .identity-popup-content-blocking-category-state-label"), - "TP category item is not set to blocked"); + + let cookiesBlocked = Services.prefs.getIntPref(TPC_PREF) == Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER; + if (cookiesBlocked) { + let category = "#identity-popup-content-blocking-category-3rdpartycookies"; + is(hidden(category + " > .identity-popup-content-blocking-category-add-blocking"), blockedByTP, + "Category item is" + (blockedByTP ? " not" : "") + " showing add blocking"); + ok(!hidden("#identity-popup-content-blocking-category-tracking-protection > #identity-popup-content-blocking-tracking-protection-label-allowed"), + "TP category item is showing the allowed label"); + } else { + let category = "#identity-popup-content-blocking-category-tracking-protection"; + // If there's an exception we always show the "Allowed" label. + ok(!hidden(category + " > #identity-popup-content-blocking-tracking-protection-label-allowed"), + "Category item is showing the allowed label"); + ok(hidden(category + " > #identity-popup-content-blocking-tracking-protection-label-blocked"), + "Category item is not set to blocked"); + } } async function testContentBlocking(tab) { info("Testing with Tracking Protection ENABLED."); info("Load a test page not containing tracking elements"); await promiseTabLoadEvent(tab, BENIGN_PAGE); testBenignPage();
new file mode 100644 --- /dev/null +++ b/browser/base/content/test/trackingUI/browser_trackingUI_trackers_subview.js @@ -0,0 +1,118 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TRACKING_PAGE = "http://tracking.example.org/browser/browser/base/content/test/trackingUI/trackingPage.html"; + +const TP_PREF = "privacy.trackingprotection.enabled"; + +add_task(async function setup() { + await UrlClassifierTestUtils.addTestTrackers(); +}); + +function openIdentityPopup() { + let mainView = document.getElementById("identity-popup-mainView"); + let viewShown = BrowserTestUtils.waitForEvent(mainView, "ViewShown"); + gIdentityHandler._identityBox.click(); + return viewShown; +} + +function waitForSecurityChange(blocked) { + return new Promise(resolve => { + let webProgressListener = { + onStateChange: () => {}, + onStatusChange: () => {}, + onLocationChange: () => {}, + onSecurityChange: (webProgress, request, oldState, state) => { + if ((!blocked && state & Ci.nsIWebProgressListener.STATE_LOADED_TRACKING_CONTENT) || + (blocked && state & Ci.nsIWebProgressListener.STATE_BLOCKED_TRACKING_CONTENT)) { + gBrowser.removeProgressListener(webProgressListener); + resolve(); + } + }, + onProgressChange: () => {}, + QueryInterface: ChromeUtils.generateQI([Ci.nsIWebProgressListener]), + }; + + gBrowser.addProgressListener(webProgressListener); + }); +} + +async function assertSitesListed(blocked) { + await BrowserTestUtils.withNewTab(TRACKING_PAGE, async function(browser) { + await openIdentityPopup(); + + let categoryItem = + document.getElementById("identity-popup-content-blocking-category-tracking-protection"); + ok(BrowserTestUtils.is_visible(categoryItem), "TP category item is visible"); + let trackersView = document.getElementById("identity-popup-trackersView"); + let viewShown = BrowserTestUtils.waitForEvent(trackersView, "ViewShown"); + categoryItem.click(); + await viewShown; + + ok(true, "Trackers view was shown"); + + let listItems = document.querySelectorAll(".identity-popup-trackersView-list-item"); + is(listItems.length, 1, "We have 1 tracker in the list"); + + let strictInfo = document.getElementById("identity-popup-trackersView-strict-info"); + is(BrowserTestUtils.is_hidden(strictInfo), Services.prefs.getBoolPref(TP_PREF), + "Strict info is hidden if TP is enabled."); + + let mainView = document.getElementById("identity-popup-mainView"); + viewShown = BrowserTestUtils.waitForEvent(mainView, "ViewShown"); + let backButton = trackersView.querySelector(".subviewbutton-back"); + backButton.click(); + await viewShown; + + ok(true, "Main view was shown"); + + let change = waitForSecurityChange(blocked); + + await ContentTask.spawn(browser, {}, function() { + content.postMessage("more-tracking", "*"); + }); + + await change; + + viewShown = BrowserTestUtils.waitForEvent(trackersView, "ViewShown"); + categoryItem.click(); + await viewShown; + + ok(true, "Trackers view was shown"); + + listItems = Array.from(document.querySelectorAll(".identity-popup-trackersView-list-item")); + is(listItems.length, 2, "We have 2 trackers in the list"); + + let listItem = listItems.find(item => item.querySelector("label").value == "trackertest.org"); + ok(listItem, "Has an item for trackertest.org"); + ok(BrowserTestUtils.is_visible(listItem), "List item is visible"); + is(listItem.classList.contains("allowed"), !blocked, + "Indicates whether the tracker was blocked or allowed"); + + listItem = listItems.find(item => item.querySelector("label").value == "itisatracker.org"); + ok(listItem, "Has an item for itisatracker.org"); + ok(BrowserTestUtils.is_visible(listItem), "List item is visible"); + is(listItem.classList.contains("allowed"), !blocked, + "Indicates whether the tracker was blocked or allowed"); + }); +} + +add_task(async function testTrackersSubView() { + Services.prefs.setBoolPref(TP_PREF, false); + await assertSitesListed(false); + Services.prefs.setBoolPref(TP_PREF, true); + await assertSitesListed(true); + let uri = Services.io.newURI("https://tracking.example.org"); + Services.perms.add(uri, "trackingprotection", Services.perms.ALLOW_ACTION); + await assertSitesListed(false); + Services.perms.remove(uri, "trackingprotection"); + await assertSitesListed(true); + Services.prefs.clearUserPref(TP_PREF); +}); + +add_task(function cleanup() { + Services.prefs.clearUserPref(TP_PREF); + UrlClassifierTestUtils.cleanupTestTrackers(); +});
--- a/browser/base/content/test/trackingUI/trackingAPI.js +++ b/browser/base/content/test/trackingUI/trackingAPI.js @@ -1,16 +1,22 @@ onmessage = event => { switch (event.data) { case "tracking": { let ifr = document.createElement("iframe"); ifr.src = "https://trackertest.org/"; document.body.appendChild(ifr); } break; + case "more-tracking": { + let ifr = document.createElement("iframe"); + ifr.src = "https://itisatracker.org/"; + document.body.appendChild(ifr); + } + break; case "cookie": { let ifr = document.createElement("iframe"); ifr.src = "https://trackertest.org/browser/browser/base/content/test/trackingUI/cookieServer.sjs"; document.body.appendChild(ifr); } break; } };
--- a/browser/components/controlcenter/content/panel.inc.xul +++ b/browser/components/controlcenter/content/panel.inc.xul @@ -77,25 +77,26 @@ </hbox> <description id="identity-popup-content-blocking-detected" crop="end">&contentBlocking.detected;</description> <description id="identity-popup-content-blocking-not-detected" crop="end">&contentBlocking.notDetected;</description> <vbox id="identity-popup-content-blocking-category-list"> - <hbox id="identity-popup-content-blocking-category-tracking-protection" - class="identity-popup-content-blocking-category" align="center" role="group"> + <toolbarbutton id="identity-popup-content-blocking-category-tracking-protection" + onclick="ContentBlocking.showTrackersSubview()" + class="identity-popup-content-blocking-category" align="center"> <image class="identity-popup-content-blocking-category-icon tracking-protection-icon"/> <label flex="1" class="identity-popup-content-blocking-category-label">&contentBlocking.trackingProtection3.label;</label> - <label flex="1" class="identity-popup-content-blocking-category-state-label">&contentBlocking.trackingProtection.blocking.label;</label> - <label flex="1" class="identity-popup-content-blocking-category-add-blocking text-link" - id="identity-popup-tracking-protection-add-blocking" - onclick="ContentBlocking.openPreferences('identityPopup-CB-tracking-protection'); gIdentityHandler.recordClick('tp_add_blocking');">&contentBlocking.trackingProtection.add.label;</label> - </hbox> + <label flex="1" id="identity-popup-content-blocking-tracking-protection-label-allowed" + class="identity-popup-content-blocking-category-state-label">&contentBlocking.trackingProtection.allowed.label;</label> + <label flex="1" id="identity-popup-content-blocking-tracking-protection-label-blocked" + class="identity-popup-content-blocking-category-state-label">&contentBlocking.trackingProtection.blocked.label;</label> + </toolbarbutton> <hbox id="identity-popup-content-blocking-category-3rdpartycookies" class="identity-popup-content-blocking-category" align="center" role="group"> <image class="identity-popup-content-blocking-category-icon thirdpartycookies-icon"/> <label flex="1" id="identity-popup-content-blocking-category-label-default" class="identity-popup-content-blocking-category-label">&contentBlocking.3rdPartyCookies.label;</label> <label flex="1" id="identity-popup-content-blocking-category-label-trackers" hidden="true" class="identity-popup-content-blocking-category-label">&contentBlocking.3rdPartyCookies.trackers.label;</label> <label flex="1" class="identity-popup-content-blocking-category-state-label">&contentBlocking.3rdPartyCookies.blocking.label;</label> @@ -244,16 +245,35 @@ <!-- More Security Information --> <button id="identity-popup-more-info" label="&identity.moreInfoLinkText2;" oncommand="gIdentityHandler.handleMoreInfoClick(event);"/> </vbox> </panelview> + <!-- Trackers SubView --> + <panelview id="identity-popup-trackersView" + role="document" + title="&contentBlocking.trackersView.label;" + descriptionheightworkaround="true"> + <vbox id="identity-popup-trackersView-list"> + </vbox> + <hbox id="identity-popup-trackersView-strict-info"> + <image/> + <label>&contentBlocking.trackersView.strictInfo.label;</label> + </hbox> + <vbox class="identity-popup-footer"> + <button id="identity-popup-trackersView-settings-button" + label="&contentBlocking.manageSettings.label;" + accesskey="&contentBlocking.manageSettings.accesskey;" + oncommand="ContentBlocking.openPreferences();"/> + </vbox> + </panelview> + <!-- Report Breakage SubView --> <panelview id="identity-popup-breakageReportView" title="&contentBlocking.breakageReportView.label;" descriptionheightworkaround="true"> <vbox id="identity-popup-breakageReportView-heading"> <description>&contentBlocking.breakageReportView2.description;</description> <label id="identity-popup-breakageReportView-learn-more" class="text-link">&contentBlocking.breakageReportView.learnMore;</label>
--- a/browser/locales/en-US/chrome/browser/browser.dtd +++ b/browser/locales/en-US/chrome/browser/browser.dtd @@ -965,34 +965,29 @@ you can use these alternative items. Oth <!ENTITY contentBlocking.title "Content Blocking"> <!ENTITY contentBlocking.detected "Blockable content detected on this site."> <!ENTITY contentBlocking.notDetected "No blockable content detected on this page."> <!ENTITY contentBlocking.disabled.label "Disabled"> <!ENTITY contentBlocking.disabled.tooltip "You have disabled Content Blocking."> <!ENTITY contentBlocking.exception.tooltip "You have disabled Content Blocking for this site."> -<!ENTITY contentBlocking.trackingProtection2.label "All Detected Trackers"> <!ENTITY contentBlocking.trackingProtection3.label "Trackers"> +<!-- LOCALIZATION NOTE (contentBlocking.trackingProtection.allowed.label): + This label signals that this type of content blocking is turned + OFF and is not blocking tracker content, so this is not + a positive thing. It forms the end of the (imaginary) sentence + "Trackers [are] Allowed"--> +<!ENTITY contentBlocking.trackingProtection.allowed.label "Allowed"> <!-- LOCALIZATION NOTE (contentBlocking.trackingProtection.blocked.label): This label signals that this type of content blocking is turned ON and is successfully blocking tracker content, so this is a positive thing. It forms the end of the (imaginary) sentence "Trackers [are] Blocked"--> <!ENTITY contentBlocking.trackingProtection.blocked.label "Blocked"> -<!-- LOCALIZATION NOTE (contentBlocking.tranckingProtection.blocking.label): - This label signals that this type of content blocking is turned - ON, so this is a positive thing. It forms the verb in the (imaginary) sentence - "Firefox is blocking Trackers"--> -<!ENTITY contentBlocking.trackingProtection.blocking.label "Blocking"> -<!-- LOCALIZATION NOTE (contentBlocking.trackingProtection.add.label): - This is displayed as a link to preferences, where the user can add - this specific type of content blocking. When this text is shown - the type of content blocking is currently not enabled. --> -<!ENTITY contentBlocking.trackingProtection.add.label "Add Blocking…"> <!ENTITY contentBlocking.3rdPartyCookies.label "Third-Party Cookies"> <!ENTITY contentBlocking.3rdPartyCookies.trackers.label "Tracking Cookies"> <!-- LOCALIZATION NOTE (contentBlocking.3rdPartyCookies.blocked.label): This label signals that this type of content blocking is turned ON and is successfully blocking third-party cookies, so this is a positive thing. It forms the end of the (imaginary) sentence "Third-Party Cookies [are] Blocked"--> @@ -1003,16 +998,22 @@ you can use these alternative items. Oth "Firefox is blocking Third-Party Cookies"--> <!ENTITY contentBlocking.3rdPartyCookies.blocking.label "Blocking"> <!-- LOCALIZATION NOTE (contentBlocking.3rdPartyCookies.add.label): This is displayed as a link to preferences, where the user can add this specific type of content blocking. When this text is shown the type of content blocking is currently not enabled. --> <!ENTITY contentBlocking.3rdPartyCookies.add.label "Add Blocking…"> +<!ENTITY contentBlocking.manageSettings.label "Manage Content Blocking"> +<!ENTITY contentBlocking.manageSettings.accesskey "M"> + +<!ENTITY contentBlocking.trackersView.label "Trackers"> +<!ENTITY contentBlocking.trackersView.strictInfo.label "To block all trackers, set content blocking to “Strict”."> + <!ENTITY contentBlocking.openBreakageReportView2.label "Report a problem"> <!ENTITY contentBlocking.breakageReportView.label "Report Problems"> <!ENTITY contentBlocking.breakageReportView2.description "Content blocking can cause problems with some websites. When you report problems, you’ll help make &brandShortName; better for everyone. (This will send a URL as well as information about your browser settings to Mozilla.)"> <!ENTITY contentBlocking.breakageReportView.learnMore "Learn More"> <!ENTITY contentBlocking.breakageReportView.collection.url.label "URL"> <!ENTITY contentBlocking.breakageReportView.collection.comments.label "What problems did you have? (Optional)"> <!ENTITY contentBlocking.breakageReportView.sendReport.label "Send Report"> <!ENTITY contentBlocking.breakageReportView.cancel.label "Cancel">
new file mode 100644 --- /dev/null +++ b/browser/themes/shared/controlcenter/info.svg @@ -0,0 +1,4 @@ +<!-- 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/. --> +<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill="context-fill" fill-rule="evenodd" d="M8 1a7 7 0 1 1-7 7 7 7 0 0 1 7-7zm0 3a1 1 0 1 1-1 1 1 1 0 0 1 1-1zm0 3a1 1 0 0 1 1 1v3a1 1 0 0 1-2 0V8a1 1 0 0 1 1-1z"></path></svg>
--- a/browser/themes/shared/controlcenter/panel.inc.css +++ b/browser/themes/shared/controlcenter/panel.inc.css @@ -4,16 +4,20 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ %endif /* Hide all conditional elements by default. */ :-moz-any([when-connection],[when-mixedcontent],[when-ciphers],[when-loginforms]) { display: none; } +#identity-popup { + --identity-popup-width: 33rem; +} + /* This is used by screenshots tests to hide intermittently different * identity popup shadows (see bug 1425253). */ #identity-popup.no-shadow { -moz-window-shadow: none; } /* Show the right elements for the right connection states. */ #identity-popup[connection=not-secure] [when-connection~=not-secure], @@ -63,18 +67,18 @@ padding: 0; /* Set default fill for icons in the identity popup. Individual icons can override this. */ fill: currentColor; fill-opacity: .6; } #identity-popup-mainView { - min-width: 33em; - max-width: 33em; + min-width: var(--identity-popup-width); + max-width: var(--identity-popup-width); } .identity-popup-section { border-top: 1px solid var(--panel-separator-color); } .identity-popup-security-content, #identity-popup-permissions-content, @@ -140,17 +144,22 @@ } .identity-popup-preferences-button > .toolbarbutton-text { display: none; } /* CONTENT */ +.identity-popup-footer, +.tracking-protection-button, +#identity-popup-trackersView-strict-info > label, +.identity-popup-trackersView-list-item > label, #identity-popup-mainView-panel-header > label, +#identity-popup-trackersView > .panel-header, #identity-popup-securityView > .panel-header, #identity-popup-breakageReportView > .panel-header, #identity-popup-content-blocking-report-breakage, #identity-popup-content-blocking-disabled-label, .identity-popup-content-blocking-category-label, .identity-popup-content-blocking-category-state-label, .identity-popup-content-blocking-category-add-blocking, .identity-popup-permission-label, @@ -173,17 +182,17 @@ #identity-popup-mainView-panel-header-span { display: inline-block; font-weight: 600; text-align: center; overflow-wrap: break-word; /* This is needed for the overflow-wrap to work correctly. * 33em is the panel width, panel-header has 1em padding on each side. */ - max-width: calc(33rem - 2em); + max-width: calc(var(--identity-popup-width) - 2em); } #identity-popup-permissions-content > description, #identity-popup-content-blocking-content > description { color: var(--panel-disabled-color); } /* This element needs the pre-wrap because we add newlines to it in the code. */ @@ -196,17 +205,17 @@ font-size: 150%; } #identity-popup-host { overflow-wrap: break-word; /* This is needed for the overflow-wrap to work correctly. * 1em + 2em + 24px is .identity-popup-security-content padding * 33em is the panel width */ - max-width: calc(33rem - 3rem - 24px); + max-width: calc(var(--identity-popup-width) - 3rem - 24px); } .identity-popup-warning-gray { padding-inline-start: 24px; background: url(chrome://browser/skin/controlcenter/warning.svg) no-repeat 0 50%; fill: #808080; stroke: #fff; -moz-context-properties: fill, stroke; @@ -377,16 +386,103 @@ description#identity-popup-content-verif #identity-popup-breakageReportView-collection-comments { height: 120px; } #identity-popup-content-blocking-content { background-image: url("chrome://browser/skin/controlcenter/tracking-protection.svg"); } +#identity-popup-content-blocking-category-tracking-protection { + /* Overwrite toolbarbutton styles */ + margin: 0; + padding-inline-start: 0; +} + +#identity-popup-content-blocking-category-tracking-protection:-moz-focusring, +#identity-popup-content-blocking-category-tracking-protection:hover { + border-radius: 2px; + background-color: var(--arrowpanel-dimmed-further); +} + +#identity-popup-content-blocking-category-tracking-protection:hover:active { + background-color: var(--arrowpanel-dimmed-even-further); +} + +#identity-popup-content-blocking-category-tracking-protection::after { + content: url(chrome://browser/skin/back-12.svg); + -moz-context-properties: fill, fill-opacity; + transform: scaleX(-1) translateY(1px); + float: right; +} + +#identity-popup-content-blocking-category-tracking-protection:-moz-locale-dir(rtl)::after { + transform: scaleX(1) translateY(1px); +} + +/* This subview could get filled with a lot of trackers, set a maximum size + * and allow it to scroll vertically.*/ +#identity-popup-trackersView { + max-height: 600px; +} + +#identity-popup-trackersView-list { + padding: 5px 20px; + -moz-box-flex: 1; + overflow: scroll; +} + +.identity-popup-trackersView-list-item { + margin: 5px 0; + overflow: hidden; +} + +.identity-popup-trackersView-list-item > label { + /* Limit to full width - container padding - icon width - icon margin */ + max-width: calc(var(--identity-popup-width) - 40px - 16px - 10px); +} + +.identity-popup-trackersView-list-item > image { + list-style-image: url(chrome://browser/skin/controlcenter/trackers-disabled.svg); + margin-inline-end: 10px; + -moz-context-properties: fill, fill-opacity; +} + +.identity-popup-trackersView-list-item.allowed > image { + list-style-image: url(chrome://browser/skin/controlcenter/trackers.svg); +} + +#identity-popup-trackersView-strict-info { + min-height: 40px; + /* Limit to full width - margin */ + max-width: calc(var(--identity-popup-width) - 12px); + min-width: calc(var(--identity-popup-width) - 12px); + background-color: #45a1ff80; + margin: 6px; + text-align: center; + -moz-box-align: center; + -moz-box-pack: center; + padding: 5px 15px; + border-radius: 3px; + color: #002275; +} + +#identity-popup-trackersView-strict-info > image { + list-style-image: url(chrome://browser/skin/controlcenter/info.svg); + -moz-context-properties: fill; + fill: currentColor; + margin-inline-end: 10px; +} + +#identity-popup-trackersView-strict-info > label { + overflow-wrap: break-word; + /* Limit to full width - container margin - container padding - icon width - icon margin */ + max-width: calc(var(--identity-popup-width) - 12px - 20px - 16px - 10px); +} + /* Disabled label */ #identity-popup-content-blocking-disabled-label { padding: 2px 5px; border-radius: 3px; margin: 5px; display: none; color: #fff; @@ -428,21 +524,30 @@ description#identity-popup-content-verif } /* Show the "detected"/"not detected" message depending on the content state. */ #identity-popup-content-blocking-content:not([detected]) > #identity-popup-content-blocking-detected, #identity-popup-content-blocking-content[detected] > #identity-popup-content-blocking-not-detected { display: none; } -#identity-popup-content-blocking-content[hasException] .identity-popup-content-blocking-category-state-label, -.identity-popup-content-blocking-category:not(.blocked) .identity-popup-content-blocking-category-state-label { +.identity-popup-content-blocking-category-state-label { display: none; } +/* TODO: This will be cleaned up by bug 1501992 */ +/* Hide the state label unless we blocked something only for third party cookies */ +#identity-popup-content-blocking-content:not([hasException]) #identity-popup-content-blocking-category-3rdpartycookies.blocked .identity-popup-content-blocking-category-state-label, +/* For trackers, either show a "blocked" or "allowed" label depending on the state. */ +#identity-popup-content-blocking-content:not([hasException]) #identity-popup-content-blocking-category-tracking-protection.blocked > #identity-popup-content-blocking-tracking-protection-label-blocked, +#identity-popup-content-blocking-category-tracking-protection:not(.blocked) > #identity-popup-content-blocking-tracking-protection-label-allowed, +#identity-popup-content-blocking-content[hasException] #identity-popup-content-blocking-tracking-protection-label-allowed { + display: -moz-box; +} + .identity-popup-content-blocking-category.blocked .identity-popup-content-blocking-category-add-blocking { display: none; } .tracking-protection-icon { list-style-image: url(chrome://browser/skin/controlcenter/trackers.svg); } @@ -463,16 +568,17 @@ description#identity-popup-content-verif .tracking-protection-button { list-style-image: url(chrome://browser/skin/tracking-protection.svg); -moz-appearance: none; margin: 1em 0 0; display: none; height: 32px; background-color: var(--arrowpanel-dimmed); color: inherit; + margin-inline-end: 8px; } .tracking-protection-button:hover { background-color: var(--arrowpanel-dimmed-further); } .tracking-protection-button:hover:active { background-color: var(--arrowpanel-dimmed-even-further); @@ -546,32 +652,41 @@ description#identity-popup-content-verif min-height: 24px; } #identity-popup-content-blocking-category-list, #identity-popup-permission-list { /* Offset the padding set on #identity-popup-permissions-content so that it shows up just below the section. The permission icons are 16px wide and should be right aligned with the section icon. */ - margin-inline-start: calc(-1em - 16px); + margin-inline-start: calc(-1em - 24px); } .identity-popup-content-blocking-category, .identity-popup-permission-item { min-height: 24px; } +.identity-popup-content-blocking-category { + padding-inline-end: 12px; +} + +.identity-popup-permission-item { + padding-inline-end: 8px; +} + #identity-popup-permission-list:not(:empty) { margin-top: 5px; } .identity-popup-content-blocking-category-icon, .identity-popup-permission-icon { width: 16px; height: 16px; + margin-inline-start: 12px; } .identity-popup-permission-icon.in-use { -moz-context-properties: fill; fill: rgb(224, 41, 29); animation: 1.5s ease in-use-blink infinite; }
--- a/browser/themes/shared/jar.inc.mn +++ b/browser/themes/shared/jar.inc.mn @@ -23,16 +23,17 @@ skin/classic/browser/addons/addon-install-installed.svg (../shared/addons/addon-install-installed.svg) skin/classic/browser/addons/addon-install-warning.svg (../shared/addons/addon-install-warning.svg) skin/classic/browser/controlcenter/3rdpartycookies.svg (../shared/controlcenter/3rdpartycookies.svg) skin/classic/browser/controlcenter/3rdpartycookies-disabled.svg (../shared/controlcenter/3rdpartycookies-disabled.svg) skin/classic/browser/controlcenter/conn-not-secure.svg (../shared/controlcenter/conn-not-secure.svg) skin/classic/browser/controlcenter/connection.svg (../shared/controlcenter/connection.svg) skin/classic/browser/controlcenter/mcb-disabled.svg (../shared/controlcenter/mcb-disabled.svg) skin/classic/browser/controlcenter/extension.svg (../shared/controlcenter/extension.svg) + skin/classic/browser/controlcenter/info.svg (../shared/controlcenter/info.svg) skin/classic/browser/controlcenter/permissions.svg (../shared/controlcenter/permissions.svg) skin/classic/browser/controlcenter/trackers.svg (../shared/controlcenter/trackers.svg) skin/classic/browser/controlcenter/trackers-disabled.svg (../shared/controlcenter/trackers-disabled.svg) skin/classic/browser/controlcenter/tracking-protection.svg (../shared/controlcenter/tracking-protection.svg) skin/classic/browser/controlcenter/warning.svg (../shared/controlcenter/warning.svg) skin/classic/browser/customizableui/empty-overflow-panel.png (../shared/customizableui/empty-overflow-panel.png) skin/classic/browser/customizableui/empty-overflow-panel@2x.png (../shared/customizableui/empty-overflow-panel@2x.png) skin/classic/browser/customizableui/density-compact.svg (../shared/customizableui/density-compact.svg)
index 4c453a7a9c928a97ab4ee4350298d709927494ce..cdbda27802a0b77d2d09e78becd64ffa26529d6c GIT binary patch literal 229376 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 z_ML~$-;J-wzF+2UMjowoL3zoVO}9+0o%0-Qyzl(^BOk4(!$-Xh)I2<?V;w0y?ry^j z!=p21g{w0ptVUM0Q8SX?ZDw@5K1*-(oH6_Sp>uSv(WQe~L$^Omd>|LO=w3Pg0XdGl z)={Obyc$ni-0HXQXtn;>vSFv`K2xc*sbh5Y20pk*Oxk$cq~Jub@yUBbbJra^CMUyO zu&CH%t(S9IyaDN<;hh=b8ms3Tgn1a}((cTh`^hu-?SrQMPZ*R4o#_$L>*_{s)VQ<r z?0nkt(3W?Gm8o1~HSRsUYe8_H!jAFW6ZkVH=MM_qG0{1HfxpqfS8V%<(l0H~NDkS( zo#<AwLjiyErPRyjn+wphulFO?Pn{m!Mt@m;J1n_NF}y!{PkHxv*Lml7$9eU<oxF{_ zYThDV8E+~tpErRwo|njr=8fPD;ra9W@|<|qJUmZ}r^u7wzURK=KH@fWFL6(C4{`T! zw{X|+7Y6_U0000000000000000000000000000000000000000000000000000000 z000000000000000000000000000000000000002szfKw_j`iGG?`kPXY`WKGAxLc8 zvd>(QSYO386D4v?1&OuzyC$N<9b-XaRgf57l)xGZ5-XZ_8;TOU3<Qa)loEYWVyd1X zu~a8TSCpXX2ojYW^R-2ZJS{<DAz4gQl)!2T5)00>tBVq^)C7sSZ*HiH64zA(iL!oP z%A$m)k{~g&EKO0A;3x<Z(-rFFMTz}#f<(!wzOtf39~nVn3O+zul<=1lBqpcaloTay zNC*-I@;c(8gf>o)$Ui?+ObRD1!%u|B@_X2lMZ~bYS$$c=^!)S$b`G1wBC!I}`=x7S zY|O|{6X#rHA7anvP2(AG*Kj>@4(CK<X-!x@!8vn(=Fp7m87k?OY0J_G918m->nSTI zn~_!2+s6z50000000000000000000000000000000000000000000000000000000 z0000000000000000000000000000000O(!fI1JVjomij~b3I%iETTbBLsTSj5#l&` z*$8?vosmw*`}vstMV{$+@mw+%Ng`Mw7J^JQqv4d4R0fm8%*6Xqm~3i1HIBrl;C(pk zL<W<}&Ln6fnrKN`HKiep2~--5gda{}v8nU~f*E3h=E$k(g)$lOR2l_8kV7R?$oQeD zY-$R10*Os!&`o~(8jb&={rJDgGqS@)VG)bZE5XU@`Ow%DCLP_l;Y<#T&0o)#jWqn3 z!Un&+5A=Wg8ufmrLv(+pu+DF9i}r6{qm~tJI2JJ>;1NS%hiI`Vw0L_KHGxhcJ5Vx6 zDXBDyLmVST^G6G6{Kb*3{ug;_zrF3MzkQ7=KXdpg|4d<}-`*C*-@ZnL-@Zoq-@Zn< z-@ZoK-@ZnfT(Wq6Fc!(ZMvy~f(DIUMViDn=pGd2T`S}nu5q0#5l$yd228YfjQR(;* zR7yI*6fqXATle$2?ePI4@MKCXCjrmqZw5b<5DXAK;by3ZQ{p)2Hsb^62~;|T!bG={ zh`0z_YR|7J+H|}Xe=X=)mCj(Y6Em&(i*clrQYc=OG<$Th_HiVF9bzLXV^a`V(4l~p z!D<@BC6bsl5}QQ#W(ybcbsUubf6+>*-+uH+{`NIW{Ps19|MoTFe)}55Qu%9?`K^(T ze>Y>9m4)FQ<1OQj=N52>agm&7IeT+v<uG&nb9A#$XOGU-&w7wmmo+OVo$Jb5nROt? zC}&>Qyqut%23}g$W9~@aschq%_U!Z7<G53@rex*w7Y6_U00000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z000000Km_Z!l`1HV<Ig@KP^N*&BbvTteFKa6pP>qhKRo4?|{o|i2g=Bh5Yqjnm0wu znwa8(v4{ad577~pRb?fT2o%cK1&z^ycn4ez7O^5&AZEgX245CxPsvE7GP^F^2(4r2 zhGSw80>Kfn7uK<$(&K0xGR2<7i6t{qNK`t@A(cv}Q|Sp`S2jQ^>;LvYpst6mR@YxV zR~CzS5&9w?!i{!{C6OI^-f(+zCY_W*jr+O*I_Q$L&2WRUh#^5A(G@O8t($(*LQ85| z;f7-o69OJF6qeLtQE2h@ENTM1YiSL%w7MRy4;IlNs39uC(y|fsWI7|Aj`#CXLkp;y z<3g~A5y1e_6BbbC?^7pNV%NQ@f)-V_!$n~ci_iUolh^a1u_;VC+ONZfd$+THmCzcB zeQ+6A#D(C5Ai^5fv77`Kg0lzW;gCVMw@=I<CB-IE5}ZgAoJlmntfUMQEtO15RY2>@ z>*GAJh$cZDQ5Dvg`)Zry&=RuW9ck(5=?<|ZdNKhmgAqO>+b3``(dRN~9qC-Mcs~C? z&b>yELuAlul4@cR;h&#KtBLvf5Ht~W^of+3!Vm`k;N|Z`{0J%~onVR>3lAIJ&xegY zK41i%OyRc-&*mRN{Eq$HnR06C;gmQI+8y`+dIFVBp)k<{jEJ}h4^n%6P0>+@w-TKq z=?o@2G1HpA7)So0Lh+)c*`tfKk0TN65F1Gun}WcC4h5_XR?{FZk;J5t*d)3)Tey&~ zcbOF0RLSplndktt@8(RBKx>E_;|5?6J%SFRC9I)5gb|lW6|^jlMy1f%IJB4;f{T|y zYzfwgrLZaZpp+DT1L<s9Cf=XQ5}ob*W}^=_zXxRbfyk1pj^R1;hH$s?Ht}+JbGc2t z`@A#UWxNFLeQq#MgImD;$aUey^KSBv@>cVv@i@F_UI5RQr@(#9y~#b!UB@lwa=GKU zzFa%58CRdbH~;_u00000000000000000000000000000000000000000000000000 z0000000000000000000000000000000000000000008)(Aca%KF2_V#ihf#%ewvGZ znknE6u^hVSZ^@gA=9;MCY_P0E5+RyGC-WcZAD1^4<>9q(PMtMKbTTC)mCB@$jYRo| z>Nq<ri%nuv<3g!)I+dQ_OClSHGWBI}n%L9?MjS~`G)Gqh=YS<=(n%>_m)8;HYRlnt zv7!}diRNi4;_z4&g%;1ECeSHl4bg0MbT)^cOlPFiqvJ?wqS>nG^7&11awUG%7!^^j zvI@=uOQNwUOgf1_VKCTAq8vpuCzg}oLU8s#JmM%!wt^@}9_=!I8(0)(8igq*nk$Qz zr?BEksT2~MO^Qn<$cS>JRdH5WN*a~UUqK3mOy#dgN|Yt3i9^0={6GeSEFsDkN7t0X zh)blhDJ(WlG*?U;N0gzaq)_Snw$L)8$yAnTXUOvRw`I-(46lWEkXOws;wAHj@Emz6 z+?U+*-0j@?+$`=`Zhx*Z7ngIFr^%hj_0MVKJ>VVVt>vZihVfjue{!#IYq^WKv0MwT zbk3uk&Ad|n;s5{u00000000000000000000000000000000000000000000000000 z0000000000000000000000000000000000000000007{>NE_#ZofW+3jJAUOs<Pxu z)ruSD-0=#NXtG2zva@RY{V~V<S#34Xen4V#qU)NCwiajxF`;r#{R7<l_FNt8lhR`I zYq<(B=HfUE)~u&$rf9B7Pr1fuF21K+BQ)2rr}Y}3x%xfT)kAZ2d#bC0=4$s;R}0P6 z>?v0R%~kIyR}IZo?I~9U%~kGctCY}O#h&Uapt<rr<;tPCvOVR>pt;gL?Q<zKSF)$N V5@@b?Pq{cWS4<1%BGX;&{{d`Z_=x}j
index 06c967ea7e9f3a56b1e5e8736f55a4402a41534a..fbdfa8c914a76beef0a1447cb45696e254af6bec GIT binary patch literal 294912 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 z9`Cb!?oNF4rLf)Q(lU>{U%B1iGSif)?$C9tJgy$WJAsH`@FTCCH9R+Mgo&>BH2QP{ z+4ty$X=QhE{TPGy6RD6|hcK+f_8o)dubVFx`IPeVxtQ&6G*^7lH=Dend6Utg@&291 z3o>lEyq$gMlec}XTVB1qz(k)>s{eW$n&wlC`1X^J-@novZj;Z`qw7KLvfd-pd(+F| zSjpCd|Nr!6CnDPR=zsT-8vp<R0RLA|Z@!VnOcBL=j#=D%2LJ#7000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000002|Ka7%;j9B?zqb%vx%I&H5B-=>IsFZKrc|^T=D{0IJqL{BSYn$%? z00000000000000000000000000000000000000000000000000000000000000000 z0000000000000000000000000000000D%7?$ViEZNq;U#NJ)vPsAKwwNTaogqP1ci zqa*)sX9EBL0000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000004mh=8&ZRRFYCs5|UyRq*i34wEP%v zC(j^fv{*opqqC0}3gaCh?u+sE#(2AmqeD?%zMg1tCm%1YGud%{VoB_4DH=(tpL<d; zkyu&$^8zIkiH4dKizM^U3)`5s8~Frz2W}pU>mUXlBE=)g{quTirrkz9;TTU(6qgv6 z)<G_3v}2GfS0FkdkZW^i4#_<~H=<!;wm>@t`C|gZxU{@oG2Upj|7H{^q@?UtUM8{4 zJuNr)*}RfVaPy=12L%L%`1l99g$ZpQM#3BAg;qfai~T&Tm=j7$R8p9NN;qCOer-E3 z6)_8klN-w46BUT^J`nipApiPFlKPQhorxvgr35AUe|<DgOSG4-k3Y&kj7tOUABb_m zIH3a3TxvmqZazOVO6`(l`SrQc|MPSEM>r{7NyM*DXpgDCj|;{V&7~8BaYj3HnfL}` zyfERYK#Y$!$>u#GO6Xlj9kWP88uO4SrYB}{^Bn*H0000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 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
index b79fb90c8518548dfb6fdf9d1c3a4a8ab8343058..01c1f53efb1b5068c0ca7282b5b6831f319d6c0c GIT binary patch literal 2448 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 Az5oCK
--- a/build/pgo/server-locations.txt +++ b/build/pgo/server-locations.txt @@ -170,16 +170,17 @@ https://sub.sectest1.example.org:443 # http://malware.example.com:80 http://unwanted.example.com:80 http://tracking.example.com:80 http://not-tracking.example.com:80 http://tracking.example.org:80 http://another-tracking.example.net:80 http://itisatracker.org:80 +https://itisatracker.org:443 http://trackertest.org:80 https://malware.example.com:443 https://unwanted.example.com:443 https://tracking.example.com:443 https://not-tracking.example.com:443 https://tracking.example.org:443 https://another-tracking.example.net:443
--- a/js/src/builtin/intl/CommonFunctions.js +++ b/js/src/builtin/intl/CommonFunctions.js @@ -1,30 +1,41 @@ /* 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/. */ /* Portions Copyright Norbert Lindenberg 2011-2012. */ +#ifdef DEBUG +#define assertIsValidAndCanonicalLanguageTag(locale, desc) \ + do { \ + let localeObj = parseLanguageTag(locale); \ + assert(localeObj !== null, \ + `${desc} is a structurally valid language tag`); \ + assert(CanonicalizeLanguageTagFromObject(localeObj) === locale, \ + `${desc} is a canonicalized language tag`); \ + } while (false) +#else +#define assertIsValidAndCanonicalLanguageTag(locale, desc) ; // Elided assertion. +#endif + /** * Returns the start index of a "Unicode locale extension sequence", which the * specification defines as: "any substring of a language tag that starts with * a separator '-' and the singleton 'u' and includes the maximum sequence of * following non-singleton subtags and their preceding '-' separators." * * Alternatively, this may be defined as: the components of a language tag that * match the extension production in RFC 5646, where the singleton component is * "u". * * Spec: ECMAScript Internationalization API Specification, 6.2.1. */ function startOfUnicodeExtensions(locale) { assert(typeof locale === "string", "locale is a string"); - assert(IsStructurallyValidLanguageTag(locale), "locale is a language tag"); - assert(CanonicalizeLanguageTag(locale) === locale, "locale is a canonicalized language tag"); #define HYPHEN 0x2D assert(std_String_fromCharCode(HYPHEN) === "-", "code unit constant should match the expected character"); // A wholly-privateuse or grandfathered locale has no extension sequences. if (callFunction(std_String_charCodeAt, locale, 1) === HYPHEN) { assert(locale[0] === "x" || locale[0] === "i", @@ -48,18 +59,16 @@ function startOfUnicodeExtensions(locale return start; } /** * Returns the end index of a Unicode locale extension sequence. */ function endOfUnicodeExtensions(locale, start) { assert(typeof locale === "string", "locale is a string"); - assert(IsStructurallyValidLanguageTag(locale), "locale is a language tag"); - assert(CanonicalizeLanguageTag(locale) === locale, "locale is a canonicalized language tag"); assert(0 <= start && start < locale.length, "start is an index into locale"); assert(Substring(locale, start, 3) === "-u-", "start points to Unicode extension sequence"); #define HYPHEN 0x2D assert(std_String_fromCharCode(HYPHEN) === "-", "code unit constant should match the expected character"); // Search for the start of the next singleton or privateuse subtag. @@ -86,38 +95,41 @@ function endOfUnicodeExtensions(locale, // sequence extends until the end of the string. return locale.length; } /** * Removes Unicode locale extension sequences from the given language tag. */ function removeUnicodeExtensions(locale) { + assertIsValidAndCanonicalLanguageTag(locale, "locale with possible Unicode extension"); + var start = startOfUnicodeExtensions(locale); if (start < 0) return locale; var end = endOfUnicodeExtensions(locale, start); var left = Substring(locale, 0, start); var right = Substring(locale, end, locale.length - end); var combined = left + right; - assert(IsStructurallyValidLanguageTag(combined), - "recombination produced an invalid language tag"); + assertIsValidAndCanonicalLanguageTag(combined, "the recombined locale"); assert(startOfUnicodeExtensions(combined) < 0, "recombination failed to remove all Unicode locale extension sequences"); return combined; } /** * Returns Unicode locale extension sequences from the given language tag. */ function getUnicodeExtensions(locale) { + assertIsValidAndCanonicalLanguageTag(locale, "locale with Unicode extension"); + var start = startOfUnicodeExtensions(locale); assert(start >= 0, "start of Unicode extension sequence not found"); var end = endOfUnicodeExtensions(locale, start); return Substring(locale, start, end - start); } /* eslint-disable complexity */ @@ -787,18 +799,17 @@ function DefaultLocaleIgnoringAvailableL if (hasOwn(candidate, oldStyleLanguageTagMappings)) candidate = oldStyleLanguageTagMappings[candidate]; } // Cache the candidate locale until the runtime default locale changes. localeCandidateCache.candidateDefaultLocale = candidate; localeCandidateCache.runtimeDefaultLocale = runtimeDefaultLocale; - assert(IsStructurallyValidLanguageTag(candidate), - "the candidate must be structurally valid"); + assertIsValidAndCanonicalLanguageTag(candidate, "the candidate locale"); assert(startOfUnicodeExtensions(candidate) < 0, "the candidate must not contain a Unicode extension sequence"); return candidate; } /** * Returns the BCP 47 language tag for the host environment's current locale. @@ -825,20 +836,17 @@ function DefaultLocale() { dateTimeFormatInternalProperties), candidate)) { locale = candidate; } else { locale = lastDitchLocale(); } - assert(IsStructurallyValidLanguageTag(locale), - "the computed default locale must be structurally valid"); - assert(locale === CanonicalizeLanguageTag(locale), - "the computed default locale must be canonical"); + assertIsValidAndCanonicalLanguageTag(locale, "the computed default locale"); assert(startOfUnicodeExtensions(locale) < 0, "the computed default locale must not contain a Unicode extension sequence"); localeCache.defaultLocale = locale; localeCache.runtimeDefaultLocale = runtimeDefaultLocale; return locale; } @@ -919,18 +927,17 @@ function CanonicalizeLocaleList(locales) k++; } // Step 8. return seen; } function BestAvailableLocaleHelper(availableLocales, locale, considerDefaultLocale) { - assert(IsStructurallyValidLanguageTag(locale), "invalid BestAvailableLocale locale structure"); - assert(locale === CanonicalizeLanguageTag(locale), "non-canonical BestAvailableLocale locale"); + assertIsValidAndCanonicalLanguageTag(locale, "BestAvailableLocale locale"); assert(startOfUnicodeExtensions(locale) < 0, "locale must contain no Unicode extensions"); // In the spec, [[availableLocales]] is formally a list of all available // locales. But in our implementation, it's an *incomplete* list, not // necessarily including the default locale (and all locales implied by it, // e.g. "de" implied by "de-CH"), if that locale isn't in every // [[availableLocales]] list (because that locale is supported through // fallback, e.g. "de-CH" supported through "de"). @@ -1244,22 +1251,19 @@ function ResolveLocale(availableLocales, if (privateIndex === -1) { foundLocale += supportedExtension; } else { var preExtension = callFunction(String_substring, foundLocale, 0, privateIndex); var postExtension = callFunction(String_substring, foundLocale, privateIndex); foundLocale = preExtension + supportedExtension + postExtension; } - // Step 9.d. - assert(IsStructurallyValidLanguageTag(foundLocale), "invalid locale after concatenation"); - - // Step 9.e (Not required in this implementation, because we don't - // canonicalize Unicode extension subtags). - assert(foundLocale === CanonicalizeLanguageTag(foundLocale), "same locale with extension"); + // Steps 9.d-e (Step 9.e is not required in this implementation, + // because we don't canonicalize Unicode extension subtags). + assertIsValidAndCanonicalLanguageTag(foundLocale, "locale after concatenation"); } // Step 10. result.locale = foundLocale; // Step 11. return result; }
--- a/js/src/gc/GC.cpp +++ b/js/src/gc/GC.cpp @@ -2459,23 +2459,31 @@ RelocateArena(Arena* arena, SliceBudget& TenuredCell* dest = Forwarded(src); MOZ_ASSERT(src->isMarkedBlack() == dest->isMarkedBlack()); MOZ_ASSERT(src->isMarkedGray() == dest->isMarkedGray()); } #endif } static inline bool +CanProtectArenas() +{ + // On some systems the page size is larger than the size of an arena so we + // can't change the mapping permissions per arena. + return SystemPageSize() <= ArenaSize; +} + +static inline bool ShouldProtectRelocatedArenas(JS::gcreason::Reason reason) { // For zeal mode collections we don't release the relocated arenas // immediately. Instead we protect them and keep them around until the next // collection so we can catch any stray accesses to them. #ifdef DEBUG - return reason == JS::gcreason::DEBUG_GC; + return reason == JS::gcreason::DEBUG_GC && CanProtectArenas(); #else return false; #endif } /* * Relocate all arenas identified by pickArenasToRelocate: for each arena, * relocate each cell within it, then add it to a list of relocated arenas. @@ -2973,40 +2981,39 @@ GCRuntime::updateCellPointers(Zone* zone // - Updating a typed object makes use of its type descriptor object // // This means we require at least three phases for update: // // 1) shapes // 2) typed object type descriptor objects // 3) all other objects // -// Also, JSScripts and LazyScripts can have pointers to each other. Each can be -// updated safely without requiring the referent to be up-to-date, but TSAN can -// warn about data races when calling IsForwarded() on the new location of a -// cell that is being updated in parallel. To avoid this, we update these in -// separate phases. +// Also, there can be data races calling IsForwarded() on the new location of a +// cell that is being updated in parallel on another thread. This can be avoided +// by updating some kinds of cells in different phases. This is done for JSScripts +// and LazyScripts, and JSScripts and Scopes. // // Since we want to minimize the number of phases, arrange kinds into three // arbitrary phases. static const AllocKinds UpdatePhaseOne { AllocKind::SCRIPT, AllocKind::BASE_SHAPE, AllocKind::SHAPE, AllocKind::ACCESSOR_SHAPE, AllocKind::OBJECT_GROUP, AllocKind::STRING, - AllocKind::JITCODE, - AllocKind::SCOPE + AllocKind::JITCODE }; // UpdatePhaseTwo is typed object descriptor objects. static const AllocKinds UpdatePhaseThree { AllocKind::LAZY_SCRIPT, + AllocKind::SCOPE, AllocKind::FUNCTION, AllocKind::FUNCTION_EXTENDED, AllocKind::OBJECT0, AllocKind::OBJECT0_BACKGROUND, AllocKind::OBJECT2, AllocKind::OBJECT2_BACKGROUND, AllocKind::OBJECT4, AllocKind::OBJECT4_BACKGROUND,