Merge autoland to mozilla-central r=merge a=merge
authorCsoregi Natalia <ncsoregi@mozilla.com>
Mon, 06 Nov 2017 12:44:18 +0200
changeset 443553 70f38f59f9fa45dda5d70d44881dc1d906de15f6
parent 443527 8b9167b8a937e09f6673d4d7d693c6ac1c25a3d7 (current diff)
parent 443552 fb90190ba5b1d880c743f90b7634397c8c3abe9b (diff)
child 443554 dc45ee24c55d1061951956321bd8481d517ce22a
push id1618
push userCallek@gmail.com
push dateThu, 11 Jan 2018 17:45:48 +0000
treeherdermozilla-release@882ca853e05a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge, merge
milestone58.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
Merge autoland to mozilla-central r=merge a=merge
dom/html/HTMLMediaElement.cpp
dom/html/HTMLMediaElement.h
dom/media/MediaDecoder.cpp
dom/media/MediaDecoder.h
toolkit/components/printing/content/printdialog.js
toolkit/components/printing/content/printdialog.xul
toolkit/components/printing/content/printjoboptions.js
toolkit/components/printing/content/printjoboptions.xul
toolkit/locales/en-US/chrome/global/printdialog.dtd
toolkit/locales/en-US/chrome/global/printjoboptions.dtd
--- a/accessible/ipc/win/handler/AccessibleHandler.cpp
+++ b/accessible/ipc/win/handler/AccessibleHandler.cpp
@@ -20,16 +20,17 @@
 #include "mozilla/mscom/Registration.h"
 #include "mozilla/UniquePtr.h"
 
 #include <objbase.h>
 #include <uiautomation.h>
 #include <winreg.h>
 
 #include "AccessibleHypertext.h"
