Merge inbound to mozilla-central. a=merge
authorshindli <shindli@mozilla.com>
Fri, 23 Nov 2018 11:38:42 +0200
changeset 447792 c2593a3058afdfeaac5c990e18794ee8257afe99
parent 447791 ad6143ff142da2f47fe8fcbfd025174a791707bf (current diff)
parent 447745 5f77d9f3460c8ab31c35326e9ef079aee758ef35 (diff)
child 447793 2094cc4738e87fd6ed68fe52ae5675aadc822876
child 447831 05b2858c743e5b2a11914ee3a9d3adf5bf35847e
push id110080
push usershindli@mozilla.com
push dateFri, 23 Nov 2018 09:44:48 +0000
treeherdermozilla-inbound@c2593a3058af [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone65.0a1
first release with
nightly linux32
c2593a3058af / 65.0a1 / 20181123100103 / files
nightly linux64
c2593a3058af / 65.0a1 / 20181123100103 / files
nightly mac
c2593a3058af / 65.0a1 / 20181123100103 / files
nightly win32
c2593a3058af / 65.0a1 / 20181123100103 / files
nightly win64
c2593a3058af / 65.0a1 / 20181123100103 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
--- a/browser/components/aboutconfig/content/aboutconfig.js
+++ b/browser/components/aboutconfig/content/aboutconfig.js
@@ -1,23 +1,26 @@
 /* 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/. */
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/Preferences.jsm");
 
+let gDefaultBranch = Services.prefs.getDefaultBranch("");
 let gPrefArray;
 
 function onLoad() {
   gPrefArray = Services.prefs.getChildList("").map(function(name) {
+    let hasUserValue = Services.prefs.prefHasUserValue(name);
     let pref = {
       name,
       value: Preferences.get(name),
-      hasUserValue: Services.prefs.prefHasUserValue(name),
+      hasUserValue,
+      hasDefaultValue: hasUserValue ? prefHasDefaultValue(name) : true,
     };
     // Try in case it's a localized string.
     // Throws an exception if there is no equivalent value in the localized files for the pref.
     // If an execption is thrown the pref value is set to the empty string.
     try {
       if (!pref.hasUserValue && /^chrome:\/\/.+\/locale\/.+\.properties/.test(pref.value)) {
         pref.value = Services.prefs.getComplexValue(name, Ci.nsIPrefLocalizedString).data;
       }
@@ -31,16 +34,26 @@ function onLoad() {
 
   document.getElementById("search").addEventListener("keypress", function(e) {
     if (e.code == "Enter") {
       filterPrefs();
     }
   });
 
   document.getElementById("prefs").appendChild(createPrefsFragment(gPrefArray));
+  document.getElementById("prefs").addEventListener("click", (event) => {
+    if (event.target.localName != "button") {
+      return;
+    }
+    let prefRow = event.target.closest("tr");
+    let prefName = prefRow.getAttribute("aria-label");
+    Services.prefs.clearUserPref(prefName);
+    gPrefArray.splice(gPrefArray.findIndex(pref => pref.name == prefName), 1);
+    prefRow.remove();
+  });
 }
 
 function filterPrefs() {
   let substring = document.getElementById("search").value.trim();
   let fragment = createPrefsFragment(gPrefArray.filter(pref => pref.name.includes(substring)));
   document.getElementById("prefs").textContent = "";
   document.getElementById("prefs").appendChild(fragment);
 }
@@ -63,12 +76,37 @@ function createPrefsFragment(prefArray) 
     nameCell.append(parts[parts.length - 1]);
     row.appendChild(nameCell);
 
     let valueCell = document.createElement("td");
     valueCell.classList.add("cell-value");
     valueCell.textContent = pref.value;
     row.appendChild(valueCell);
 
+    let buttonCell = document.createElement("td");
+    if (!pref.hasDefaultValue) {
+      let button = document.createElement("button");
+      document.l10n.setAttributes(button, "about-config-pref-delete");
+      buttonCell.appendChild(button);
+    }
+    row.appendChild(buttonCell);
+
     fragment.appendChild(row);
   }
   return fragment;
 }
+
+function prefHasDefaultValue(name) {
+  try {
+    switch (Services.prefs.getPrefType(name)) {
+      case Ci.nsIPrefBranch.PREF_STRING:
+        gDefaultBranch.getStringPref(name);
+        return true;
+      case Ci.nsIPrefBranch.PREF_INT:
+        gDefaultBranch.getIntPref(name);
+        return true;
+      case Ci.nsIPrefBranch.PREF_BOOL:
+        gDefaultBranch.getBoolPref(name);
+        return true;
+    }
+  } catch (ex) {}
+  return false;
+}
--- a/browser/components/aboutconfig/content/aboutconfig.notftl
+++ b/browser/components/aboutconfig/content/aboutconfig.notftl
@@ -1,8 +1,10 @@
 # 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/.
 
 about-config-title = about:config
 
+about-config-pref-delete = Delete
+
 about-config-search =
     .placeholder = Search
--- a/browser/components/aboutconfig/test/browser/browser.ini
+++ b/browser/components/aboutconfig/test/browser/browser.ini
@@ -1,5 +1,6 @@
 [DEFAULT]
 
 [browser_basic.js]
+[browser_edit.js]
 [browser_search.js]
 skip-if = debug # Bug 1507747
new file mode 100644
--- /dev/null
+++ b/browser/components/aboutconfig/test/browser/browser_edit.js
@@ -0,0 +1,26 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const PAGE_URL = "chrome://browser/content/aboutconfig/aboutconfig.html";
+
+add_task(async function test_delete_user_pref() {
+  Services.prefs.setBoolPref("userAddedPref", true);
+  await BrowserTestUtils.withNewTab({
+    gBrowser,
+    url: PAGE_URL,
+  }, browser => {
+    return ContentTask.spawn(browser, null, () => {
+      let list = [...content.document.getElementById("prefs")
+        .getElementsByTagName("tr")];
+      function getRow(name) {
+        return list.find(row => row.querySelector("td").textContent == name);
+      }
+      Assert.ok(getRow("userAddedPref"));
+      getRow("userAddedPref").lastChild.lastChild.click();
+      list = [...content.document.getElementById("prefs")
+        .getElementsByTagName("tr")];
+      Assert.ok(!getRow("userAddedPref"));
+      Assert.ok(!Services.prefs.getChildList("").includes("userAddedPref"));
+    });
+  });
+});
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -10264,16 +10264,33 @@ nsDocShell::DoURILoad(nsIURI* aURI,
 
   nsresult rv;
   nsCOMPtr<nsIURILoader> uriLoader = do_GetService(NS_URI_LOADER_CONTRACTID, &rv);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   if (IsFrame()) {
+    bool doesNotReturnData = false;
+    NS_URIChainHasFlags(aURI, nsIProtocolHandler::URI_DOES_NOT_RETURN_DATA,
+                        &doesNotReturnData);
+
+    if (doesNotReturnData) {
+      // If this is an iframe, it must have a parent. Let's count the
+      // no-data-URL telemetry on the parent document, because probably this one
+      // is an about page.
+      nsCOMPtr<nsIDocShellTreeItem> parent;
+      GetSameTypeParent(getter_AddRefs(parent));
+      MOZ_ASSERT(parent);
+
+      nsIDocument* parentDocument = parent->GetDocument();
+      if (parentDocument) {
+        parentDocument->SetDocumentAndPageUseCounter(eUseCounter_custom_no_data_URL);
+      }
+    }
 
     MOZ_ASSERT(aContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_IFRAME ||
                aContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_FRAME,
                "DoURILoad thinks this is a frame and InternalLoad does not");
 
     // Only allow view-source scheme in top-level docshells. view-source is
     // the only scheme to which this applies at the moment due to potential
     // timing attacks to read data from cross-origin iframes. If this widens
--- a/dom/base/UseCounters.conf
+++ b/dom/base/UseCounters.conf
@@ -55,16 +55,19 @@ method PushManager.subscribe
 method PushSubscription.unsubscribe
 
 // window.sidebar
 attribute Window.sidebar
 
 // External interface
 method External.AddSearchProvider
 
+// no-data URLs for iframes
+custom no_data_URL used in iframes
+
 // AppCache API
 method OfflineResourceList.swapCache
 method OfflineResourceList.update
 attribute OfflineResourceList.status
 attribute OfflineResourceList.onchecking
 attribute OfflineResourceList.onerror
 attribute OfflineResourceList.onnoupdate
 attribute OfflineResourceList.ondownloading
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -7981,16 +7981,35 @@ void
 nsGlobalWindowInner::ForgetSharedWorker(SharedWorker* aSharedWorker)
 {
   MOZ_ASSERT(aSharedWorker);
   MOZ_ASSERT(mSharedWorkers.Contains(aSharedWorker));
 
   mSharedWorkers.RemoveElement(aSharedWorker);
 }
 
+void
+nsGlobalWindowInner::StorageAccessGranted()
+{
+  // If we have a partitioned localStorage, it's time to replace it with a real
+  // one in order to receive notifications.
+
+  if (mLocalStorage &&
+      mLocalStorage->Type() == Storage::ePartitionedLocalStorage) {
+    IgnoredErrorResult error;
+    GetLocalStorage(error);
+    if (NS_WARN_IF(error.Failed())) {
+      return;
+    }
+
+    MOZ_ASSERT(mLocalStorage &&
+               mLocalStorage->Type() == Storage::eLocalStorage);
+  }
+}
+
 mozilla::dom::TabGroup*
 nsPIDOMWindowInner::TabGroup()
 {
   return nsGlobalWindowInner::Cast(this)->TabGroupInner();
 }
 
 /* static */ already_AddRefed<nsGlobalWindowInner>
 nsGlobalWindowInner::Create(nsGlobalWindowOuter *aOuterWindow, bool aIsChrome)
--- a/dom/base/nsGlobalWindowInner.h
+++ b/dom/base/nsGlobalWindowInner.h
@@ -1236,16 +1236,21 @@ public:
   // Get the toplevel principal, returns null if this is a toplevel window.
   nsIPrincipal* GetTopLevelPrincipal();
 
   // Get the parent principal, returns null if this or the parent are not a
   // toplevel window. This is mainly used to determine the anti-tracking storage
   // area.
   nsIPrincipal* GetTopLevelStorageAreaPrincipal();
 
+  // This method is called if this window loads a 3rd party tracking resource
+  // and the storage is just been granted. The window can reset the partitioned
+  // storage objects and switch to the first party cookie jar.
+  void StorageAccessGranted();
+
 protected:
   static void NotifyDOMWindowDestroyed(nsGlobalWindowInner* aWindow);
   void NotifyWindowIDDestroyed(const char* aTopic);
 
   static void NotifyDOMWindowFrozen(nsGlobalWindowInner* aWindow);
   static void NotifyDOMWindowThawed(nsGlobalWindowInner* aWindow);
 
   virtual void UpdateParentTarget() override;
--- a/testing/marionette/assert.js
+++ b/testing/marionette/assert.js
@@ -24,16 +24,18 @@ XPCOMUtils.defineLazyModuleGetters(this,
   browser: "chrome://marionette/content/browser.js",
 });
 
 this.EXPORTED_SYMBOLS = ["assert"];
 
 const isFennec = () => AppConstants.platform == "android";
 const isFirefox = () =>
     Services.appinfo.ID == "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}";
+const isThunderbird = () =>
+    Services.appinfo.ID == "{3550f703-e582-4d05-9a08-453d09bdfdc6}";
 
 /**
  * Shorthands for common assertions made in Marionette.
  *
  * @namespace
  */
 this.assert = {};
 
@@ -87,16 +89,31 @@ assert.session = function(driver, msg = 
  *     If current browser is not Firefox.
  */
 assert.firefox = function(msg = "") {
   msg = msg || "Only supported in Firefox";
   assert.that(isFirefox, msg, UnsupportedOperationError)();
 };
 
 /**
+ * Asserts that the current browser is Firefox Desktop or Thunderbird.
+ *
+ * @param {string=} msg
+ *     Custom error message.
+ *
+ * @throws {UnsupportedOperationError}
+ *     If current browser is not Firefox or Thunderbird.
+ */
+assert.desktop = function(msg = "") {
+  msg = msg || "Only supported in desktop applications";
+  assert.that(obj => isFirefox(obj) || isThunderbird(obj),
+      msg, UnsupportedOperationError)();
+};
+
+/**
  * Asserts that the current browser is Fennec, or Firefox for Android.
  *
  * @param {string=} msg
  *     Custom error message.
  *
  * @throws {UnsupportedOperationError}
  *     If current browser is not Fennec.
  */
--- a/testing/marionette/browser.js
+++ b/testing/marionette/browser.js
@@ -93,16 +93,20 @@ browser.getBrowserForTab = function(tab)
 browser.getTabBrowser = function(window) {
   // Fennec
   if ("BrowserApp" in window) {
     return window.BrowserApp;
 
   // Firefox
   } else if ("gBrowser" in window) {
     return window.gBrowser;
+
+  // Thunderbird
+  } else if (window.document.getElementById("tabmail")) {
+    return window.document.getElementById("tabmail");
   }
 
   return null;
 };
 
 /**
  * Creates a browsing context wrapper.
  *
--- a/testing/marionette/components/marionette.js
+++ b/testing/marionette/components/marionette.js
@@ -319,16 +319,17 @@ class MarionetteParentProcess {
         } else {
           this.uninit();
         }
         break;
 
       case "profile-after-change":
         Services.obs.addObserver(this, "command-line-startup");
         Services.obs.addObserver(this, "sessionstore-windows-restored");
+        Services.obs.addObserver(this, "mail-startup-done");
         Services.obs.addObserver(this, "toplevel-window-ready");
 
         for (let [pref, value] of EnvironmentPrefs.from(ENV_PRESERVE_PREFS)) {
           Preferences.set(pref, value);
         }
         break;
 
       // In safe mode the command line handlers are getting parsed after the
@@ -373,16 +374,22 @@ class MarionetteParentProcess {
             let parserError = ev.target.querySelector("parsererror");
             log.fatal(parserError.textContent);
             this.uninit();
             Services.startup.quit(Ci.nsIAppStartup.eForceQuit);
           }
         }, {once: true});
         break;
 
+      // Thunderbird only, instead of sessionstore-windows-restored.
+      case "mail-startup-done":
+        this.finalUIStartup = true;
+        this.init();
+        break;
+
       case "sessionstore-windows-restored":
         Services.obs.removeObserver(this, topic);
         Services.obs.removeObserver(this, "toplevel-window-ready");
 
         // When Firefox starts on Windows, an additional GFX sanity test
         // window may appear off-screen.  Marionette should wait for it
         // to close.
         for (let win of Services.wm.getEnumerator(null)) {
--- a/testing/marionette/driver.js
+++ b/testing/marionette/driver.js
@@ -65,16 +65,17 @@ const {
 } = ChromeUtils.import("chrome://marionette/content/sync.js", {});
 
 XPCOMUtils.defineLazyGetter(this, "logger", Log.get);
 XPCOMUtils.defineLazyGlobalGetters(this, ["URL"]);
 
 this.EXPORTED_SYMBOLS = ["GeckoDriver"];
 
 const APP_ID_FIREFOX = "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}";
+const APP_ID_THUNDERBIRD = "{3550f703-e582-4d05-9a08-453d09bdfdc6}";
 
 const FRAME_SCRIPT = "chrome://marionette/content/listener.js";
 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
 const SUPPORTED_STRATEGIES = new Set([
   element.Strategy.ClassName,
   element.Strategy.Selector,
   element.Strategy.ID,
@@ -713,17 +714,26 @@ GeckoDriver.prototype.newSession = async
   if (this.a11yChecks && accessibility.service) {
     logger.info("Preemptively starting accessibility service in Chrome");
   }
 
   let registerBrowsers = this.registerPromise();
   let browserListening = this.listeningPromise();
 
   let waitForWindow = function() {
-    let win = Services.wm.getMostRecentWindow("navigator:browser");
+    let windowType;
+    switch (this.appId) {
+      case APP_ID_THUNDERBIRD:
+        windowType = "mail:3pane";
+        break;
+      default:
+        windowType = "navigator:browser";
+        break;
+    }
+    let win = Services.wm.getMostRecentWindow(windowType);
     if (!win) {
       // if the window isn't even created, just poll wait for it
       let checkTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
       checkTimer.initWithCallback(waitForWindow.bind(this), 100,
           Ci.nsITimer.TYPE_ONE_SHOT);
     } else if (win.document.readyState != "complete") {
       // otherwise, wait for it to be fully loaded before proceeding
       let listener = ev => {
@@ -3298,17 +3308,17 @@ GeckoDriver.prototype.quit = async funct
   });
 
   Services.startup.quit(mode);
 
   return {cause: await quitApplication};
 };
 
 GeckoDriver.prototype.installAddon = function(cmd) {
-  assert.firefox();
+  assert.desktop();
 
   let path = cmd.parameters.path;
   let temp = cmd.parameters.temporary || false;
   if (typeof path == "undefined" || typeof path != "string" ||
       typeof temp != "boolean") {
     throw new InvalidArgumentError();
   }
 
--- a/toolkit/components/antitracking/AntiTrackingCommon.cpp
+++ b/toolkit/components/antitracking/AntiTrackingCommon.cpp
@@ -531,16 +531,19 @@ AntiTrackingCommon::AddFirstPartyStorage
   NS_ConvertUTF16toUTF8 grantedOrigin(origin);
 
   nsAutoCString permissionKey;
   CreatePermissionKey(trackingOrigin, grantedOrigin, permissionKey);
 
   // Let's store the permission in the current parent window.
   topInnerWindow->SaveStorageAccessGranted(permissionKey);
 
+  // Let's inform the parent window.
+  parentWindow->StorageAccessGranted();
+
   nsIChannel* channel =
     pwin->GetCurrentInnerWindow()->GetExtantDoc()->GetChannel();
 
   pwin->NotifyContentBlockingState(blockReason, channel, false, trackingURI);
 
   ReportUnblockingConsole(parentWindow, NS_ConvertUTF8toUTF16(trackingOrigin),
                           origin, aReason);