+#include "AccessibleHypertext2.h"
 #include "Accessible2_i.c"
 #include "Accessible2_2_i.c"
 #include "Accessible2_3_i.c"
 #include "AccessibleHyperlink_i.c"
 
 namespace mozilla {
 namespace a11y {
 
@@ -204,18 +205,19 @@ AccessibleHandler::QueryHandlerInterface
   }
 
   if (aIid == IID_IServiceProvider) {
     RefPtr<IServiceProvider> svcProv(static_cast<IServiceProvider*>(this));
     svcProv.forget(aOutInterface);
     return S_OK;
   }
 
-  if (aIid == IID_IAccessibleText || aIid == IID_IAccessibleHypertext) {
-    RefPtr<IAccessibleHypertext> textTearoff(new AccessibleTextTearoff(this));
+  if (aIid == IID_IAccessibleText || aIid == IID_IAccessibleHypertext ||
+      aIid == IID_IAccessibleHypertext2) {
+    RefPtr<IAccessibleHypertext2> textTearoff(new AccessibleTextTearoff(this));
     textTearoff.forget(aOutInterface);
     return S_OK;
   }
 
   if (aIid == IID_IProvideClassInfo) {
     RefPtr<IProvideClassInfo> clsInfo(this);
     clsInfo.forget(aOutInterface);
     return S_OK;
--- a/accessible/ipc/win/handler/AccessibleTextTearoff.cpp
+++ b/accessible/ipc/win/handler/AccessibleTextTearoff.cpp
@@ -8,16 +8,17 @@
 #error This code is NOT for internal Gecko use!
 #endif // defined(MOZILLA_INTERNAL_API)
 
 #include "AccessibleTextTearoff.h"
 
 #include "AccessibleHandlerControl.h"
 #include "AccessibleText_i.c"
 #include "AccessibleHypertext_i.c"
+#include "AccessibleHypertext2_i.c"
 #include "Factory.h"
 
 #include "mozilla/Assertions.h"
 
 namespace mozilla {
 namespace a11y {
 
 AccessibleTextTearoff::AccessibleTextTearoff(AccessibleHandler* aHandler)
@@ -33,23 +34,24 @@ AccessibleTextTearoff::ResolveAccHyperte
     return S_OK;
   }
 
   RefPtr<IUnknown> proxy(mHandler->GetProxy());
   if (!proxy) {
     return E_UNEXPECTED;
   }
 
-  return proxy->QueryInterface(IID_IAccessibleHypertext,
+  return proxy->QueryInterface(IID_IAccessibleHypertext2,
                                getter_AddRefs(mAccHypertextProxy));
 }
 
 IMPL_IUNKNOWN_QUERY_HEAD(AccessibleTextTearoff)
 IMPL_IUNKNOWN_QUERY_IFACE(IAccessibleText)
 IMPL_IUNKNOWN_QUERY_IFACE(IAccessibleHypertext)
+IMPL_IUNKNOWN_QUERY_IFACE(IAccessibleHypertext2)
 IMPL_IUNKNOWN_QUERY_TAIL_AGGREGATED(mHandler)
 
 HRESULT
 AccessibleTextTearoff::addSelection(long startOffset, long endOffset)
 {
   HRESULT hr = ResolveAccHypertext();
   if (FAILED(hr)) {
     return hr;
@@ -333,11 +335,22 @@ AccessibleTextTearoff::get_hyperlinkInde
   HRESULT hr = ResolveAccHypertext();
   if (FAILED(hr)) {
     return hr;
   }
 
   return mAccHypertextProxy->get_hyperlinkIndex(charIndex, hyperlinkIndex);
 }
 
+HRESULT
+AccessibleTextTearoff::get_hyperlinks(IAccessibleHyperlink*** hyperlinks,
+                                      long* nHyperlinks)
+{
+  HRESULT hr = ResolveAccHypertext();
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return mAccHypertextProxy->get_hyperlinks(hyperlinks, nHyperlinks);
+}
 
 } // namespace a11y
 } // namespace mozilla
--- a/accessible/ipc/win/handler/AccessibleTextTearoff.h
+++ b/accessible/ipc/win/handler/AccessibleTextTearoff.h
@@ -7,24 +7,24 @@
 #if defined(MOZILLA_INTERNAL_API)
 #error This code is NOT for internal Gecko use!
 #endif // defined(MOZILLA_INTERNAL_API)
 
 #ifndef mozilla_a11y_AccessibleTextTearoff_h
 #define mozilla_a11y_AccessibleTextTearoff_h
 
 #include "AccessibleHandler.h"
-#include "AccessibleHypertext.h"
+#include "AccessibleHypertext2.h"
 #include "IUnknownImpl.h"
 #include "mozilla/RefPtr.h"
 
 namespace mozilla {
 namespace a11y {
 
-class AccessibleTextTearoff final : public IAccessibleHypertext
+class AccessibleTextTearoff final : public IAccessibleHypertext2
 {
 public:
   explicit AccessibleTextTearoff(AccessibleHandler* aHandler);
 
   DECL_IUNKNOWN
 
   // IAccessibleText
   STDMETHODIMP addSelection(long startOffset, long endOffset) override;
@@ -67,20 +67,24 @@ public:
   STDMETHODIMP get_oldText(IA2TextSegment *oldText) override;
 
   // IAccessibleHypertext
   STDMETHODIMP get_nHyperlinks(long *hyperlinkCount) override;
   STDMETHODIMP get_hyperlink(long index,
                              IAccessibleHyperlink **hyperlink) override;
   STDMETHODIMP get_hyperlinkIndex(long charIndex, long *hyperlinkIndex) override;
 
+  // IAccessibleHypertext2
+  STDMETHODIMP get_hyperlinks(IAccessibleHyperlink*** hyperlinks,
+                              long* nHyperlinks) override;
+
 private:
   ~AccessibleTextTearoff() = default;
   HRESULT ResolveAccHypertext();
 
   RefPtr<AccessibleHandler>     mHandler;
-  RefPtr<IAccessibleHypertext>  mAccHypertextProxy;
+  RefPtr<IAccessibleHypertext2>  mAccHypertextProxy;
 };
 
 } // namespace a11y
 } // namespace mozilla
 
 #endif // mozilla_a11y_AccessibleTextTearoff_h
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -649,16 +649,17 @@ pref("mousewheel.with_shift.action", 4);
 pref("mousewheel.with_alt.action", 2);
 pref("mousewheel.with_meta.action", 1); // win key on Win, Super/Hyper on Linux
 #endif
 pref("mousewheel.with_control.action",3);
 pref("mousewheel.with_win.action", 1);
 
 pref("browser.xul.error_pages.enabled", true);
 pref("browser.xul.error_pages.expert_bad_cert", false);
+pref("browser.xul.error_pages.show_safe_browsing_details_on_load", false);
 
 // Enable captive portal detection.
 pref("network.captive-portal-service.enabled", true);
 
 // If true, network link events will change the value of navigator.onLine
 pref("network.manage-offline-status", true);
 
 // We want to make sure mail URLs are handled externally...
--- a/browser/base/content/content.js
+++ b/browser/base/content/content.js
@@ -194,16 +194,23 @@ var AboutBlockedSiteListener = {
           "https://www.antiphishing.org//");
         break;
     }
 
     // Set the firefox support url.
     doc.getElementById("firefox_support").setAttribute("href",
       "https://support.mozilla.org/kb/how-does-phishing-and-malware-protection-work");
 
+    // Show safe browsing details on load if the pref is set to true.
+    let showDetails = Services.prefs.getBoolPref("browser.xul.error_pages.show_safe_browsing_details_on_load");
+    if (showDetails) {
+      let details = content.document.getElementById("errorDescriptionContainer");
+      details.removeAttribute("hidden");
+    }
+
     // Set safe browsing advisory link.
     let advisoryUrl = Services.prefs.getCharPref(
       "browser.safebrowsing.provider." + provider + ".advisoryURL", "");
     if (!advisoryUrl) {
       let el = content.document.getElementById("advisoryDesc");
       el.remove();
       return;
     }
--- a/browser/base/content/test/static/browser_all_files_referenced.js
+++ b/browser/base/content/test/static/browser_all_files_referenced.js
@@ -55,20 +55,16 @@ var whitelist = [
   // The l10n build system can't package string files only for some platforms.
   // See bug 1339424 for why this is hard to fix.
   {file: "chrome://global/locale/fallbackMenubar.properties",
    platforms: ["linux", "win"]},
   {file: "chrome://global/locale/printPageSetup.dtd", platforms: ["macosx"]},
   {file: "chrome://global/locale/printPreviewProgress.dtd",
    platforms: ["macosx"]},
   {file: "chrome://global/locale/printProgress.dtd", platforms: ["macosx"]},
-  {file: "chrome://global/locale/printdialog.dtd",
-   platforms: ["macosx", "win"]},
-  {file: "chrome://global/locale/printjoboptions.dtd",
-   platforms: ["macosx", "win"]},
 
   // devtools/client/inspector/bin/dev-server.js
   {file: "chrome://devtools/content/inspector/markup/markup.xhtml",
    isFromDevTools: true},
 
   // Kept for add-on compatibility, should be removed in bug 851471.
   {file: "chrome://mozapps/skin/downloads/downloadIcon.png"},
 
--- a/browser/components/preferences/SiteDataManager.jsm
+++ b/browser/components/preferences/SiteDataManager.jsm
@@ -1,11 +1,11 @@
 "use strict";
 
-const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "OfflineAppCacheHelper",
                                   "resource:///modules/offlineAppCache.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ContextualIdentityService",
                                   "resource://gre/modules/ContextualIdentityService.jsm");
@@ -44,47 +44,49 @@ this.SiteDataManager = {
   },
 
   _getQuotaUsage() {
     // Clear old data and requests first
     this._sites.clear();
     this._cancelGetQuotaUsage();
     this._getQuotaUsagePromise = new Promise(resolve => {
       let onUsageResult = request => {
-        let items = request.result;
-        for (let item of items) {
-          if (!item.persisted && item.usage <= 0) {
-            // An non-persistent-storage site with 0 byte quota usage is redundant for us so skip it.
-            continue;
-          }
-          let principal =
-            Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(item.origin);
-          let uri = principal.URI;
-          if (uri.scheme == "http" || uri.scheme == "https") {
-            let site = this._sites.get(uri.host);
-            if (!site) {
-              site = {
-                persisted: false,
-                quotaUsage: 0,
-                principals: [],
-                appCacheList: [],
-              };
+        if (request.resultCode == Cr.NS_OK) {
+          let items = request.result;
+          for (let item of items) {
+            if (!item.persisted && item.usage <= 0) {
+              // An non-persistent-storage site with 0 byte quota usage is redundant for us so skip it.
+              continue;
             }
-            // Assume 3 sites:
-            //   - Site A (not persisted): https://www.foo.com
-            //   - Site B (not persisted): https://www.foo.com^userContextId=2
-            //   - Site C (persisted):     https://www.foo.com:1234
-            // Although only C is persisted, grouping by host, as a result,
-            // we still mark as persisted here under this host group.
-            if (item.persisted) {
-              site.persisted = true;
+            let principal =
+              Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(item.origin);
+            let uri = principal.URI;
+            if (uri.scheme == "http" || uri.scheme == "https") {
+              let site = this._sites.get(uri.host);
+              if (!site) {
+                site = {
+                  persisted: false,
+                  quotaUsage: 0,
+                  principals: [],
+                  appCacheList: [],
+                };
+              }
+              // Assume 3 sites:
+              //   - Site A (not persisted): https://www.foo.com
+              //   - Site B (not persisted): https://www.foo.com^userContextId=2
+              //   - Site C (persisted):     https://www.foo.com:1234
+              // Although only C is persisted, grouping by host, as a result,
+              // we still mark as persisted here under this host group.
+              if (item.persisted) {
+                site.persisted = true;
+              }
+              site.principals.push(principal);
+              site.quotaUsage += item.usage;
+              this._sites.set(uri.host, site);
             }
-            site.principals.push(principal);
-            site.quotaUsage += item.usage;
-            this._sites.set(uri.host, site);
           }
         }
         resolve();
       };
       // XXX: The work of integrating localStorage into Quota Manager is in progress.
       //      After the bug 742822 and 1286798 landed, localStorage usage will be included.
       //      So currently only get indexedDB usage.
       this._quotaUsageRequest = this._qms.getUsage(onUsageResult);
@@ -217,78 +219,96 @@ this.SiteDataManager = {
             cookie.host, cookie.name, cookie.path, false, cookie.originAttributes);
         }
       }
 
       Services.obs.notifyObservers(null, "browser:purge-domain-data", principal.URI.host);
     }
   },
 
-  _removeServiceWorkers(site) {
+  _unregisterServiceWorker(serviceWorker) {
+    return new Promise(resolve => {
+      let unregisterCallback = {
+        unregisterSucceeded: resolve,
+        unregisterFailed: resolve, // We don't care about failures.
+        QueryInterface: XPCOMUtils.generateQI([Ci.nsIServiceWorkerUnregisterCallback])
+      };
+      serviceWorkerManager.propagateUnregister(serviceWorker.principal, unregisterCallback, serviceWorker.scope);
+    });
+  },
+
+  _removeServiceWorkersForSites(sites) {
+    let promises = [];
+    let targetHosts = sites.map(s => s.principals[0].URI.host);
     let serviceWorkers = serviceWorkerManager.getAllRegistrations();
     for (let i = 0; i < serviceWorkers.length; i++) {
       let sw = serviceWorkers.queryElementAt(i, Ci.nsIServiceWorkerRegistrationInfo);
-      for (let principal of site.principals) {
-        if (sw.principal.equals(principal)) {
-          serviceWorkerManager.removeAndPropagate(sw.principal.URI.host);
-          break;
-        }
+      // Sites are grouped and removed by host so we unregister service workers by the same host as well
+      if (targetHosts.includes(sw.principal.URI.host)) {
+        promises.push(this._unregisterServiceWorker(sw));
       }
     }
+    return Promise.all(promises);
   },
 
   remove(hosts) {
-    let promises = [];
     let unknownHost = "";
+    let targetSites = [];
     for (let host of hosts) {
       let site = this._sites.get(host);
       if (site) {
         this._removePermission(site);
         this._removeAppCache(site);
         this._removeCookie(site);
-        this._removeServiceWorkers(site);
-        promises.push(this._removeQuotaUsage(site));
+        targetSites.push(site);
       } else {
         unknownHost = host;
         break;
       }
     }
-    if (promises.length > 0) {
-      Promise.all(promises).then(() => this.updateSites());
+
+    if (targetSites.length > 0) {
+      this._removeServiceWorkersForSites(targetSites)
+          .then(() => {
+            let promises = targetSites.map(s => this._removeQuotaUsage(s));
+            return Promise.all(promises);
+          })
+          .then(() => this.updateSites());
     }
     if (unknownHost) {
       throw `SiteDataManager: removing unknown site of ${unknownHost}`;
     }
   },
 
   async removeAll() {
     Services.cache2.clear();
     Services.cookies.removeAll();
     OfflineAppCacheHelper.clear();
 
     // Iterate through the service workers and remove them.
+    let promises = [];
     let serviceWorkers = serviceWorkerManager.getAllRegistrations();
     for (let i = 0; i < serviceWorkers.length; i++) {
       let sw = serviceWorkers.queryElementAt(i, Ci.nsIServiceWorkerRegistrationInfo);
-      let host = sw.principal.URI.host;
-      serviceWorkerManager.removeAndPropagate(host);
+      promises.push(this._unregisterServiceWorker(sw));
     }
+    await Promise.all(promises);
 
     // Refresh sites using quota usage again.
     // This is for the case:
     //   1. User goes to the about:preferences Site Data section.
     //   2. With the about:preferences opened, user visits another website.
     //   3. The website saves to quota usage, like indexedDB.
     //   4. User goes back to the Site Data section and commands to clear all site data.
     // For this case, we should refresh the site list so not to miss the website in the step 3.
     // We don't do "Clear All" on the quota manager like the cookie, appcache, http cache above
     // because that would clear browser data as well too,
     // see https://bugzilla.mozilla.org/show_bug.cgi?id=1312361#c9
     await this._getQuotaUsage();
-    let promises = [];
+    promises = [];
     for (let site of this._sites.values()) {
       this._removePermission(site);
       promises.push(this._removeQuotaUsage(site));
     }
     return Promise.all(promises).then(() => this.updateSites());
   },
 
   isPrivateCookie(cookie) {
--- a/browser/components/preferences/blocklists.js
+++ b/browser/components/preferences/blocklists.js
@@ -107,47 +107,26 @@ var gBlocklistManager = {
     for (let list of this._blockLists) {
       if (list.selected) {
         selected = list;
         break;
       }
     }
 
     if (activeList !== selected.id) {
-      const Cc = Components.classes, Ci = Components.interfaces;
-      let msg = this._bundle.getFormattedString("blocklistChangeRequiresRestart",
-                                                [this._brandShortName]);
-      let title = this._bundle.getFormattedString("shouldRestartTitle",
-                                                  [this._brandShortName]);
-      let shouldProceed = Services.prompt.confirm(window, title, msg);
-      if (shouldProceed) {
-        let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"]
-                           .createInstance(Ci.nsISupportsPRBool);
-        Services.obs.notifyObservers(cancelQuit, "quit-application-requested",
-                                     "restart");
-        shouldProceed = !cancelQuit.data;
+      let trackingTable = Services.prefs.getCharPref(TRACKING_TABLE_PREF);
+      if (selected.id != CONTENT_LIST_ID) {
+        trackingTable = trackingTable.replace("," + CONTENT_LIST_ID + TRACK_SUFFIX, "");
+      } else {
+        trackingTable += "," + CONTENT_LIST_ID + TRACK_SUFFIX;
+      }
+      Services.prefs.setCharPref(TRACKING_TABLE_PREF, trackingTable);
+      Services.prefs.setCharPref(UPDATE_TIME_PREF, 42);
+    }
 
-        if (shouldProceed) {
-          let trackingTable = Services.prefs.getCharPref(TRACKING_TABLE_PREF);
-          if (selected.id != CONTENT_LIST_ID) {
-            trackingTable = trackingTable.replace("," + CONTENT_LIST_ID + TRACK_SUFFIX, "");
-          } else {
-            trackingTable += "," + CONTENT_LIST_ID + TRACK_SUFFIX;
-          }
-          Services.prefs.setCharPref(TRACKING_TABLE_PREF, trackingTable);
-          Services.prefs.setCharPref(UPDATE_TIME_PREF, 42);
-
-          Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit |
-                                Ci.nsIAppStartup.eRestart);
-        }
-      }
-
-      // Don't close the dialog in case we didn't quit.
-      return;
-    }
     window.close();
   },
 
   _loadBlockLists() {
     this._blockLists = [];
 
     // Load blocklists into a table.
     let branch = Services.prefs.getBranch(LISTS_PREF_BRANCH);
--- a/browser/components/preferences/in-content/tests/browser.ini
+++ b/browser/components/preferences/in-content/tests/browser.ini
@@ -1,13 +1,15 @@
 [DEFAULT]
 support-files =
   head.js
   privacypane_tests_perwindow.js
   site_data_test.html
+  service_worker_test.html
+  service_worker_test.js
   addons/set_homepage.xpi
   addons/set_newtab.xpi
   offline/offline.html
   offline/manifest.appcache
 
 [browser_applications_selection.js]
 skip-if = os == 'linux' # bug 1382057
 [browser_advanced_update.js]
--- a/browser/components/preferences/in-content/tests/browser_siteData.js
+++ b/browser/components/preferences/in-content/tests/browser_siteData.js
@@ -10,23 +10,26 @@ Cu.import("resource://gre/modules/XPCOMU
 Services.scriptloader.loadSubScript("resource://testing-common/sinon-2.3.2.js");
 
 const TEST_QUOTA_USAGE_HOST = "example.com";
 const TEST_QUOTA_USAGE_ORIGIN = "https://" + TEST_QUOTA_USAGE_HOST;
 const TEST_QUOTA_USAGE_URL = TEST_QUOTA_USAGE_ORIGIN + "/browser/browser/components/preferences/in-content/tests/site_data_test.html";
 const TEST_OFFLINE_HOST = "example.org";
 const TEST_OFFLINE_ORIGIN = "https://" + TEST_OFFLINE_HOST;
 const TEST_OFFLINE_URL = TEST_OFFLINE_ORIGIN + "/browser/browser/components/preferences/in-content/tests/offline/offline.html";
+const TEST_SERVICE_WORKER_URL = TEST_OFFLINE_ORIGIN + "/browser/browser/components/preferences/in-content/tests/service_worker_test.html";
 const REMOVE_DIALOG_URL = "chrome://browser/content/preferences/siteDataRemoveSelected.xul";
 
 const { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {});
 const { DownloadUtils } = Cu.import("resource://gre/modules/DownloadUtils.jsm", {});
 const { SiteDataManager } = Cu.import("resource:///modules/SiteDataManager.jsm", {});
 const { OfflineAppCacheHelper } = Cu.import("resource:///modules/offlineAppCache.jsm", {});
 
+XPCOMUtils.defineLazyServiceGetter(this, "serviceWorkerManager", "@mozilla.org/serviceworkers/manager;1", "nsIServiceWorkerManager");
+
 const mockOfflineAppCacheHelper = {
   clear: null,
 
   originalClear: null,
 
   register() {
     this.originalClear = OfflineAppCacheHelper.clear;
     this.clear = sinon.spy();
@@ -82,16 +85,50 @@ const cacheUsageGetter = {
 };
 
 function promiseCookiesCleared() {
   return TestUtils.topicObserved("cookie-changed", (subj, data) => {
     return data === "cleared";
   });
 }
 
+async function loadServiceWorkerTestPage(url) {
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
+  await BrowserTestUtils.waitForCondition(() => {
+    return ContentTask.spawn(tab.linkedBrowser, {}, () =>
+      content.document.body.getAttribute("data-test-service-worker-registered") === "true");
+  }, `Fail to load service worker test ${url}`);
+  await BrowserTestUtils.removeTab(tab);
+}
+
+function promiseServiceWorkerRegisteredFor(url) {
+  return BrowserTestUtils.waitForCondition(() => {
+    try {
+      let principal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(url);
+      let sw = serviceWorkerManager.getRegistrationByPrincipal(principal, principal.URI.spec);
+      if (sw) {
+        ok(true, `Found the service worker registered for ${url}`);
+        return true;
+      }
+    } catch (e) {}
+    return false;
+  }, `Should register service worker for ${url}`);
+}
+
+function promiseServiceWorkersCleared() {
+  return BrowserTestUtils.waitForCondition(() => {
+    let serviceWorkers = serviceWorkerManager.getAllRegistrations();
+    if (serviceWorkers.length == 0) {
+      ok(true, "Cleared all service workers");
+      return true;
+    }
+    return false;
+  }, "Should clear all service workers");
+}
+
 registerCleanupFunction(function() {
   delete window.sinon;
   mockOfflineAppCacheHelper.unregister();
 });
 
 // Test listing site using quota usage or site using appcache
 add_task(async function() {
   await SpecialPowers.pushPrefEnv({set: [["browser.storageManager.enabled", true]]});
@@ -245,125 +282,60 @@ add_task(async function() {
   is(cacheUsage, 0, "The cache usage should be removed");
   is(quotaUsage, 0, "The quota usage should be removed");
   is(totalUsage, 0, "The total usage should be removed");
   // Test accepting "Clear All Data" ends
 
   await BrowserTestUtils.removeTab(gBrowser.selectedTab);
 });
 
-// Test sorting
+// Test clearing service wroker through the "Clear All" button
+add_task(async function() {
+  await SpecialPowers.pushPrefEnv({set: [["browser.storageManager.enabled", true]]});
+  // Register a test service
+  await loadServiceWorkerTestPage(TEST_SERVICE_WORKER_URL);
+  await openPreferencesViaOpenPreferencesAPI("privacy", { leaveOpen: true });
+  // Test the initial states
+  await promiseServiceWorkerRegisteredFor(TEST_SERVICE_WORKER_URL);
+  // Click the "Clear All" button
+  let doc = gBrowser.selectedBrowser.contentDocument;
+  let clearBtn = doc.getElementById("clearSiteDataButton");
+  let acceptPromise = promiseAlertDialogOpen("accept");
+  let updatePromise = promiseSiteDataManagerSitesUpdated();
+  clearBtn.doCommand();
+  await acceptPromise;
+  await updatePromise;
+  await promiseServiceWorkersCleared();
+  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
+});
+
+// Test clearing service wroker through the settings panel
 add_task(async function() {
   await SpecialPowers.pushPrefEnv({set: [["browser.storageManager.enabled", true]]});
-  mockSiteDataManager.register(SiteDataManager);
-  mockSiteDataManager.fakeSites = [
-    {
-      usage: 1024,
-      principal: Services.scriptSecurityManager
-                         .createCodebasePrincipalFromOrigin("https://account.xyz.com"),
-      persisted: true
-    },
-    {
-      usage: 1024 * 2,
-      principal: Services.scriptSecurityManager
-                         .createCodebasePrincipalFromOrigin("https://books.foo.com"),
-      persisted: false
-    },
-    {
-      usage: 1024 * 3,
-      principal: Services.scriptSecurityManager
-                         .createCodebasePrincipalFromOrigin("http://cinema.bar.com"),
-      persisted: true
-    },
-  ];
-
-  let updatePromise = promiseSiteDataManagerSitesUpdated();
+  // Register a test service worker
+  await loadServiceWorkerTestPage(TEST_SERVICE_WORKER_URL);
   await openPreferencesViaOpenPreferencesAPI("privacy", { leaveOpen: true });
-  await updatePromise;
+  // Test the initial states
+  await promiseServiceWorkerRegisteredFor(TEST_SERVICE_WORKER_URL);
+  // Open the Site Data Settings panel and remove the site
   await openSiteDataSettingsDialog();
-
-  let dialog = content.gSubDialog._topDialog;
-  let dialogFrame = dialog._frame;
-  let frameDoc = dialogFrame.contentDocument;
-  let hostCol = frameDoc.getElementById("hostCol");
-  let usageCol = frameDoc.getElementById("usageCol");
-  let statusCol = frameDoc.getElementById("statusCol");
-  let sitesList = frameDoc.getElementById("sitesList");
-
-  // Test default sorting
-  assertSortByUsage("descending");
-
-  // Test sorting on the usage column
-  usageCol.click();
-  assertSortByUsage("ascending");
-  usageCol.click();
-  assertSortByUsage("descending");
-
-  // Test sorting on the host column
-  hostCol.click();
-  assertSortByHost("ascending");
-  hostCol.click();
-  assertSortByHost("descending");
-
-  // Test sorting on the permission status column
-  statusCol.click();
-  assertSortByStatus("ascending");
-  statusCol.click();
-  assertSortByStatus("descending");
-
-  mockSiteDataManager.unregister();
+  let acceptRemovePromise = promiseAlertDialogOpen("accept");
+  let updatePromise = promiseSiteDataManagerSitesUpdated();
+  ContentTask.spawn(gBrowser.selectedBrowser, { TEST_OFFLINE_HOST }, args => {
+    let host = args.TEST_OFFLINE_HOST;
+    let frameDoc = content.gSubDialog._topDialog._frame.contentDocument;
+    let sitesList = frameDoc.getElementById("sitesList");
+    let site = sitesList.querySelector(`richlistitem[host="${host}"]`);
+    if (site) {
+      let removeBtn = frameDoc.getElementById("removeSelected");
+      let saveBtn = frameDoc.getElementById("save");
+      site.click();
+      removeBtn.doCommand();
+      saveBtn.doCommand();
+    } else {
+      ok(false, `Should have one site of ${host}`);
+    }
+  });
+  await acceptRemovePromise;
+  await updatePromise;
+  await promiseServiceWorkersCleared();
   await BrowserTestUtils.removeTab(gBrowser.selectedTab);
-
-  function assertSortByHost(order) {
-    let siteItems = sitesList.getElementsByTagName("richlistitem");
-    for (let i = 0; i < siteItems.length - 1; ++i) {
-      let aHost = siteItems[i].getAttribute("host");
-      let bHost = siteItems[i + 1].getAttribute("host");
-      let result = aHost.localeCompare(bHost);
-      if (order == "ascending") {
-        Assert.lessOrEqual(result, 0, "Should sort sites in the ascending order by host");
-      } else {
-        Assert.greaterOrEqual(result, 0, "Should sort sites in the descending order by host");
-      }
-    }
-  }
-
-  function assertSortByStatus(order) {
-    let siteItems = sitesList.getElementsByTagName("richlistitem");
-    for (let i = 0; i < siteItems.length - 1; ++i) {
-      let aHost = siteItems[i].getAttribute("host");
-      let bHost = siteItems[i + 1].getAttribute("host");
-      let a = findSiteByHost(aHost);
-      let b = findSiteByHost(bHost);
-      let result = 0;
-      if (a.persisted && !b.persisted) {
-        result = 1;
-      } else if (!a.persisted && b.persisted) {
-        result = -1;
-      }
-      if (order == "ascending") {
-        Assert.lessOrEqual(result, 0, "Should sort sites in the ascending order by permission status");
-      } else {
-        Assert.greaterOrEqual(result, 0, "Should sort sites in the descending order by permission status");
-      }
-    }
-  }
-
-  function assertSortByUsage(order) {
-    let siteItems = sitesList.getElementsByTagName("richlistitem");
-    for (let i = 0; i < siteItems.length - 1; ++i) {
-      let aHost = siteItems[i].getAttribute("host");
-      let bHost = siteItems[i + 1].getAttribute("host");
-      let a = findSiteByHost(aHost);
-      let b = findSiteByHost(bHost);
-      let result = a.usage - b.usage;
-      if (order == "ascending") {
-        Assert.lessOrEqual(result, 0, "Should sort sites in the ascending order by usage");
-      } else {
-        Assert.greaterOrEqual(result, 0, "Should sort sites in the descending order by usage");
-      }
-    }
-  }
-
-  function findSiteByHost(host) {
-    return mockSiteDataManager.fakeSites.find(site => site.principal.URI.host == host);
-  }
 });
--- a/browser/components/preferences/in-content/tests/browser_siteData3.js
+++ b/browser/components/preferences/in-content/tests/browser_siteData3.js
@@ -101,8 +101,126 @@ add_task(async function() {
 
   expected = prefStrBundle.getString("persistent");
   let status = siteItems[0].getAttribute("status");
   is(status, expected, "Should mark persisted status across scheme, port and origin attributes");
 
   mockSiteDataManager.unregister();
   await BrowserTestUtils.removeTab(gBrowser.selectedTab);
 });
+
+// Test sorting
+add_task(async function() {
+  await SpecialPowers.pushPrefEnv({set: [["browser.storageManager.enabled", true]]});
+  mockSiteDataManager.register(SiteDataManager);
+  mockSiteDataManager.fakeSites = [
+    {
+      usage: 1024,
+      principal: Services.scriptSecurityManager
+                         .createCodebasePrincipalFromOrigin("https://account.xyz.com"),
+      persisted: true
+    },
+    {
+      usage: 1024 * 2,
+      principal: Services.scriptSecurityManager
+                         .createCodebasePrincipalFromOrigin("https://books.foo.com"),
+      persisted: false
+    },
+    {
+      usage: 1024 * 3,
+      principal: Services.scriptSecurityManager
+                         .createCodebasePrincipalFromOrigin("http://cinema.bar.com"),
+      persisted: true
+    },
+  ];
+
+  let updatePromise = promiseSiteDataManagerSitesUpdated();
+  await openPreferencesViaOpenPreferencesAPI("privacy", { leaveOpen: true });
+  await updatePromise;
+  await openSiteDataSettingsDialog();
+
+  let dialog = content.gSubDialog._topDialog;
+  let dialogFrame = dialog._frame;
+  let frameDoc = dialogFrame.contentDocument;
+  let hostCol = frameDoc.getElementById("hostCol");
+  let usageCol = frameDoc.getElementById("usageCol");
+  let statusCol = frameDoc.getElementById("statusCol");
+  let sitesList = frameDoc.getElementById("sitesList");
+
+  // Test default sorting
+  assertSortByUsage("descending");
+
+  // Test sorting on the usage column
+  usageCol.click();
+  assertSortByUsage("ascending");
+  usageCol.click();
+  assertSortByUsage("descending");
+
+  // Test sorting on the host column
+  hostCol.click();
+  assertSortByHost("ascending");
+  hostCol.click();
+  assertSortByHost("descending");
+
+  // Test sorting on the permission status column
+  statusCol.click();
+  assertSortByStatus("ascending");
+  statusCol.click();
+  assertSortByStatus("descending");
+
+  mockSiteDataManager.unregister();
+  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
+
+  function assertSortByHost(order) {
+    let siteItems = sitesList.getElementsByTagName("richlistitem");
+    for (let i = 0; i < siteItems.length - 1; ++i) {
+      let aHost = siteItems[i].getAttribute("host");
+      let bHost = siteItems[i + 1].getAttribute("host");
+      let result = aHost.localeCompare(bHost);
+      if (order == "ascending") {
+        Assert.lessOrEqual(result, 0, "Should sort sites in the ascending order by host");
+      } else {
+        Assert.greaterOrEqual(result, 0, "Should sort sites in the descending order by host");
+      }
+    }
+  }
+
+  function assertSortByStatus(order) {
+    let siteItems = sitesList.getElementsByTagName("richlistitem");
+    for (let i = 0; i < siteItems.length - 1; ++i) {
+      let aHost = siteItems[i].getAttribute("host");
+      let bHost = siteItems[i + 1].getAttribute("host");
+      let a = findSiteByHost(aHost);
+      let b = findSiteByHost(bHost);
+      let result = 0;
+      if (a.persisted && !b.persisted) {
+        result = 1;
+      } else if (!a.persisted && b.persisted) {
+        result = -1;
+      }
+      if (order == "ascending") {
+        Assert.lessOrEqual(result, 0, "Should sort sites in the ascending order by permission status");
+      } else {
+        Assert.greaterOrEqual(result, 0, "Should sort sites in the descending order by permission status");
+      }
+    }
+  }
+
+  function assertSortByUsage(order) {
+    let siteItems = sitesList.getElementsByTagName("richlistitem");
+    for (let i = 0; i < siteItems.length - 1; ++i) {
+      let aHost = siteItems[i].getAttribute("host");
+      let bHost = siteItems[i + 1].getAttribute("host");
+      let a = findSiteByHost(aHost);
+      let b = findSiteByHost(bHost);
+      let result = a.usage - b.usage;
+      if (order == "ascending") {
+        Assert.lessOrEqual(result, 0, "Should sort sites in the ascending order by usage");
+      } else {
+        Assert.greaterOrEqual(result, 0, "Should sort sites in the descending order by usage");
+      }
+    }
+  }
+
+  function findSiteByHost(host) {
+    return mockSiteDataManager.fakeSites.find(site => site.principal.URI.host == host);
+  }
+});
--- a/browser/components/preferences/in-content/tests/head.js
+++ b/browser/components/preferences/in-content/tests/head.js
@@ -249,17 +249,17 @@ const mockSiteDataManager = {
   _originalRemoveQuotaUsage: null,
 
   getUsage(onUsageResult) {
     let result = this.fakeSites.map(site => ({
       origin: site.principal.origin,
       usage: site.usage,
       persisted: site.persisted
     }));
-    onUsageResult({ result });
+    onUsageResult({ result, resultCode: Components.results.NS_OK });
   },
 
   _removeQuotaUsage(site) {
     var target = site.principals[0].URI.host;
     this.fakeSites = this.fakeSites.filter(fakeSite => {
       return fakeSite.principal.URI.host != target;
     });
   },
new file mode 100755
--- /dev/null
+++ b/browser/components/preferences/in-content/tests/service_worker_test.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <meta http-equiv="Cache-Control" content="public" />
+    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1">
+
+    <title>Service Worker Test</title>
+
+  </head>
+
+  <body>
+    <h1>Service Worker Test</h1>
+    <script type="text/javascript">
+      navigator.serviceWorker.register("service_worker_test.js")
+               .then(regis => document.body.setAttribute("data-test-service-worker-registered", "true"));
+    </script>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/components/preferences/in-content/tests/service_worker_test.js
@@ -0,0 +1,1 @@
+// empty worker, always succeed!
--- a/browser/extensions/formautofill/FormAutofillParent.jsm
+++ b/browser/extensions/formautofill/FormAutofillParent.jsm
@@ -367,21 +367,21 @@ FormAutofillParent.prototype = {
       // Avoid updating the fields that users don't modify.
       let originalAddress = this.profileStorage.addresses.get(address.guid);
       for (let field in address.record) {
         if (address.untouchedFields.includes(field) && originalAddress[field]) {
           address.record[field] = originalAddress[field];
         }
       }
 
-      if (!this.profileStorage.addresses.mergeIfPossible(address.guid, address.record)) {
+      if (!this.profileStorage.addresses.mergeIfPossible(address.guid, address.record, true)) {
         this._recordFormFillingTime("address", "autofill-update", timeStartedFillingMS);
 
         FormAutofillDoorhanger.show(target, "update").then((state) => {
-          let changedGUIDs = this.profileStorage.addresses.mergeToStorage(address.record);
+          let changedGUIDs = this.profileStorage.addresses.mergeToStorage(address.record, true);
           switch (state) {
             case "create":
               if (!changedGUIDs.length) {
                 changedGUIDs.push(this.profileStorage.addresses.add(address.record));
               }
               break;
             case "update":
               if (!changedGUIDs.length) {
--- a/browser/extensions/formautofill/ProfileStorage.jsm
+++ b/browser/extensions/formautofill/ProfileStorage.jsm
@@ -1148,16 +1148,37 @@ class AutofillRecords {
       }
       if (typeof record[key] !== "string" &&
           typeof record[key] !== "number") {
         throw new Error(`"${key}" contains invalid data type.`);
       }
     }
   }
 
+  /**
+   * Merge the record if storage has multiple mergeable records.
+   * @param {Object} targetRecord
+   *        The record for merge.
+   * @param {boolean} [strict = false]
+   *        In strict merge mode, we'll treat the subset record with empty field
+   *        as unable to be merged, but mergeable if in non-strict mode.
+   * @returns {Array.<string>}
+   *          Return an array of the merged GUID string.
+   */
+  mergeToStorage(targetRecord, strict = false) {
+    let mergedGUIDs = [];
+    for (let record of this.data) {
+      if (!record.deleted && this.mergeIfPossible(record.guid, targetRecord, strict)) {
+        mergedGUIDs.push(record.guid);
+      }
+    }
+    this.log.debug("Existing records matching and merging count is", mergedGUIDs.length);
+    return mergedGUIDs;
+  }
+
   // A test-only helper.
   _nukeAllRecords() {
     this._store.data[this._collectionName] = [];
     // test-only, so there's no good reason to request a save!
   }
 
   _stripComputedFields(record) {
     this.VALID_COMPUTED_FIELDS.forEach(field => delete record[field]);
@@ -1168,20 +1189,17 @@ class AutofillRecords {
 
   // An interface to be inherited.
   _computeFields(record) {}
 
   // An interface to be inherited.
   _normalizeFields(record) {}
 
   // An interface to be inherited.
-  mergeIfPossible(guid, record) {}
-
-  // An interface to be inherited.
-  mergeToStorage(targetRecord) {}
+  mergeIfPossible(guid, record, strict) {}
 }
 
 class Addresses extends AutofillRecords {
   constructor(store) {
     super(store, "addresses", VALID_ADDRESS_FIELDS, VALID_ADDRESS_COMPUTED_FIELDS, ADDRESS_SCHEMA_VERSION);
   }
 
   _recordReadProcessor(address) {
@@ -1366,28 +1384,31 @@ class Addresses extends AutofillRecords 
 
   /**
    * Merge new address into the specified address if mergeable.
    *
    * @param  {string} guid
    *         Indicates which address to merge.
    * @param  {Object} address
    *         The new address used to merge into the old one.
+   * @param  {boolean} strict
+   *         In strict merge mode, we'll treat the subset record with empty field
+   *         as unable to be merged, but mergeable if in non-strict mode.
    * @returns {boolean}
    *          Return true if address is merged into target with specific guid or false if not.
    */
-  mergeIfPossible(guid, address) {
+  mergeIfPossible(guid, address, strict) {
     this.log.debug("mergeIfPossible:", guid, address);
 
     let addressFound = this._findByGUID(guid);
     if (!addressFound) {
       throw new Error("No matching address.");
     }
 
-    let addressToMerge = this._clone(address);
+    let addressToMerge = strict ? this._clone(address) : this._cloneAndCleanUp(address);
     this._normalizeRecord(addressToMerge);
     let hasMatchingField = false;
 
     for (let field of this.VALID_FIELDS) {
       let existingField = addressFound[field];
       let incomingField = addressToMerge[field];
       if (incomingField !== undefined && existingField !== undefined) {
         if (incomingField != existingField) {
@@ -1412,45 +1433,36 @@ class Addresses extends AutofillRecords 
     }
 
     // We merge the address only when at least one field has the same value.
     if (!hasMatchingField) {
       this.log.debug("Unable to merge because no field has the same value");
       return false;
     }
 
-    // Early return if the data is the same.
-    let exactlyMatch = this.VALID_FIELDS.every((field) =>
-      addressFound[field] === addressToMerge[field]
-    );
-    if (exactlyMatch) {
+    // Early return if the data is the same or subset.
+    let noNeedToUpdate = this.VALID_FIELDS.every((field) => {
+      // When addressFound doesn't contain a field, it's unnecessary to update
+      // if the same field in addressToMerge is omitted or an empty string.
+      if (addressFound[field] === undefined) {
+        return !addressToMerge[field];
+      }
+
+      // When addressFound contains a field, it's unnecessary to update if
+      // the same field in addressToMerge is omitted or a duplicate.
+      return (addressToMerge[field] === undefined) ||
+             (addressFound[field] === addressToMerge[field]);
+    });
+    if (noNeedToUpdate) {
       return true;
     }
 
     this.update(guid, addressToMerge, true);
     return true;
   }
-
-  /**
-   * Merge the address if storage has multiple mergeable records.
-   * @param {Object} targetAddress
-   *        The address for merge.
-   * @returns {Array.<string>}
-   *          Return an array of the merged GUID string.
-   */
-  mergeToStorage(targetAddress) {
-    let mergedGUIDs = [];
-    for (let address of this.data) {
-      if (!address.deleted && this.mergeIfPossible(address.guid, targetAddress)) {
-        mergedGUIDs.push(address.guid);
-      }
-    }
-    this.log.debug("Existing records matching and merging count is", mergedGUIDs.length);
-    return mergedGUIDs;
-  }
 }
 
 class CreditCards extends AutofillRecords {
   constructor(store) {
     super(store, "creditCards", VALID_CREDIT_CARD_FIELDS, VALID_CREDIT_CARD_COMPUTED_FIELDS, CREDIT_CARD_SCHEMA_VERSION);
   }
 
   _getMaskedCCNumber(ccNumber) {
--- a/browser/extensions/formautofill/test/mochitest/test_on_address_submission.html
+++ b/browser/extensions/formautofill/test/mochitest/test_on_address_submission.html
@@ -114,16 +114,34 @@ add_task(async function check_storage_af
   clickOnElement("input[type=submit]");
 
   let expectedAddresses = TEST_ADDRESSES.slice(0);
   await onStorageChanged("update");
   let matching = await checkAddresses(expectedAddresses);
   ok(matching, "Updated address merged as expected");
 });
 
+// Submit a subset address manually.
+add_task(async function submit_subset_manually() {
+  document.querySelector("form").reset();
+  for (let key in TEST_ADDRESSES[0]) {
+    await setInput("#" + key, TEST_ADDRESSES[0][key]);
+  }
+
+  // Set organization field to empty
+  await setInput("#organization", "");
+  clickOnElement("input[type=submit]");
+
+  let expectedAddresses = TEST_ADDRESSES.slice(0);
+
+  await sleep(1000);
+  let matching = await checkAddresses(expectedAddresses);
+  ok(matching, "The storage is still the same after submitting a subset");
+});
+
 </script>
 
 <div>
 
   <form onsubmit="return false">
     <p>This is a basic form for submitting test.</p>
     <p><label>organization: <input id="organization" name="organization" autocomplete="organization" type="text"></label></p>
     <p><label>streetAddress: <input id="street-address" name="street-address" autocomplete="street-address" type="text"></label></p>
--- a/browser/extensions/formautofill/test/unit/test_addressRecords.js
+++ b/browser/extensions/formautofill/test/unit/test_addressRecords.js
@@ -66,17 +66,17 @@ const MERGE_TESTCASES = [
     expectedAddress: {
       "given-name": "Timothy",
       "street-address": "331 E. Evelyn Avenue",
       "tel": "+16509030800",
       country: "US",
     },
   },
   {
-    description: "Merge a subset",
+    description: "Loose merge a subset",
     addressInStorage: {
       "given-name": "Timothy",
       "street-address": "331 E. Evelyn Avenue",
       "tel": "+16509030800",
       country: "US",
     },
     addressToMerge: {
       "given-name": "Timothy",
@@ -84,16 +84,39 @@ const MERGE_TESTCASES = [
       "tel": "+16509030800",
     },
     expectedAddress: {
       "given-name": "Timothy",
       "street-address": "331 E. Evelyn Avenue",
       "tel": "+16509030800",
       country: "US",
     },
+    noNeedToUpdate: true,
+  },
+  {
+    description: "Strict merge a subset without empty string",
+    addressInStorage: {
+      "given-name": "Timothy",
+      "street-address": "331 E. Evelyn Avenue",
+      "tel": "+16509030800",
+      country: "US",
+    },
+    addressToMerge: {
+      "given-name": "Timothy",
+      "street-address": "331 E. Evelyn Avenue",
+      "tel": "+16509030800",
+    },
+    expectedAddress: {
+      "given-name": "Timothy",
+      "street-address": "331 E. Evelyn Avenue",
+      "tel": "+16509030800",
+      country: "US",
+    },
+    strict: true,
+    noNeedToUpdate: true,
   },
   {
     description: "Merge an address with partial overlaps",
     addressInStorage: {
       "given-name": "Timothy",
       "street-address": "331 E. Evelyn Avenue",
       "tel": "+16509030800",
     },
@@ -439,26 +462,37 @@ MERGE_TESTCASES.forEach((testcase) => {
       (subject, data) =>
         data == "update" && subject.QueryInterface(Ci.nsISupportsString).data == guid
     );
 
     // Force to create sync metadata.
     profileStorage.addresses.pullSyncChanges();
     do_check_eq(getSyncChangeCounter(profileStorage.addresses, guid), 1);
 
-    Assert.ok(profileStorage.addresses.mergeIfPossible(guid, testcase.addressToMerge));
-    await onMerged;
+    Assert.ok(profileStorage.addresses.mergeIfPossible(guid,
+                                                       testcase.addressToMerge,
+                                                       testcase.strict));
+    if (!testcase.noNeedToUpdate) {
+      await onMerged;
+    }
 
     addresses = profileStorage.addresses.getAll();
     Assert.equal(addresses.length, 1);
-    Assert.notEqual(addresses[0].timeLastModified, timeLastModified);
     do_check_record_matches(addresses[0], testcase.expectedAddress);
+    if (testcase.noNeedToUpdate) {
+      Assert.equal(addresses[0].timeLastModified, timeLastModified);
 
-    // Record merging should bump the change counter.
-    do_check_eq(getSyncChangeCounter(profileStorage.addresses, guid), 2);
+      // No need to bump the change counter if the data is unchanged.
+      do_check_eq(getSyncChangeCounter(profileStorage.addresses, guid), 1);
+    } else {
+      Assert.notEqual(addresses[0].timeLastModified, timeLastModified);
+
+      // Record merging should bump the change counter.
+      do_check_eq(getSyncChangeCounter(profileStorage.addresses, guid), 2);
+    }
   });
 });
 
 add_task(async function test_merge_same_address() {
   let profileStorage = await initProfileStorage(TEST_STORE_FILE_NAME, [TEST_ADDRESS_1]);
   let addresses = profileStorage.addresses.getAll();
   let guid = addresses[0].guid;
   let timeLastModified = addresses[0].timeLastModified;
@@ -487,23 +521,38 @@ add_task(async function test_merge_unabl
   do_check_eq(getSyncChangeCounter(profileStorage.addresses, guid), 1);
 
   // Unable to merge because of conflict
   do_check_eq(profileStorage.addresses.mergeIfPossible(guid, TEST_ADDRESS_3), false);
 
   // Unable to merge because no overlap
   do_check_eq(profileStorage.addresses.mergeIfPossible(guid, TEST_ADDRESS_4), false);
 
+  // Unable to strict merge because subset with empty string
+  let subset = Object.assign({}, TEST_ADDRESS_1);
+  subset.organization = "";
+  do_check_eq(profileStorage.addresses.mergeIfPossible(guid, subset, true), false);
+
   // Shouldn't bump the change counter
   do_check_eq(getSyncChangeCounter(profileStorage.addresses, guid), 1);
 });
 
 add_task(async function test_mergeToStorage() {
   let profileStorage = await initProfileStorage(TEST_STORE_FILE_NAME,
                                                 [TEST_ADDRESS_1, TEST_ADDRESS_2]);
   // Merge an address to storage
   let anotherAddress = profileStorage.addresses._clone(TEST_ADDRESS_2);
   profileStorage.addresses.add(anotherAddress);
   anotherAddress.email = "timbl@w3.org";
   do_check_eq(profileStorage.addresses.mergeToStorage(anotherAddress).length, 2);
   do_check_eq(profileStorage.addresses.getAll()[1].email, anotherAddress.email);
   do_check_eq(profileStorage.addresses.getAll()[2].email, anotherAddress.email);
 });
+
+add_task(async function test_mergeToStorage_strict() {
+  let profileStorage = await initProfileStorage(TEST_STORE_FILE_NAME,
+                                                [TEST_ADDRESS_1, TEST_ADDRESS_2]);
+  // Try to merge a subset with empty string
+  let anotherAddress = profileStorage.addresses._clone(TEST_ADDRESS_1);
+  anotherAddress.email = "";
+  do_check_eq(profileStorage.addresses.mergeToStorage(anotherAddress, true).length, 0);
+  do_check_eq(profileStorage.addresses.getAll()[0].email, TEST_ADDRESS_1.email);
+});
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -914,9 +914,9 @@ permissions.remove.tooltip = Clear this 
 # between the Firefox version and the "What's new" link in the About dialog,
 # e.g.: "48.0.2 (32-bit) <What's new>" or "51.0a1 (2016-09-05) (64-bit)".
 aboutDialog.architecture.sixtyFourBit = 64-bit
 aboutDialog.architecture.thirtyTwoBit = 32-bit
 
 # LOCALIZATION NOTE (certImminentDistrust.message):
 # Shown in the browser console when visiting a website that is trusted today,
 # but won't be in the future unless the site operator makes a change.
-certImminentDistrust.message = The security certificate in use on this web site will no longer be trusted in a future release of Firefox. For more information, visit https://wiki.mozilla.org/CA/Upcoming_Distrust_Actions
\ No newline at end of file
+certImminentDistrust.message = The security certificate in use on this website will no longer be trusted in a future release. For more information, visit https://wiki.mozilla.org/CA/Upcoming_Distrust_Actions
\ No newline at end of file
--- a/browser/locales/en-US/chrome/browser/preferences/preferences.properties
+++ b/browser/locales/en-US/chrome/browser/preferences/preferences.properties
@@ -59,18 +59,16 @@ mozNameTemplate=%1$S %2$S
 # LOCALIZATION NOTE (mozstdName, etc.): These labels appear in the tracking
 # protection block lists dialog, mozNameTemplate is used to create the final
 # string. Note that in the future these two strings (name, desc) could be
 # displayed on two different lines.
 mozstdName=Disconnect.me basic protection (Recommended).
 mozstdDesc=Allows some trackers so websites function properly.
 mozfullName=Disconnect.me strict protection.
 mozfullDesc2=Blocks known trackers. Some websites may not function properly.
-# LOCALIZATION NOTE (blocklistChangeRequiresRestart): %S = brandShortName
-blocklistChangeRequiresRestart=%S must restart to change block lists.
 
 #### Master Password
 
 pw_change2empty_in_fips_mode=You are currently in FIPS mode. FIPS requires a non-empty Master Password.
 pw_change_failed_title=Password Change Failed
 
 #### Fonts
 
--- a/devtools/client/netmonitor/src/components/RequestListContent.js
+++ b/devtools/client/netmonitor/src/components/RequestListContent.js
@@ -51,17 +51,16 @@ class RequestListContent extends Compone
       onWaterfallMouseDown: PropTypes.func.isRequired,
       scale: PropTypes.number,
       selectedRequestId: PropTypes.string,
     };
   }
 
   constructor(props) {
     super(props);
-    this.setScalingStyles = this.setScalingStyles.bind(this);
     this.isScrolledToBottom = this.isScrolledToBottom.bind(this);
     this.onHover = this.onHover.bind(this);
     this.onScroll = this.onScroll.bind(this);
     this.onKeyDown = this.onKeyDown.bind(this);
     this.onContextMenu = this.onContextMenu.bind(this);
     this.onFocusedNodeChange = this.onFocusedNodeChange.bind(this);
   }
 
@@ -72,19 +71,16 @@ class RequestListContent extends Compone
       getTabTarget: connector.getTabTarget,
       getLongString: connector.getLongString,
       openStatistics: (open) => dispatch(Actions.openStatistics(connector, open)),
     });
     this.tooltip = new HTMLTooltip(window.parent.document, { type: "arrow" });
   }
 
   componentDidMount() {
-    // Set the CSS variables for waterfall scaling
-    this.setScalingStyles();
-
     // Install event handler for displaying a tooltip
     this.tooltip.startTogglingOnHover(this.refs.contentEl, this.onHover, {
       toggleDelay: REQUESTS_TOOLTIP_TOGGLE_DELAY,
       interactive: true
     });
 
     // Install event handler to hide the tooltip on scroll
     this.refs.contentEl.addEventListener("scroll", this.onScroll, true);
@@ -93,54 +89,31 @@ class RequestListContent extends Compone
   componentWillUpdate(nextProps) {
     // Check if the list is scrolled to bottom before the UI update.
     // The scroll is ever needed only if new rows are added to the list.
     const delta = nextProps.displayedRequests.size - this.props.displayedRequests.size;
     this.shouldScrollBottom = delta > 0 && this.isScrolledToBottom();
   }
 
   componentDidUpdate(prevProps) {
-    // Update the CSS variables for waterfall scaling after props change
-    this.setScalingStyles(prevProps);
-
     let node = this.refs.contentEl;
     // Keep the list scrolled to bottom if a new row was added
     if (this.shouldScrollBottom && node.scrollTop !== MAX_SCROLL_HEIGHT) {
       // Using maximum scroll height rather than node.scrollHeight to avoid sync reflow.
       node.scrollTop = MAX_SCROLL_HEIGHT;
     }
   }
 
   componentWillUnmount() {
     this.refs.contentEl.removeEventListener("scroll", this.onScroll, true);
 
     // Uninstall the tooltip event handler
     this.tooltip.stopTogglingOnHover();
   }
 
-  /**
-   * Set the CSS variables for waterfall scaling. If React supported setting CSS
-   * variables as part of the "style" property of a DOM element, we would use that.
-   *
-   * However, React doesn't support this, so we need to use a hack and update the
-   * DOM element directly: https://github.com/facebook/react/issues/6411
-   */
-  setScalingStyles(prevProps) {
-    const { scale } = this.props;
-    if (prevProps && prevProps.scale === scale) {
-      return;
-    }
-
-    const { style } = this.refs.contentEl;
-    style.removeProperty("--timings-scale");
-    style.removeProperty("--timings-rev-scale");
-    style.setProperty("--timings-scale", scale);
-    style.setProperty("--timings-rev-scale", 1 / scale);
-  }
-
   isScrolledToBottom() {
     const { contentEl } = this.refs;
     const lastChildEl = contentEl.lastElementChild;
 
     if (!lastChildEl) {
       return false;
     }
 
@@ -245,27 +218,29 @@ class RequestListContent extends Compone
       columns,
       displayedRequests,
       firstRequestStartedMillis,
       onCauseBadgeMouseDown,
       onItemMouseDown,
       onSecurityIconMouseDown,
       onThumbnailMouseDown,
       onWaterfallMouseDown,
+      scale,
       selectedRequestId,
     } = this.props;
 
     return (
       div({ className: "requests-list-wrapper"},
         div({ className: "requests-list-table"},
           div({
             ref: "contentEl",
             className: "requests-list-contents",
             tabIndex: 0,
             onKeyDown: this.onKeyDown,
+            style: {"--timings-scale": scale, "--timings-rev-scale": 1 / scale}
           },
             RequestListHeader(),
             displayedRequests.map((item, index) => RequestListItem({
               firstRequestStartedMillis,
               fromCache: item.status === "304" || item.fromCache,
               columns,
               item,
               index,
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -1596,22 +1596,33 @@ HTMLMediaElement::MozRequestDebugInfo(Er
       });
   } else {
     promise->MaybeResolve(result);
   }
 
   return promise.forget();
 }
 
-void
+already_AddRefed<Promise>
 HTMLMediaElement::MozDumpDebugInfo()
 {
+  ErrorResult rv;
+  RefPtr<Promise> promise = CreateDOMPromise(rv);
+  if (NS_WARN_IF(rv.Failed())) {
+    return nullptr;
+  }
   if (mDecoder) {
-    mDecoder->DumpDebugInfo();
-  }
+    mDecoder->DumpDebugInfo()->Then(mAbstractMainThread,
+                                    __func__,
+                                    promise.get(),
+                                    &Promise::MaybeResolveWithUndefined);
+  } else {
+    promise->MaybeResolveWithUndefined();
+  }
+  return promise.forget();
 }
 
 void
 HTMLMediaElement::SetVisible(bool aVisible)
 {
   if (!mDecoder) {
     return;
   }
@@ -6283,26 +6294,27 @@ nsresult HTMLMediaElement::DispatchEvent
 
   return nsContentUtils::DispatchTrustedEvent(OwnerDoc(),
                                               static_cast<nsIContent*>(this),
                                               aName,
                                               false,
                                               false);
 }
 
-nsresult HTMLMediaElement::DispatchAsyncEvent(const nsAString& aName)
+void
+HTMLMediaElement::DispatchAsyncEvent(const nsAString& aName)
 {
   LOG_EVENT(LogLevel::Debug, ("%p Queuing event %s", this,
             NS_ConvertUTF16toUTF8(aName).get()));
 
   // Save events that occur while in the bfcache. These will be dispatched
   // if the page comes out of the bfcache.
   if (mEventDeliveryPaused) {
     mPendingEvents.AppendElement(aName);
-    return NS_OK;
+    return;
   }
 
   nsCOMPtr<nsIRunnable> event;
 
   if (aName.EqualsLiteral("playing")) {
     event = new nsNotifyAboutPlayingRunner(this, TakePendingPlayPromises());
   } else {
     event = new nsAsyncEventRunner(aName, this);
@@ -6317,18 +6329,16 @@ nsresult HTMLMediaElement::DispatchAsync
     }
   } else if (aName.EqualsLiteral("waiting")) {
     mPlayTime.Pause();
     HiddenVideoStop();
   } else if (aName.EqualsLiteral("pause")) {
     mPlayTime.Pause();
     HiddenVideoStop();
   }
-
-  return NS_OK;
 }
 
 nsresult HTMLMediaElement::DispatchPendingMediaEvents()
 {
   NS_ASSERTION(!mEventDeliveryPaused,
                "Must not be in bfcache when dispatching pending media events");
 
   uint32_t count = mPendingEvents.Length();
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -215,19 +215,19 @@ public:
 
   // Called to indicate the download is progressing.
   virtual void DownloadProgressed() final override;
 
   // Called by the media decoder to indicate whether the media cache has
   // suspended the channel.
   virtual void NotifySuspendedByCache(bool aSuspendedByCache) final override;
 
-  virtual bool IsActive() const final override;
+  bool IsActive() const;
 
-  virtual bool IsHidden() const final override;
+  bool IsHidden() const;
 
   // Called by the media decoder and the video frame to get the
   // ImageContainer containing the video data.
   virtual VideoFrameContainer* GetVideoFrameContainer() final override;
   layers::ImageContainer* GetImageContainer();
 
   /**
    * Call this to reevaluate whether we should start/stop due to our owner
@@ -241,17 +241,17 @@ public:
   void UpdateSrcStreamVideoPrincipal(const PrincipalHandle& aPrincipalHandle);
 
   // Called after the MediaStream we're playing rendered a frame to aContainer
   // with a different principalHandle than the previous frame.
   void PrincipalHandleChangedForVideoFrameContainer(VideoFrameContainer* aContainer,
                                                     const PrincipalHandle& aNewPrincipalHandle);
 
   // Dispatch events
-  virtual nsresult DispatchAsyncEvent(const nsAString& aName) final override;
+  virtual void DispatchAsyncEvent(const nsAString& aName) final override;
 
   // Triggers a recomputation of readyState.
   void UpdateReadyState() override { UpdateReadyStateInternal(); }
 
   // Dispatch events that were raised while in the bfcache
   nsresult DispatchPendingMediaEvents();
 
   // Return true if we can activate autoplay assuming enough data has arrived.
@@ -619,17 +619,17 @@ public:
   // Returns a string describing the state of the media player internal
   // data. Used for debugging purposes.
   void GetMozDebugReaderData(nsAString& aString);
 
   // Returns a promise which will be resolved after collecting debugging
   // data from decoder/reader/MDSM. Used for debugging purposes.
   already_AddRefed<Promise> MozRequestDebugInfo(ErrorResult& aRv);
 
-  void MozDumpDebugInfo();
+  already_AddRefed<Promise> MozDumpDebugInfo();
 
   // For use by mochitests. Enabling pref "media.test.video-suspend"
   void SetVisible(bool aVisible);
 
   // For use by mochitests. Enabling pref "media.test.video-suspend"
   bool HasSuspendTaint() const;
 
   // Synchronously, return the next video frame and mark the element unable to
--- a/dom/media/MediaCache.cpp
+++ b/dom/media/MediaCache.cpp
@@ -1250,16 +1250,33 @@ MediaCache::Update()
       FindReusableBlock(now, block->mOwners[0].mStream,
                         block->mOwners[0].mStreamBlock, maxBlocks);
     if (destinationBlockIndex < 0) {
       // Nowhere to place this overflow block. We won't be able to
       // place any more overflow blocks.
       break;
     }
 
+    // Don't evict |destinationBlockIndex| if it is within [cur, end) otherwise
+    // a new channel will be opened to download this block again which is bad.
+    bool inCurrentCachedRange = false;
+    for (BlockOwner& owner : mIndex[destinationBlockIndex].mOwners) {
+      MediaCacheStream* stream = owner.mStream;
+      int64_t end = OffsetToBlockIndexUnchecked(
+        stream->GetCachedDataEndInternal(stream->mStreamOffset));
+      int64_t cur = OffsetToBlockIndexUnchecked(stream->mStreamOffset);
+      if (cur <= owner.mStreamBlock && owner.mStreamBlock < end) {
+        inCurrentCachedRange = true;
+        break;
+      }
+    }
+    if (inCurrentCachedRange) {
+      continue;
+    }
+
     if (IsBlockFree(destinationBlockIndex) ||
         PredictNextUse(now, destinationBlockIndex) > latestPredictedUseForOverflow) {
       // Reuse blocks in the main part of the cache that are less useful than
       // the least useful overflow blocks
 
       nsresult rv = mBlockCache->MoveBlock(blockIndex, destinationBlockIndex);
 
       if (NS_SUCCEEDED(rv)) {
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -1548,42 +1548,45 @@ MediaDecoder::GetDebugInfo()
     this,
     mInfo ? mInfo->mAudio.mChannels : 0,
     mInfo ? mInfo->mAudio.mRate : 0,
     mInfo ? mInfo->HasAudio() : 0,
     mInfo ? mInfo->HasVideo() : 0,
     PlayStateStr());
 }
 
-void
+RefPtr<GenericPromise>
 MediaDecoder::DumpDebugInfo()
 {
   MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
   nsCString str = GetDebugInfo();
 
   nsAutoCString readerStr;
   GetMozDebugReaderData(readerStr);
   if (!readerStr.IsEmpty()) {
     str += "\nreader data:\n";
     str += readerStr;
   }
 
   if (!GetStateMachine()) {
     DUMP("%s", str.get());
-    return;
+    return GenericPromise::CreateAndResolve(true, __func__);
   }
 
-  GetStateMachine()->RequestDebugInfo()->Then(
-    SystemGroup::AbstractMainThreadFor(TaskCategory::Other), __func__,
-    [str] (const nsACString& aString) {
+  return GetStateMachine()->RequestDebugInfo()->Then(
+    SystemGroup::AbstractMainThreadFor(TaskCategory::Other),
+    __func__,
+    [str](const nsACString& aString) {
       DUMP("%s", str.get());
       DUMP("%s", aString.Data());
+      return GenericPromise::CreateAndResolve(true, __func__);
     },
-    [str] () {
+    [str]() {
       DUMP("%s", str.get());
+      return GenericPromise::CreateAndResolve(true, __func__);
     });
 }
 
 RefPtr<MediaDecoder::DebugInfoPromise>
 MediaDecoder::RequestDebugInfo()
 {
   MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
 
--- a/dom/media/MediaDecoder.h
+++ b/dom/media/MediaDecoder.h
@@ -384,17 +384,17 @@ private:
   }
 
   virtual MediaDecoderOwner::NextFrameStatus NextFrameBufferedStatus();
 
   // Returns a string describing the state of the media player internal
   // data. Used for debugging purposes.
   virtual void GetMozDebugReaderData(nsACString& aString);
 
-  virtual void DumpDebugInfo();
+  RefPtr<GenericPromise> DumpDebugInfo();
 
   using DebugInfoPromise = MozPromise<nsCString, bool, true>;
   RefPtr<DebugInfoPromise> RequestDebugInfo();
 
 protected:
   virtual ~MediaDecoder();
 
   // Called when the first audio and/or video from the media file has been loaded
--- a/dom/media/MediaDecoderOwner.h
+++ b/dom/media/MediaDecoderOwner.h
@@ -23,17 +23,17 @@ class HTMLMediaElement;
 
 class MediaDecoderOwner
 {
 public:
   // Called by the media decoder to indicate that the download is progressing.
   virtual void DownloadProgressed() = 0;
 
   // Dispatch an asynchronous event to the decoder owner
-  virtual nsresult DispatchAsyncEvent(const nsAString& aName) = 0;
+  virtual void DispatchAsyncEvent(const nsAString& aName) = 0;
 
   // Triggers a recomputation of readyState.
   virtual void UpdateReadyState() = 0;
 
   /**
    * Fires a timeupdate event. If aPeriodic is true, the event will only
    * be fired if we've not fired a timeupdate event (for any reason) in the
    * last 250ms, as required by the spec when the current time is periodically
@@ -113,22 +113,16 @@ public:
     // The next frame of audio/video is unavailable for the decoder is seeking.
     NEXT_FRAME_UNAVAILABLE_SEEKING,
     // The next frame of audio/video is unavailable for some other reasons
     NEXT_FRAME_UNAVAILABLE,
     // Sentinel value
     NEXT_FRAME_UNINITIALIZED
   };
 
-  // Check if the decoder owner is active.
-  virtual bool IsActive() const = 0;
-
-  // Check if the decoder owner is hidden.
-  virtual bool IsHidden() const = 0;
-
   // Called by media decoder when the audible state changed
   virtual void SetAudibleState(bool aAudible) = 0;
 
   // Notified by the decoder that XPCOM shutdown has begun.
   // The decoder owner should call Shutdown() on the decoder and drop the
   // reference to the decoder to prevent further calls into the decoder.
   virtual void NotifyXPCOMShutdown() = 0;
 
--- a/dom/media/gtest/MockMediaDecoderOwner.h
+++ b/dom/media/gtest/MockMediaDecoderOwner.h
@@ -9,20 +9,17 @@
 #include "mozilla/AbstractThread.h"
 
 namespace mozilla
 {
 
 class MockMediaDecoderOwner : public MediaDecoderOwner
 {
 public:
-  nsresult DispatchAsyncEvent(const nsAString& aName) override
-  {
-    return NS_OK;
-  }
+  void DispatchAsyncEvent(const nsAString& aName) override {}
   void FireTimeUpdate(bool aPeriodic) override {}
   bool GetPaused() override { return false; }
   void MetadataLoaded(const MediaInfo* aInfo,
                       UniquePtr<const MetadataTags> aTags) override
   {
   }
   void NetworkError() override {}
   void DecodeError(const MediaResult& aError) override {}
@@ -31,18 +28,16 @@ public:
   void PlaybackEnded() override {}
   void SeekStarted() override {}
   void SeekCompleted() override {}
   void DownloadProgressed() override {}
   void UpdateReadyState() override {}
   void FirstFrameLoaded() override {}
   void DispatchEncrypted(const nsTArray<uint8_t>& aInitData,
                          const nsAString& aInitDataType) override {}
-  bool IsActive() const override { return true; }
-  bool IsHidden() const override { return false; }
   void DownloadSuspended() override {}
   void DownloadResumed(bool aForceNetworkLoading) override {}
   void NotifySuspendedByCache(bool aIsSuspended) override {}
   void NotifyDecoderPrincipalChanged() override {}
   void SetAudibleState(bool aAudible) override {}
   void NotifyXPCOMShutdown() override {}
   AbstractThread* AbstractMainThread() const override
   {
--- a/dom/media/test/manifest.js
+++ b/dom/media/test/manifest.js
@@ -1625,18 +1625,16 @@ const DEBUG_TEST_LOOP_FOREVER = false;
 //      the token back to the manager. The manager may either start the next run
 //      or end the mochitest if all the tests are done.
 function MediaTestManager() {
 
   // Set a very large timeout to prevent Mochitest timeout.
   // Instead MediaTestManager will manage timeout of each test.
   SimpleTest.requestLongerTimeout(1000);
 
-  this.hasTimeout = false;
-
   // Return how many seconds elapsed since |begin|.
   function elapsedTime(begin) {
     var end = new Date();
     return (end.getTime() - begin.getTime()) / 1000;
   }
   // Sets up a MediaTestManager to runs through the 'tests' array, which needs
   // to be one of, or have the same fields as, the g*Test arrays of tests. Uses
   // the user supplied 'startTest' function to initialize the test. This
@@ -1678,20 +1676,20 @@ function MediaTestManager() {
 
   // Registers that the test corresponding to 'token' has been started.
   // Don't call more than once per token.
   this.started = function(token, handler) {
     this.tokens.push(token);
     this.numTestsRunning++;
     this.handlers[token] = handler;
 
-    var onTimeout = () => {
-      this.hasTimeout = true;
+    var onTimeout = async () => {
       ok(false, "Test timed out!");
       info(`${token} timed out!`);
+      await dumpDebugInfoForToken(token);
       this.finished(token);
     };
     // Default timeout to 180s for each test.
     // Call SimpleTest._originalSetTimeout() to bypass the flaky timeout checker.
     this.timers[token] = SimpleTest._originalSetTimeout.call(window, onTimeout, 180000);
 
     is(this.numTestsRunning, this.tokens.length,
        "[started " + token + " t=" + elapsedTime(this.startTime) + "] Length of array should match number of running tests");
@@ -1747,19 +1745,16 @@ function MediaTestManager() {
         !DEBUG_TEST_LOOP_FOREVER &&
         this.tokens.length == 0 &&
         !this.isShutdown)
     {
       this.isShutdown = true;
       if (this.onFinished) {
         this.onFinished();
       }
-      if (this.hasTimeout) {
-        dumpDebugInfo();
-      }
       var onCleanup = () => {
         var end = new Date();
         SimpleTest.info("Finished at " + end + " (" + (end.getTime() / 1000) + "s)");
         SimpleTest.info("Running time: " + elapsedTime(this.startTime) + "s");
         SimpleTest.finish();
       };
       mediaTestCleanup(onCleanup);
       return;
@@ -1784,16 +1779,30 @@ function mediaTestCleanup(callback) {
     SpecialPowers.exactGC(callback);
 }
 
 // B2G emulator and Android 2.3 are condidered slow platforms
 function isSlowPlatform() {
   return SpecialPowers.Services.appinfo.name == "B2G" || getAndroidVersion() == 10;
 }
 
+async function dumpDebugInfoForToken(token) {
+  for (let v of document.getElementsByTagName("video")) {
+    if (token === v.token) {
+      return v.mozDumpDebugInfo();
+    }
+  }
+  for (let a of document.getElementsByTagName("audio")) {
+    if (token === a.token) {
+      return a.mozDumpDebugInfo();
+    }
+  }
+  return Promise.resolve();
+}
+
 function dumpDebugInfo() {
   for (var v of document.getElementsByTagName("video")) {
     v.mozDumpDebugInfo();
   }
   for (var a of document.getElementsByTagName("audio")) {
     a.mozDumpDebugInfo();
   }
 }
--- a/dom/webidl/HTMLMediaElement.webidl
+++ b/dom/webidl/HTMLMediaElement.webidl
@@ -103,17 +103,17 @@ partial interface HTMLMediaElement {
   [Func="HasDebuggerOrTabsPrivilege"]
   readonly attribute MediaSource? mozMediaSourceObject;
   [Func="HasDebuggerOrTabsPrivilege"]
   readonly attribute DOMString mozDebugReaderData;
   [Func="HasDebuggerOrTabsPrivilege", NewObject]
   Promise<DOMString> mozRequestDebugInfo();
 
   [Pref="media.test.dumpDebugInfo"]
-  void mozDumpDebugInfo();
+  Promise<void> mozDumpDebugInfo();
 
   attribute MediaStream? srcObject;
 
   attribute boolean mozPreservesPitch;
   readonly attribute boolean mozAutoplayEnabled;
 
   // NB: for internal use with the video controls:
   [Func="IsChromeOrXBL"] attribute boolean mozAllowCasting;
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -7265,29 +7265,67 @@ nsTextFrame::DrawTextRunAndDecorations(R
       bCoord = (frameBStart - dec.mBaselineOffset) / app;
 
       params.color = dec.mColor;
       params.offset = metrics.*lineOffset;
       params.style = dec.mStyle;
       PaintDecorationLine(params);
     };
 
+    // We create a clip region in order to draw the decoration lines only in the
+    // range of the text. Restricting the draw area prevents the decoration lines
+    // to be drawn multiple times when a part of the text is selected.
+
+    // We skip clipping for the following cases:
+    // - drawing the whole text
+    // - having different orientation of the text and the writing-mode, such as
+    //   "text-combine-upright" (Bug 1408825)
+    bool skipClipping = aRange.Length() == mTextRun->GetLength() ||
+                        verticalDec != verticalRun;
+
+    gfxRect clipRect;
+    if (!skipClipping) {
+      // Get the inline-size according to the specified range.
+      gfxFloat clipLength = mTextRun->GetAdvanceWidth(aRange, aParams.provider);
+
+      clipRect.width = verticalDec ? frameSize.width : clipLength / app;
+      clipRect.height = verticalDec ? clipLength / app : frameSize.height;
+
+      const bool isInlineReversed = mTextRun->IsInlineReversed();
+      if (verticalDec) {
+        clipRect.y = (isInlineReversed ? aTextBaselinePt.y - clipLength
+                                       : aTextBaselinePt.y) / app;
+      } else {
+        clipRect.x = (isInlineReversed ? aTextBaselinePt.x - clipLength
+                                       : aTextBaselinePt.x) / app;
+      }
+
+      clipRect.Round();
+      params.context->Clip(clipRect);
+    }
+
     // Underlines
     params.decoration = NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE;
     for (const LineDecoration& dec : Reversed(aDecorations.mUnderlines)) {
       paintDecorationLine(dec, &Metrics::underlineSize,
                           &Metrics::underlineOffset);
     }
 
     // Overlines
     params.decoration = NS_STYLE_TEXT_DECORATION_LINE_OVERLINE;
     for (const LineDecoration& dec : Reversed(aDecorations.mOverlines)) {
       paintDecorationLine(dec, &Metrics::underlineSize, &Metrics::maxAscent);
     }
 
+    // Some glyphs and emphasis marks may extend outside the region, so we reset
+    // the clip region here. For an example, italic glyphs.
+    if (!skipClipping) {
+      params.context->PopClip();
+    }
+
     {
       gfxContextMatrixAutoSaveRestore unscaledRestorer;
       if (scaledRestorer.HasMatrix()) {
         unscaledRestorer.SetContext(aParams.context);
         aParams.context->SetMatrix(scaledRestorer.Matrix());
       }
 
       // CSS 2.1 mandates that text be painted after over/underlines,
@@ -7295,22 +7333,31 @@ nsTextFrame::DrawTextRunAndDecorations(R
       DrawTextRun(aRange, aTextBaselinePt, aParams);
     }
 
     // Emphasis marks
     DrawEmphasisMarks(aParams.context, wm,
                       aTextBaselinePt, aParams.framePt, aRange,
                       aParams.decorationOverrideColor, aParams.provider);
 
+    // Re-apply the clip region when the line-through is being drawn.
+    if (!skipClipping) {
+      params.context->Clip(clipRect);
+    }
+
     // Line-throughs
     params.decoration = NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH;
     for (const LineDecoration& dec : Reversed(aDecorations.mStrikes)) {
       paintDecorationLine(dec, &Metrics::strikeoutSize,
                           &Metrics::strikeoutOffset);
     }
+
+    if (!skipClipping) {
+      params.context->PopClip();
+    }
 }
 
 void
 nsTextFrame::DrawText(Range aRange, const gfx::Point& aTextBaselinePt,
                       const DrawTextParams& aParams)
 {
   TextDecorations decorations;
   GetTextDecorations(aParams.textStyle->PresContext(),
--- a/layout/reftests/selection/reftest.list
+++ b/layout/reftests/selection/reftest.list
@@ -29,22 +29,25 @@ random-if(Android) needs-focus != pseudo
 # These tests uses Highlight and HighlightText color keywords, they are not same as text selection color on Mac.
 random-if(Android) fails-if(cocoaWidget) needs-focus == non-themed-widget.html non-themed-widget-ref.html
 random-if(Android) fails-if(cocoaWidget) needs-focus == themed-widget.html themed-widget-ref.html
 == addrange-1.html addrange-ref.html
 fuzzy-if(skiaContent,1,1200) == addrange-2.html addrange-ref.html
 == splitText-normalize.html splitText-normalize-ref.html
 == modify-range.html modify-range-ref.html
 == dom-mutations.html dom-mutations-ref.html
-fuzzy-if(OSX==1010,9,1) fuzzy-if(OSX&&skiaContent,6,1) fuzzy-if(skiaContent&&!OSX,1,2138) == trailing-space-1.html trailing-space-1-ref.html
+fuzzy-if(OSX==1010,9,1) fuzzy-if(OSX&&skiaContent,6,1) fuzzy-if(skiaContent&&!OSX,1,2138) fails-if(webrender) == trailing-space-1.html trailing-space-1-ref.html # Bug 1414125
 != invalidation-1-ref.html invalidation-2-ref.html
 == invalidation-1a.html invalidation-1-ref.html
 == invalidation-1b.html invalidation-1-ref.html
 == invalidation-1c.html invalidation-1-ref.html
 == invalidation-1d.html invalidation-1-ref.html
 == invalidation-1e.html invalidation-1-ref.html
 == invalidation-1f.html invalidation-1-ref.html
 == invalidation-2a.html invalidation-2-ref.html
 == invalidation-2b.html invalidation-2-ref.html
 == invalidation-2c.html invalidation-2-ref.html
 == invalidation-2d.html invalidation-2-ref.html
 == invalidation-2e.html invalidation-2-ref.html
 == invalidation-2f.html invalidation-2-ref.html
+fuzzy(7,2) fuzzy-if(OSX,1,1) fails-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu)&&isDebugBuild&&!browserIsRemote) fails-if(Android) needs-focus == rtl-selection-with-decoration.html rtl-selection-with-decoration-ref.html
+fails-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu)&&isDebugBuild&&!browserIsRemote) fails-if(Android) needs-focus == semitransparent-decoration-line.html semitransparent-decoration-line-ref.html
+fuzzy-if(OSX,1,6) fails-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu)&&isDebugBuild&&!browserIsRemote) fails-if(Android) needs-focus == writing-mode.html writing-mode-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/selection/rtl-selection-with-decoration-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8" />
+    <title>rtl-selection-with-decoration-ref</title>
+    <style>
+      p {
+        font-size: 2em;
+        text-decoration-color: rgba(0, 0, 0, 0.3);
+      }
+      #text1 {
+        text-decoration-line: line-through underline;
+      }
+      #text2 {
+        text-decoration-line: line-through overline;
+      }
+    </style>
+  </head>
+  <body>
+    <p id="text1" lang="he">זוהי עובדה הקורא שדעתו מבוססת של תהיה</p>
+    <p id="text2" lang="he" dir="rtl">זוהי עובדה הקורא שדעתו מבוססת של תהיה</p>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/selection/rtl-selection-with-decoration.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8" />
+    <title>rtl-selection-with-decoration</title>
+    <style>
+      p {
+        font-size: 2em;
+        text-decoration-color: rgba(0, 0, 0, 0.3);
+      }
+      #text1 {
+        text-decoration-line: line-through underline;
+      }
+      #text2 {
+        text-decoration-line: line-through overline;
+      }
+      ::-moz-selection {
+        background-color: white;
+        color: black;
+      }
+    </style>
+    <script type="text/javascript" charset="utf-8">
+      function select() {
+        window.getSelection().removeAllRanges();
+        var elems = document.getElementsByTagName('p');
+        for (var i = 0; i < elems.length; ++i) {
+          var range = document.createRange();
+          range.setStart(elems[i].firstChild, 2);
+          range.setEnd(elems[i].firstChild, 9);
+          window.getSelection().addRange(range);
+        }
+      }
+    </script>
+  </head>
+  <body onload="select()">
+    <p id="text1" lang="he">זוהי עובדה הקורא שדעתו מבוססת של תהיה</p>
+    <p id="text2" lang="he" dir="rtl">זוהי עובדה הקורא שדעתו מבוססת של תהיה</p>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/selection/semitransparent-decoration-line-ref.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8" />
+    <title>semitransparent-decoration-line-ref</title>
+    <style>
+      .underline {
+        text-decoration: underline rgba(0,0,0,0.3);
+        font-size: 2em;
+      }
+      .overline {
+        text-decoration: overline rgba(0,0,0,0.3);
+        font-size: 2em;
+      }
+      .line-through {
+        text-decoration: line-through rgba(0,0,0,0.3);
+        font-size: 2em;
+      }
+    </style>
+  </head>
+  <body>
+    <p class="underline">Lorem ipsum dolor sit amet</p>
+    <p class="overline">Lorem ipsum dolor sit amet</p>
+    <p class="line-through">Lorem ipsum dolor sit amet</p>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/selection/semitransparent-decoration-line.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8" />
+    <title>semitransparent-decoration-line</title>
+    <style>
+      .underline {
+        text-decoration: underline rgba(0,0,0,0.3);
+        font-size: 2em;
+      }
+      .overline {
+        text-decoration: overline rgba(0,0,0,0.3);
+        font-size: 2em;
+      }
+      .line-through {
+        text-decoration: line-through rgba(0,0,0,0.3);
+        font-size: 2em;
+      }
+      ::-moz-selection {
+        background-color: white;
+        color: black;
+      }
+    </style>
+    <script type="text/javascript" charset="utf-8">
+      function select() {
+        window.getSelection().removeAllRanges();
+        var elems = document.getElementsByTagName('p');
+        for (var i = 0; i < elems.length; ++i) {
+          var range = document.createRange();
+          range.setStart(elems[i].firstChild, 2);
+          range.setEnd(elems[i].firstChild, 9);
+          window.getSelection().addRange(range);
+        }
+      }
+    </script>
+  </head>
+  <body onload="select()">
+    <p class="underline">Lorem ipsum dolor sit amet</p>
+    <p class="overline">Lorem ipsum dolor sit amet</p>
+    <p class="line-through">Lorem ipsum dolor sit amet</p>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/selection/writing-mode-ref.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8" />
+    <title>writing-mode-ref</title>
+    <style>
+      p {
+        display: inline-block;
+        margin: 10px;
+      }
+      .line-through {
+        text-decoration: line-through wavy rgba(0, 0, 0, 0.3);
+      }
+      .wm-vertical-lr {
+        writing-mode: vertical-lr;
+      }
+      .wm-vertical-rl {
+        writing-mode: vertical-rl;
+      }
+      .wm-sideways-lr {
+        writing-mode: sideways-lr;
+      }
+      .wm-sideways-rl {
+        writing-mode: sideways-rl;
+      }
+    </style>
+  </head>
+  <body>
+    <p lang="zh" dir="ltr" class="wm-vertical-lr line-through">你好世界!Hello World!</p>
+    <p lang="zh" dir="ltr" class="wm-vertical-rl line-through">你好世界!Hello World!</p>
+    <p lang="zh" dir="ltr" class="wm-sideways-lr line-through">你好世界!Hello World!</p>
+    <p lang="zh" dir="ltr" class="wm-sideways-rl line-through">你好世界!Hello World!</p>
+    <br/>
+    <p lang="zh" dir="rtl" class="wm-vertical-lr line-through">你好世界!Hello World!</p>
+    <p lang="zh" dir="rtl" class="wm-vertical-rl line-through">你好世界!Hello World!</p>
+    <p lang="zh" dir="rtl" class="wm-sideways-lr line-through">你好世界!Hello World!</p>
+    <p lang="zh" dir="rtl" class="wm-sideways-rl line-through">你好世界!Hello World!</p>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/selection/writing-mode.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8" />
+    <title>writing-mode</title>
+    <style>
+      p {
+        display: inline-block;
+        margin: 10px;
+      }
+      .line-through {
+        text-decoration: line-through wavy rgba(0, 0, 0, 0.3);
+      }
+      ::-moz-selection {
+        background-color: white;
+        color: black;
+      }
+      .wm-vertical-lr {
+        writing-mode: vertical-lr;
+        display: inline-block;
+      }
+      .wm-vertical-rl {
+        writing-mode: vertical-rl;
+      }
+      .wm-sideways-lr {
+        writing-mode: sideways-lr;
+      }
+      .wm-sideways-rl {
+        writing-mode: sideways-rl;
+      }
+    </style>
+    <script type="text/javascript" charset="utf-8">
+      function select() {
+        window.getSelection().removeAllRanges();
+        var elems = document.getElementsByTagName('p');
+        for (var i = 0; i < elems.length; ++i) {
+          var range = document.createRange();
+          range.setStart(elems[i].firstChild, 2);
+          range.setEnd(elems[i].firstChild, 9);
+          window.getSelection().addRange(range);
+        }
+      }
+    </script>
+  </head>
+  <body onload="select()">
+    <p lang="zh" dir="ltr" class="wm-vertical-lr line-through">你好世界!Hello World!</p>
+    <p lang="zh" dir="ltr" class="wm-vertical-rl line-through">你好世界!Hello World!</p>
+    <p lang="zh" dir="ltr" class="wm-sideways-lr line-through">你好世界!Hello World!</p>
+    <p lang="zh" dir="ltr" class="wm-sideways-rl line-through">你好世界!Hello World!</p>
+    <br/>
+    <p lang="zh" dir="rtl" class="wm-vertical-lr line-through">你好世界!Hello World!</p>
+    <p lang="zh" dir="rtl" class="wm-vertical-rl line-through">你好世界!Hello World!</p>
+    <p lang="zh" dir="rtl" class="wm-sideways-lr line-through">你好世界!Hello World!</p>
+    <p lang="zh" dir="rtl" class="wm-sideways-rl line-through">你好世界!Hello World!</p>
+  </body>
+</html>
--- a/layout/reftests/text-shadow/reftest.list
+++ b/layout/reftests/text-shadow/reftest.list
@@ -8,17 +8,17 @@ random-if(Android) == basic-negcoord.xul
 == blur-opacity.html blur-opacity-ref.html
 
 == basic.html basic-ref.html
 == basic-negcoord.html basic-negcoord-ref.html
 == basic-opacity.html basic-opacity-ref.html
 != blur.html blur-notref.html
 == color-inherit.html color-inherit-ref.html
 == color-parserorder.html color-parserorder-ref.html
-== decorations-multiple-zorder.html decorations-multiple-zorder-ref.html
+fails-if(webrender) == decorations-multiple-zorder.html decorations-multiple-zorder-ref.html # Bug 1414125
 == multiple-noblur.html multiple-noblur-ref.html
 == quirks-decor-noblur.html quirks-decor-noblur-ref.html
 == standards-decor-noblur.html standards-decor-noblur-ref.html
 == padding-decoration.html padding-decoration-ref.html
 == textindent.html textindent-ref.html
 == lineoverflow.html lineoverflow-ref.html
 
 == overflow-not-scrollable-1.html overflow-not-scrollable-1-ref.html
--- a/media/libstagefright/binding/H264.cpp
+++ b/media/libstagefright/binding/H264.cpp
@@ -957,24 +957,26 @@ H264::ExtractExtraData(const mozilla::Me
 H264::HasSPS(const mozilla::MediaByteBuffer* aExtraData)
 {
   return NumSPS(aExtraData) > 0;
 }
 
 /* static */ uint8_t
 H264::NumSPS(const mozilla::MediaByteBuffer* aExtraData)
 {
-  if (!aExtraData) {
+  if (!aExtraData || aExtraData->IsEmpty()) {
     return 0;
   }
 
   BufferReader reader(aExtraData);
-  const uint8_t* ptr = reader.Read(5);
+  if (!reader.Read(5)) {
+    return 0;
+  }
   auto res = reader.ReadU8();
-  if (!ptr || res.isErr()) {
+  if (res.isErr()) {
     return 0;
   }
   return res.unwrap() & 0x1f;
 }
 
 /* static */ bool
 H264::CompareExtraData(const mozilla::MediaByteBuffer* aExtraData1,
                        const mozilla::MediaByteBuffer* aExtraData2)
--- a/servo/Cargo.lock
+++ b/servo/Cargo.lock
@@ -3471,17 +3471,17 @@ dependencies = [
  "url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "uuid 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "webdriver 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "webrender"
 version = "0.53.1"
-source = "git+https://github.com/servo/webrender#82e85d7f407ff38f08fb0901724918704d6d2e4a"
+source = "git+https://github.com/servo/webrender#058dd77d35d938974d16dcf12e2f75a0a6e4cdef"
 dependencies = [
  "app_units 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "bincode 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "bitflags 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-foundation 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-graphics 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-text 7.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -3498,17 +3498,17 @@ dependencies = [
  "thread_profiler 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
  "webrender_api 0.53.1 (git+https://github.com/servo/webrender)",
 ]
 
 [[package]]
 name = "webrender_api"
 version = "0.53.1"
-source = "git+https://github.com/servo/webrender#82e85d7f407ff38f08fb0901724918704d6d2e4a"
+source = "git+https://github.com/servo/webrender#058dd77d35d938974d16dcf12e2f75a0a6e4cdef"
 dependencies = [
  "app_units 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "bincode 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "bitflags 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-foundation 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-graphics 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "dwrote 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
--- a/toolkit/components/browser/nsIPrintingPromptService.idl
+++ b/toolkit/components/browser/nsIPrintingPromptService.idl
@@ -9,17 +9,17 @@
 #include "nsIWebBrowserPrint.idl"
 #include "nsIWebProgressListener.idl"
 #include "nsIPrintProgressParams.idl"
 #include "nsIPrintSettings.idl"
 #include "nsIObserver.idl"
 
 interface nsIDOMWindow;
 
-[scriptable, uuid(328daa3e-09e4-455f-bb6f-0a921766042f)]
+[scriptable, uuid(72006d06-a2a5-4250-ae92-04b2f0e2ab8d)]
 interface nsIPrintingPromptService : nsISupports
 {
    /**
    *  This service enables embedders to implement their own Print and Progress Dialogs.
    *  Each platform has a "base" or "basckstop" implementation of the service. The 
    *  service is automatically registered at start up.
    *
    *  Historically, platform toolkits with native dialogs have implemented them in the GFX layer
@@ -33,28 +33,27 @@ interface nsIPrintingPromptService : nsI
    *
    *           Returning NS_OK assumes that the PrintSettings object was correctly filled in and
    *           if it does not have valid fields for printer name, etc. it may also terminate.
    *
    *           Defaults for platform service:           
    *             showPrintDialog       - displays a native dialog
    *             showPageSetup         - displays a XUL dialog
    *             showProgress          - displays a XUL dialog
-   *             showPrinterProperties - n/a
    *
    *           Summary for Windows Embedders:
    *             Stated once again: There is no "fallback" native platform support in GFX for the
    *             displaying of the native print dialog. The current default implementation for Windows
    *             display a native print dialog but a XUL-based progress dialog.
    *             If you wish to have a native progress dialog on Windows you will have to create and 
    *             register your own service.
    *  
    *             Note: The Windows version Mozilla implements this service which is 
    *                   automatically built and registered for you. You can use it as an example.
-   *                   It is located at "mozilla/toolkit/components/printingui/win". That service
+   *                   It is located at "widget/windows/nsPrintDialogService.cpp". That service
    *                   is capable of displaying a native print dialog and a XUL progress dialog.
    *
    *             To fly your own dialog you may:
    *
    *              1) Implement this service to display at least the Print Dialog and a Print Progress Dialog
    *                 or you may implement just one of the dialogs and pass back NS_ERROR_NOT_IMPLEMENTED
    *                 for any of the others.
    *
@@ -68,30 +67,27 @@ interface nsIPrintingPromptService : nsI
    *  Mac: The GFX layer still supports default toolkit behavior for displaying the Print Dialog.
    *       If an embedder implemented service returns NS_ERROR_NOT_IMPLEMENTED for "showPrintDialog"
    *       The toolkit will display the native print dialog.
    *
    *       Defaults for platform service:           
    *       Mac OS9: showPrintDialog       - displays a native dialog
    *                showPageSetup         - displays a native dialog
    *                showProgress          - displays a XUL dialog
-   *                showPrinterProperties - n/a
-   *                
+   *
    *       Mac OSX: showPrintDialog       - displays a native dialog
    *                showPageSetup         - displays a native dialog
    *                showProgress          - not implemented (provided by OS)
-   *                showPrinterProperties - n/a
-   *                
+   *
    *  GTK: There are no native dialog for GTK.
    *
-   *       Defaults for platform service:           
-   *         showPrintDialog       - displays a XUL dialog
-   *         showPageSetup         - displays a XUL dialog
+   *       Defaults for platform service:
+   *         showPrintDialog       - displays a native dialog
+   *         showPageSetup         - displays a native dialog
    *         showProgress          - displays a XUL dialog
-   *         showPrinterProperties - displays a XUL dialog
    *
    */
  
 
 
   /**
    *  Show the Print Dialog 
    *
@@ -136,30 +132,16 @@ interface nsIPrintingPromptService : nsI
    *  @param aObs - An observer to know if the contents of the Print Settings 
    *                object has changed while the dialog is being shown. 
    *                For example, some platforms may implement an "Apply" button (not required)
    */
   void showPageSetup(in mozIDOMWindowProxy parent,
                      in nsIPrintSettings printSettings,
                      in nsIObserver aObs);
 
-  /**
-   *  Sometimes platforms need to bring up a special properties dialog for showing
-   *  print specific properties. Although the PrintSettings has a place to set the 
-   *  printer name, here is is an argument to be clear as to what printer is being
-   *  asked to have the properties set for it. The Printer name in the PS is ignored.
-   *
-   *  @param parent - a DOM windows the dialog will be parented to (required)
-   *  @param printerName - name of printer (required)
-   *  @param printSettings - PrintSettings for page setup (required)
-   */
-  void showPrinterProperties(in mozIDOMWindowProxy parent,
-                             in wstring printerName,
-                             in nsIPrintSettings printSettings);
-
 };
 
 %{C++
 // {260FEDC5-524D-4aa6-9A41-E829F4C78B92}
 #define NS_PRINTINGPROMPTSERVICE_IID \
  {0x260fedc5, 0x524d, 0x4aa6, { 0x9a, 0x41, 0xe8, 0x29, 0xf4, 0xc7, 0x8b, 0x92}}
 %}
 
deleted file mode 100644
--- a/toolkit/components/printing/content/printdialog.js
+++ /dev/null
@@ -1,408 +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/. */
-
-var dialog;
-var printService       = null;
-var gOriginalNumCopies = 1;
-
-var paramBlock;
-var gPrefs             = null;
-var gPrintSettings     = null;
-var gWebBrowserPrint   = null;
-var gPrintSetInterface = Components.interfaces.nsIPrintSettings;
-var doDebug            = false;
-
-// ---------------------------------------------------
-function initDialog() {
-  dialog = {};
-
-  dialog.propertiesButton = document.getElementById("properties");
-  dialog.descText         = document.getElementById("descText");
-
-  dialog.printrangeGroup = document.getElementById("printrangeGroup");
-  dialog.allpagesRadio   = document.getElementById("allpagesRadio");
-  dialog.rangeRadio      = document.getElementById("rangeRadio");
-  dialog.selectionRadio  = document.getElementById("selectionRadio");
-  dialog.frompageInput   = document.getElementById("frompageInput");
-  dialog.frompageLabel   = document.getElementById("frompageLabel");
-  dialog.topageInput     = document.getElementById("topageInput");
-  dialog.topageLabel     = document.getElementById("topageLabel");
-
-  dialog.numCopiesInput  = document.getElementById("numCopiesInput");
-
-  dialog.printframeGroup      = document.getElementById("printframeGroup");
-  dialog.aslaidoutRadio       = document.getElementById("aslaidoutRadio");
-  dialog.selectedframeRadio   = document.getElementById("selectedframeRadio");
-  dialog.eachframesepRadio    = document.getElementById("eachframesepRadio");
-  dialog.printframeGroupLabel = document.getElementById("printframeGroupLabel");
-
-  dialog.fileCheck       = document.getElementById("fileCheck");
-  dialog.printerLabel    = document.getElementById("printerLabel");
-  dialog.printerList     = document.getElementById("printerList");
-
-  dialog.printButton     = document.documentElement.getButton("accept");
-
-  // <data> elements
-  dialog.printName       = document.getElementById("printButton");
-  dialog.fpDialog        = document.getElementById("fpDialog");
-
-  dialog.enabled         = false;
-}
-
-// ---------------------------------------------------
-function checkInteger(element) {
-  var value = element.value;
-  if (value && value.length > 0) {
-    value = value.replace(/[^0-9]/g, "");
-    if (!value) value = "";
-    element.value = value;
-  }
-  if (!value || value < 1 || value > 999)
-    dialog.printButton.setAttribute("disabled", "true");
-  else
-    dialog.printButton.removeAttribute("disabled");
-}
-
-// ---------------------------------------------------
-function stripTrailingWhitespace(element) {
-  var value = element.value;
-  value = value.replace(/\s+$/, "");
-  element.value = value;
-}
-
-// ---------------------------------------------------
-function getPrinterDescription(printerName) {
-  return gPrefs.getCharPref("print.printer_" + printerName + ".printer_description", "");
-}
-
-// ---------------------------------------------------
-function listElement(aListElement) {
-    this.listElement = aListElement;
-  }
-
-listElement.prototype =
-  {
-    clearList() {
-          // remove the menupopup node child of the menulist.
-          var popup = this.listElement.firstChild;
-          if (popup) {
-            this.listElement.removeChild(popup);
-          }
-        },
-
-    appendPrinterNames(aDataObject) {
-          if ((null == aDataObject) || !aDataObject.hasMore()) {
-            // disable dialog
-            this.listElement.setAttribute("value", "");
-            this.listElement.setAttribute("label",
-              document.getElementById("printingBundle")
-                      .getString("noprinter"));
-
-            this.listElement.setAttribute("disabled", "true");
-            dialog.printerLabel.setAttribute("disabled", "true");
-            dialog.propertiesButton.setAttribute("disabled", "true");
-            dialog.fileCheck.setAttribute("disabled", "true");
-            dialog.printButton.setAttribute("disabled", "true");
-          } else {
-            // build popup menu from printer names
-            var list = document.getElementById("printerList");
-            do {
-              let printerNameStr = aDataObject.getNext();
-              list.appendItem(printerNameStr, printerNameStr, getPrinterDescription(printerNameStr));
-            } while (aDataObject.hasMore());
-            this.listElement.removeAttribute("disabled");
-          }
-        }
-  };
-
-// ---------------------------------------------------
-function getPrinters() {
-  var selectElement = new listElement(dialog.printerList);
-  selectElement.clearList();
-
-  var printerEnumerator;
-  try {
-    printerEnumerator =
-        Components.classes["@mozilla.org/gfx/printerenumerator;1"]
-                  .getService(Components.interfaces.nsIPrinterEnumerator)
-                  .printerNameList;
-  } catch (e) { printerEnumerator = null; }
-
-  selectElement.appendPrinterNames(printerEnumerator);
-  selectElement.listElement.value = printService.defaultPrinterName;
-
-  // make sure we load the prefs for the initially selected printer
-  setPrinterDefaultsForSelectedPrinter();
-}
-
-
-// ---------------------------------------------------
-// update gPrintSettings with the defaults for the selected printer
-function setPrinterDefaultsForSelectedPrinter() {
-  gPrintSettings.printerName = dialog.printerList.value;
-
-  dialog.descText.value = getPrinterDescription(gPrintSettings.printerName);
-
-  // First get any defaults from the printer
-  printService.initPrintSettingsFromPrinter(gPrintSettings.printerName, gPrintSettings);
-
-  // now augment them with any values from last time
-  printService.initPrintSettingsFromPrefs(gPrintSettings, true, gPrintSetInterface.kInitSaveAll);
-
-  if (doDebug) {
-    dump("setPrinterDefaultsForSelectedPrinter: printerName='" + gPrintSettings.printerName + "', paperName='" + gPrintSettings.paperName + "'\n");
-  }
-}
-
-// ---------------------------------------------------
-function displayPropertiesDialog() {
-  gPrintSettings.numCopies = dialog.numCopiesInput.value;
-  try {
-    var printingPromptService = Components.classes["@mozilla.org/embedcomp/printingprompt-service;1"]
-                                                 .getService(Components.interfaces.nsIPrintingPromptService);
-    if (printingPromptService) {
-      printingPromptService.showPrinterProperties(null, dialog.printerList.value, gPrintSettings);
-      dialog.numCopiesInput.value = gPrintSettings.numCopies;
-    }
-  } catch (e) {
-    dump("problems getting printingPromptService\n");
-  }
-}
-
-// ---------------------------------------------------
-function doPrintRange(inx) {
-  if (inx == 1) {
-    dialog.frompageInput.removeAttribute("disabled");
-    dialog.frompageLabel.removeAttribute("disabled");
-    dialog.topageInput.removeAttribute("disabled");
-    dialog.topageLabel.removeAttribute("disabled");
-  } else {
-    dialog.frompageInput.setAttribute("disabled", "true");
-    dialog.frompageLabel.setAttribute("disabled", "true");
-    dialog.topageInput.setAttribute("disabled", "true");
-    dialog.topageLabel.setAttribute("disabled", "true");
-  }
-}
-
-// ---------------------------------------------------
-function loadDialog() {
-  var print_copies        = 1;
-  var print_selection_radio_enabled = false;
-  var print_frametype     = gPrintSetInterface.kSelectedFrame;
-  var print_howToEnableUI = gPrintSetInterface.kFrameEnableNone;
-  var print_tofile        = "";
-
-  try {
-    gPrefs = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch);
-
-    printService = Components.classes["@mozilla.org/gfx/printsettings-service;1"];
-    if (printService) {
-      printService = printService.getService();
-      if (printService) {
-        printService = printService.QueryInterface(Components.interfaces.nsIPrintSettingsService);
-      }
-    }
-  } catch (e) {}
-
-  // Note: getPrinters sets up the PrintToFile control
-  getPrinters();
-
-  if (gPrintSettings) {
-    print_tofile        = gPrintSettings.printToFile;
-    gOriginalNumCopies  = gPrintSettings.numCopies;
-
-    print_copies        = gPrintSettings.numCopies;
-    print_frametype     = gPrintSettings.printFrameType;
-    print_howToEnableUI = gPrintSettings.howToEnableFrameUI;
-    print_selection_radio_enabled = gPrintSettings.GetPrintOptions(gPrintSetInterface.kEnableSelectionRB);
-  }
-
-  if (doDebug) {
-    dump("loadDialog*********************************************\n");
-    dump("print_tofile            " + print_tofile + "\n");
-    dump("print_frame             " + print_frametype + "\n");
-    dump("print_howToEnableUI     " + print_howToEnableUI + "\n");
-    dump("selection_radio_enabled " + print_selection_radio_enabled + "\n");
-  }
-
-  dialog.printrangeGroup.selectedItem = dialog.allpagesRadio;
-  if (print_selection_radio_enabled) {
-    dialog.selectionRadio.removeAttribute("disabled");
-  } else {
-    dialog.selectionRadio.setAttribute("disabled", "true");
-  }
-  doPrintRange(dialog.rangeRadio.selected);
-  dialog.frompageInput.value  = 1;
-  dialog.topageInput.value    = 1;
-  dialog.numCopiesInput.value = print_copies;
-
-  if (doDebug) {
-    dump("print_howToEnableUI: " + print_howToEnableUI + "\n");
-  }
-
-  // print frame
-  if (print_howToEnableUI == gPrintSetInterface.kFrameEnableAll) {
-    dialog.aslaidoutRadio.removeAttribute("disabled");
-
-    dialog.selectedframeRadio.removeAttribute("disabled");
-    dialog.eachframesepRadio.removeAttribute("disabled");
-    dialog.printframeGroupLabel.removeAttribute("disabled");
-
-    // initialize radio group
-    dialog.printframeGroup.selectedItem = dialog.selectedframeRadio;
-
-  } else if (print_howToEnableUI == gPrintSetInterface.kFrameEnableAsIsAndEach) {
-    dialog.aslaidoutRadio.removeAttribute("disabled"); // enable
-
-    dialog.selectedframeRadio.setAttribute("disabled", "true"); // disable
-    dialog.eachframesepRadio.removeAttribute("disabled"); // enable
-    dialog.printframeGroupLabel.removeAttribute("disabled"); // enable
-
-    // initialize
-    dialog.printframeGroup.selectedItem = dialog.eachframesepRadio;
-
-  } else {
-    dialog.aslaidoutRadio.setAttribute("disabled", "true");
-    dialog.selectedframeRadio.setAttribute("disabled", "true");
-    dialog.eachframesepRadio.setAttribute("disabled", "true");
-    dialog.printframeGroupLabel.setAttribute("disabled", "true");
-  }
-
-  dialog.printButton.label = dialog.printName.getAttribute("label");
-}
-
-// ---------------------------------------------------
-function onLoad() {
-  // Init dialog.
-  initDialog();
-
-  // param[0]: nsIPrintSettings object
-  // param[1]: container for return value (1 = print, 0 = cancel)
-
-  gPrintSettings   = window.arguments[0].QueryInterface(gPrintSetInterface);
-  gWebBrowserPrint = window.arguments[1].QueryInterface(Components.interfaces.nsIWebBrowserPrint);
-  paramBlock       = window.arguments[2].QueryInterface(Components.interfaces.nsIDialogParamBlock);
-
-  // default return value is "cancel"
-  paramBlock.SetInt(0, 0);
-
-  loadDialog();
-}
-
-// ---------------------------------------------------
-function onAccept() {
-  let promise;
-
-  if (gPrintSettings == null) {
-    promise = Promise.resolve();
-  } else {
-    var print_howToEnableUI = gPrintSetInterface.kFrameEnableNone;
-
-    // save these out so they can be picked up by the device spec
-    gPrintSettings.printerName = dialog.printerList.value;
-    print_howToEnableUI        = gPrintSettings.howToEnableFrameUI;
-    gPrintSettings.printToFile = dialog.fileCheck.checked;
-
-    if (gPrintSettings.printToFile) {
-      promise = chooseFile();
-    } else {
-      promise = Promise.resolve();
-    }
-
-    promise = promise.then(() => {
-      if (dialog.allpagesRadio.selected) {
-        gPrintSettings.printRange = gPrintSetInterface.kRangeAllPages;
-      } else if (dialog.rangeRadio.selected) {
-        gPrintSettings.printRange = gPrintSetInterface.kRangeSpecifiedPageRange;
-      } else if (dialog.selectionRadio.selected) {
-        gPrintSettings.printRange = gPrintSetInterface.kRangeSelection;
-      }
-      gPrintSettings.startPageRange = dialog.frompageInput.value;
-      gPrintSettings.endPageRange   = dialog.topageInput.value;
-      gPrintSettings.numCopies      = dialog.numCopiesInput.value;
-
-      var frametype = gPrintSetInterface.kNoFrames;
-      if (print_howToEnableUI != gPrintSetInterface.kFrameEnableNone) {
-        if (dialog.aslaidoutRadio.selected) {
-          frametype = gPrintSetInterface.kFramesAsIs;
-        } else if (dialog.selectedframeRadio.selected) {
-          frametype = gPrintSetInterface.kSelectedFrame;
-        } else if (dialog.eachframesepRadio.selected) {
-          frametype = gPrintSetInterface.kEachFrameSep;
-        } else {
-          frametype = gPrintSetInterface.kSelectedFrame;
-        }
-      }
-      gPrintSettings.printFrameType = frametype;
-      if (doDebug) {
-        dump("onAccept*********************************************\n");
-        dump("frametype      " + frametype + "\n");
-        dump("numCopies      " + gPrintSettings.numCopies + "\n");
-        dump("printRange     " + gPrintSettings.printRange + "\n");
-        dump("printerName    " + gPrintSettings.printerName + "\n");
-        dump("startPageRange " + gPrintSettings.startPageRange + "\n");
-        dump("endPageRange   " + gPrintSettings.endPageRange + "\n");
-        dump("printToFile    " + gPrintSettings.printToFile + "\n");
-      }
-    });
-  }
-
-  promise.then(() => {
-    var saveToPrefs = false;
-
-    saveToPrefs = gPrefs.getBoolPref("print.save_print_settings");
-
-    if (saveToPrefs && printService != null) {
-      var flags = gPrintSetInterface.kInitSavePaperSize |
-                  gPrintSetInterface.kInitSaveEdges |
-                  gPrintSetInterface.kInitSaveInColor |
-                  gPrintSetInterface.kInitSaveShrinkToFit |
-                  gPrintSetInterface.kInitSaveScaling;
-      printService.savePrintSettingsToPrefs(gPrintSettings, true, flags);
-    }
-
-    // set return value to "print"
-    if (paramBlock) {
-      paramBlock.SetInt(0, 1);
-    } else {
-      dump("*** FATAL ERROR: No paramBlock\n");
-    }
-
-    window.close();
-  });
-
-  return false;
-}
-
-// ---------------------------------------------------
-function onCancel() {
-  // set return value to "cancel"
-  if (paramBlock) {
-    paramBlock.SetInt(0, 0);
-  } else {
-    dump("*** FATAL ERROR: No paramBlock\n");
-  }
-
-  return true;
-}
-
-// ---------------------------------------------------
-const nsIFilePicker = Components.interfaces.nsIFilePicker;
-function chooseFile() {
-  return new Promise(resolve => {
-    var fp = Components.classes["@mozilla.org/filepicker;1"]
-                       .createInstance(nsIFilePicker);
-    fp.init(window, dialog.fpDialog.getAttribute("label"), nsIFilePicker.modeSave);
-    fp.appendFilters(nsIFilePicker.filterAll);
-    fp.open(rv => {
-      if (rv != Components.interfaces.nsIFilePicker.returnCancel &&
-          fp.file && fp.file.path) {
-        gPrintSettings.toFileName = fp.file.path;
-        resolve(null);
-      }
-    });
-  });
-}
deleted file mode 100644
--- a/toolkit/components/printing/content/printdialog.xul
+++ /dev/null
@@ -1,126 +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/. -->
-
-<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
-<!DOCTYPE dialog SYSTEM "chrome://global/locale/printdialog.dtd">
-
-<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
-  onload="onLoad();"
-  ondialogaccept="return onAccept();"
-  oncancel="return onCancel();"
-  buttoniconaccept="print"
-  title="&printDialog.title;"
-  persist="screenX screenY"
-  screenX="24" screenY="24">
-
-  <script type="application/javascript" src="chrome://global/content/printdialog.js"/>
-
-  <stringbundle id="printingBundle" src="chrome://global/locale/printing.properties"/>
-
-  <groupbox>
-    <caption label="&printer.label;"/>
-
-    <grid>
-      <columns>
-        <column/>
-        <column flex="1"/>
-        <column/>
-      </columns>
-
-      <rows>
-        <row align="center">
-          <hbox align="center" pack="end">
-            <label id="printerLabel"
-                   value="&printerInput.label;"
-                   accesskey="&printerInput.accesskey;"
-                   control="printerList"/>
-          </hbox>
-          <menulist id="printerList" flex="1" type="description" oncommand="setPrinterDefaultsForSelectedPrinter();"/>
-          <button id="properties"
-                  label="&propertiesButton.label;"
-                  accesskey="&propertiesButton.accesskey;"
-                  icon="properties"
-                  oncommand="displayPropertiesDialog();"/>
-        </row>
-        <row align="center">
-          <hbox align="center" pack="end">
-            <label id="descTextLabel" control="descText" value="&descText.label;"/>
-          </hbox>
-          <label id="descText"/>
-          <checkbox id="fileCheck"
-                    checked="false"
-                    label="&fileCheck.label;"
-                    accesskey="&fileCheck.accesskey;"
-                    pack="end"/>
-        </row>
-      </rows>
-    </grid>
-  </groupbox>
-
-  <hbox>
-    <groupbox flex="1">
-      <caption label="&printrangeGroup.label;"/>
-
-      <radiogroup id="printrangeGroup">
-        <radio id="allpagesRadio"
-               label="&allpagesRadio.label;"
-               accesskey="&allpagesRadio.accesskey;"
-               oncommand="doPrintRange(0)"/>
-        <hbox align="center">
-          <radio id="rangeRadio"
-                 label="&rangeRadio.label;"
-                 accesskey="&rangeRadio.accesskey;"
-                 oncommand="doPrintRange(1)"/>
-          <label id="frompageLabel"
-                 control="frompageInput"
-                 value="&frompageInput.label;"
-                 accesskey="&frompageInput.accesskey;"/>
-          <textbox id="frompageInput" style="width:5em;" onkeyup="checkInteger(this)"/>
-          <label id="topageLabel"
-                 control="topageInput"
-                 value="&topageInput.label;"
-                 accesskey="&topageInput.accesskey;"/>
-          <textbox id="topageInput" style="width:5em;" onkeyup="checkInteger(this)"/>
-        </hbox>
-        <radio id="selectionRadio"
-               label="&selectionRadio.label;"
-               accesskey="&selectionRadio.accesskey;"
-               oncommand="doPrintRange(2)"/>
-      </radiogroup>
-    </groupbox>
-
-    <groupbox flex="1">
-      <caption label="&copies.label;"/>
-      <hbox align="center">
-        <label control="numCopiesInput"
-               value="&numCopies.label;"
-               accesskey="&numCopies.accesskey;"/>
-        <textbox id="numCopiesInput" style="width:5em;" onkeyup="checkInteger(this)"/>
-      </hbox>
-    </groupbox>
-  </hbox>
-
-  <groupbox flex="1">
-    <caption label="&printframeGroup.label;" id="printframeGroupLabel"/>
-    <radiogroup id="printframeGroup">
-      <radio id="aslaidoutRadio"
-             label="&aslaidoutRadio.label;"
-             accesskey="&aslaidoutRadio.accesskey;"/>
-      <radio id="selectedframeRadio"
-             label="&selectedframeRadio.label;"
-             accesskey="&selectedframeRadio.accesskey;"/>
-      <radio id="eachframesepRadio"
-             label="&eachframesepRadio.label;"
-             accesskey="&eachframesepRadio.accesskey;"/>
-    </radiogroup>
-  </groupbox>
-
-  <!-- used to store titles and labels -->
-  <data style="display:none;" id="printButton" label="&printButton.label;"/>
-  <data style="display:none;" id="fpDialog" label="&fpDialog.title;"/>
-
-</dialog>
-
deleted file mode 100644
--- a/toolkit/components/printing/content/printjoboptions.js
+++ /dev/null
@@ -1,373 +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/. */
-
-var dialog;
-var gPrintBundle;
-var gPrintSettings = null;
-var gPrintSettingsInterface  = Components.interfaces.nsIPrintSettings;
-var gPaperArray;
-var gPrefs;
-var gParamBlock;
-
-var gPrintSetInterface = Components.interfaces.nsIPrintSettings;
-var doDebug            = true;
-
-// ---------------------------------------------------
-function checkDouble(element, maxVal) {
-  var value = element.value;
-  if (value && value.length > 0) {
-    value = value.replace(/[^\.|^0-9]/g, "");
-    if (!value) {
-      element.value = "";
-    } else if (value > maxVal) {
-      element.value = maxVal;
-    } else {
-      element.value = value;
-    }
-  }
-}
-
-// ---------------------------------------------------
-function isListOfPrinterFeaturesAvailable() {
-  return gPrefs.getBoolPref("print.tmp.printerfeatures." + gPrintSettings.printerName + ".has_special_printerfeatures", false);
-}
-
-// ---------------------------------------------------
-function getDoubleStr(val, dec) {
-  var str = val.toString();
-  var inx = str.indexOf(".");
-  return str.substring(0, inx + dec + 1);
-}
-
-// ---------------------------------------------------
-function initDialog() {
-  gPrintBundle = document.getElementById("printBundle");
-
-  dialog = {};
-
-  dialog.paperList       = document.getElementById("paperList");
-  dialog.paperGroup      = document.getElementById("paperGroup");
-
-  dialog.jobTitleLabel   = document.getElementById("jobTitleLabel");
-  dialog.jobTitleGroup   = document.getElementById("jobTitleGroup");
-  dialog.jobTitleInput   = document.getElementById("jobTitleInput");
-
-  dialog.colorGroup      = document.getElementById("colorGroup");
-  dialog.colorRadioGroup = document.getElementById("colorRadioGroup");
-  dialog.colorRadio      = document.getElementById("colorRadio");
-  dialog.grayRadio       = document.getElementById("grayRadio");
-
-  dialog.topInput        = document.getElementById("topInput");
-  dialog.bottomInput     = document.getElementById("bottomInput");
-  dialog.leftInput       = document.getElementById("leftInput");
-  dialog.rightInput      = document.getElementById("rightInput");
-}
-
-// ---------------------------------------------------
-function round10(val) {
-  return Math.round(val * 10) / 10;
-}
-
-
-// ---------------------------------------------------
-function paperListElement(aPaperListElement) {
-    this.paperListElement = aPaperListElement;
-  }
-
-paperListElement.prototype =
-  {
-    clearPaperList() {
-          // remove the menupopup node child of the menulist.
-          this.paperListElement.firstChild.remove();
-        },
-
-    appendPaperNames(aDataObject) {
-          var popupNode = document.createElement("menupopup");
-          for (var i = 0;i < aDataObject.length;i++) {
-            var paperObj = aDataObject[i];
-            var itemNode = document.createElement("menuitem");
-            var label;
-            try {
-              label = gPrintBundle.getString(paperObj.name);
-            } catch (e) {
-              /* No name in string bundle ? Then build one manually (this
-               * usually happens when gPaperArray was build by createPaperArrayFromPrinterFeatures() ...) */
-              if (paperObj.inches) {
-                label = paperObj.name + " (" + round10(paperObj.width) + "x" + round10(paperObj.height) + " inch)";
-              } else {
-                label = paperObj.name + " (" + paperObj.width + "x" + paperObj.height + " mm)";
-              }
-            }
-            itemNode.setAttribute("label", label);
-            itemNode.setAttribute("value", i);
-            popupNode.appendChild(itemNode);
-          }
-          this.paperListElement.appendChild(popupNode);
-        }
-  };
-
-// ---------------------------------------------------
-function createPaperArrayFromDefaults() {
-  var paperNames   = ["letterSize", "legalSize", "exectiveSize", "a5Size", "a4Size", "a3Size", "a2Size", "a1Size", "a0Size"];
-  // var paperNames   = ["&letterRadio.label;", "&legalRadio.label;", "&exectiveRadio.label;", "&a4Radio.label;", "&a3Radio.label;"];
-  var paperWidths  = [ 8.5,  8.5,  7.25, 148.0, 210.0, 287.0, 420.0, 594.0,  841.0];
-  var paperHeights = [11.0, 14.0, 10.50, 210.0, 297.0, 420.0, 594.0, 841.0, 1189.0];
-  var paperInches  = [true, true, true,  false, false, false, false, false, false];
-
-  gPaperArray = [];
-
-  for (var i = 0;i < paperNames.length;i++) {
-    var obj    = {};
-    obj.name   = paperNames[i];
-    obj.width  = paperWidths[i];
-    obj.height = paperHeights[i];
-    obj.inches = paperInches[i];
-
-    /* Calculate the width/height in millimeters */
-    if (paperInches[i]) {
-      obj.width_mm  = paperWidths[i] * 25.4;
-      obj.height_mm = paperHeights[i] * 25.4;
-    } else {
-      obj.width_mm  = paperWidths[i];
-      obj.height_mm = paperHeights[i];
-    }
-    gPaperArray[i] = obj;
-  }
-}
-
-// ---------------------------------------------------
-function createPaperArrayFromPrinterFeatures() {
-  var printername = gPrintSettings.printerName;
-  if (doDebug) {
-    dump("createPaperArrayFromPrinterFeatures for " + printername + ".\n");
-  }
-
-  gPaperArray = [];
-
-  var numPapers = gPrefs.getIntPref("print.tmp.printerfeatures." + printername + ".paper.count");
-
-  if (doDebug) {
-    dump("processing " + numPapers + " entries...\n");
-  }
-
-  for (var i = 0;i < numPapers;i++) {
-    var obj       = {};
-    obj.name      = gPrefs.getCharPref("print.tmp.printerfeatures." + printername + ".paper." + i + ".name");
-    obj.width_mm  = gPrefs.getIntPref("print.tmp.printerfeatures." + printername + ".paper." + i + ".width_mm");
-    obj.height_mm = gPrefs.getIntPref("print.tmp.printerfeatures." + printername + ".paper." + i + ".height_mm");
-    obj.inches    = gPrefs.getBoolPref("print.tmp.printerfeatures." + printername + ".paper." + i + ".is_inch");
-
-    /* Calculate the width/height in paper's native units (either inches or millimeters) */
-    if (obj.inches) {
-      obj.width  = obj.width_mm / 25.4;
-      obj.height = obj.height_mm / 25.4;
-    } else {
-      obj.width  = obj.width_mm;
-      obj.height = obj.height_mm;
-    }
-
-    gPaperArray[i] = obj;
-
-    if (doDebug) {
-      dump("paper index=" + i + ", name=" + obj.name + ", width=" + obj.width + ", height=" + obj.height + ".\n");
-    }
-  }
-}
-
-// ---------------------------------------------------
-function createPaperArray() {
-  if (isListOfPrinterFeaturesAvailable()) {
-    createPaperArrayFromPrinterFeatures();
-  } else {
-    createPaperArrayFromDefaults();
-  }
-}
-
-// ---------------------------------------------------
-function createPaperSizeList(selectedInx) {
-  var selectElement = new paperListElement(dialog.paperList);
-  selectElement.clearPaperList();
-
-  selectElement.appendPaperNames(gPaperArray);
-
-  if (selectedInx > -1) {
-    selectElement.paperListElement.selectedIndex = selectedInx;
-  }
-
-  // dialog.paperList = selectElement;
-}
-
-// ---------------------------------------------------
-function loadDialog() {
-  var print_paper_unit       = 0;
-  var print_paper_width      = 0.0;
-  var print_paper_height     = 0.0;
-  var print_paper_name       = "";
-  var print_color            = true;
-  var print_jobtitle         = "";
-
-  gPrefs = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch);
-
-  if (gPrintSettings) {
-    print_paper_unit       = gPrintSettings.paperSizeUnit;
-    print_paper_width      = gPrintSettings.paperWidth;
-    print_paper_height     = gPrintSettings.paperHeight;
-    print_paper_name       = gPrintSettings.paperName;
-    print_color            = gPrintSettings.printInColor;
-    print_jobtitle         = gPrintSettings.title;
-  }
-
-  if (doDebug) {
-    dump("loadDialog******************************\n");
-    dump("paperSizeType   " + print_paper_unit + "\n");
-    dump("paperWidth      " + print_paper_width + "\n");
-    dump("paperHeight     " + print_paper_height + "\n");
-    dump("paperName       " + print_paper_name + "\n");
-    dump("print_color     " + print_color + "\n");
-    dump("print_jobtitle   " + print_jobtitle + "\n");
-  }
-
-  createPaperArray();
-
-  var paperSelectedInx = 0;
-  for (var i = 0;i < gPaperArray.length;i++) {
-    if (print_paper_name == gPaperArray[i].name) {
-      paperSelectedInx = i;
-      break;
-    }
-  }
-
-  if (doDebug) {
-    if (i == gPaperArray.length)
-      dump("loadDialog: No paper found.\n");
-    else
-      dump("loadDialog: found paper '" + gPaperArray[paperSelectedInx].name + "'.\n");
-  }
-
-  createPaperSizeList(paperSelectedInx);
-
-  /* Enable/disable and/or hide/unhide widgets based in the information
-   * whether the selected printer and/or print module supports the matching
-   * feature or not */
-  if (isListOfPrinterFeaturesAvailable()) {
-    // job title
-    if (gPrefs.getBoolPref("print.tmp.printerfeatures." + gPrintSettings.printerName + ".can_change_jobtitle"))
-      dialog.jobTitleInput.removeAttribute("disabled");
-    else
-      dialog.jobTitleInput.setAttribute("disabled", "true");
-    if (gPrefs.getBoolPref("print.tmp.printerfeatures." + gPrintSettings.printerName + ".supports_jobtitle_change"))
-      dialog.jobTitleGroup.removeAttribute("hidden");
-    else
-      dialog.jobTitleGroup.setAttribute("hidden", "true");
-
-    // paper size
-    if (gPrefs.getBoolPref("print.tmp.printerfeatures." + gPrintSettings.printerName + ".can_change_paper_size"))
-      dialog.paperList.removeAttribute("disabled");
-    else
-      dialog.paperList.setAttribute("disabled", "true");
-    if (gPrefs.getBoolPref("print.tmp.printerfeatures." + gPrintSettings.printerName + ".supports_paper_size_change"))
-      dialog.paperGroup.removeAttribute("hidden");
-    else
-      dialog.paperGroup.setAttribute("hidden", "true");
-
-    // color/grayscale radio
-    if (gPrefs.getBoolPref("print.tmp.printerfeatures." + gPrintSettings.printerName + ".can_change_printincolor"))
-      dialog.colorRadioGroup.removeAttribute("disabled");
-    else
-      dialog.colorRadioGroup.setAttribute("disabled", "true");
-    if (gPrefs.getBoolPref("print.tmp.printerfeatures." + gPrintSettings.printerName + ".supports_printincolor_change"))
-      dialog.colorGroup.removeAttribute("hidden");
-    else
-      dialog.colorGroup.setAttribute("hidden", "true");
-  }
-
-  if (print_color) {
-    dialog.colorRadioGroup.selectedItem = dialog.colorRadio;
-  } else {
-    dialog.colorRadioGroup.selectedItem = dialog.grayRadio;
-  }
-
-  dialog.jobTitleInput.value = print_jobtitle;
-
-  dialog.topInput.value    = gPrintSettings.edgeTop.toFixed(2);
-  dialog.bottomInput.value = gPrintSettings.edgeBottom.toFixed(2);
-  dialog.leftInput.value   = gPrintSettings.edgeLeft.toFixed(2);
-  dialog.rightInput.value  = gPrintSettings.edgeRight.toFixed(2);
-}
-
-// ---------------------------------------------------
-function onLoad() {
-  // Init dialog.
-  initDialog();
-
-  gPrintSettings = window.arguments[0].QueryInterface(gPrintSetInterface);
-  gParamBlock = window.arguments[1].QueryInterface(Components.interfaces.nsIDialogParamBlock);
-
-  if (doDebug) {
-    if (gPrintSettings == null) alert("PrintSettings is null!");
-    if (gParamBlock == null) alert("nsIDialogParam is null!");
-  }
-
-  // default return value is "cancel"
-  gParamBlock.SetInt(0, 0);
-
-  loadDialog();
-}
-
-// ---------------------------------------------------
-function onAccept() {
-  var print_paper_unit        = gPrintSettingsInterface.kPaperSizeInches;
-  var print_paper_width       = 0.0;
-  var print_paper_height      = 0.0;
-  var print_paper_name        = "";
-
-  if (gPrintSettings != null) {
-    var paperSelectedInx = dialog.paperList.selectedIndex;
-    if (gPaperArray[paperSelectedInx].inches) {
-      print_paper_unit = gPrintSettingsInterface.kPaperSizeInches;
-    } else {
-      print_paper_unit = gPrintSettingsInterface.kPaperSizeMillimeters;
-    }
-    print_paper_width      = gPaperArray[paperSelectedInx].width;
-    print_paper_height     = gPaperArray[paperSelectedInx].height;
-    print_paper_name       = gPaperArray[paperSelectedInx].name;
-
-    gPrintSettings.paperSizeUnit   = print_paper_unit;
-    gPrintSettings.paperWidth      = print_paper_width;
-    gPrintSettings.paperHeight     = print_paper_height;
-    gPrintSettings.paperName       = print_paper_name;
-
-    // save these out so they can be picked up by the device spec
-    gPrintSettings.printInColor     = dialog.colorRadio.selected;
-    gPrintSettings.title            = dialog.jobTitleInput.value;
-
-    gPrintSettings.edgeTop          = dialog.topInput.value;
-    gPrintSettings.edgeBottom       = dialog.bottomInput.value;
-    gPrintSettings.edgeLeft         = dialog.leftInput.value;
-    gPrintSettings.edgeRight        = dialog.rightInput.value;
-
-    if (doDebug) {
-      dump("onAccept******************************\n");
-      dump("paperSizeUnit    " + print_paper_unit + "\n");
-      dump("paperWidth       " + print_paper_width + "\n");
-      dump("paperHeight      " + print_paper_height + "\n");
-      dump("paperName       '" + print_paper_name + "'\n");
-
-      dump("printInColor     " + gPrintSettings.printInColor + "\n");
-    }
-  } else {
-    dump("************ onAccept gPrintSettings: " + gPrintSettings + "\n");
-  }
-
-  if (gParamBlock) {
-    // set return value to "ok"
-    gParamBlock.SetInt(0, 1);
-  } else {
-    dump("*** FATAL ERROR: paramBlock missing\n");
-  }
-
-  return true;
-}
deleted file mode 100644
--- a/toolkit/components/printing/content/printjoboptions.xul
+++ /dev/null
@@ -1,110 +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/. -->
-
-<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
-<!DOCTYPE dialog SYSTEM "chrome://global/locale/printjoboptions.dtd">
-
-<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
-  onload="onLoad();"
-  ondialogaccept="return onAccept();"
-  title="&printJobOptions.title;"
-  persist="screenX screenY"
-  screenX="24" screenY="24">
-
-  <script type="application/javascript" src="chrome://global/content/printjoboptions.js"/>
-
-  <stringbundle id="printBundle" src="chrome://global/locale/printPageSetup.properties"/>
-
-  <grid>
-    <columns>
-      <column/>
-      <column flex="1"/>
-    </columns>
-
-    <rows>
-      <row id="jobTitleGroup">
-        <hbox align="center" pack="end">
-          <label id="jobTitleLabel"
-                 value="&jobTitleInput.label;"
-                 accesskey="&jobTitleInput.accesskey;"
-                 control="jobTitleInput"/>
-        </hbox>
-        <textbox id="jobTitleInput" flex="1"/>
-      </row>
-
-      <row id="paperGroup">
-        <hbox align="center" pack="end">
-          <label id="paperLabel"
-                 value="&paperInput.label;"
-                 accesskey="&paperInput.accesskey;"
-                 control="paperList"/>
-        </hbox>
-        <menulist id="paperList" flex="1">
-          <menupopup/>
-        </menulist>
-      </row>
-
-      <row id="colorGroup">
-        <hbox align="center" pack="end">
-          <label control="colorRadioGroup" value="&colorGroup.label;"/>
-        </hbox>
-        <radiogroup id="colorRadioGroup" orient="horizontal">
-          <radio id="grayRadio"
-                 label="&grayRadio.label;"
-                 accesskey="&grayRadio.accesskey;"/>
-          <radio id="colorRadio"
-                 label="&colorRadio.label;"
-                 accesskey="&colorRadio.accesskey;"/>
-        </radiogroup>
-      </row>
-    </rows>
-  </grid>
-
-  <grid>
-    <columns>
-      <column/>
-    </columns>
-    <rows>
-      <row>
-       <groupbox flex="1">
-        <caption label="&edgeMarginInput.label;"/>
-        <hbox>
-          <hbox align="center">
-            <label id="topLabel"
-                   value="&topInput.label;"
-                   accesskey="&topInput.accesskey;"
-                   control="topInput"/>
-            <textbox id="topInput" style="width:5em;" onkeyup="checkDouble(this, 0.5)"/>
-          </hbox>
-          <hbox align="center">
-            <label id="bottomLabel"
-                   value="&bottomInput.label;"
-                   accesskey="&bottomInput.accesskey;"
-                   control="bottomInput"/>
-            <textbox id="bottomInput" style="width:5em;" onkeyup="checkDouble(this, 0.5)"/>
-          </hbox>
-          <hbox align="center">
-            <label id="leftLabel"
-                   value="&leftInput.label;"
-                   accesskey="&leftInput.accesskey;"
-                   control="leftInput"/>
-            <textbox id="leftInput" style="width:5em;" onkeyup="checkDouble(this, 0.5)"/>
-          </hbox>
-          <hbox align="center">
-            <label id="rightLabel"
-                   value="&rightInput.label;"
-                   accesskey="&rightInput.accesskey;"
-                   control="rightInput"/>
-            <textbox id="rightInput" style="width:5em;" onkeyup="checkDouble(this, 0.5)"/>
-          </hbox>
-        </hbox>
-        </groupbox>
-      </row>
-
-    </rows>
-  </grid>
-
-</dialog>
--- a/toolkit/components/printing/jar.mn
+++ b/toolkit/components/printing/jar.mn
@@ -1,22 +1,18 @@
 # 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/.
 
 toolkit.jar:
 #ifndef XP_MACOSX
-#ifdef XP_UNIX
-   content/global/printdialog.js                    (content/printdialog.js)
-   content/global/printdialog.xul                   (content/printdialog.xul)
-   content/global/printjoboptions.js                (content/printjoboptions.js)
-   content/global/printjoboptions.xul               (content/printjoboptions.xul)
-#endif
+#ifdef XP_WIN
    content/global/printPageSetup.js                 (content/printPageSetup.js)
    content/global/printPageSetup.xul                (content/printPageSetup.xul)
+#endif
    content/global/printPreviewProgress.js           (content/printPreviewProgress.js)
    content/global/printPreviewProgress.xul          (content/printPreviewProgress.xul)
    content/global/printProgress.js                  (content/printProgress.js)
    content/global/printProgress.xul                 (content/printProgress.xul)
 #endif
    content/global/printPreviewBindings.xml          (content/printPreviewBindings.xml)
    content/global/printUtils.js                     (content/printUtils.js)
    content/global/simplifyMode.css                  (content/simplifyMode.css)
--- a/toolkit/components/printingui/ipc/nsPrintingProxy.cpp
+++ b/toolkit/components/printingui/ipc/nsPrintingProxy.cpp
@@ -183,24 +183,16 @@ nsPrintingProxy::ShowProgress(mozIDOMWin
 NS_IMETHODIMP
 nsPrintingProxy::ShowPageSetup(mozIDOMWindowProxy *parent,
                                nsIPrintSettings *printSettings,
                                nsIObserver *aObs)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
-NS_IMETHODIMP
-nsPrintingProxy::ShowPrinterProperties(mozIDOMWindowProxy *parent,
-                                       const char16_t *printerName,
-                                       nsIPrintSettings *printSettings)
-{
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
 nsresult
 nsPrintingProxy::SavePrintSettings(nsIPrintSettings* aPS,
                                    bool aUsePrinterNamePrefix,
                                    uint32_t aFlags)
 {
   nsresult rv;
   nsCOMPtr<nsIPrintSettingsService> printSettingsSvc =
     do_GetService("@mozilla.org/gfx/printsettings-service;1", &rv);
--- a/toolkit/components/printingui/mac/nsPrintingPromptService.cpp
+++ b/toolkit/components/printingui/mac/nsPrintingPromptService.cpp
@@ -67,22 +67,16 @@ nsPrintingPromptService::ShowPageSetup(m
                                            NS_PRINTDIALOGSERVICE_CONTRACTID));
   if (dlgPrint) {
     return dlgPrint->ShowPageSetup(nsPIDOMWindowOuter::From(parent), printSettings);
   }
 
   return NS_ERROR_FAILURE;
 }
 
-NS_IMETHODIMP
-nsPrintingPromptService::ShowPrinterProperties(mozIDOMWindowProxy *parent, const char16_t *printerName, nsIPrintSettings *printSettings)
-{
-    return NS_ERROR_NOT_IMPLEMENTED;
-}
-
 
 //*****************************************************************************
 // nsPrintingPromptService::nsIWebProgressListener
 //*****************************************************************************
 
 NS_IMETHODIMP
 nsPrintingPromptService::OnStateChange(nsIWebProgress *aWebProgress, nsIRequest *aRequest, uint32_t aStateFlags, nsresult aStatus)
 {
--- a/toolkit/components/printingui/unixshared/nsPrintingPromptService.cpp
+++ b/toolkit/components/printingui/unixshared/nsPrintingPromptService.cpp
@@ -1,62 +1,27 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/. */
 
 #include "nsPrintingPromptService.h"
 
-#include "nsArray.h"
-#include "nsIComponentManager.h"
-#include "nsIDialogParamBlock.h"
 #include "nsIDOMWindow.h"
 #include "nsIServiceManager.h"
 #include "nsISupportsUtils.h"
 #include "nsString.h"
 #include "nsIPrintDialogService.h"
 
 // Printing Progress Includes
 #include "nsPrintProgress.h"
 #include "nsPrintProgressParams.h"
 
-static const char *kPrintDialogURL         = "chrome://global/content/printdialog.xul";
 static const char *kPrintProgressDialogURL = "chrome://global/content/printProgress.xul";
 static const char *kPrtPrvProgressDialogURL = "chrome://global/content/printPreviewProgress.xul";
-static const char *kPageSetupDialogURL     = "chrome://global/content/printPageSetup.xul";
-static const char *kPrinterPropertiesURL   = "chrome://global/content/printjoboptions.xul";
-
-/****************************************************************
- ************************* ParamBlock ***************************
- ****************************************************************/
-
-class ParamBlock {
-
-public:
-    ParamBlock()
-    {
-        mBlock = 0;
-    }
-    ~ParamBlock()
-    {
-        NS_IF_RELEASE(mBlock);
-    }
-    nsresult Init() {
-      return CallCreateInstance(NS_DIALOGPARAMBLOCK_CONTRACTID, &mBlock);
-    }
-    nsIDialogParamBlock * operator->() const MOZ_NO_ADDREF_RELEASE_ON_RETURN { return mBlock; }
-    operator nsIDialogParamBlock * () const { return mBlock; }
-
-private:
-    nsIDialogParamBlock *mBlock;
-};
-
-/****************************************************************
- ***************** nsPrintingPromptService **********************
- ****************************************************************/
 
 NS_IMPL_ISUPPORTS(nsPrintingPromptService, nsIPrintingPromptService, nsIWebProgressListener)
 
 nsPrintingPromptService::nsPrintingPromptService()
 {
 }
 
 nsPrintingPromptService::~nsPrintingPromptService()
@@ -74,31 +39,23 @@ nsPrintingPromptService::Init()
 NS_IMETHODIMP
 nsPrintingPromptService::ShowPrintDialog(mozIDOMWindowProxy *parent,
                                          nsIWebBrowserPrint *webBrowserPrint,
                                          nsIPrintSettings *printSettings)
 {
     NS_ENSURE_ARG(webBrowserPrint);
     NS_ENSURE_ARG(printSettings);
 
-    // Try to access a component dialog
     nsCOMPtr<nsIPrintDialogService> dlgPrint(do_GetService(
                                              NS_PRINTDIALOGSERVICE_CONTRACTID));
     if (dlgPrint)
       return dlgPrint->Show(nsPIDOMWindowOuter::From(parent),
                             printSettings, webBrowserPrint);
 
-    // Show the built-in dialog instead
-    ParamBlock block;
-    nsresult rv = block.Init();
-    if (NS_FAILED(rv))
-      return rv;
-
-    block->SetInt(0, 0);
-    return DoDialog(parent, block, webBrowserPrint, printSettings, kPrintDialogURL);
+    return NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 nsPrintingPromptService::ShowProgress(mozIDOMWindowProxy*      parent,
                                       nsIWebBrowserPrint*      webBrowserPrint,    // ok to be null
                                       nsIPrintSettings*        printSettings,      // ok to be null
                                       nsIObserver*             openDialogObserver, // ok to be null
                                       bool                     isForPrinting,
@@ -138,120 +95,25 @@ nsPrintingPromptService::ShowProgress(mo
 
 NS_IMETHODIMP
 nsPrintingPromptService::ShowPageSetup(mozIDOMWindowProxy *parent,
                                        nsIPrintSettings *printSettings,
                                        nsIObserver *aObs)
 {
     NS_ENSURE_ARG(printSettings);
 
-    // Try to access a component dialog
     nsCOMPtr<nsIPrintDialogService> dlgPrint(do_GetService(
                                              NS_PRINTDIALOGSERVICE_CONTRACTID));
     if (dlgPrint)
       return dlgPrint->ShowPageSetup(nsPIDOMWindowOuter::From(parent),
                                      printSettings);
 
-    ParamBlock block;
-    nsresult rv = block.Init();
-    if (NS_FAILED(rv))
-      return rv;
-
-    block->SetInt(0, 0);
-    return DoDialog(parent, block, nullptr, printSettings, kPageSetupDialogURL);
-}
-
-NS_IMETHODIMP
-nsPrintingPromptService::ShowPrinterProperties(mozIDOMWindowProxy *parent,
-                                               const char16_t *printerName,
-                                               nsIPrintSettings *printSettings)
-{
-    /* fixme: We simply ignore the |aPrinter| argument here
-     * We should get the supported printer attributes from the printer and
-     * populate the print job options dialog with these data instead of using
-     * the "default set" here.
-     * However, this requires changes on all platforms and is another big chunk
-     * of patches ... ;-(
-     */
-    NS_ENSURE_ARG(printerName);
-    NS_ENSURE_ARG(printSettings);
-
-    ParamBlock block;
-    nsresult rv = block.Init();
-    if (NS_FAILED(rv))
-      return rv;
-
-    block->SetInt(0, 0);
-    return DoDialog(parent, block, nullptr, printSettings, kPrinterPropertiesURL);
-
+    return NS_ERROR_FAILURE;
 }
 
-nsresult
-nsPrintingPromptService::DoDialog(mozIDOMWindowProxy *aParent,
-                                  nsIDialogParamBlock *aParamBlock,
-                                  nsIWebBrowserPrint *aWebBrowserPrint,
-                                  nsIPrintSettings* aPS,
-                                  const char *aChromeURL)
-{
-    NS_ENSURE_ARG(aParamBlock);
-    NS_ENSURE_ARG(aPS);
-    NS_ENSURE_ARG(aChromeURL);
-
-    if (!mWatcher)
-        return NS_ERROR_FAILURE;
-
-    // get a parent, if at all possible
-    // (though we'd rather this didn't fail, it's OK if it does. so there's
-    // no failure or null check.)
-    nsCOMPtr<mozIDOMWindowProxy> activeParent;
-    if (!aParent)
-    {
-        mWatcher->GetActiveWindow(getter_AddRefs(activeParent));
-        aParent = activeParent;
-    }
-
-    // create a nsIMutableArray of the parameters
-    // being passed to the window
-    nsCOMPtr<nsIMutableArray> array = nsArray::Create();
-
-    nsCOMPtr<nsISupports> psSupports(do_QueryInterface(aPS));
-    NS_ASSERTION(psSupports, "PrintSettings must be a supports");
-    array->AppendElement(psSupports);
-
-    if (aWebBrowserPrint) {
-      nsCOMPtr<nsISupports> wbpSupports(do_QueryInterface(aWebBrowserPrint));
-      NS_ASSERTION(wbpSupports, "nsIWebBrowserPrint must be a supports");
-      array->AppendElement(wbpSupports);
-    }
-
-    nsCOMPtr<nsISupports> blkSupps(do_QueryInterface(aParamBlock));
-    NS_ASSERTION(blkSupps, "IOBlk must be a supports");
-    array->AppendElement(blkSupps);
-
-    nsCOMPtr<mozIDOMWindowProxy> dialog;
-    nsresult rv = mWatcher->OpenWindow(aParent, aChromeURL, "_blank",
-                              "centerscreen,chrome,modal,titlebar", array,
-                              getter_AddRefs(dialog));
-
-    // if aWebBrowserPrint is not null then we are printing
-    // so we want to pass back NS_ERROR_ABORT on cancel
-    if (NS_SUCCEEDED(rv) && aWebBrowserPrint)
-    {
-        int32_t status;
-        aParamBlock->GetInt(0, &status);
-        return status == 0?NS_ERROR_ABORT:NS_OK;
-    }
-
-    return rv;
-}
-
-//////////////////////////////////////////////////////////////////////
-// nsIWebProgressListener
-//////////////////////////////////////////////////////////////////////
-
 NS_IMETHODIMP
 nsPrintingPromptService::OnStateChange(nsIWebProgress *aWebProgress, nsIRequest *aRequest, uint32_t aStateFlags, nsresult aStatus)
 {
   if ((aStateFlags & STATE_STOP) && mWebProgressListener) {
     mWebProgressListener->OnStateChange(aWebProgress, aRequest, aStateFlags, aStatus);
     if (mPrintProgress) {
       mPrintProgress->CloseProgressDialog(true);
     }
--- a/toolkit/components/printingui/unixshared/nsPrintingPromptService.h
+++ b/toolkit/components/printingui/unixshared/nsPrintingPromptService.h
@@ -14,17 +14,16 @@
 
 #include "nsCOMPtr.h"
 #include "nsIPrintingPromptService.h"
 #include "nsPIPromptService.h"
 #include "nsIWindowWatcher.h"
 
 // Printing Progress Includes
 #include "nsPrintProgress.h"
-#include "nsPrintProgressParams.h"
 #include "nsIWebProgressListener.h"
 
 class nsIDOMWindow;
 class nsIDialogParamBlock;
 
 class nsPrintingPromptService: public nsIPrintingPromptService,
                                public nsIWebProgressListener
 {
@@ -38,21 +37,15 @@ public:
   NS_DECL_NSIPRINTINGPROMPTSERVICE
   NS_DECL_NSIWEBPROGRESSLISTENER
   NS_DECL_ISUPPORTS
 
 protected:
   virtual ~nsPrintingPromptService();
 
 private:
-  nsresult DoDialog(mozIDOMWindowProxy *aParent,
-                    nsIDialogParamBlock *aParamBlock,
-                    nsIWebBrowserPrint *aWebBrowserPrint,
-                    nsIPrintSettings* aPS,
-                    const char *aChromeURL);
-
   nsCOMPtr<nsIWindowWatcher> mWatcher;
   nsCOMPtr<nsIPrintProgress> mPrintProgress;
   nsCOMPtr<nsIWebProgressListener> mWebProgressListener;
 };
 
 #endif
 
--- a/toolkit/components/printingui/win/nsPrintingPromptService.cpp
+++ b/toolkit/components/printingui/win/nsPrintingPromptService.cpp
@@ -111,22 +111,16 @@ nsPrintingPromptService::ShowPageSetup(m
                                              NS_PRINTDIALOGSERVICE_CONTRACTID));
     if (dlgPrint)
       return dlgPrint->ShowPageSetup(nsPIDOMWindowOuter::From(parent),
                                      printSettings);
 
     return NS_ERROR_FAILURE;
 }
 
-NS_IMETHODIMP
-nsPrintingPromptService::ShowPrinterProperties(mozIDOMWindowProxy *parent, const char16_t *printerName, nsIPrintSettings *printSettings)
-{
-    return NS_ERROR_NOT_IMPLEMENTED;
-}
-
 //////////////////////////////////////////////////////////////////////
 // nsIWebProgressListener
 //////////////////////////////////////////////////////////////////////
 
 NS_IMETHODIMP
 nsPrintingPromptService::OnStateChange(nsIWebProgress *aWebProgress, nsIRequest *aRequest, uint32_t aStateFlags, nsresult aStatus)
 {
     if ((aStateFlags & STATE_STOP) && mWebProgressListener)
deleted file mode 100644
--- a/toolkit/locales/en-US/chrome/global/printdialog.dtd
+++ /dev/null
@@ -1,44 +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/. -->
-
-<!-- extracted from printdialog.xul -->
-
-<!ENTITY printButton.label "Print">
-
-<!ENTITY printDialog.title "Print">
-
-<!ENTITY fpDialog.title "Save File">
-
-<!ENTITY fileCheck.label "Print to File">
-<!ENTITY fileCheck.accesskey "F">
-<!ENTITY propertiesButton.label "Properties…">
-<!ENTITY propertiesButton.accesskey "o">
-<!ENTITY descText.label "Printer Description:">
-<!ENTITY printer.label "Printer">
-<!ENTITY printerInput.label "Printer Name:">
-<!ENTITY printerInput.accesskey "N">
-
-<!ENTITY printrangeGroup.label "Print Range">
-<!ENTITY allpagesRadio.label "All Pages">
-<!ENTITY allpagesRadio.accesskey "A">
-<!ENTITY rangeRadio.label  "Pages">
-<!ENTITY rangeRadio.accesskey  "P">
-<!ENTITY frompageInput.label  "from">
-<!ENTITY frompageInput.accesskey  "r">
-<!ENTITY topageInput.label  "to">
-<!ENTITY topageInput.accesskey  "t">
-<!ENTITY selectionRadio.label "Selection">
-<!ENTITY selectionRadio.accesskey "S">
-
-<!ENTITY copies.label "Copies">
-<!ENTITY numCopies.label "Number of copies:">
-<!ENTITY numCopies.accesskey "c">
-
-<!ENTITY printframeGroup.label "Print Frames">
-<!ENTITY aslaidoutRadio.label "As laid out on the screen">
-<!ENTITY aslaidoutRadio.accesskey "u">
-<!ENTITY selectedframeRadio.label  "The selected frame">
-<!ENTITY selectedframeRadio.accesskey  "m">
-<!ENTITY eachframesepRadio.label  "Each frame separately">
-<!ENTITY eachframesepRadio.accesskey  "E">
deleted file mode 100644
--- a/toolkit/locales/en-US/chrome/global/printjoboptions.dtd
+++ /dev/null
@@ -1,29 +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/. -->
-
-<!-- extracted from printjoboptions.xul -->
-
-<!ENTITY printJobOptions.title "Printer Properties">
-
-<!ENTITY paperInput.label "Paper Size:">
-<!ENTITY paperInput.accesskey "P">
-
-<!ENTITY jobTitleInput.label "Job Title:">
-<!ENTITY jobTitleInput.accesskey "J">
-
-<!ENTITY colorGroup.label "Color:">
-<!ENTITY grayRadio.label "Grayscale">
-<!ENTITY grayRadio.accesskey "G">
-<!ENTITY colorRadio.label "Color">
-<!ENTITY colorRadio.accesskey "C">
-
-<!ENTITY edgeMarginInput.label "Gap from edge of paper to Margin">
-<!ENTITY topInput.label "Top:">
-<!ENTITY topInput.accesskey "T">
-<!ENTITY bottomInput.label "Bottom:">
-<!ENTITY bottomInput.accesskey "B">
-<!ENTITY leftInput.label "Left:">
-<!ENTITY leftInput.accesskey "L">
-<!ENTITY rightInput.label "Right:">
-<!ENTITY rightInput.accesskey "R">
--- a/toolkit/locales/jar.mn
+++ b/toolkit/locales/jar.mn
@@ -65,19 +65,19 @@
   locale/@AB_CD@/global/languageNames.properties        (%chrome/global/languageNames.properties)
   locale/@AB_CD@/global/mozilla.dtd                     (%chrome/global/mozilla.dtd)
 #ifndef MOZ_FENNEC
   locale/@AB_CD@/global/narrate.properties              (%chrome/global/narrate.properties)
 #endif
   locale/@AB_CD@/global/notification.dtd                (%chrome/global/notification.dtd)
   locale/@AB_CD@/global/preferences.dtd                 (%chrome/global/preferences.dtd)
 #ifndef MOZ_FENNEC
-  locale/@AB_CD@/global/printdialog.dtd                 (%chrome/global/printdialog.dtd)
-  locale/@AB_CD@/global/printjoboptions.dtd             (%chrome/global/printjoboptions.dtd)
+#ifndef MOZ_GTK
   locale/@AB_CD@/global/printPageSetup.dtd              (%chrome/global/printPageSetup.dtd)
+#endif
   locale/@AB_CD@/global/printPreview.dtd                (%chrome/global/printPreview.dtd)
   locale/@AB_CD@/global/printPreviewProgress.dtd        (%chrome/global/printPreviewProgress.dtd)
   locale/@AB_CD@/global/printdialog.properties          (%chrome/global/printdialog.properties)
   locale/@AB_CD@/global/printProgress.dtd               (%chrome/global/printProgress.dtd)
 #endif
   locale/@AB_CD@/global/regionNames.properties          (%chrome/global/regionNames.properties)
   locale/@AB_CD@/global/resetProfile.dtd                (%chrome/global/resetProfile.dtd)
   locale/@AB_CD@/global/resetProfile.properties         (%chrome/global/resetProfile.properties)
--- a/toolkit/themes/linux/global/jar.mn
+++ b/toolkit/themes/linux/global/jar.mn
@@ -19,17 +19,16 @@ toolkit.jar:
    skin/classic/global/listbox.css
    skin/classic/global/menu.css
    skin/classic/global/menulist.css
    skin/classic/global/netError.css
 *  skin/classic/global/notification.css
    skin/classic/global/numberbox.css
    skin/classic/global/popup.css
    skin/classic/global/preferences.css
-   skin/classic/global/printPageSetup.css
    skin/classic/global/printPreview.css
    skin/classic/global/radio.css
    skin/classic/global/scrollbox.css
    skin/classic/global/splitter.css
    skin/classic/global/tabbox.css
    skin/classic/global/textbox.css
    skin/classic/global/toolbar.css
    skin/classic/global/toolbarbutton.css
--- a/widget/gtk/WakeLockListener.cpp
+++ b/widget/gtk/WakeLockListener.cpp
@@ -7,16 +7,20 @@
 
 #ifdef MOZ_ENABLE_DBUS
 
 #include "WakeLockListener.h"
 
 #include <dbus/dbus.h>
 #include <dbus/dbus-glib-lowlevel.h>
 
+#if defined(MOZ_X11)
+#include "prlink.h"
+#endif
+
 #define FREEDESKTOP_SCREENSAVER_TARGET    "org.freedesktop.ScreenSaver"
 #define FREEDESKTOP_SCREENSAVER_OBJECT    "/ScreenSaver"
 #define FREEDESKTOP_SCREENSAVER_INTERFACE "org.freedesktop.ScreenSaver"
 
 #define SESSION_MANAGER_TARGET            "org.gnome.SessionManager"
 #define SESSION_MANAGER_OBJECT            "/org/gnome/SessionManager"
 #define SESSION_MANAGER_INTERFACE         "org.gnome.SessionManager"
 
@@ -27,16 +31,19 @@ using namespace mozilla;
 NS_IMPL_ISUPPORTS(WakeLockListener, nsIDOMMozWakeLockListener)
 
 StaticRefPtr<WakeLockListener> WakeLockListener::sSingleton;
 
 
 enum DesktopEnvironment {
   FreeDesktop,
   GNOME,
+#if defined(MOZ_X11)
+  XScreenSaver,
+#endif
   Unsupported,
 };
 
 class WakeLockTopic
 {
 public:
   WakeLockTopic(const nsAString& aTopic, DBusConnection* aConnection)
     : mTopic(NS_ConvertUTF16toUTF8(aTopic))
@@ -54,16 +61,21 @@ public:
 private:
   bool SendInhibit();
   bool SendUninhibit();
 
   bool SendFreeDesktopInhibitMessage();
   bool SendGNOMEInhibitMessage();
   bool SendMessage(DBusMessage* aMessage);
 
+#if defined(MOZ_X11)
+  static bool CheckXScreenSaverSupport();
+  static bool InhibitXScreenSaver(bool inhibit);
+#endif
+
   static void ReceiveInhibitReply(DBusPendingCall* aPending, void* aUserData);
   void InhibitFailed();
   void InhibitSucceeded(uint32_t aInhibitRequest);
 
   nsCString mTopic;
   RefPtr<DBusConnection> mConnection;
 
   DesktopEnvironment mDesktopEnvironment;
@@ -138,29 +150,98 @@ WakeLockTopic::SendGNOMEInhibitMessage()
                            DBUS_TYPE_STRING, &topic,
                            DBUS_TYPE_UINT32, &flags,
                            DBUS_TYPE_INVALID);
 
   return SendMessage(message);
 }
 
 
+#if defined(MOZ_X11)
+
+typedef Bool (*_XScreenSaverQueryExtension_fn)(Display* dpy, int* event_base,
+                                               int* error_base);
+typedef Bool (*_XScreenSaverQueryVersion_fn)(Display* dpy, int* major,
+                                             int* minor);
+typedef void (*_XScreenSaverSuspend_fn)(Display* dpy, Bool suspend);
+
+static PRLibrary* sXssLib = nullptr;
+static _XScreenSaverQueryExtension_fn _XSSQueryExtension = nullptr;
+static _XScreenSaverQueryVersion_fn _XSSQueryVersion = nullptr;
+static _XScreenSaverSuspend_fn _XSSSuspend = nullptr;
+
+/* static */ bool
+WakeLockTopic::CheckXScreenSaverSupport()
+{
+  if (!sXssLib) {
+    sXssLib = PR_LoadLibrary("libXss.so.1");
+    if (!sXssLib) {
+      return false;
+    }
+  }
+
+  _XSSQueryExtension = (_XScreenSaverQueryExtension_fn)
+      PR_FindFunctionSymbol(sXssLib, "XScreenSaverQueryExtension");
+  _XSSQueryVersion = (_XScreenSaverQueryVersion_fn)
+      PR_FindFunctionSymbol(sXssLib, "XScreenSaverQueryVersion");
+  _XSSSuspend = (_XScreenSaverSuspend_fn)
+      PR_FindFunctionSymbol(sXssLib, "XScreenSaverSuspend");
+  if (!_XSSQueryExtension || !_XSSQueryVersion || !_XSSSuspend) {
+    return false;
+  }
+
+  GdkDisplay* gDisplay = gdk_display_get_default();
+  if (!GDK_IS_X11_DISPLAY(gDisplay)) return false;
+  Display* display = GDK_DISPLAY_XDISPLAY(gDisplay);
+
+  int throwaway;
+  if (!_XSSQueryExtension(display, &throwaway, &throwaway)) return false;
+
+  int major, minor;
+  if (!_XSSQueryVersion(display, &major, &minor)) return false;
+  // Needs to be compatible with version 1.1
+  if (major != 1) return false;
+  if (minor < 1) return false;
+
+  return true;
+}
+
+/* static */ bool
+WakeLockTopic::InhibitXScreenSaver(bool inhibit)
+{
+  // Should only be called if CheckXScreenSaverSupport returns true.
+  // There's a couple of safety checks here nonetheless.
+  if (!_XSSSuspend) return false;
+  GdkDisplay* gDisplay = gdk_display_get_default();
+  if (!GDK_IS_X11_DISPLAY(gDisplay)) return false;
+  Display* display = GDK_DISPLAY_XDISPLAY(gDisplay);
+  _XSSSuspend(display, inhibit);
+  return true;
+}
+
+#endif
+
+
 bool
 WakeLockTopic::SendInhibit()
 {
   bool sendOk = false;
 
   switch (mDesktopEnvironment)
   {
   case FreeDesktop:
     sendOk = SendFreeDesktopInhibitMessage();
     break;
   case GNOME:
     sendOk = SendGNOMEInhibitMessage();
     break;
+#if defined(MOZ_X11)
+  case XScreenSaver:
+    return InhibitXScreenSaver(true);
+#endif
   case Unsupported:
     return false;
   }
 
   if (sendOk) {
     mWaitingForReply = true;
   }
 
@@ -180,16 +261,21 @@ WakeLockTopic::SendUninhibit()
                                    "UnInhibit"));
   } else if (mDesktopEnvironment == GNOME) {
     message = already_AddRefed<DBusMessage>(
       dbus_message_new_method_call(SESSION_MANAGER_TARGET,
                                    SESSION_MANAGER_OBJECT,
                                    SESSION_MANAGER_INTERFACE,
                                    "Uninhibit"));
   }
+#if defined(MOZ_X11)
+  else if (mDesktopEnvironment == XScreenSaver) {
+    return InhibitXScreenSaver(false);
+  }
+#endif
 
   if (!message) {
     return false;
   }
 
   dbus_message_append_args(message,
                            DBUS_TYPE_UINT32, &mInhibitRequest,
                            DBUS_TYPE_INVALID);
@@ -244,18 +330,21 @@ WakeLockTopic::UninhibitScreensaver()
 
 void
 WakeLockTopic::InhibitFailed()
 {
   mWaitingForReply = false;
 
   if (mDesktopEnvironment == FreeDesktop) {
     mDesktopEnvironment = GNOME;
+#if defined(MOZ_X11)
+  } else if (mDesktopEnvironment == GNOME && CheckXScreenSaverSupport()) {
+    mDesktopEnvironment = XScreenSaver;
+#endif
   } else {
-    NS_ASSERTION(mDesktopEnvironment == GNOME, "Unknown desktop environment");
     mDesktopEnvironment = Unsupported;
     mShouldInhibit = false;
   }
 
   if (!mShouldInhibit) {
     // We were interrupted by UninhibitScreensaver() before we could find the
     // correct desktop environment.
     return;