Merge m-c to graphics
authorKartikaya Gupta <kgupta@mozilla.com>
Mon, 21 Nov 2016 10:24:00 -0500
changeset 341871 de6d43636e4252fc43371dce0808231e7ff3bea6
parent 341870 0fc195181db205bf2d621413c93071182e5ad0ed (current diff)
parent 323581 0534254e9a40b4bade2577c631fe4cfa0b5db41d (diff)
child 341872 e37eaf3f1d2e99c5f7362d580b8358fcb86bb24c
child 341874 998a69cbaf54e31bb5d1d95d49cde70cfe89153c
push id31345
push userkwierso@gmail.com
push dateFri, 10 Feb 2017 20:35:09 +0000
treeherdermozilla-central@a288fe35e494 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone53.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 m-c to graphics MozReview-Commit-ID: HlYQkwnswIh
b2g/components/UpdatePrompt.js
browser/components/extensions/test/browser/browser_ext_browserAction_popup_preload.js
browser/installer/package-manifest.in
browser/themes/shared/downloads/menubutton-dropmarker.svg
browser/themes/shared/filters.svg
config/external/nss/Makefile.in
config/external/nss/crmf/moz.build
config/external/nss/moz.build
config/external/nss/nss.mk
config/external/nss/nss.symbols
devtools/client/webide/content/permissionstable.js
devtools/client/webide/content/permissionstable.xhtml
devtools/client/webide/test/test_device_permissions.html
devtools/client/webide/themes/permissionstable.css
dom/animation/test/mozilla/file_spacing_transform.html
dom/animation/test/mozilla/test_spacing_transform.html
dom/apps/PermissionsTable.jsm
dom/apps/moz.build
dom/apps/tests/create_test_receipts.py
dom/apps/tests/head.js
dom/permission/tests/file_framework.js
dom/permission/tests/file_shim.html
dom/permission/tests/mochitest-time.ini
dom/permission/tests/test_browser.html
dom/permission/tests/test_idle.html
dom/permission/tests/test_input-manage.html
dom/permission/tests/test_keyboard.html
dom/permission/tests/test_networkstats-manage.html
dom/permission/tests/test_power.html
dom/permission/tests/test_presentation-device-manage.html
dom/permission/tests/test_systemXHR.html
dom/permission/tests/test_tcp-socket.html
dom/permission/tests/test_time.html
dom/permission/tests/test_udp-socket.html
dom/permission/tests/unit/test_bug808734.js
dom/permission/tests/unit/xpcshell.ini
dom/system/SystemUpdate.manifest
dom/system/SystemUpdateManager.js
dom/system/SystemUpdateService.jsm
dom/system/nsISystemUpdateProvider.idl
dom/system/tests/preload-SystemUpdateManager-jsm.js
dom/system/tests/test_system_update_enabled.html
dom/webidl/SystemUpdate.webidl
gfx/layers/ipc/CompositorBridgeParent.cpp
gfx/layers/ipc/CompositorBridgeParent.h
gfx/layers/ipc/CompositorVsyncScheduler.cpp
gfx/layers/ipc/CompositorVsyncScheduler.h
gfx/layers/ipc/CompositorVsyncSchedulerOwner.h
gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp
gfx/layers/ipc/CrossProcessCompositorBridgeParent.h
gfx/layers/moz.build
gfx/thebes/gfxPlatform.cpp
js/src/tests/Intl/Collator/construct-newtarget.js
js/src/tests/Intl/DateTimeFormat/construct-newtarget.js
js/src/tests/Intl/NumberFormat/construct-newtarget.js
js/src/tests/ecma_2017/AsyncFunctions/construct-newtarget.js
js/src/tests/ecma_2017/AsyncFunctions/subclass.js
js/src/tests/ecma_6/Generators/construct-newtarget.js
js/src/tests/ecma_6/Generators/subclass.js
js/src/wasm/WasmBinaryFormat.cpp
js/src/wasm/WasmBinaryFormat.h
js/xpconnect/tests/chrome/test_wrappers-2.xul
layout/style/test/test_computed_style_min_size_auto.html
layout/style/test/test_flexbox_min_size_auto.html
modules/libpref/init/all.js
security/moz.build
security/nss.symbols
taskcluster/ci/build/linux.yml
taskcluster/taskgraph/transforms/gecko_v2_whitelist.py
testing/marionette/components/MarionetteComponents.manifest
testing/marionette/components/marionette.js
testing/marionette/components/marionettecomponent.js
testing/mozharness/mozharness/mozilla/building/buildbase.py
testing/web-platform/harness/requirements_b2g.txt
testing/web-platform/harness/wptrunner/browsers/b2g.py
testing/web-platform/meta/XMLHttpRequest/XMLHttpRequest-withCredentials.any.js.ini
testing/web-platform/meta/battery-status/battery-charging-manual.html.ini
testing/web-platform/meta/battery-status/battery-discharging-manual.html.ini
testing/web-platform/meta/battery-status/battery-full-manual.html.ini
testing/web-platform/meta/battery-status/battery-plugging-in-manual.html.ini
testing/web-platform/meta/battery-status/battery-unplugging-manual.html.ini
testing/web-platform/meta/battery-status/support-iframe-initial.html.ini
testing/web-platform/meta/battery-status/support-iframe.html.ini
testing/web-platform/meta/battery-status/support-window-open.html.ini
testing/web-platform/meta/html/browsers/the-window-object/security-window/window-security.sub.html.ini
testing/web-platform/meta/html/browsers/windows/nested-browsing-contexts/frameElement.sub.html.ini
testing/web-platform/meta/html/dom/documents/dom-tree-accessors/Document.currentScript.sub.html.ini
testing/web-platform/meta/html/semantics/embedded-content/media-elements/interfaces/TrackEvent/constructor.html.ini
testing/web-platform/meta/html/semantics/forms/the-input-element/radio.html.ini
testing/web-platform/meta/html/semantics/grouping-content/the-ol-element/ol.start-reflection-2.html.ini
testing/web-platform/meta/html/webappapis/system-state-and-capabilities/the-navigator-object/NavigatorID.worker.js.ini
testing/web-platform/meta/mediacapture-streams/MediaStream-id-manual.https.html.ini
testing/web-platform/meta/url/url-domainToUnicode.html.ini
testing/web-platform/meta/workers/interfaces/WorkerUtils/navigator/window-only.worker.js.ini
testing/web-platform/tests/XMLHttpRequest/event-upload-progress-crossorigin.htm
testing/web-platform/tests/XMLHttpRequest/event-upload-progress-crossorigin.sub.htm
testing/web-platform/tests/XMLHttpRequest/send-non-same-origin.htm
testing/web-platform/tests/XMLHttpRequest/send-non-same-origin.sub.htm
testing/web-platform/tests/conformance-checkers/html/elements/dl/model-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/dl/model-novalid.html
testing/web-platform/tests/dom/ranges/Range-mutations.html
testing/web-platform/tests/encrypted-media/scripts/playback-temporary-multikey-multisession.js
testing/web-platform/tests/fetch/api/request/request-cache.html
testing/web-platform/tests/html/browsers/history/the-location-interface/security_location_0.htm
testing/web-platform/tests/html/browsers/history/the-location-interface/security_location_0.sub.htm
testing/web-platform/tests/html/browsers/the-window-object/security-window/window-security.html
testing/web-platform/tests/html/browsers/the-window-object/security-window/window-security.sub.html
testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/frameElement.html
testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/frameElement.sub.html
testing/web-platform/tests/html/browsers/windows/targeting-cross-origin-nested-browsing-contexts.html
testing/web-platform/tests/html/browsers/windows/targeting-cross-origin-nested-browsing-contexts.sub.html
testing/web-platform/tests/html/dom/documents/dom-tree-accessors/Document.currentScript.html
testing/web-platform/tests/html/dom/documents/dom-tree-accessors/Document.currentScript.sub.html
testing/web-platform/tests/html/semantics/document-metadata/the-base-element/base_href_specified.html
testing/web-platform/tests/html/semantics/document-metadata/the-base-element/base_href_specified.sub.html
testing/web-platform/tests/svg/import/shapes-rect-01-t-manual.svg
testing/web-platform/tests/url/url-domainToUnicode.html
testing/web-platform/tests/workers/interfaces/WorkerUtils/navigator/window-only.worker.js
toolkit/components/extensions/test/mochitest/mochitest-common.ini
toolkit/components/places/tests/cpp/mock_Link.h
toolkit/components/places/tests/cpp/moz.build
toolkit/components/places/tests/cpp/places_test_harness.h
toolkit/components/places/tests/cpp/places_test_harness_tail.h
toolkit/components/places/tests/cpp/test_IHistory.cpp
toolkit/components/places/tests/gtest/mock_Link.h
toolkit/components/places/tests/gtest/moz.build
toolkit/components/places/tests/gtest/places_test_harness.h
toolkit/components/places/tests/gtest/places_test_harness_tail.h
toolkit/components/places/tests/gtest/test_IHistory.cpp
toolkit/themes/osx/global/filters.svg
toolkit/themes/shared/filters.svg
toolkit/themes/shared/icons/menubutton-dropmarker.svg
--- a/Makefile.in
+++ b/Makefile.in
@@ -170,17 +170,17 @@ install-manifests: faster
 faster: install-dist/idl
 	$(MAKE) -C faster FASTER_RECURSIVE_MAKE=1
 endif
 
 .PHONY: tup
 tup:
 	$(call BUILDSTATUS,TIERS make tup)
 	$(call BUILDSTATUS,TIER_START make)
-	$(MAKE) install-manifests buildid.h source-repo.h
+	$(MAKE) buildid.h source-repo.h
 	$(call BUILDSTATUS,TIER_FINISH make)
 	$(call BUILDSTATUS,TIER_START tup)
 	@$(TUP) $(if $(findstring s,$(filter-out --%,$(MAKEFLAGS))),,--verbose)
 	$(call BUILDSTATUS,TIER_FINISH tup)
 
 # process_install_manifest needs to be invoked with --no-remove when building
 # js as standalone because automated builds are building nspr separately and
 # that would remove the resulting files.
--- a/addon-sdk/source/lib/dev/volcan.js
+++ b/addon-sdk/source/lib/dev/volcan.js
@@ -3291,27 +3291,16 @@ module.exports={
           "request": {
             "type": "screenshotToDataURL"
           },
           "response": {
             "value": {
               "_retval": "longstring"
             }
           }
-        },
-        {
-          "name": "getRawPermissionsTable",
-          "request": {
-            "type": "getRawPermissionsTable"
-          },
-          "response": {
-            "value": {
-              "_retval": "json"
-            }
-          }
         }
       ],
       "events": {}
     }
   },
   "from": "root"
 }
 
--- a/addon-sdk/source/test/test-api-utils.js
+++ b/addon-sdk/source/test/test-api-utils.js
@@ -276,16 +276,25 @@ exports.testValidateMapWithMissingKeyAnd
     },
     baz: {
       map: v => "foo"
     }
   });
   assert.deepEqual(val, { baz: "foo" });
 };
 
+function forEachEnabled() {
+  try {
+    eval(`for each (var x in {}) {}`);
+  } catch (e) {
+    return false;
+  }
+  return true;
+}
+
 exports.testAddIterator = function testAddIterator (assert) {
   let obj = {};
   let keys = ["foo", "bar", "baz"];
   let vals = [1, 2, 3];
   let keysVals = [["foo", 1], ["bar", 2], ["baz", 3]];
   apiUtils.addIterator(
     obj,
     function keysValsGen() {
@@ -298,19 +307,22 @@ exports.testAddIterator = function testA
   for (let key in obj)
     keysItr.push(key);
 
   assert.equal(keysItr.length, keys.length,
                    "the keys iterator returns the correct number of items");
   for (let i = 0; i < keys.length; i++)
     assert.equal(keysItr[i], keys[i], "the key is correct");
 
-  let valsItr = [];
-  for each (let val in obj)
-    valsItr.push(val);
-  assert.equal(valsItr.length, vals.length,
-                   "the vals iterator returns the correct number of items");
-  for (let i = 0; i < vals.length; i++)
-    assert.equal(valsItr[i], vals[i], "the val is correct");
-
+  if (forEachEnabled()) {
+    eval(`
+    let valsItr = [];
+    for each (let val in obj)
+      valsItr.push(val);
+    assert.equal(valsItr.length, vals.length,
+                     "the vals iterator returns the correct number of items");
+    for (let i = 0; i < vals.length; i++)
+      assert.equal(valsItr[i], vals[i], "the val is correct");
+  `);
+  }
 };
 
 require("sdk/test").run(exports);
--- a/addon-sdk/source/test/test-content-script.js
+++ b/addon-sdk/source/test/test-content-script.js
@@ -418,19 +418,19 @@ exports["test Auto Unwrap Custom Attribu
   );
 
 });
 
 exports["test Object Tag"] = createProxyTest("", function (helper) {
 
   helper.createWorker(
     'new ' + function ContentScriptScope() {
-      // <object>, <embed> and other tags return typeof 'function'
+      // <object>, <embed> and other tags return typeof 'object'
       let flash = document.createElement("object");
-      assert(typeof flash == "function", "<object> is typeof 'function'");
+      assert(typeof flash == "object", "<object> is typeof 'function'");
       assert(flash.toString().match(/\[object HTMLObjectElement.*\]/), "<object> is HTMLObjectElement");
       assert("setAttribute" in flash, "<object> has a setAttribute method");
       done();
     }
   );
 
 });
 
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -495,19 +495,16 @@ pref("app.update.socket.retryTimeout", 3
 // Max of 20 consecutive retries (total 10 minutes) before giving up and marking
 // the update download as failed.
 // Note: Offline errors will always retry when the network comes online.
 pref("app.update.socket.maxErrors", 20);
 
 // Enable update logging for now, to diagnose growing pains in the
 // field.
 pref("app.update.log", true);
-
-// SystemUpdate API
-pref("dom.system_update.active", "@mozilla.org/updates/update-prompt;1");
 #else
 // Explicitly disable the shutdown watchdog.  It's enabled by default.
 // When the updater is disabled, we want to know about shutdown hangs.
 pref("shutdown.watchdog.timeoutSecs", -1);
 #endif
 
 // Allow webapps update checking
 pref("webapps.update.enabled", true);
@@ -934,19 +931,16 @@ pref("identity.fxaccounts.remote.oauth.u
 pref("identity.fxaccounts.remote.profile.uri", "https://profile.accounts.firefox.com/v1");
 
 // Disable Firefox Accounts device registration until bug 1238895 is fixed.
 pref("identity.fxaccounts.skipDeviceRegistration", true);
 
 // Enable mapped array buffer.
 pref("dom.mapped_arraybuffer.enabled", true);
 
-// SystemUpdate API
-pref("dom.system_update.enabled", true);
-
 // UDPSocket API
 pref("dom.udpsocket.enabled", true);
 
 // Enable TV Manager API
 pref("dom.tv.enabled", true);
 
 // Enable Inputport Manager API
 pref("dom.inputport.enabled", true);
--- a/b2g/components/B2GComponents.manifest
+++ b/b2g/components/B2GComponents.manifest
@@ -4,23 +4,16 @@ category agent-style-sheets browser-cont
 # AlertsService.js
 component {fe33c107-82a4-41d6-8c64-5353267e04c9} AlertsService.js
 contract @mozilla.org/system-alerts-service;1 {fe33c107-82a4-41d6-8c64-5353267e04c9}
 
 # ContentPermissionPrompt.js
 component {8c719f03-afe0-4aac-91ff-6c215895d467} ContentPermissionPrompt.js
 contract @mozilla.org/content-permission/prompt;1 {8c719f03-afe0-4aac-91ff-6c215895d467}
 
-#ifdef MOZ_UPDATER
-# UpdatePrompt.js
-component {88b3eb21-d072-4e3b-886d-f89d8c49fe59} UpdatePrompt.js
-contract @mozilla.org/updates/update-prompt;1 {88b3eb21-d072-4e3b-886d-f89d8c49fe59}
-category system-update-provider MozillaProvider @mozilla.org/updates/update-prompt;1,{88b3eb21-d072-4e3b-886d-f89d8c49fe59}
-#endif
-
 #ifdef MOZ_B2G
 # DirectoryProvider.js
 component {9181eb7c-6f87-11e1-90b1-4f59d80dd2e5} DirectoryProvider.js
 contract @mozilla.org/b2g/directory-provider;1 {9181eb7c-6f87-11e1-90b1-4f59d80dd2e5}
 category xpcom-directory-providers b2g-directory-provider @mozilla.org/b2g/directory-provider;1
 #endif
 
 # SystemMessageGlue.js
--- a/b2g/components/ContentPermissionPrompt.js
+++ b/b2g/components/ContentPermissionPrompt.js
@@ -19,17 +19,16 @@ const PROMPT_FOR_UNKNOWN = ["audio-captu
                             "video-capture"];
 // Due to privary issue, permission requests like GetUserMedia should prompt
 // every time instead of providing session persistence.
 const PERMISSION_NO_SESSION = ["audio-capture", "video-capture"];
 const ALLOW_MULTIPLE_REQUESTS = ["audio-capture", "video-capture"];
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/PermissionsTable.jsm");
 
 var permissionManager = Cc["@mozilla.org/permissionmanager;1"].getService(Ci.nsIPermissionManager);
 var secMan = Cc["@mozilla.org/scriptsecuritymanager;1"].getService(Ci.nsIScriptSecurityManager);
 
 var permissionSpecificChecker = {};
 
 XPCOMUtils.defineLazyModuleGetter(this, "SystemAppProxy",
                                   "resource://gre/modules/SystemAppProxy.jsm");
@@ -62,66 +61,16 @@ function buildDefaultChoices(aTypesInfo)
         choices = {};
       }
       choices[type.access] = type.options[0];
     }
   }
   return choices;
 }
 
-/**
- * aTypesInfo is an array of {permission, access, action, deny} which keeps
- * the information of each permission. This arrary is initialized in
- * ContentPermissionPrompt.prompt and used among functions.
- *
- * aTypesInfo[].permission : permission name
- * aTypesInfo[].access     : permission name + request.access
- * aTypesInfo[].action     : the default action of this permission
- * aTypesInfo[].deny       : true if security manager denied this app's origin
- *                           principal.
- * Note:
- *   aTypesInfo[].permission will be sent to prompt only when
- *   aTypesInfo[].action is PROMPT_ACTION and aTypesInfo[].deny is false.
- */
-function rememberPermission(aTypesInfo, aPrincipal, aSession)
-{
-  function convertPermToAllow(aPerm, aPrincipal)
-  {
-    let type =
-      permissionManager.testExactPermissionFromPrincipal(aPrincipal, aPerm);
-    if (shouldPrompt(aPerm, type)) {
-      debug("add " + aPerm + " to permission manager with ALLOW_ACTION");
-      if (!aSession) {
-        permissionManager.addFromPrincipal(aPrincipal,
-                                           aPerm,
-                                           Ci.nsIPermissionManager.ALLOW_ACTION);
-      } else if (PERMISSION_NO_SESSION.indexOf(aPerm) < 0) {
-        permissionManager.addFromPrincipal(aPrincipal,
-                                           aPerm,
-                                           Ci.nsIPermissionManager.ALLOW_ACTION,
-                                           Ci.nsIPermissionManager.EXPIRE_SESSION, 0);
-      }
-    }
-  }
-
-  for (let i in aTypesInfo) {
-    // Expand the permission to see if we have multiple access properties
-    // to convert
-    let perm = aTypesInfo[i].permission;
-    let access = PermissionsTable[perm].access;
-    if (access) {
-      for (let idx in access) {
-        convertPermToAllow(perm + "-" + access[idx], aPrincipal);
-      }
-    } else {
-      convertPermToAllow(perm, aPrincipal);
-    }
-  }
-}
-
 function ContentPermissionPrompt() {}
 
 ContentPermissionPrompt.prototype = {
 
   handleExistingPermission: function handleExistingPermission(request,
                                                               typesInfo) {
     typesInfo.forEach(function(type) {
       type.action =
@@ -291,17 +240,16 @@ ContentPermissionPrompt.prototype = {
     this.sendToBrowserWindow("cancel-permission-prompt", request,
                              typesInfo);
   },
 
   delegatePrompt: function(request, typesInfo, callback) {
     this.sendToBrowserWindow("permission-prompt", request, typesInfo,
                              function(type, remember, choices) {
       if (type == "permission-allow") {
-        rememberPermission(typesInfo, request.principal, !remember);
         if (callback) {
           callback();
         }
         request.allow(choices);
         return;
       }
 
       let addDenyPermission = function(type) {
deleted file mode 100644
--- a/b2g/components/UpdatePrompt.js
+++ /dev/null
@@ -1,783 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
- * vim: sw=2 ts=8 et :
- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-const Cc = Components.classes;
-const Ci = Components.interfaces;
-const Cu = Components.utils;
-const Cr = Components.results;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/AppConstants.jsm");
-
-const VERBOSE = 1;
-var log =
-  VERBOSE ?
-  function log_dump(msg) { dump("UpdatePrompt: "+ msg +"\n"); } :
-  function log_noop(msg) { };
-
-const PREF_APPLY_PROMPT_TIMEOUT          = "b2g.update.apply-prompt-timeout";
-const PREF_APPLY_IDLE_TIMEOUT            = "b2g.update.apply-idle-timeout";
-const PREF_DOWNLOAD_WATCHDOG_TIMEOUT     = "b2g.update.download-watchdog-timeout";
-const PREF_DOWNLOAD_WATCHDOG_MAX_RETRIES = "b2g.update.download-watchdog-max-retries";
-
-const NETWORK_ERROR_OFFLINE = 111;
-const HTTP_ERROR_OFFSET     = 1000;
-
-const STATE_DOWNLOADING = 'downloading';
-
-XPCOMUtils.defineLazyServiceGetter(Services, "aus",
-                                   "@mozilla.org/updates/update-service;1",
-                                   "nsIApplicationUpdateService");
-
-XPCOMUtils.defineLazyServiceGetter(Services, "um",
-                                   "@mozilla.org/updates/update-manager;1",
-                                   "nsIUpdateManager");
-
-XPCOMUtils.defineLazyServiceGetter(Services, "idle",
-                                   "@mozilla.org/widget/idleservice;1",
-                                   "nsIIdleService");
-
-XPCOMUtils.defineLazyServiceGetter(Services, "settings",
-                                   "@mozilla.org/settingsService;1",
-                                   "nsISettingsService");
-
-XPCOMUtils.defineLazyServiceGetter(Services, 'env',
-                                   '@mozilla.org/process/environment;1',
-                                   'nsIEnvironment');
-
-function useSettings() {
-  // When we're running in the real phone, then we can use settings.
-  // But when we're running as part of xpcshell, there is no settings database
-  // and trying to use settings in this scenario causes lots of weird
-  // assertions at shutdown time.
-  if (typeof useSettings.result === "undefined") {
-    useSettings.result = !Services.env.get("XPCSHELL_TEST_PROFILE_DIR");
-  }
-  return useSettings.result;
-}
-
-XPCOMUtils.defineLazyModuleGetter(this, "SystemAppProxy",
-                                  "resource://gre/modules/SystemAppProxy.jsm");
-
-function UpdateCheckListener(updatePrompt) {
-  this._updatePrompt = updatePrompt;
-}
-
-UpdateCheckListener.prototype = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIUpdateCheckListener]),
-
-  _updatePrompt: null,
-
-  onCheckComplete: function UCL_onCheckComplete(request, updates, updateCount) {
-    if (Services.um.activeUpdate) {
-      // We're actively downloading an update, that's the update the user should
-      // see, even if a newer update is available.
-      this._updatePrompt.setUpdateStatus("active-update");
-      this._updatePrompt.showUpdateAvailable(Services.um.activeUpdate);
-      return;
-    }
-
-    if (updateCount == 0) {
-      this._updatePrompt.setUpdateStatus("no-updates");
-
-      if (this._updatePrompt._systemUpdateListener) {
-        this._updatePrompt._systemUpdateListener.onError("no-updates");
-      }
-
-      return;
-    }
-
-    let update = Services.aus.selectUpdate(updates, updateCount);
-    if (!update) {
-      this._updatePrompt.setUpdateStatus("already-latest-version");
-
-      if (this._updatePrompt._systemUpdateListener) {
-        this._updatePrompt._systemUpdateListener.onError("already-latest-version");
-      }
-
-      return;
-    }
-
-    this._updatePrompt.setUpdateStatus("check-complete");
-    this._updatePrompt.showUpdateAvailable(update);
-  },
-
-  onError: function UCL_onError(request, update) {
-    // nsIUpdate uses a signed integer for errorCode while any platform errors
-    // require all 32 bits.
-    let errorCode = update.errorCode >>> 0;
-    let isNSError = (errorCode >>> 31) == 1;
-    let errorMsg = "check-error-";
-
-    if (errorCode == NETWORK_ERROR_OFFLINE) {
-      errorMsg = "retry-when-online";
-      this._updatePrompt.setUpdateStatus(errorMsg);
-    } else if (isNSError) {
-      errorMsg = "check-error-" + errorCode;
-      this._updatePrompt.setUpdateStatus(errorMsg);
-    } else if (errorCode > HTTP_ERROR_OFFSET) {
-      let httpErrorCode = errorCode - HTTP_ERROR_OFFSET;
-      errorMsg = "check-error-http-" + httpErrorCode;
-      this._updatePrompt.setUpdateStatus(errorMsg);
-    }
-
-    if (this._updatePrompt._systemUpdateListener) {
-      this._updatePrompt._systemUpdateListener.onError(errorMsg);
-    }
-
-    Services.aus.QueryInterface(Ci.nsIUpdateCheckListener);
-    Services.aus.onError(request, update);
-  }
-};
-
-function UpdatePrompt() {
-  this.wrappedJSObject = this;
-  this._updateCheckListener = new UpdateCheckListener(this);
-}
-
-UpdatePrompt.prototype = {
-  classID: Components.ID("{88b3eb21-d072-4e3b-886d-f89d8c49fe59}"),
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIUpdatePrompt,
-                                         Ci.nsIUpdateCheckListener,
-                                         Ci.nsIRequestObserver,
-                                         Ci.nsIProgressEventSink,
-                                         Ci.nsIObserver,
-                                         Ci.nsISystemUpdateProvider]),
-  _xpcom_factory: XPCOMUtils.generateSingletonFactory(UpdatePrompt),
-
-  _update: null,
-  _applyPromptTimer: null,
-  _waitingForIdle: false,
-  _updateCheckListner: null,
-  _systemUpdateListener: null,
-  _availableParameters: {
-    "deviceinfo.last_updated": null,
-    "gecko.updateStatus": null,
-    "app.update.channel": null,
-    "app.update.interval": null,
-    "app.update.url": null,
-  },
-  _pendingUpdateAvailablePackageInfo: null,
-  _isPendingUpdateReady: false,
-  _updateErrorQueue: [ ],
-  _receivedUpdatePromptReady: false,
-
-  // nsISystemUpdateProvider
-  checkForUpdate: function() {
-    this.forceUpdateCheck();
-  },
-
-  startDownload: function() {
-    this.downloadUpdate(this._update);
-  },
-
-  stopDownload: function() {
-    this.handleDownloadCancel();
-  },
-
-  applyUpdate: function() {
-    this.handleApplyPromptResult({result: "restart"});
-  },
-
-  setParameter: function(aName, aValue) {
-    if (!this._availableParameters.hasOwnProperty(aName)) {
-      return false;
-    }
-
-    this._availableParameters[aName] = aValue;
-
-    switch (aName) {
-      case "app.update.channel":
-      case "app.update.url":
-        Services.prefs.setCharPref(aName, aValue);
-        break;
-      case "app.update.interval":
-        Services.prefs.setIntPref(aName, parseInt(aValue, 10));
-        break;
-    }
-
-    return true;
-  },
-
-  getParameter: function(aName) {
-    if (!this._availableParameters.hasOwnProperty(aName)) {
-      return null;
-    }
-
-    return this._availableParameters[aName];
-  },
-
-  setListener: function(aListener) {
-    this._systemUpdateListener = aListener;
-
-    // If an update is available or ready, trigger the event right away at this point.
-    if (this._pendingUpdateAvailablePackageInfo) {
-      this._systemUpdateListener.onUpdateAvailable(this._pendingUpdateAvailablePackageInfo.type,
-                                             this._pendingUpdateAvailablePackageInfo.version,
-                                             this._pendingUpdateAvailablePackageInfo.description,
-                                             this._pendingUpdateAvailablePackageInfo.buildDate,
-                                             this._pendingUpdateAvailablePackageInfo.size);
-      // Set null when the listener is attached.
-      this._pendingUpdateAvailablePackageInfo = null;
-    }
-
-    if (this._isPendingUpdateReady) {
-      this._systemUpdateListener.onUpdateReady();
-      this._isPendingUpdateReady = false;
-    }
-  },
-
-  unsetListener: function(aListener) {
-    this._systemUpdateListener = null;
-  },
-
-  get applyPromptTimeout() {
-    return Services.prefs.getIntPref(PREF_APPLY_PROMPT_TIMEOUT);
-  },
-
-  get applyIdleTimeout() {
-    return Services.prefs.getIntPref(PREF_APPLY_IDLE_TIMEOUT);
-  },
-
-  handleContentStart: function UP_handleContentStart() {
-    SystemAppProxy.addEventListener("mozContentEvent", this);
-  },
-
-  // nsIUpdatePrompt
-
-  // FIXME/bug 737601: we should have users opt-in to downloading
-  // updates when on a billed pipe.  Initially, opt-in for 3g, but
-  // that doesn't cover all cases.
-  checkForUpdates: function UP_checkForUpdates() { },
-
-  showUpdateAvailable: function UP_showUpdateAvailable(aUpdate) {
-    let packageInfo = {};
-    packageInfo.version = aUpdate.displayVersion;
-    packageInfo.description = aUpdate.statusText;
-    packageInfo.buildDate = aUpdate.buildID;
-
-    let patch = aUpdate.selectedPatch;
-    if (!patch && aUpdate.patchCount > 0) {
-      // For now we just check the first patch to get size information if a
-      // patch hasn't been selected yet.
-      patch = aUpdate.getPatchAt(0);
-    }
-
-    if (patch) {
-      packageInfo.size = patch.size;
-      packageInfo.type = patch.type;
-    } else {
-      log("Warning: no patches available in update");
-    }
-
-    this._pendingUpdateAvailablePackageInfo = packageInfo;
-
-    if (this._systemUpdateListener) {
-      this._systemUpdateListener.onUpdateAvailable(packageInfo.type,
-                                             packageInfo.version,
-                                             packageInfo.description,
-                                             packageInfo.buildDate,
-                                             packageInfo.size);
-      // Set null since the event is fired.
-      this._pendingUpdateAvailablePackageInfo = null;
-    }
-
-    if (!this.sendUpdateEvent("update-available", aUpdate)) {
-
-      log("Unable to prompt for available update, forcing download");
-      this.downloadUpdate(aUpdate);
-    }
-  },
-
-  showUpdateDownloaded: function UP_showUpdateDownloaded(aUpdate, aBackground) {
-    if (this._systemUpdateListener) {
-      this._systemUpdateListener.onUpdateReady();
-    } else {
-      this._isPendingUpdateReady = true;
-    }
-
-    // The update has been downloaded and staged. We send the update-downloaded
-    // event right away. After the user has been idle for a while, we send the
-    // update-prompt-restart event, increasing the chances that we can apply the
-    // update quietly without user intervention.
-    this.sendUpdateEvent("update-downloaded", aUpdate);
-
-    if (Services.idle.idleTime >= this.applyIdleTimeout) {
-      this.showApplyPrompt(aUpdate);
-      return;
-    }
-
-    let applyIdleTimeoutSeconds = this.applyIdleTimeout / 1000;
-    // We haven't been idle long enough, so register an observer
-    log("Update is ready to apply, registering idle timeout of " +
-        applyIdleTimeoutSeconds + " seconds before prompting.");
-
-    this._update = aUpdate;
-    this.waitForIdle();
-  },
-
-  storeUpdateError: function UP_storeUpdateError(aUpdate) {
-    log("Storing update error for later use");
-    this._updateErrorQueue.push(aUpdate);
-  },
-
-  sendStoredUpdateError: function UP_sendStoredUpdateError() {
-    log("Sending stored update error");
-    this._updateErrorQueue.forEach(aUpdate => {
-      this.sendUpdateEvent("update-error", aUpdate);
-    });
-    this._updateErrorQueue = [ ];
-  },
-
-  showUpdateError: function UP_showUpdateError(aUpdate) {
-    log("Update error, state: " + aUpdate.state + ", errorCode: " +
-        aUpdate.errorCode);
-    if (this._systemUpdateListener) {
-      this._systemUpdateListener.onError("update-error: " + aUpdate.errorCode + " " + aUpdate.statusText);
-    }
-
-    if (!this._receivedUpdatePromptReady) {
-      this.storeUpdateError(aUpdate);
-    } else {
-      this.sendUpdateEvent("update-error", aUpdate);
-    }
-
-    this.setUpdateStatus(aUpdate.statusText);
-  },
-
-  showUpdateHistory: function UP_showUpdateHistory(aParent) { },
-  showUpdateInstalled: function UP_showUpdateInstalled() {
-    this.setParameter("deviceinfo.last_updated", Date.now());
-
-    if (useSettings()) {
-      let lock = Services.settings.createLock();
-      lock.set("deviceinfo.last_updated", Date.now(), null, null);
-    }
-  },
-
-  // Custom functions
-
-  waitForIdle: function UP_waitForIdle() {
-    if (this._waitingForIdle) {
-      return;
-    }
-
-    this._waitingForIdle = true;
-    Services.idle.addIdleObserver(this, this.applyIdleTimeout / 1000);
-    Services.obs.addObserver(this, "quit-application", false);
-  },
-
-  setUpdateStatus: function UP_setUpdateStatus(aStatus) {
-     this.setParameter("gecko.updateStatus", aStatus);
-
-     if (useSettings()) {
-       log("Setting gecko.updateStatus: " + aStatus);
-
-       let lock = Services.settings.createLock();
-       lock.set("gecko.updateStatus", aStatus, null);
-     }
-  },
-
-  showApplyPrompt: function UP_showApplyPrompt(aUpdate) {
-    // Notify update package is ready to apply
-    if (this._systemUpdateListener) {
-      this._systemUpdateListener.onUpdateReady();
-    } else {
-      // Set the flag to true and fire the onUpdateReady event when the listener is attached.
-      this._isPendingUpdateReady = true;
-    }
-
-    if (!this.sendUpdateEvent("update-prompt-apply", aUpdate)) {
-      log("Unable to prompt, forcing restart");
-      this.restartProcess();
-      return;
-    }
-
-    if (AppConstants.MOZ_B2G_RIL) {
-      let window = Services.wm.getMostRecentWindow("navigator:browser");
-      let pinReq = window.navigator.mozIccManager.getCardLock("pin");
-      pinReq.onsuccess = function(e) {
-        if (e.target.result.enabled) {
-          // The SIM is pin locked. Don't use a fallback timer. This means that
-          // the user has to press Install to apply the update. If we use the
-          // timer, and the timer reboots the phone, then the phone will be
-          // unusable until the SIM is unlocked.
-          log("SIM is pin locked. Not starting fallback timer.");
-        } else {
-          // This means that no pin lock is enabled, so we go ahead and start
-          // the fallback timer.
-          this._applyPromptTimer = this.createTimer(this.applyPromptTimeout);
-        }
-      }.bind(this);
-      pinReq.onerror = function(e) {
-        this._applyPromptTimer = this.createTimer(this.applyPromptTimeout);
-      }.bind(this);
-    } else {
-      // Schedule a fallback timeout in case the UI is unable to respond or show
-      // a prompt for some reason.
-      this._applyPromptTimer = this.createTimer(this.applyPromptTimeout);
-    }
-  },
-
-  _copyProperties: ["appVersion", "buildID", "detailsURL", "displayVersion",
-                    "errorCode", "isOSUpdate", "platformVersion",
-                    "previousAppVersion", "state", "statusText"],
-
-  sendUpdateEvent: function UP_sendUpdateEvent(aType, aUpdate) {
-    let detail = {};
-    for (let property of this._copyProperties) {
-      detail[property] = aUpdate[property];
-    }
-
-    let patch = aUpdate.selectedPatch;
-    if (!patch && aUpdate.patchCount > 0) {
-      // For now we just check the first patch to get size information if a
-      // patch hasn't been selected yet.
-      patch = aUpdate.getPatchAt(0);
-    }
-
-    if (patch) {
-      detail.size = patch.size;
-      detail.updateType = patch.type;
-    } else {
-      log("Warning: no patches available in update");
-    }
-
-    this._update = aUpdate;
-    return this.sendChromeEvent(aType, detail);
-  },
-
-  sendChromeEvent: function UP_sendChromeEvent(aType, aDetail) {
-    let detail = aDetail || {};
-    detail.type = aType;
-
-    let sent = SystemAppProxy.dispatchEvent(detail);
-    if (!sent) {
-      log("Warning: Couldn't send update event " + aType +
-          ": no content browser. Will send again when content becomes available.");
-      return false;
-    }
-    return true;
-  },
-
-  handleAvailableResult: function UP_handleAvailableResult(aDetail) {
-    // If the user doesn't choose "download", the updater will implicitly call
-    // showUpdateAvailable again after a certain period of time
-    switch (aDetail.result) {
-      case "download":
-        this.downloadUpdate(this._update);
-        break;
-    }
-  },
-
-  handleApplyPromptResult: function UP_handleApplyPromptResult(aDetail) {
-    if (this._applyPromptTimer) {
-      this._applyPromptTimer.cancel();
-      this._applyPromptTimer = null;
-    }
-
-    switch (aDetail.result) {
-      // Battery not okay, do not wait for idle to re-prompt
-      case "low-battery":
-        break;
-      case "wait":
-        // Wait until the user is idle before prompting to apply the update
-        this.waitForIdle();
-        break;
-      case "restart":
-        this.finishUpdate();
-        this._update = null;
-        break;
-    }
-  },
-
-  downloadUpdate: function UP_downloadUpdate(aUpdate) {
-    if (!aUpdate) {
-      aUpdate = Services.um.activeUpdate;
-      if (!aUpdate) {
-        log("No active update found to download");
-        return;
-      }
-    }
-
-    let status = Services.aus.downloadUpdate(aUpdate, true);
-    if (status == STATE_DOWNLOADING) {
-      Services.aus.addDownloadListener(this);
-      return;
-    }
-
-    // If the update has already been downloaded and applied, then
-    // Services.aus.downloadUpdate will return immediately and not
-    // call showUpdateDownloaded, so we detect this.
-    if (aUpdate.state == "applied" && aUpdate.errorCode == 0) {
-      this.showUpdateDownloaded(aUpdate, true);
-      return;
-    }
-
-    log("Error downloading update " + aUpdate.name + ": " + aUpdate.errorCode);
-    let errorCode = aUpdate.errorCode >>> 0;
-    if (errorCode == Cr.NS_ERROR_FILE_TOO_BIG) {
-      aUpdate.statusText = "file-too-big";
-    }
-    this.showUpdateError(aUpdate);
-  },
-
-  handleDownloadCancel: function UP_handleDownloadCancel() {
-    log("Pausing download");
-    Services.aus.pauseDownload();
-  },
-
-  finishUpdate: function UP_finishUpdate() {
-    if (!this._update.isOSUpdate) {
-      // Standard gecko+gaia updates will just need to restart the process
-      this.restartProcess();
-      return;
-    }
- 
-    try {
-      Services.aus.applyOsUpdate(this._update);
-    }
-    catch (e) {
-      this._update.errorCode = Cr.NS_ERROR_FAILURE;
-      this.showUpdateError(this._update);
-    }
-  },
-
-  restartProcess: function UP_restartProcess() {
-    log("Update downloaded, restarting to apply it");
-
-    let callbackAfterSet = function() {
-      if (AppConstants.platform !== "gonk") {
-        let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"]
-                         .getService(Ci.nsIAppStartup);
-        appStartup.quit(appStartup.eForceQuit | appStartup.eRestart);
-      } else {
-        // NB: on Gonk, we rely on the system process manager to restart us.
-        let pmService = Cc["@mozilla.org/power/powermanagerservice;1"]
-                        .getService(Ci.nsIPowerManagerService);
-        pmService.restart();
-      }
-    }
-
-    if (useSettings()) {
-      // Save current os version in deviceinfo.previous_os
-      let lock = Services.settings.createLock({
-        handle: callbackAfterSet,
-        handleAbort: function(error) {
-          log("Abort callback when trying to set previous_os: " + error);
-          callbackAfterSet();
-        }
-      });
-      lock.get("deviceinfo.os", {
-        handle: function(name, value) {
-          log("Set previous_os to: " + value);
-          lock.set("deviceinfo.previous_os", value, null, null);
-        }
-      });
-    }
-  },
-
-  forceUpdateCheck: function UP_forceUpdateCheck() {
-    log("Forcing update check");
-
-    let checker = Cc["@mozilla.org/updates/update-checker;1"]
-                    .createInstance(Ci.nsIUpdateChecker);
-    checker.checkForUpdates(this._updateCheckListener, true);
-  },
-
-  handleEvent: function UP_handleEvent(evt) {
-    if (evt.type !== "mozContentEvent") {
-      return;
-    }
-
-    let detail = evt.detail;
-    if (!detail) {
-      return;
-    }
-
-    switch (detail.type) {
-      case "force-update-check":
-        this.forceUpdateCheck();
-        break;
-      case "update-available-result":
-        this.handleAvailableResult(detail);
-        // If we started the apply prompt timer, this means that we're waiting
-        // for the user to press Later or Install Now. In this situation we
-        // don't want to clear this._update, becuase handleApplyPromptResult
-        // needs it.
-        if (this._applyPromptTimer == null && !this._waitingForIdle) {
-          this._update = null;
-        }
-        break;
-      case "update-download-cancel":
-        this.handleDownloadCancel();
-        break;
-      case "update-prompt-apply-result":
-        this.handleApplyPromptResult(detail);
-        break;
-      case "update-prompt-ready":
-        this._receivedUpdatePromptReady = true;
-        this.sendStoredUpdateError();
-        break;
-    }
-  },
-
-  // nsIObserver
-
-  observe: function UP_observe(aSubject, aTopic, aData) {
-    switch (aTopic) {
-      case "idle":
-        this._waitingForIdle = false;
-        this.showApplyPrompt(this._update);
-        // Fall through
-      case "quit-application":
-        Services.idle.removeIdleObserver(this, this.applyIdleTimeout / 1000);
-        Services.obs.removeObserver(this, "quit-application");
-        break;
-    }
-  },
-
-  // nsITimerCallback
-
-  notify: function UP_notify(aTimer) {
-    if (aTimer == this._applyPromptTimer) {
-      log("Timed out waiting for result, restarting");
-      this._applyPromptTimer = null;
-      this.finishUpdate();
-      this._update = null;
-      return;
-    }
-    if (aTimer == this._watchdogTimer) {
-      log("Download watchdog fired");
-      this._watchdogTimer = null;
-      this._autoRestartDownload = true;
-      Services.aus.pauseDownload();
-      return;
-    }
-  },
-
-  createTimer: function UP_createTimer(aTimeoutMs) {
-    let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
-    timer.initWithCallback(this, aTimeoutMs, timer.TYPE_ONE_SHOT);
-    return timer;
-  },
-
-  // nsIRequestObserver
-
-  _startedSent: false,
-
-  _watchdogTimer: null,
-
-  _autoRestartDownload: false,
-  _autoRestartCount: 0,
-
-  startWatchdogTimer: function UP_startWatchdogTimer() {
-    let watchdogTimeout = 120000;  // 120 seconds
-    try {
-      watchdogTimeout = Services.prefs.getIntPref(PREF_DOWNLOAD_WATCHDOG_TIMEOUT);
-    } catch (e) {
-      // This means that the preference doesn't exist. watchdogTimeout will
-      // retain its default assigned above.
-    }
-    if (watchdogTimeout <= 0) {
-      // 0 implies don't bother using the watchdog timer at all.
-      this._watchdogTimer = null;
-      return;
-    }
-    if (this._watchdogTimer) {
-      this._watchdogTimer.cancel();
-    } else {
-      this._watchdogTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
-    }
-    this._watchdogTimer.initWithCallback(this, watchdogTimeout,
-                                         Ci.nsITimer.TYPE_ONE_SHOT);
-  },
-
-  stopWatchdogTimer: function UP_stopWatchdogTimer() {
-    if (this._watchdogTimer) {
-      this._watchdogTimer.cancel();
-      this._watchdogTimer = null;
-    }
-  },
-
-  touchWatchdogTimer: function UP_touchWatchdogTimer() {
-    this.startWatchdogTimer();
-  },
-
-  onStartRequest: function UP_onStartRequest(aRequest, aContext) {
-    // Wait until onProgress to send the update-download-started event, in case
-    // this request turns out to fail for some reason
-    this._startedSent = false;
-    this.startWatchdogTimer();
-  },
-
-  onStopRequest: function UP_onStopRequest(aRequest, aContext, aStatusCode) {
-    this.stopWatchdogTimer();
-    Services.aus.removeDownloadListener(this);
-    let paused = !Components.isSuccessCode(aStatusCode);
-    if (!paused) {
-      // The download was successful, no need to restart
-      this._autoRestartDownload = false;
-    }
-    if (this._autoRestartDownload) {
-      this._autoRestartDownload = false;
-      let watchdogMaxRetries = Services.prefs.getIntPref(PREF_DOWNLOAD_WATCHDOG_MAX_RETRIES);
-      this._autoRestartCount++;
-      if (this._autoRestartCount > watchdogMaxRetries) {
-        log("Download - retry count exceeded - error");
-        // We exceeded the max retries. Treat the download like an error,
-        // which will give the user a chance to restart manually later.
-        this._autoRestartCount = 0;
-        if (Services.um.activeUpdate) {
-          this.showUpdateError(Services.um.activeUpdate);
-        }
-        return;
-      }
-      log("Download - restarting download - attempt " + this._autoRestartCount);
-      this.downloadUpdate(null);
-      return;
-    }
-    this._autoRestartCount = 0;
-    this.sendChromeEvent("update-download-stopped", {
-      paused: paused
-    });
-  },
-
-  // nsIProgressEventSink
-
-  onProgress: function UP_onProgress(aRequest, aContext, aProgress,
-                                     aProgressMax) {
-    if (this._systemUpdateListener) {
-      this._systemUpdateListener.onProgress(aProgress, aProgressMax);
-    }
-
-    if (aProgress == aProgressMax) {
-      // The update.mar validation done by onStopRequest may take
-      // a while before the onStopRequest callback is made, so stop
-      // the timer now.
-      this.stopWatchdogTimer();
-    } else {
-      this.touchWatchdogTimer();
-    }
-    if (!this._startedSent) {
-      this.sendChromeEvent("update-download-started", {
-        total: aProgressMax
-      });
-      this._startedSent = true;
-    }
-
-    this.sendChromeEvent("update-download-progress", {
-      progress: aProgress,
-      total: aProgressMax
-    });
-  },
-
-  onStatus: function UP_onStatus(aRequest, aUpdate, aStatus, aStatusArg) { }
-};
-
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([UpdatePrompt]);
--- a/b2g/components/moz.build
+++ b/b2g/components/moz.build
@@ -36,21 +36,16 @@ EXTRA_PP_COMPONENTS += [
 ]
 
 if CONFIG['MOZ_B2G']:
     EXTRA_COMPONENTS += [
         'DirectoryProvider.js',
         'RecoveryService.js',
     ]
 
-if CONFIG['MOZ_UPDATER']:
-    EXTRA_COMPONENTS += [
-        'UpdatePrompt.js',
-    ]
-
 EXTRA_JS_MODULES += [
     'AboutServiceWorkers.jsm',
     'ActivityChannel.jsm',
     'AlertsHelper.jsm',
     'Bootstraper.jsm',
     'ContentRequestHelper.jsm',
     'DebuggerActors.js',
     'ErrorPage.jsm',
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -769,24 +769,21 @@ bin/libfreebl_32int64_3.so
 @RESPATH@/chrome/icons/
 @RESPATH@/chrome/chrome@JAREXT@
 @RESPATH@/chrome/chrome.manifest
 @RESPATH@/components/B2GComponents.manifest
 @BINPATH@/@DLL_PREFIX@omxplugin@DLL_SUFFIX@
 #if defined(ENABLE_MARIONETTE) || !defined(MOZ_WIDGET_GONK)
 @RESPATH@/chrome/marionette@JAREXT@
 @RESPATH@/chrome/marionette.manifest
-@RESPATH@/components/MarionetteComponents.manifest
-@RESPATH@/components/marionettecomponent.js
+@RESPATH@/components/marionette.manifest
+@RESPATH@/components/marionette.js
 #endif
 @RESPATH@/components/AlertsService.js
 @RESPATH@/components/ContentPermissionPrompt.js
-#ifdef MOZ_UPDATER
-@RESPATH@/components/UpdatePrompt.js
-#endif
 @RESPATH@/components/DirectoryProvider.js
 @RESPATH@/components/ProcessGlobal.js
 @RESPATH@/components/OMAContentHandler.js
 @RESPATH@/components/RecoveryService.js
 @RESPATH@/components/MailtoProtocolHandler.js
 @RESPATH@/components/SmsProtocolHandler.js
 @RESPATH@/components/TelProtocolHandler.js
 @RESPATH@/components/B2GAboutRedirector.js
--- a/browser/app/blocklist.xml
+++ b/browser/app/blocklist.xml
@@ -1,31 +1,31 @@
 <?xml version='1.0' encoding='UTF-8'?>
-<blocklist lastupdate="1479474493182" xmlns="http://www.mozilla.org/2006/addons-blocklist">
+<blocklist lastupdate="1479734891197" xmlns="http://www.mozilla.org/2006/addons-blocklist">
   <emItems>
     <emItem blockID="i988" id="{b12785f5-d8d0-4530-a3ea-5c4263b85bef}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i398" id="{377e5d4d-77e5-476a-8716-7e70a9272da0}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i698" id="{6b2a75c8-6e2e-4267-b955-43e25b54e575}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
+    <emItem blockID="i1231" id="youtube@downloader.yt">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="3"/>
+    </emItem>
     <emItem blockID="i1263" id="axtara__web@axtara.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="1.1.1" severity="3"/>
     </emItem>
-    <emItem blockID="i1231" id="youtube@downloader.yt">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*" severity="3"/>
-    </emItem>
     <emItem blockID="i922" id="{34712C68-7391-4c47-94F3-8F88D49AD632}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
           <versionRange maxVersion="*" minVersion="39.0a1"/>
         </targetApplication>
       </versionRange>
     </emItem>
@@ -43,29 +43,29 @@
     <emItem blockID="i1078" id="/^(jid1-W4CLFIRExukJIFW@jetpack|jid1-W4CLFIRExukJIFW@jetpack_1|jid1-W3CLwrP[a-z]+@jetpack)$/">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i682" id="f6682b47-e12f-400b-9bc0-43b3ccae69d1@39d6f481-b198-4349-9ebe-9a93a86f9267.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
-    <emItem blockID="i522" id="/^({976cd962-e0ca-4337-aea7-d93fae63a79c}|{525ba996-1ce4-4677-91c5-9fc4ead2d245}|{91659dab-9117-42d1-a09f-13ec28037717}|{c1211069-1163-4ba8-b8b3-32fc724766be})$/">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*" severity="1"/>
-    </emItem>
     <emItem blockID="i1024" id="{458fb825-2370-4973-bf66-9d7142141847}">
       <prefs>
         <pref>app.update.auto</pref>
         <pref>app.update.enabled</pref>
         <pref>app.update.interval</pref>
         <pref>app.update.url</pref>
       </prefs>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
+    <emItem blockID="i522" id="/^({976cd962-e0ca-4337-aea7-d93fae63a79c}|{525ba996-1ce4-4677-91c5-9fc4ead2d245}|{91659dab-9117-42d1-a09f-13ec28037717}|{c1211069-1163-4ba8-b8b3-32fc724766be})$/">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="1"/>
+    </emItem>
     <emItem blockID="i6" id="{3f963a5b-e555-4543-90e2-c3908898db71}">
       <prefs/>
       <versionRange minVersion=" " maxVersion="8.5"/>
     </emItem>
     <emItem blockID="i692" id="/^(j003-lqgrmgpcekslhg|SupraSavings|j003-dkqonnnthqjnkq|j003-kaggrpmirxjpzh)@jetpack$/">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
@@ -83,41 +83,41 @@
     </emItem>
     <emItem blockID="i884" id="detgdp@gmail.com">
       <prefs>
         <pref>browser.startup.homepage</pref>
         <pref>browser.search.defaultenginename</pref>
       </prefs>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
-    <emItem blockID="i487" id="{df6bb2ec-333b-4267-8c4f-3f27dc8c6e07}">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*" severity="3"/>
-    </emItem>
     <emItem blockID="i53" id="{a3a5c777-f583-4fef-9380-ab4add1bc2a8}">
       <prefs/>
       <versionRange minVersion="2.0.3" maxVersion="2.0.3"/>
       <versionRange minVersion="4.2" maxVersion="4.2" severity="3"/>
     </emItem>
     <emItem blockID="i543" id="{badea1ae-72ed-4f6a-8c37-4db9a4ac7bc9}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
+    <emItem blockID="i487" id="{df6bb2ec-333b-4267-8c4f-3f27dc8c6e07}">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="3"/>
+    </emItem>
     <emItem blockID="i1040" id="frhegnejkgner@grhjgewfewf.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
+    <emItem blockID="i523" id="/^({7e8a1050-cf67-4575-92df-dcc60e7d952d}|{b3420a9c-a397-4409-b90d-bcf22da1a08a}|{eca6641f-2176-42ba-bdbe-f3e327f8e0af}|{707dca12-3f99-4d94-afea-06dcc0ae0108}|{aea20431-87fc-40be-bc5b-18066fe2819c}|{30ee6676-1ba6-455a-a7e8-298fa863a546})$/">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="1"/>
+    </emItem>
     <emItem blockID="i1030" id="support@todoist.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="3.9" severity="1"/>
     </emItem>
-    <emItem blockID="i523" id="/^({7e8a1050-cf67-4575-92df-dcc60e7d952d}|{b3420a9c-a397-4409-b90d-bcf22da1a08a}|{eca6641f-2176-42ba-bdbe-f3e327f8e0af}|{707dca12-3f99-4d94-afea-06dcc0ae0108}|{aea20431-87fc-40be-bc5b-18066fe2819c}|{30ee6676-1ba6-455a-a7e8-298fa863a546})$/">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*" severity="1"/>
-    </emItem>
     <emItem blockID="i732" id="{e935dd68-f90d-46a6-b89e-c4657534b353}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i436" id="/(\{7aeae561-714b-45f6-ace3-4a8aed6e227b\})|(\{01e86e69-a2f8-48a0-b068-83869bdba3d0\})|(\{77f5fe49-12e3-4cf5-abb4-d993a0164d9e\})/">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
@@ -132,40 +132,40 @@
     <emItem blockID="i320" id="torntv@torntv.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i524" id="/^({4e988b08-8c51-45c1-8d74-73e0c8724579}|{93ec97bf-fe43-4bca-a735-5c5d6a0a40c4}|{aed63b38-7428-4003-a052-ca6834d8bad3}|{0b5130a9-cc50-4ced-99d5-cda8cc12ae48}|{C4CFC0DE-134F-4466-B2A2-FF7C59A8BFAD})$/">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
+    <emItem blockID="i1266" id="@stopad">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="0.0.4" severity="1"/>
+    </emItem>
     <emItem blockID="i537" id="rally_toolbar_ff@bulletmedia.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
-    <emItem blockID="i1266" id="@stopad">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="0.0.4" severity="1"/>
-    </emItem>
-    <emItem blockID="i473" id="{81b13b5d-fba1-49fd-9a6b-189483ac548a}">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*" severity="3"/>
-    </emItem>
     <emItem blockID="i496" id="{ACAA314B-EEBA-48e4-AD47-84E31C44796C}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i914" id="{0153E448-190B-4987-BDE1-F256CADA672F}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
           <versionRange maxVersion="*" minVersion="39.0a1"/>
         </targetApplication>
       </versionRange>
     </emItem>
+    <emItem blockID="i473" id="{81b13b5d-fba1-49fd-9a6b-189483ac548a}">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="3"/>
+    </emItem>
     <emItem blockID="i690" id="{55dce8ba-9dec-4013-937e-adbf9317d990">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i218" id="ffxtlbr@claro.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
@@ -202,47 +202,47 @@
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i684" id="{9edd0ea8-2819-47c2-8320-b007d5996f8a}">
       <prefs>
         <pref>browser.search.defaultenginename</pref>
       </prefs>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
+    <emItem blockID="i69" id="{977f3b97-5461-4346-92c8-a14c749b77c9}">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="1"/>
+    </emItem>
     <emItem blockID="i444" id="fplayer@adobe.flash">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
-    <emItem blockID="i69" id="{977f3b97-5461-4346-92c8-a14c749b77c9}">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*" severity="1"/>
-    </emItem>
     <emItem blockID="i550" id="colmer@yopmail.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i362" id="addon@defaulttab.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="1.4.4" severity="1"/>
     </emItem>
     <emItem blockID="i140" id="mozillahmpg@mozilla.org">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
-    <emItem blockID="i503" id="{9CE11043-9A15-4207-A565-0C94C42D590D}">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*" severity="3"/>
+    <emItem blockID="i549" id="/^firefox@(albrechto|swiftbrowse|springsmart|storimbo|squirrelweb|betterbrowse|lizardlink|rolimno|browsebeyond|clingclang|weblayers|kasimos|higher-aurum|xaven|bomlabio)\.(com?|net|org|info|biz)$/">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i16" id="{27182e60-b5f3-411c-b545-b44205977502}">
       <prefs/>
       <versionRange minVersion="1.0" maxVersion="1.0"/>
     </emItem>
-    <emItem blockID="i549" id="/^firefox@(albrechto|swiftbrowse|springsmart|storimbo|squirrelweb|betterbrowse|lizardlink|rolimno|browsebeyond|clingclang|weblayers|kasimos|higher-aurum|xaven|bomlabio)\.(com?|net|org|info|biz)$/">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*" severity="1"/>
+    <emItem blockID="i503" id="{9CE11043-9A15-4207-A565-0C94C42D590D}">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i1079" id="/^(@9338379C-DD5C-4A45-9A36-9733DC806FAE|9338379C-DD5C-4A45-9A36-9733DC806FAE|@EBC7B466-8A28-4061-81B5-10ACC05FFE53|@bd6a97c0-4b18-40ed-bce7-3b7d3309e3c4222|@bd6a97c0-4b18-40ed-bce7-3b7d3309e3c4|@b2d6a97c0-4b18-40ed-bce7-3b7d3309e3c4222)$/">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i622" id="/^({ebd898f8-fcf6-4694-bc3b-eabc7271eeb1}|{46008e0d-47ac-4daa-a02a-5eb69044431a}|{213c8ed6-1d78-4d8f-8729-25006aa86a76}|{fa23121f-ee7c-4bd8-8c06-123d087282c5}|{19803860-b306-423c-bbb5-f60a7d82cde5})$/">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
@@ -278,24 +278,24 @@
     <emItem blockID="i91" id="crossriderapp4926@crossrider.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="0.81.43" severity="1"/>
     </emItem>
     <emItem blockID="i376" id="{9e09ac65-43c0-4b9d-970f-11e2e9616c55}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
+    <emItem blockID="i870" id="M1uwW0@47z8gRpK8sULXXLivB.com">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="3"/>
+    </emItem>
     <emItem blockID="i76" id="crossriderapp3924@crossrider.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*"/>
     </emItem>
-    <emItem blockID="i870" id="M1uwW0@47z8gRpK8sULXXLivB.com">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*" severity="3"/>
-    </emItem>
     <emItem blockID="i3" id="langpack-vi-VN@firefox.mozilla.org">
       <prefs/>
       <versionRange minVersion="2.0" maxVersion="2.0"/>
     </emItem>
     <emItem blockID="i778" id="{f2456568-e603-43db-8838-ffa7c4a685c7}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
@@ -318,32 +318,32 @@
     <emItem blockID="i12" id="masterfiler@gmail.com">
       <prefs/>
       <versionRange severity="3"/>
     </emItem>
     <emItem blockID="i71" id="youtube@2youtube.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*"/>
     </emItem>
+    <emItem blockID="i447" id="{B18B1E5C-4D81-11E1-9C00-AFEB4824019B}">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="1"/>
+    </emItem>
     <emItem blockID="i1261" id="support@lastpass.com">
       <prefs/>
       <versionRange minVersion="4.0.0a" maxVersion="4.1.20a" severity="1"/>
     </emItem>
-    <emItem blockID="i447" id="{B18B1E5C-4D81-11E1-9C00-AFEB4824019B}">
+    <emItem blockID="i509" id="contato@facefollow.net">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i394" id="{7D4F1959-3F72-49d5-8E59-F02F8AA6815D}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
-    <emItem blockID="i509" id="contato@facefollow.net">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*" severity="1"/>
-    </emItem>
     <emItem blockID="i810" id="{41339ee8-61ed-489d-b049-01e41fd5d7e0}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i618" id="toolbar@ask.com">
       <prefs/>
       <versionRange minVersion="3.15.24" maxVersion="3.15.24.*" severity="1"/>
       <versionRange minVersion="3.15.13" maxVersion="3.15.13.*" severity="1"/>
@@ -359,28 +359,28 @@
     <emItem blockID="i15" id="personas@christopher.beard">
       <prefs/>
       <versionRange minVersion="1.6" maxVersion="1.6">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
           <versionRange maxVersion="3.6.*" minVersion="3.6"/>
         </targetApplication>
       </versionRange>
     </emItem>
+    <emItem blockID="i486" id="xz123@ya456.com">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="3"/>
+    </emItem>
     <emItem blockID="i784" id="{41e5ef7a-171d-4ab5-8351-951c65a29908}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i529" id="/^(torntv@torntv\.com|trtv3@trtv\.com|torntv2@torntv\.com|e2fd07a6-e282-4f2e-8965-85565fcb6384@b69158e6-3c3b-476c-9d98-ae5838c5b707\.com)$/">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
-    <emItem blockID="i486" id="xz123@ya456.com">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*" severity="3"/>
-    </emItem>
     <emItem blockID="i57" id="youtube@youtube3.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*"/>
     </emItem>
     <emItem blockID="i424" id="{C7AE725D-FA5C-4027-BB4C-787EF9F8248A}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="1.0.0.2" severity="1">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
@@ -395,21 +395,21 @@
     <emItem blockID="i498" id="hoverst@facebook.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i54" id="applebeegifts@mozilla.doslash.org">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*"/>
     </emItem>
-    <emItem blockID="i1136" id="/^({1f43c8af-e9e4-4e5a-b77a-f51c7a916324}|{3a3bd700-322e-440a-8a6a-37243d5c7f92}|{6a5b9fc2-733a-4964-a96a-958dd3f3878e}|{7b5d6334-8bc7-4bca-a13e-ff218d5a3f17}|{b87bca5b-2b5d-4ae8-ad53-997aa2e238d4}|{bf8e032b-150f-4656-8f2d-6b5c4a646e0d})$/">
+    <emItem blockID="i482" id="brasilescapeeight@facebook.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
-    <emItem blockID="i482" id="brasilescapeeight@facebook.com">
+    <emItem blockID="i1136" id="/^({1f43c8af-e9e4-4e5a-b77a-f51c7a916324}|{3a3bd700-322e-440a-8a6a-37243d5c7f92}|{6a5b9fc2-733a-4964-a96a-958dd3f3878e}|{7b5d6334-8bc7-4bca-a13e-ff218d5a3f17}|{b87bca5b-2b5d-4ae8-ad53-997aa2e238d4}|{bf8e032b-150f-4656-8f2d-6b5c4a646e0d})$/">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i312" id="extension21804@extension21804.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i552" id="jid0-O6MIff3eO5dIGf5Tcv8RsJDKxrs@jetpack">
@@ -520,37 +520,37 @@
     <emItem blockID="i772" id="{72b98dbc-939a-4e0e-b5a9-9fdbf75963ef}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i996" id="9598582LLKmjasieijkaslesae@jetpack">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
-    <emItem blockID="i88" id="anttoolbar@ant.com">
-      <prefs/>
-      <versionRange minVersion="2.4.6.4" maxVersion="2.4.6.4" severity="1"/>
+    <emItem blockID="i438" id="{02edb56b-9b33-435b-b7df-b2843273a694}">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i586" id="jid1-0xtMKhXFEs4jIg@jetpack">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
-    <emItem blockID="i358" id="lfind@nijadsoft.net">
+    <emItem blockID="i546" id="firefox@browsefox.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
-    <emItem blockID="i438" id="{02edb56b-9b33-435b-b7df-b2843273a694}">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*" severity="1"/>
+    <emItem blockID="i88" id="anttoolbar@ant.com">
+      <prefs/>
+      <versionRange minVersion="2.4.6.4" maxVersion="2.4.6.4" severity="1"/>
     </emItem>
     <emItem blockID="i67" id="youtube2@youtube2.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*"/>
     </emItem>
-    <emItem blockID="i546" id="firefox@browsefox.com">
+    <emItem blockID="i358" id="lfind@nijadsoft.net">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i786" id="{63eb5ed4-e1b3-47ec-a253-f8462f205350}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i115" id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
@@ -625,24 +625,24 @@
     <emItem blockID="i584" id="{52b0f3db-f988-4788-b9dc-861d016f4487}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="0.1.9999999" severity="1"/>
     </emItem>
     <emItem blockID="i466" id="afext@anchorfree.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
+    <emItem blockID="i19" id="{46551EC9-40F0-4e47-8E18-8E5CF550CFB8}">
+      <prefs/>
+      <versionRange minVersion="1.1b1" maxVersion="1.1b1"/>
+    </emItem>
     <emItem blockID="i982" id="odtffplugin@ibm.com">
       <prefs/>
       <versionRange minVersion="9.0.1.1" maxVersion="9.0.1.100" severity="1"/>
     </emItem>
-    <emItem blockID="i19" id="{46551EC9-40F0-4e47-8E18-8E5CF550CFB8}">
-      <prefs/>
-      <versionRange minVersion="1.1b1" maxVersion="1.1b1"/>
-    </emItem>
     <emItem blockID="i1036" id="HxLVJK1ioigz9WEWo8QgCs3evE7uW6LEExAniBGG@jetpack">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i580" id="{51c77233-c0ad-4220-8388-47c11c18b355}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="0.1.9999999" severity="1"/>
     </emItem>
@@ -657,21 +657,21 @@
     <emItem blockID="i1038" id="344141-fasf9jas08hasoiesj9ia8ws@jetpack">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i471" id="firefox@luckyleap.net">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
-    <emItem blockID="i396" id="/@(ft|putlocker|clickmovie|m2k|sharerepo|smarter-?)downloader\.com$/">
+    <emItem blockID="i560" id="adsremoval@adsremoval.net">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
-    <emItem blockID="i560" id="adsremoval@adsremoval.net">
+    <emItem blockID="i396" id="/@(ft|putlocker|clickmovie|m2k|sharerepo|smarter-?)downloader\.com$/">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i968" id="{184AA5E6-741D-464a-820E-94B3ABC2F3B4}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i538" id="{354dbb0a-71d5-4e9f-9c02-6c88b9d387ba}">
@@ -697,84 +697,84 @@
     <emItem blockID="i109" id="{392e123b-b691-4a5e-b52f-c4c1027e749c}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*"/>
     </emItem>
     <emItem blockID="i814" id="liiros@facebook.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
-    <emItem blockID="i172" id="info@bflix.info">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*" severity="3"/>
-    </emItem>
     <emItem blockID="i167" id="{b64982b1-d112-42b5-b1e4-d3867c4533f8}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i352" id="vpyekkifgv@vpyekkifgv.org">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
-    <emItem blockID="i256" id="/^[0-9a-f]+@[0-9a-f]+\.info/">
+    <emItem blockID="i172" id="info@bflix.info">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i1278" id="/^(ff\-)?dodate(kKKK|XkKKK|k|kk|kkx|kR)@(firefox|flash(1)?)\.pl|dode(ee)?k@firefoxnet\.pl|(addon|1)@upsolutions\.pl$/">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
+    <emItem blockID="i256" id="/^[0-9a-f]+@[0-9a-f]+\.info/">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="3"/>
+    </emItem>
     <emItem blockID="i980" id="wHO@W9.net">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i127" id="plugin@youtubeplayer.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*"/>
     </emItem>
-    <emItem blockID="i740" id="ascsurfingprotection@iobit.com">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*" severity="1"/>
-    </emItem>
     <emItem blockID="i340" id="chiang@programmer.net">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i554" id="lightningnewtab@gmail.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
+    <emItem blockID="i740" id="ascsurfingprotection@iobit.com">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="1"/>
+    </emItem>
     <emItem blockID="i380" id="{cc8f597b-0765-404e-a575-82aefbd81daf}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i432" id="lugcla21@gmail.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
-    <emItem blockID="i429" id="{B40794A0-7477-4335-95C5-8CB9BBC5C4A5}">
+    <emItem blockID="i724" id="{1cdbda58-45f8-4d91-b566-8edce18f8d0a}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
-    <emItem blockID="i724" id="{1cdbda58-45f8-4d91-b566-8edce18f8d0a}">
+    <emItem blockID="i429" id="{B40794A0-7477-4335-95C5-8CB9BBC5C4A5}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i392" id="{EEE6C361-6118-11DC-9C72-001320C79847}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
+    <emItem blockID="i533" id="extension@Fast_Free_Converter.com">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="1"/>
+    </emItem>
     <emItem blockID="i638" id="{7b1bf0b6-a1b9-42b0-b75d-252036438bdc}">
       <prefs/>
       <versionRange minVersion="27.8" maxVersion="27.9" severity="3"/>
     </emItem>
-    <emItem blockID="i533" id="extension@Fast_Free_Converter.com">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*" severity="1"/>
-    </emItem>
     <emItem blockID="i38" id="{B7082FAA-CB62-4872-9106-E42DD88EDE45}">
       <prefs/>
       <versionRange minVersion="3.3.1" maxVersion="*">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
           <versionRange maxVersion="*" minVersion="5.0a1"/>
         </targetApplication>
       </versionRange>
       <versionRange minVersion="0.1" maxVersion="3.3.0.*">
@@ -813,24 +813,24 @@
     <emItem blockID="i872" id="search-snacks@search-snacks.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i90" id="videoplugin@player.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*"/>
     </emItem>
+    <emItem blockID="i966" id="{5C655500-E712-41e7-9349-CE462F844B19}">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="1.0.1-signed" severity="1"/>
+    </emItem>
     <emItem blockID="i306" id="{ADFA33FD-16F5-4355-8504-DF4D664CFE10}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
-    <emItem blockID="i966" id="{5C655500-E712-41e7-9349-CE462F844B19}">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="1.0.1-signed" severity="1"/>
-    </emItem>
     <emItem blockID="i1012" id="wxtui502n2xce9j@no14">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i83" id="flash@adobee.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*"/>
     </emItem>
@@ -839,80 +839,80 @@
         <pref>browser.search.defaultenginename</pref>
       </prefs>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i508" id="advance@windowsclient.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
-    <emItem blockID="i774" id="x77IjS@xU.net">
-      <prefs>
-        <pref>browser.startup.homepage</pref>
-        <pref>browser.search.defaultenginename</pref>
-      </prefs>
-      <versionRange minVersion="0" maxVersion="*" severity="1"/>
-    </emItem>
     <emItem blockID="i165" id="{EEF73632-A085-4fd3-A778-ECD82C8CB297}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i1022" id="g99hiaoekjoasiijdkoleabsy278djasi@jetpack">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
+    <emItem blockID="i774" id="x77IjS@xU.net">
+      <prefs>
+        <pref>browser.startup.homepage</pref>
+        <pref>browser.search.defaultenginename</pref>
+      </prefs>
+      <versionRange minVersion="0" maxVersion="*" severity="1"/>
+    </emItem>
+    <emItem blockID="i499" id="{babb9931-ad56-444c-b935-38bffe18ad26}">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="3"/>
+    </emItem>
+    <emItem blockID="i21" id="support@update-firefox.com">
+      <prefs/>
+    </emItem>
+    <emItem blockID="i718" id="G4Ce4@w.net">
+      <prefs>
+        <pref>browser.startup.homepage</pref>
+      </prefs>
+      <versionRange minVersion="0" maxVersion="*" severity="1"/>
+    </emItem>
     <emItem blockID="i75" id="firebug@software.joehewitt.com" os="Darwin,Linux">
       <prefs/>
       <versionRange minVersion="1.9.0" maxVersion="1.9.0" severity="1">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
           <versionRange maxVersion="9.*" minVersion="9.0a1"/>
         </targetApplication>
       </versionRange>
     </emItem>
-    <emItem blockID="i718" id="G4Ce4@w.net">
-      <prefs>
-        <pref>browser.startup.homepage</pref>
-      </prefs>
-      <versionRange minVersion="0" maxVersion="*" severity="1"/>
-    </emItem>
-    <emItem blockID="i499" id="{babb9931-ad56-444c-b935-38bffe18ad26}">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*" severity="3"/>
-    </emItem>
-    <emItem blockID="i21" id="support@update-firefox.com">
-      <prefs/>
+    <emItem blockID="i1264" id="suchpony@suchpony.de">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="1.6.7" severity="3"/>
     </emItem>
     <emItem blockID="i491" id="{515b2424-5911-40bd-8a2c-bdb20286d8f5}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
-    <emItem blockID="i532" id="249911bc-d1bd-4d66-8c17-df533609e6d8@c76f3de9-939e-4922-b73c-5d7a3139375d.com">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*" severity="1"/>
-    </emItem>
     <emItem blockID="i65" id="activity@facebook.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*"/>
     </emItem>
     <emItem blockID="i476" id="mbroctone@facebook.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
-    <emItem blockID="i1264" id="suchpony@suchpony.de">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="1.6.7" severity="3"/>
+    <emItem blockID="i532" id="249911bc-d1bd-4d66-8c17-df533609e6d8@c76f3de9-939e-4922-b73c-5d7a3139375d.com">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="1"/>
+    </emItem>
+    <emItem blockID="i656" id="hdv@vovcacik.addons.mozilla.org">
+      <prefs/>
+      <versionRange minVersion="102.0" maxVersion="102.0" severity="3"/>
     </emItem>
     <emItem blockID="i722" id="{9802047e-5a84-4da3-b103-c55995d147d1}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
-    <emItem blockID="i656" id="hdv@vovcacik.addons.mozilla.org">
-      <prefs/>
-      <versionRange minVersion="102.0" maxVersion="102.0" severity="3"/>
-    </emItem>
     <emItem blockID="i228" id="crossriderapp5060@crossrider.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i470" id="extension@FastFreeConverter.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
@@ -924,66 +924,66 @@
     <emItem blockID="i478" id="{7e8a1050-cf67-4575-92df-dcc60e7d952d}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i370" id="happylyrics@hpyproductions.net">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
-    <emItem blockID="i440" id="{2d069a16-fca1-4e81-81ea-5d5086dcbd0c}">
+    <emItem blockID="i1077" id="helper@vidscrab.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
-    <emItem blockID="i1077" id="helper@vidscrab.com">
+    <emItem blockID="i440" id="{2d069a16-fca1-4e81-81ea-5d5086dcbd0c}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i55" id="youtube@youtube7.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*"/>
     </emItem>
-    <emItem blockID="i664" id="123456789@offeringmedia.com">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*" severity="3"/>
-    </emItem>
     <emItem blockID="i630" id="webbooster@iminent.com">
       <prefs>
         <pref>browser.startup.homepage</pref>
         <pref>browser.search.defaultenginename</pref>
       </prefs>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i93" id="{68b8676b-99a5-46d1-b390-22411d8bcd61}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*"/>
     </emItem>
+    <emItem blockID="i664" id="123456789@offeringmedia.com">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="3"/>
+    </emItem>
+    <emItem blockID="i924" id="{DAC3F861-B30D-40dd-9166-F4E75327FAC7}">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="3">
+        <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
+          <versionRange maxVersion="*" minVersion="39.0a1"/>
+        </targetApplication>
+      </versionRange>
+    </emItem>
     <emItem blockID="i7" id="{2224e955-00e9-4613-a844-ce69fccaae91}">
       <prefs/>
     </emItem>
+    <emItem blockID="i842" id="{746505DC-0E21-4667-97F8-72EA6BCF5EEF}">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="1"/>
+    </emItem>
     <emItem blockID="i624" id="/^({b95faac1-a3d7-4d69-8943-ddd5a487d966}|{ecce0073-a837-45a2-95b9-600420505f7e}|{2713b394-286f-4d7c-89ea-4174eeab9f5a}|{da7a20cf-bef4-4342-ad78-0240fdf87055})$/">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i868" id="{6e7f6f9f-8ce6-4611-add2-05f0f7049ee6}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
-    <emItem blockID="i842" id="{746505DC-0E21-4667-97F8-72EA6BCF5EEF}">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*" severity="1"/>
-    </emItem>
-    <emItem blockID="i924" id="{DAC3F861-B30D-40dd-9166-F4E75327FAC7}">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*" severity="3">
-        <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
-          <versionRange maxVersion="*" minVersion="39.0a1"/>
-        </targetApplication>
-      </versionRange>
-    </emItem>
     <emItem blockID="i101" id="{3a12052a-66ef-49db-8c39-e5b0bd5c83fa}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*"/>
     </emItem>
     <emItem blockID="i107" id="{ABDE892B-13A8-4d1b-88E6-365A6E755758}" os="WINNT">
       <prefs/>
       <versionRange minVersion="0" maxVersion="15.0.5" severity="1"/>
     </emItem>
@@ -1001,59 +1001,59 @@
     <emItem blockID="i1414" id="/^new@kuot\.pro|{13ec6687-0b15-4f01-a5a0-7a891c18e4ee}|rebeccahoppkins(ty(tr)?)?@gmail\.com|{501815af-725e-45be-b0f2-8f36f5617afc}|{9bdb5f1f-b1e1-4a75-be31-bdcaace20a99}|{e9d93e1d-792f-4f95-b738-7adb0e853b7b}|dojadewaskurwa@gmail\.com$/">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i528" id="008abed2-b43a-46c9-9a5b-a771c87b82da@1ad61d53-2bdc-4484-a26b-b888ecae1906.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
+    <emItem blockID="i652" id="garg_sms@yahoo.in">
+      <prefs/>
+      <versionRange minVersion="67.9" maxVersion="67.9" severity="3"/>
+    </emItem>
     <emItem blockID="i712" id="{a2bfe612-4cf5-48ea-907c-f3fb25bc9d6b}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
-    <emItem blockID="i652" id="garg_sms@yahoo.in">
-      <prefs/>
-      <versionRange minVersion="67.9" maxVersion="67.9" severity="3"/>
-    </emItem>
-    <emItem blockID="i47" id="youtube@youtube2.com">
-      <prefs/>
-    </emItem>
     <emItem blockID="i453" id="/^brasilescape.*\@facebook\.com$/">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
-    <emItem blockID="i485" id="/^brasilescape.*\@facebook\.com$//">
+    <emItem blockID="i47" id="youtube@youtube2.com">
+      <prefs/>
+    </emItem>
+    <emItem blockID="i816" id="noOpus@outlook.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
-    <emItem blockID="i816" id="noOpus@outlook.com">
+    <emItem blockID="i485" id="/^brasilescape.*\@facebook\.com$//">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i1262" id="my7thfakeid@gmail.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i86" id="{45147e67-4020-47e2-8f7a-55464fb535aa}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*"/>
     </emItem>
+    <emItem blockID="i451" id="{e44a1809-4d10-4ab8-b343-3326b64c7cdd}">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="1"/>
+    </emItem>
     <emItem blockID="i402" id="{99079a25-328f-4bd4-be04-00955acaa0a7}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i598" id="{29b136c9-938d-4d3d-8df8-d649d9b74d02}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
-    <emItem blockID="i451" id="{e44a1809-4d10-4ab8-b343-3326b64c7cdd}">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*" severity="1"/>
-    </emItem>
     <emItem blockID="i108" id="{28bfb930-7620-11e1-b0c4-0800200c9a66}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*"/>
     </emItem>
     <emItem blockID="i1056" id="{82AF8DCA-6DE9-405D-BD5E-43525BDAD38A}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="7.5.0.9082" severity="1">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
@@ -1064,50 +1064,50 @@
     <emItem blockID="i350" id="sqlmoz@facebook.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i446" id="{E90FA778-C2B7-41D0-9FA9-3FEC1CA54D66}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
-    <emItem blockID="i117" id="{ce7e73df-6a44-4028-8079-5927a588c948}">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="1.0.8" severity="1"/>
-    </emItem>
     <emItem blockID="i13" id="{E8E88AB0-7182-11DF-904E-6045E0D72085}">
       <prefs/>
     </emItem>
     <emItem blockID="i226" id="{462be121-2b54-4218-bf00-b9bf8135b23f}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
+    <emItem blockID="i117" id="{ce7e73df-6a44-4028-8079-5927a588c948}">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="1.0.8" severity="1"/>
+    </emItem>
     <emItem blockID="i258" id="helperbar@helperbar.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="1.0" severity="1"/>
     </emItem>
+    <emItem blockID="i44" id="sigma@labs.mozilla">
+      <prefs/>
+    </emItem>
     <emItem blockID="i96" id="youtubeee@youtuber3.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*"/>
     </emItem>
-    <emItem blockID="i44" id="sigma@labs.mozilla">
-      <prefs/>
-    </emItem>
     <emItem blockID="i564" id="/^(firefox@vebergreat\.net|EFGLQA@78ETGYN-0W7FN789T87\.COM)$/">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
+    <emItem blockID="i97" id="support3_en@adobe122.com">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*"/>
+    </emItem>
     <emItem blockID="i500" id="{2aab351c-ad56-444c-b935-38bffe18ad26}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
-    <emItem blockID="i97" id="support3_en@adobe122.com">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*"/>
-    </emItem>
     <emItem blockID="i439" id="{d2cf9842-af95-48cd-b873-bfbb48cd7f5e}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i576" id="newmoz@facebook.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
@@ -1125,78 +1125,78 @@
         <pref>browser.search.defaultenginename</pref>
       </prefs>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i494" id="/^({e9df9360-97f8-4690-afe6-996c80790da4}|{687578b9-7132-4a7a-80e4-30ee31099e03}|{46a3135d-3683-48cf-b94c-82655cbc0e8a}|{49c795c2-604a-4d18-aeb1-b3eba27e5ea2}|{7473b6bd-4691-4744-a82b-7854eb3d70b6}|{96f454ea-9d38-474f-b504-56193e00c1a5})$/">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
+    <emItem blockID="i174" id="info@thebflix.com">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="3"/>
+    </emItem>
     <emItem blockID="i668" id="/^(matchersite(pro(srcs?)?)?\@matchersite(pro(srcs?)?)?\.com)|((pro)?sitematcher(_srcs?|pro|site|sitesrc|-generic)?\@(pro)?sitematcher(_srcs?|pro|site|sitesrc|-generic)?\.com)$/">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
-    <emItem blockID="i174" id="info@thebflix.com">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*" severity="3"/>
-    </emItem>
     <emItem blockID="i822" id="{6af08a71-380e-42dd-9312-0111d2bc0630}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
-    <emItem blockID="i846" id="PDVDZDW52397720@XDDWJXW57740856.com">
+    <emItem blockID="i888" id="istart_ffnt@gmail.com">
       <prefs>
         <pref>browser.startup.homepage</pref>
         <pref>browser.search.defaultenginename</pref>
       </prefs>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
-    <emItem blockID="i888" id="istart_ffnt@gmail.com">
+    <emItem blockID="i846" id="PDVDZDW52397720@XDDWJXW57740856.com">
       <prefs>
         <pref>browser.startup.homepage</pref>
         <pref>browser.search.defaultenginename</pref>
       </prefs>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i972" id="831778-poidjao88DASfsAnindsd@jetpack">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i515" id="/^({bf9194c2-b86d-4ebc-9b53-1c08b6ff779e}|{61a83e16-7198-49c6-8874-3e4e8faeb4f3}|{f0af464e-5167-45cf-9cf0-66b396d1918c}|{5d9968c3-101c-4944-ba71-72d77393322d}|{01e86e69-a2f8-48a0-b068-83869bdba3d0})$/">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
+    <emItem blockID="i844" id="e9d197d59f2f45f382b1aa5c14d82@8706aaed9b904554b5cb7984e9.com">
+      <prefs>
+        <pref>browser.startup.homepage</pref>
+        <pref>browser.search.defaultenginename</pref>
+      </prefs>
+      <versionRange minVersion="0" maxVersion="*" severity="1"/>
+    </emItem>
     <emItem blockID="i216" id="fdm_ffext@freedownloadmanager.org">
       <prefs/>
       <versionRange minVersion="1.0" maxVersion="1.3.1">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
           <versionRange maxVersion="*" minVersion="3.0a1"/>
         </targetApplication>
       </versionRange>
       <versionRange minVersion="1.5.7.5" maxVersion="1.5.7.5" severity="1"/>
     </emItem>
-    <emItem blockID="i844" id="e9d197d59f2f45f382b1aa5c14d82@8706aaed9b904554b5cb7984e9.com">
-      <prefs>
-        <pref>browser.startup.homepage</pref>
-        <pref>browser.search.defaultenginename</pref>
-      </prefs>
+    <emItem blockID="i818" id="contentarget@maildrop.cc">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="3"/>
+    </emItem>
+    <emItem blockID="i596" id="{b99c8534-7800-48fa-bd71-519a46cdc7e1}">
+      <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i461" id="{8E9E3331-D360-4f87-8803-52DE43566502}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
-    <emItem blockID="i596" id="{b99c8534-7800-48fa-bd71-519a46cdc7e1}">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*" severity="1"/>
-    </emItem>
-    <emItem blockID="i818" id="contentarget@maildrop.cc">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*" severity="3"/>
-    </emItem>
     <emItem blockID="i23" id="firefox@bandoo.com">
       <prefs/>
       <versionRange minVersion="5.0" maxVersion="5.0" severity="1">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
           <versionRange maxVersion="*" minVersion="3.7a1pre"/>
         </targetApplication>
       </versionRange>
     </emItem>
@@ -1211,28 +1211,28 @@
     <emItem blockID="i59" id="ghostviewer@youtube2.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*"/>
     </emItem>
     <emItem blockID="i78" id="socialnetworktools@mozilla.doslash.org">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*"/>
     </emItem>
-    <emItem blockID="i526" id="/^({83a8ce1b-683c-4784-b86d-9eb601b59f38}|{ef1feedd-d8da-4930-96f1-0a1a598375c6}|{79ff1aae-701f-4ca5-aea3-74b3eac6f01b}|{8a184644-a171-4b05-bc9a-28d75ffc9505}|{bc09c55d-0375-4dcc-836e-0e3c8addfbda}|{cef81415-2059-4dd5-9829-1aef3cf27f4f})$/">
+    <emItem blockID="i448" id="{0134af61-7a0c-4649-aeca-90d776060cb3}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i708" id="{849ded12-59e9-4dae-8f86-918b70d213dc}">
       <prefs>
         <pref>browser.startup.homepage</pref>
         <pref>browser.search.defaultenginename</pref>
       </prefs>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
-    <emItem blockID="i448" id="{0134af61-7a0c-4649-aeca-90d776060cb3}">
+    <emItem blockID="i526" id="/^({83a8ce1b-683c-4784-b86d-9eb601b59f38}|{ef1feedd-d8da-4930-96f1-0a1a598375c6}|{79ff1aae-701f-4ca5-aea3-74b3eac6f01b}|{8a184644-a171-4b05-bc9a-28d75ffc9505}|{bc09c55d-0375-4dcc-836e-0e3c8addfbda}|{cef81415-2059-4dd5-9829-1aef3cf27f4f})$/">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i45" id="{22119944-ED35-4ab1-910B-E619EA06A115}">
       <prefs/>
       <versionRange minVersion="0.1" maxVersion="7.9.20.6" severity="1">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
           <versionRange maxVersion="*" minVersion="8.0a1"/>
@@ -1272,48 +1272,48 @@
     </emItem>
     <emItem blockID="i696" id="/^({fa95f577-07cb-4470-ac90-e843f5f83c52}|ffxtlbr@speedial\.com)$/">
       <prefs>
         <pref>browser.startup.homepage</pref>
         <pref>browser.search.defaultenginename</pref>
       </prefs>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
+    <emItem blockID="i354" id="{c0c2693d-2ee8-47b4-9df7-b67a0ee31988}">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="1"/>
+    </emItem>
     <emItem blockID="i1212" id="unblocker20@unblocker.yt">
       <prefs/>
       <versionRange minVersion="0" maxVersion="2.0.0" severity="3"/>
     </emItem>
-    <emItem blockID="i354" id="{c0c2693d-2ee8-47b4-9df7-b67a0ee31988}">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*" severity="1"/>
-    </emItem>
     <emItem blockID="i84" id="pink@rosaplugin.info">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*"/>
     </emItem>
     <emItem blockID="i547" id="{87934c42-161d-45bc-8cef-ef18abe2a30c}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="3.7.9999999999" severity="1"/>
     </emItem>
     <emItem blockID="i364" id="{FE1DEEEA-DB6D-44b8-83F0-34FC0F9D1052}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i356" id="{341f4dac-1966-47ff-aacf-0ce175f1498a}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
+    <emItem blockID="i536" id="{25D77636-38B1-1260-887C-2D4AFA92D6A4}">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="3"/>
+    </emItem>
     <emItem blockID="i336" id="CortonExt@ext.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
-    <emItem blockID="i536" id="{25D77636-38B1-1260-887C-2D4AFA92D6A4}">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*" severity="3"/>
-    </emItem>
     <emItem blockID="i374" id="update@firefox.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i452" id="{77beece6-3997-403a-92fa-0055bfcf88e5}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
@@ -1359,56 +1359,56 @@
     <emItem blockID="i1034" id="a88a77ahjjfjakckmmabsy278djasi@jetpack">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i562" id="iobitapps@mybrowserbar.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
-    <emItem blockID="i970" id="hha8771ui3-Fo9j9h7aH98jsdfa8sda@jetpack">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*" severity="3"/>
-    </emItem>
     <emItem blockID="i916" id="{97E22097-9A2F-45b1-8DAF-36AD648C7EF4}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
           <versionRange maxVersion="*" minVersion="39.0a1"/>
         </targetApplication>
       </versionRange>
     </emItem>
-    <emItem blockID="i338" id="{1FD91A9C-410C-4090-BBCC-55D3450EF433}">
+    <emItem blockID="i430" id="1chtw@facebook.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
-    <emItem blockID="i430" id="1chtw@facebook.com">
+    <emItem blockID="i970" id="hha8771ui3-Fo9j9h7aH98jsdfa8sda@jetpack">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="3"/>
+    </emItem>
+    <emItem blockID="i338" id="{1FD91A9C-410C-4090-BBCC-55D3450EF433}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i344" id="lrcsTube@hansanddeta.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i79" id="GifBlock@facebook.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*"/>
     </emItem>
+    <emItem blockID="i483" id="brasilescapefive@facebook.com">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="3"/>
+    </emItem>
     <emItem blockID="i469" id="OKitSpace@OKitSpace.es">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i780" id="{b6ef1336-69bb-45b6-8cba-e578fc0e4433}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
-    <emItem blockID="i483" id="brasilescapefive@facebook.com">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*" severity="3"/>
-    </emItem>
     <emItem blockID="i1058" id="amo-validator-bypass@example.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i48" id="admin@youtubespeedup.com">
       <prefs/>
     </emItem>
     <emItem blockID="i766" id="/^[a-z0-9]+@foxysecure[a-z0-9]*\.com$/">
@@ -1485,26 +1485,26 @@
     <emItem blockID="i752" id="savingsslider@mybrowserbar.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i77" id="{fa277cfc-1d75-4949-a1f9-4ac8e41b2dfd}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*"/>
     </emItem>
-    <emItem blockID="i64" id="royal@facebook.com">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*"/>
-    </emItem>
     <emItem blockID="i728" id="l@AdLJ7uz.net">
       <prefs>
         <pref>browser.startup.homepage</pref>
       </prefs>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
+    <emItem blockID="i64" id="royal@facebook.com">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*"/>
+    </emItem>
     <emItem blockID="i468" id="05dd836e-2cbd-4204-9ff3-2f8a8665967d@a8876730-fb0c-4057-a2fc-f9c09d438e81.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i60" id="youtb3@youtb3.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*"/>
     </emItem>
@@ -1543,21 +1543,21 @@
     <emItem blockID="i314" id="crossriderapp8812@crossrider.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i838" id="{87b5a11e-3b54-42d2-9102-0a7cb1f79ebf}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
-    <emItem blockID="i678" id="{C4A4F5A0-4B89-4392-AFAC-D58010E349AF}">
+    <emItem blockID="i570" id="jid1-vW9nopuIAJiRHw@jetpack">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
-    <emItem blockID="i570" id="jid1-vW9nopuIAJiRHw@jetpack">
+    <emItem blockID="i678" id="{C4A4F5A0-4B89-4392-AFAC-D58010E349AF}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i14" id="mozilla_cc@internetdownloadmanager.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="6.9.8">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
           <versionRange maxVersion="*" minVersion="3.7a1pre"/>
@@ -1611,21 +1611,21 @@
     <emItem blockID="i516" id="/^({3f3cddf8-f74d-430c-bd19-d2c9147aed3d}|{515b2424-5911-40bd-8a2c-bdb20286d8f5}|{17464f93-137e-4646-a0c6-0dc13faf0113}|{d1b5aad5-d1ae-4b20-88b1-feeaeb4c1ebc}|{aad50c91-b136-49d9-8b30-0e8d3ead63d0})$/">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i51" id="admin@youtubeplayer.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*"/>
     </emItem>
-    <emItem blockID="i372" id="5nc3QHFgcb@r06Ws9gvNNVRfH.com">
+    <emItem blockID="i246" id="support@vide1flash2.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
-    <emItem blockID="i246" id="support@vide1flash2.com">
+    <emItem blockID="i372" id="5nc3QHFgcb@r06Ws9gvNNVRfH.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i680" id="jid1-bKSXgRwy1UQeRA@jetpack">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i802" id="{18d5a8fe-5428-485b-968f-b97b05a92b54}">
@@ -1658,53 +1658,53 @@
       <prefs/>
       <versionRange minVersion="99.7" maxVersion="99.7" severity="3"/>
       <versionRange minVersion="100.7" maxVersion="100.7" severity="3"/>
     </emItem>
     <emItem blockID="i162" id="{EB7508CA-C7B2-46E0-8C04-3E94A035BD49}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
-    <emItem blockID="i62" id="jid0-EcdqvFOgWLKHNJPuqAnawlykCGZ@jetpack">
+    <emItem blockID="i103" id="kdrgun@gmail.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*"/>
     </emItem>
-    <emItem blockID="i103" id="kdrgun@gmail.com">
+    <emItem blockID="i62" id="jid0-EcdqvFOgWLKHNJPuqAnawlykCGZ@jetpack">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*"/>
     </emItem>
     <emItem blockID="i1119" id="/^(test3@test.org|test2@test.org|test@test.org|support@mozilla.org)$/">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
-    <emItem blockID="i484" id="plugin@getwebcake.com">
+    <emItem blockID="i519" id="703db0db-5fe9-44b6-9f53-c6a91a0ad5bd@7314bc82-969e-4d2a-921b-e5edd0b02cf1.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
-    <emItem blockID="i519" id="703db0db-5fe9-44b6-9f53-c6a91a0ad5bd@7314bc82-969e-4d2a-921b-e5edd0b02cf1.com">
+    <emItem blockID="i484" id="plugin@getwebcake.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i477" id="mbrnovone@facebook.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i495" id="kallow@facebook.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i836" id="hansin@topvest.id">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
-    <emItem blockID="i542" id="/^({bf67a47c-ea97-4caf-a5e3-feeba5331231}|{24a0cfe1-f479-4b19-b627-a96bf1ea3a56})$/">
+    <emItem blockID="i286" id="{58bd07eb-0ee0-4df0-8121-dc9b693373df}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
-    <emItem blockID="i286" id="{58bd07eb-0ee0-4df0-8121-dc9b693373df}">
+    <emItem blockID="i542" id="/^({bf67a47c-ea97-4caf-a5e3-feeba5331231}|{24a0cfe1-f479-4b19-b627-a96bf1ea3a56})$/">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i52" id="ff-ext@youtube">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*"/>
     </emItem>
     <emItem blockID="i676" id="SpecialSavings@SpecialSavings.com">
@@ -1716,64 +1716,64 @@
         <pref>browser.startup.homepage</pref>
       </prefs>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i540" id="/^(ffxtlbr@mixidj\.com|{c0c2693d-2ee8-47b4-9df7-b67a0ee31988}|{67097627-fd8e-4f6b-af4b-ecb65e50112e}|{f6f0f973-a4a3-48cf-9a7a-b7a69c30d71a}|{a3d0e35f-f1da-4ccb-ae77-e9d27777e68d}|{1122b43d-30ee-403f-9bfa-3cc99b0caddd})$/">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
+    <emItem blockID="i493" id="12x3q@3244516.com">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="3"/>
+    </emItem>
     <emItem blockID="i1265" id="@video_downloader_pro">
       <prefs/>
       <versionRange minVersion="1.2.1" maxVersion="1.2.5" severity="1"/>
     </emItem>
-    <emItem blockID="i493" id="12x3q@3244516.com">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*" severity="3"/>
-    </emItem>
     <emItem blockID="i348" id="{13c9f1f9-2322-4d5c-81df-6d4bf8476ba4}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i442" id="pennerdu@faceobooks.ws">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
+    <emItem blockID="i450" id="{dff137ae-1ffd-11e3-8277-b8ac6f996f26}">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="3"/>
+    </emItem>
     <emItem blockID="i99" id="pfzPXmnzQRXX6@2iABkVe.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*"/>
     </emItem>
-    <emItem blockID="i450" id="{dff137ae-1ffd-11e3-8277-b8ac6f996f26}">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*" severity="3"/>
-    </emItem>
     <emItem blockID="i238" id="/^pink@.*\.info$/">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
           <versionRange maxVersion="*" minVersion="18.0"/>
         </targetApplication>
       </versionRange>
     </emItem>
+    <emItem blockID="i750" id="{46eddf51-a4f6-4476-8d6c-31c5187b2a2f}">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="1"/>
+    </emItem>
     <emItem blockID="i282" id="{33e0daa6-3af3-d8b5-6752-10e949c61516}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="1.1.999" severity="1"/>
     </emItem>
-    <emItem blockID="i750" id="{46eddf51-a4f6-4476-8d6c-31c5187b2a2f}">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*" severity="1"/>
+    <emItem blockID="i489" id="astrovia@facebook.com">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i1245" id="{4ED1F68A-5463-4931-9384-8FFF5ED91D92}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="3.9.9" severity="1"/>
     </emItem>
-    <emItem blockID="i489" id="astrovia@facebook.com">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*" severity="3"/>
-    </emItem>
     <emItem blockID="i18" id="msntoolbar@msn.com">
       <prefs/>
       <versionRange minVersion=" " maxVersion="6.*"/>
     </emItem>
     <emItem blockID="i455" id="7d51fb17-b199-4d8f-894e-decaff4fc36a@a298838b-7f50-4c7c-9277-df6abbd42a0c.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
@@ -1796,32 +1796,32 @@
     <emItem blockID="i926" id="{B1FC07E1-E05B-4567-8891-E63FBE545BA8}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
           <versionRange maxVersion="*" minVersion="39.0a1"/>
         </targetApplication>
       </versionRange>
     </emItem>
+    <emItem blockID="i382" id="{6926c7f7-6006-42d1-b046-eba1b3010315}">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="1"/>
+    </emItem>
+    <emItem blockID="i782" id="safebrowse@safebrowse.co">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="3"/>
+    </emItem>
     <emItem blockID="i806" id="{d9284e50-81fc-11da-a72b-0800200c9a66}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="7.7.34" severity="1">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
           <versionRange maxVersion="*" minVersion="34.0a1"/>
         </targetApplication>
       </versionRange>
     </emItem>
-    <emItem blockID="i782" id="safebrowse@safebrowse.co">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*" severity="3"/>
-    </emItem>
-    <emItem blockID="i382" id="{6926c7f7-6006-42d1-b046-eba1b3010315}">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*" severity="1"/>
-    </emItem>
     <emItem blockID="i812" id="{1e4ea5fc-09e5-4f45-a43b-c048304899fc}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i56" id="flash@adobe.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*"/>
     </emItem>
@@ -1836,21 +1836,21 @@
     <emItem blockID="i472" id="linksicle@linksicle.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i98" id="youtubeeing@youtuberie.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*"/>
     </emItem>
-    <emItem blockID="i1213" id="unblocker20__web@unblocker.yt">
+    <emItem blockID="i578" id="jid1-XLjasWL55iEE1Q@jetpack">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
-    <emItem blockID="i578" id="jid1-XLjasWL55iEE1Q@jetpack">
+    <emItem blockID="i1213" id="unblocker20__web@unblocker.yt">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i1233" id="cloudmask@cloudmask.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="2.0.788"/>
     </emItem>
     <emItem blockID="i582" id="discoverypro@discoverypro.com">
@@ -1876,48 +1876,48 @@
     <emItem blockID="i521" id="/^({66b103a7-d772-4fcd-ace4-16f79a9056e0}|{6926c7f7-6006-42d1-b046-eba1b3010315}|{72cabc40-64b2-46ed-8648-26d831761150}|{73ee2cf2-7b76-4c49-b659-c3d8cf30825d}|{ca6446a5-73d5-4c35-8aa1-c71dc1024a18}|{5373a31d-9410-45e2-b299-4f61428f0be4})$/">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i322" id="jid0-Y6TVIzs0r7r4xkOogmJPNAGFGBw@jetpack">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
+    <emItem blockID="i1129" id="youtubeunblocker__web@unblocker.yt">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="3"/>
+    </emItem>
     <emItem blockID="i24" id="{6E19037A-12E3-4295-8915-ED48BC341614}">
       <prefs/>
       <versionRange minVersion="0.1" maxVersion="1.3.328.4" severity="1">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
           <versionRange maxVersion="*" minVersion="3.7a1pre"/>
         </targetApplication>
       </versionRange>
     </emItem>
-    <emItem blockID="i1129" id="youtubeunblocker__web@unblocker.yt">
+    <emItem blockID="i808" id="{c96d1ae6-c4cf-4984-b110-f5f561b33b5a}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i1214" id="firefoxdav@icloud.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="1.4.22" severity="1"/>
     </emItem>
-    <emItem blockID="i66" id="youtubeer@youtuber.com">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*"/>
-    </emItem>
-    <emItem blockID="i808" id="{c96d1ae6-c4cf-4984-b110-f5f561b33b5a}">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*" severity="3"/>
-    </emItem>
     <emItem blockID="i4" id="{4B3803EA-5230-4DC3-A7FC-33638F3D3542}">
       <prefs/>
       <versionRange minVersion="1.2" maxVersion="1.2">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
           <versionRange maxVersion="*" minVersion="3.0a1"/>
         </targetApplication>
       </versionRange>
     </emItem>
+    <emItem blockID="i66" id="youtubeer@youtuber.com">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*"/>
+    </emItem>
     <emItem blockID="i517" id="/^({16e193c8-1706-40bf-b6f3-91403a9a22be}|{284fed43-2e13-4afe-8aeb-50827d510e20}|{5e3cc5d8-ed11-4bed-bc47-35b4c4bc1033}|{7429e64a-1fd4-4112-a186-2b5630816b91}|{8c9980d7-0f09-4459-9197-99b3e559660c}|{8f1d9545-0bb9-4583-bb3c-5e1ac1e2920c})$/">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i460" id="{845cab51-d8d2-472f-8bd9-2b44642d97c2}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
@@ -1943,44 +1943,44 @@
     <emItem blockID="i920" id="{FCE04E1F-9378-4f39-96F6-5689A9159E45}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
           <versionRange maxVersion="*" minVersion="39.0a1"/>
         </targetApplication>
       </versionRange>
     </emItem>
-    <emItem blockID="i507" id="4zffxtbr-bs@VideoDownloadConverter_4z.com">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="5.75.3.25126" severity="1"/>
+    <emItem blockID="i224" id="{336D0C35-8A85-403a-B9D2-65C292C39087}">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i1050" id="87aukfkausiopoawjsuifhasefgased278djasi@jetpack">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
-    <emItem blockID="i224" id="{336D0C35-8A85-403a-B9D2-65C292C39087}">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*" severity="1"/>
+    <emItem blockID="i507" id="4zffxtbr-bs@VideoDownloadConverter_4z.com">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="5.75.3.25126" severity="1"/>
     </emItem>
     <emItem blockID="i22" id="ShopperReports@ShopperReports.com">
       <prefs/>
       <versionRange minVersion="3.1.22.0" maxVersion="3.1.22.0"/>
     </emItem>
     <emItem blockID="i1137" id="/^({d50bfa5f-291d-48a8-909c-5f1a77b31948}|{d54bc985-6e7b-46cd-ad72-a4a266ad879e}|{d89e5de3-5543-4363-b320-a98cf150f86a}|{f3465017-6f51-4980-84a5-7bee2f961eba}|{fae25f38-ff55-46ea-888f-03b49aaf8812})$/">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
+    <emItem blockID="i342" id="lbmsrvfvxcblvpane@lpaezhjez.org">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="1"/>
+    </emItem>
     <emItem blockID="i1230" id="addon@gemaoff">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
-    <emItem blockID="i342" id="lbmsrvfvxcblvpane@lpaezhjez.org">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*" severity="1"/>
-    </emItem>
     <emItem blockID="i840" id="{4889ddce-7a83-45e6-afc9-1e4f1149fff4}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i10" id="{8CE11043-9A15-4207-A565-0C94C42D590D}">
       <prefs/>
     </emItem>
     <emItem blockID="i788" id="{729c9605-0626-4792-9584-4cbe65b243e6}">
--- a/browser/base/content/browser-addons.js
+++ b/browser/base/content/browser-addons.js
@@ -83,35 +83,43 @@ const gXPInstallObserver = {
     // the next confirmation
     if (installInfo.installs.every(i => i.state != AddonManager.STATE_DOWNLOADED)) {
       showNextConfirmation();
       return;
     }
 
     const anchorID = "addons-notification-icon";
 
-    // Make notifications persist a minimum of 30 seconds
+    // Make notifications persistent
     var options = {
       displayURI: installInfo.originatingURI,
-      timeout: Date.now() + 30000,
+      persistent: true,
+    };
+
+    let acceptInstallation = () => {
+      for (let install of installInfo.installs)
+        install.install();
+      installInfo = null;
+
+      Services.telemetry
+              .getHistogramById("SECURITY_UI")
+              .add(Ci.nsISecurityUITelemetry.WARNING_CONFIRM_ADDON_INSTALL_CLICK_THROUGH);
     };
 
     let cancelInstallation = () => {
       if (installInfo) {
         for (let install of installInfo.installs) {
           // The notification may have been closed because the add-ons got
           // cancelled elsewhere, only try to cancel those that are still
           // pending install.
           if (install.state != AddonManager.STATE_CANCELLED)
             install.cancel();
         }
       }
 
-      this.acceptInstallation = null;
-
       showNextConfirmation();
     };
 
     let unsigned = installInfo.installs.filter(i => i.addon.signedState <= AddonManager.SIGNEDSTATE_MISSING);
     let someUnsigned = unsigned.length > 0 && unsigned.length < installInfo.installs.length;
 
     options.eventCallback = (aEvent) => {
       switch (aEvent) {
@@ -137,26 +145,16 @@ const gXPInstallObserver = {
                 gNavigatorBundle.getString("addonInstall.unsigned"));
               unsignedLabel.setAttribute("class",
                 "addon-install-confirmation-unsigned");
               container.appendChild(unsignedLabel);
             }
 
             addonList.appendChild(container);
           }
-
-          this.acceptInstallation = () => {
-            for (let install of installInfo.installs)
-              install.install();
-            installInfo = null;
-
-            Services.telemetry
-                    .getHistogramById("SECURITY_UI")
-                    .add(Ci.nsISecurityUITelemetry.WARNING_CONFIRM_ADDON_INSTALL_CLICK_THROUGH);
-          };
           break;
       }
     };
 
     options.learnMoreURL = Services.urlFormatter.formatURLPref("app.support.baseURL");
 
     let messageString;
     let notification = document.getElementById("addon-install-confirmation-notification");
@@ -182,36 +180,40 @@ const gXPInstallObserver = {
 
     let brandBundle = document.getElementById("bundle_brand");
     let brandShortName = brandBundle.getString("brandShortName");
 
     messageString = PluralForm.get(installInfo.installs.length, messageString);
     messageString = messageString.replace("#1", brandShortName);
     messageString = messageString.replace("#2", installInfo.installs.length);
 
-    let cancelButton = document.getElementById("addon-install-confirmation-cancel");
-    cancelButton.label = gNavigatorBundle.getString("addonInstall.cancelButton.label");
-    cancelButton.accessKey = gNavigatorBundle.getString("addonInstall.cancelButton.accesskey");
+    let action = {
+      label: gNavigatorBundle.getString("addonInstall.acceptButton.label"),
+      accessKey: gNavigatorBundle.getString("addonInstall.acceptButton.accesskey"),
+      callback: acceptInstallation,
+    };
 
-    let acceptButton = document.getElementById("addon-install-confirmation-accept");
-    acceptButton.label = gNavigatorBundle.getString("addonInstall.acceptButton.label");
-    acceptButton.accessKey = gNavigatorBundle.getString("addonInstall.acceptButton.accesskey");
+    let secondaryAction = {
+      label: gNavigatorBundle.getString("addonInstall.cancelButton.label"),
+      accessKey: gNavigatorBundle.getString("addonInstall.cancelButton.accesskey"),
+      callback: () => {},
+    };
 
     if (height) {
       notification.style.minHeight = height + "px";
     }
 
     let tab = gBrowser.getTabForBrowser(browser);
     if (tab) {
       gBrowser.selectedTab = tab;
     }
 
     let popup = PopupNotifications.show(browser, "addon-install-confirmation",
-                                        messageString, anchorID, null, null,
-                                        options);
+                                        messageString, anchorID, action,
+                                        [secondaryAction], options);
 
     removeNotificationOnEnd(popup, installInfo.installs);
 
     Services.telemetry
             .getHistogramById("SECURITY_UI")
             .add(Ci.nsISecurityUITelemetry.WARNING_CONFIRM_ADDON_INSTALL);
   },
 
@@ -225,19 +227,20 @@ const gXPInstallObserver = {
     if (!browser || gBrowser.browsers.indexOf(browser) == -1)
       return;
 
     const anchorID = "addons-notification-icon";
     var messageString, action;
     var brandShortName = brandBundle.getString("brandShortName");
 
     var notificationID = aTopic;
-    // Make notifications persist a minimum of 30 seconds
+    // Make notifications persistent
     var options = {
       displayURI: installInfo.originatingURI,
+      persistent: true,
       timeout: Date.now() + 30000,
     };
 
     switch (aTopic) {
     case "addon-install-disabled": {
       notificationID = "xpinstall-disabled";
 
       if (gPrefService.prefIsLocked("xpinstall.enabled")) {
@@ -300,39 +303,56 @@ const gXPInstallObserver = {
         return;
       notificationID = "addon-progress";
       messageString = gNavigatorBundle.getString("addonDownloadingAndVerifying");
       messageString = PluralForm.get(installInfo.installs.length, messageString);
       messageString = messageString.replace("#1", installInfo.installs.length);
       options.installs = installInfo.installs;
       options.contentWindow = browser.contentWindow;
       options.sourceURI = browser.currentURI;
-      options.eventCallback = (aEvent) => {
+      options.eventCallback = function(aEvent) {
         switch (aEvent) {
+          case "shown":
+            let notificationElement = [...this.owner.panel.childNodes]
+                                      .find(n => n.notification == this);
+            if (notificationElement) {
+              if (Preferences.get("xpinstall.customConfirmationUI", false)) {
+                notificationElement.setAttribute("mainactiondisabled", "true");
+              } else {
+                notificationElement.button.hidden = true;
+              }
+            }
+            break;
           case "removed":
             options.contentWindow = null;
             options.sourceURI = null;
             break;
         }
       };
+      action = {
+        label: gNavigatorBundle.getString("addonInstall.acceptButton.label"),
+        accessKey: gNavigatorBundle.getString("addonInstall.acceptButton.accesskey"),
+        callback: () => {},
+      };
+      let secondaryAction = {
+        label: gNavigatorBundle.getString("addonInstall.cancelButton.label"),
+        accessKey: gNavigatorBundle.getString("addonInstall.cancelButton.accesskey"),
+        callback: () => {
+          for (let install of installInfo.installs) {
+            if (install.state != AddonManager.STATE_CANCELLED) {
+              install.cancel();
+            }
+          }
+        },
+      };
       let notification = PopupNotifications.show(browser, notificationID, messageString,
-                                                 anchorID, null, null, options);
+                                                 anchorID, action,
+                                                 [secondaryAction], options);
       notification._startTime = Date.now();
 
-      let cancelButton = document.getElementById("addon-progress-cancel");
-      cancelButton.label = gNavigatorBundle.getString("addonInstall.cancelButton.label");
-      cancelButton.accessKey = gNavigatorBundle.getString("addonInstall.cancelButton.accesskey");
-
-      let acceptButton = document.getElementById("addon-progress-accept");
-      if (Preferences.get("xpinstall.customConfirmationUI", false)) {
-        acceptButton.label = gNavigatorBundle.getString("addonInstall.acceptButton.label");
-        acceptButton.accessKey = gNavigatorBundle.getString("addonInstall.acceptButton.accesskey");
-      } else {
-        acceptButton.hidden = true;
-      }
       break; }
     case "addon-install-failed": {
       // TODO This isn't terribly ideal for the multiple failure case
       for (let install of installInfo.installs) {
         let host;
         try {
           host  = options.displayURI.host;
         } catch (e) {
@@ -562,17 +582,17 @@ var LightWeightThemeWebInstaller = {
           label: gNavigatorBundle.getString("lwthemeNeedsRestart.button"),
           accessKey: gNavigatorBundle.getString("lwthemeNeedsRestart.accesskey"),
           callback: function() {
             BrowserUtils.restartApplication();
           }
         };
 
         let options = {
-          timeout: Date.now() + 30000
+          persistent: true
         };
 
         PopupNotifications.show(gBrowser.selectedBrowser, "addon-theme-change",
                                 messageString, "addons-notification-icon",
                                 action, null, options);
       },
 
       onEnabled: function(aAddon) {
--- a/browser/base/content/browser-plugins.js
+++ b/browser/base/content/browser-plugins.js
@@ -273,16 +273,17 @@ var gPluginHandler = {
         notification.reshow();
         browser.messageManager.sendAsyncMessage("BrowserPlugins:NotificationShown");
       }
       return;
     }
 
     let options = {
       dismissed: !showNow,
+      persistent: showNow,
       eventCallback: this._clickToPlayNotificationEventCallback,
       primaryPlugin: primaryPluginPermission,
       pluginData: pluginData,
       principal: principal,
     };
     PopupNotifications.show(browser, "click-to-play-plugins",
                             "", "plugins-notification-icon",
                             null, null, options);
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -5965,18 +5965,22 @@ var OfflineApps = {
 
     let warnQuotaKB = Services.prefs.getIntPref("offline-apps.quota.warn");
     // This message shows the quota in MB, and so we divide the quota (in kb) by 1024.
     let message = gNavigatorBundle.getFormattedString("offlineApps.usage",
                                                       [ uri.host,
                                                         warnQuotaKB / 1024 ]);
 
     let anchorID = "indexedDB-notification-icon";
+    let options = {
+      persistent: true,
+      hideClose: true,
+    };
     PopupNotifications.show(browser, "offline-app-usage", message,
-                            anchorID, mainAction);
+                            anchorID, mainAction, null, options);
 
     // Now that we've warned once, prevent the warning from showing up
     // again.
     Services.perms.add(uri, "offline-app",
                        Ci.nsIOfflineCacheUpdateService.ALLOW_NO_WARN);
   },
 
   // XXX: duplicated in preferences/advanced.js
@@ -6025,37 +6029,39 @@ var OfflineApps = {
     let notification = PopupNotifications.getNotification(notificationID, browser);
 
     if (notification) {
       notification.options.controlledItems.push([
         Cu.getWeakReference(browser), docId, uri
       ]);
     } else {
       let mainAction = {
-        label: gNavigatorBundle.getString("offlineApps.allow"),
-        accessKey: gNavigatorBundle.getString("offlineApps.allowAccessKey"),
+        label: gNavigatorBundle.getString("offlineApps.allowStoring.label"),
+        accessKey: gNavigatorBundle.getString("offlineApps.allowStoring.accesskey"),
         callback: function() {
           for (let [ciBrowser, ciDocId, ciUri] of notification.options.controlledItems) {
             OfflineApps.allowSite(ciBrowser, ciDocId, ciUri);
           }
         }
       };
       let secondaryActions = [{
-        label: gNavigatorBundle.getString("offlineApps.never"),
-        accessKey: gNavigatorBundle.getString("offlineApps.neverAccessKey"),
+        label: gNavigatorBundle.getString("offlineApps.dontAllow.label"),
+        accessKey: gNavigatorBundle.getString("offlineApps.dontAllow.accesskey"),
         callback: function() {
           for (let [, , ciUri] of notification.options.controlledItems) {
             OfflineApps.disallowSite(ciUri);
           }
         }
       }];
-      let message = gNavigatorBundle.getFormattedString("offlineApps.available",
+      let message = gNavigatorBundle.getFormattedString("offlineApps.available2",
                                                         [host]);
       let anchorID = "indexedDB-notification-icon";
       let options = {
+        persistent: true,
+        hideClose: true,
         controlledItems : [[Cu.getWeakReference(browser), docId, uri]]
       };
       notification = PopupNotifications.show(browser, notificationID, message,
                                              anchorID, mainAction,
                                              secondaryActions, options);
     }
   },
 
@@ -6127,103 +6133,55 @@ var IndexedDBPromptHelper = {
     var requestor = subject.QueryInterface(Ci.nsIInterfaceRequestor);
 
     var browser = requestor.getInterface(Ci.nsIDOMNode);
     if (browser.ownerGlobal != window) {
       // Only listen for notifications for browsers in our chrome window.
       return;
     }
 
-    var host = browser.currentURI.asciiHost;
+    // Get the host name if available or the file path otherwise.
+    var host = browser.currentURI.asciiHost || browser.currentURI.path;
 
     var message;
     var responseTopic;
     if (topic == this._permissionsPrompt) {
-      message = gNavigatorBundle.getFormattedString("offlineApps.available",
+      message = gNavigatorBundle.getFormattedString("offlineApps.available2",
                                                     [ host ]);
       responseTopic = this._permissionsResponse;
     }
 
-    const hiddenTimeoutDuration = 30000; // 30 seconds
-    const firstTimeoutDuration = 300000; // 5 minutes
-
-    var timeoutId;
-
     var observer = requestor.getInterface(Ci.nsIObserver);
 
     var mainAction = {
-      label: gNavigatorBundle.getString("offlineApps.allow"),
-      accessKey: gNavigatorBundle.getString("offlineApps.allowAccessKey"),
+      label: gNavigatorBundle.getString("offlineApps.allowStoring.label"),
+      accessKey: gNavigatorBundle.getString("offlineApps.allowStoring.accesskey"),
       callback: function() {
-        clearTimeout(timeoutId);
         observer.observe(null, responseTopic,
                          Ci.nsIPermissionManager.ALLOW_ACTION);
       }
     };
 
     var secondaryActions = [
       {
-        label: gNavigatorBundle.getString("offlineApps.never"),
-        accessKey: gNavigatorBundle.getString("offlineApps.neverAccessKey"),
+        label: gNavigatorBundle.getString("offlineApps.dontAllow.label"),
+        accessKey: gNavigatorBundle.getString("offlineApps.dontAllow.accesskey"),
         callback: function() {
-          clearTimeout(timeoutId);
           observer.observe(null, responseTopic,
                            Ci.nsIPermissionManager.DENY_ACTION);
         }
       }
     ];
 
-    // This will be set to the result of PopupNotifications.show().
-    var notification;
-
-    function timeoutNotification() {
-      // Remove the notification.
-      if (notification) {
-        notification.remove();
-      }
-
-      // Clear all of our timeout stuff. We may be called directly, not just
-      // when the timeout actually elapses.
-      clearTimeout(timeoutId);
-
-      // And tell the page that the popup timed out.
-      observer.observe(null, responseTopic,
-                       Ci.nsIPermissionManager.UNKNOWN_ACTION);
-    }
-
-    var options = {
-      eventCallback: function(state) {
-        // Don't do anything if the timeout has not been set yet.
-        if (!timeoutId) {
-          return;
-        }
-
-        // If the popup is being dismissed start the short timeout.
-        if (state == "dismissed") {
-          clearTimeout(timeoutId);
-          timeoutId = setTimeout(timeoutNotification, hiddenTimeoutDuration);
-          return;
-        }
-
-        // If the popup is being re-shown then clear the timeout allowing
-        // unlimited waiting.
-        if (state == "shown") {
-          clearTimeout(timeoutId);
-        }
-      }
-    };
-
-    notification = PopupNotifications.show(browser, topic, message,
-                                           this._notificationIcon, mainAction,
-                                           secondaryActions, options);
-
-    // Set the timeoutId after the popup has been created, and use the long
-    // timeout value. If the user doesn't notice the popup after this amount of
-    // time then it is most likely not visible and we want to alert the page.
-    timeoutId = setTimeout(timeoutNotification, firstTimeoutDuration);
+    PopupNotifications.show(browser, topic, message,
+                            this._notificationIcon, mainAction,
+                            secondaryActions, {
+                              persistent: true,
+                              hideClose: true,
+                            });
   }
 };
 
 function CanCloseWindow()
 {
   // Avoid redundant calls to canClose from showing multiple
   // PermitUnload dialogs.
   if (Services.startup.shuttingDown || window.skipNextCanClose) {
--- a/browser/base/content/popup-notifications.inc
+++ b/browser/base/content/popup-notifications.inc
@@ -17,17 +17,17 @@
           <menupopup id="webRTC-selectCamera-menupopup"/>
         </menulist>
       </popupnotificationcontent>
 
       <popupnotificationcontent id="webRTC-selectWindowOrScreen" orient="vertical">
         <label id="webRTC-selectWindow-label"
                control="webRTC-selectWindow-menulist"/>
         <menulist id="webRTC-selectWindow-menulist"
-                  oncommand="webrtcUI.updateMainActionLabel(this);">
+                  oncommand="webrtcUI.updateWarningLabel(this);">
           <menupopup id="webRTC-selectWindow-menupopup"/>
         </menulist>
         <description id="webRTC-all-windows-shared" hidden="true">&getUserMedia.allWindowsShared.message;</description>
       </popupnotificationcontent>
 
       <popupnotificationcontent id="webRTC-preview" hidden="true">
         <html:video id="webRTC-previewVideo"/>
         <vbox id="webRTC-previewWarningBox">
@@ -61,21 +61,13 @@
     </popupnotification>
 
 
     <popupnotification id="addon-progress-notification" hidden="true">
       <popupnotificationcontent orient="vertical">
         <progressmeter id="addon-progress-notification-progressmeter"/>
         <label id="addon-progress-notification-progresstext" crop="end"/>
       </popupnotificationcontent>
-      <button id="addon-progress-cancel"
-              oncommand="this.parentNode.cancel();"/>
-      <button id="addon-progress-accept" disabled="true"/>
     </popupnotification>
 
     <popupnotification id="addon-install-confirmation-notification" hidden="true">
       <popupnotificationcontent id="addon-install-confirmation-content" orient="vertical"/>
-      <button id="addon-install-confirmation-cancel"
-              oncommand="PopupNotifications.getNotification('addon-install-confirmation').remove();"/>
-      <button id="addon-install-confirmation-accept"
-              oncommand="gXPInstallObserver.acceptInstallation();
-                         PopupNotifications.getNotification('addon-install-confirmation').remove();"/>
     </popupnotification>
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -2855,17 +2855,24 @@
               var isBusy = aOtherTab.hasAttribute("busy");
               if (isBusy) {
                 aOurTab.setAttribute("busy", "true");
                 modifiedAttrs.push("busy");
                 if (aOurTab.selected)
                   this.mIsBusy = true;
               }
 
-              this._swapBrowserDocShells(aOurTab, otherBrowser);
+              this._swapBrowserDocShells(aOurTab, otherBrowser, Ci.nsIBrowser.SWAP_DEFAULT);
+            }
+
+            // Unregister the previously opened URI
+            if (otherBrowser.registeredOpenURI) {
+              this._unifiedComplete.unregisterOpenPage(otherBrowser.registeredOpenURI,
+                                                       otherBrowser.getAttribute("usercontextid") || 0);
+              delete otherBrowser.registeredOpenURI;
             }
 
             // Handle findbar data (if any)
             let otherFindBar = aOtherTab._findBar;
             if (otherFindBar &&
                 otherFindBar.findMode == otherFindBar.FIND_NORMAL) {
               let ourFindBar = this.getFindBar(aOurTab);
               ourFindBar._findField.value = otherFindBar._findField.value;
@@ -2888,19 +2895,31 @@
 
             if (modifiedAttrs.length) {
               this._tabAttrModified(aOurTab, modifiedAttrs);
             }
           ]]>
         </body>
       </method>
 
+      <method name="swapBrowsers">
+        <parameter name="aOurTab"/>
+        <parameter name="aOtherBrowser"/>
+        <parameter name="aFlags"/>
+        <body>
+          <![CDATA[
+            this._swapBrowserDocShells(aOurTab, aOtherBrowser, aFlags);
+          ]]>
+        </body>
+      </method>
+
       <method name="_swapBrowserDocShells">
         <parameter name="aOurTab"/>
         <parameter name="aOtherBrowser"/>
+        <parameter name="aFlags"/>
         <body>
           <![CDATA[
             // Unhook our progress listener
             const filter = this._tabFilters.get(aOurTab);
             let tabListener = this._tabListeners.get(aOurTab);
             let ourBrowser = this.getBrowserForTab(aOurTab);
             ourBrowser.webProgress.removeProgressListener(filter);
             filter.removeProgressListener(tabListener);
@@ -2932,25 +2951,27 @@
             }
 
             // Register new outerWindowIDs.
             this._outerWindowIDBrowserMap.set(ourBrowser.outerWindowID, ourBrowser);
             if (remoteBrowser) {
               remoteBrowser._outerWindowIDBrowserMap.set(aOtherBrowser.outerWindowID, aOtherBrowser);
             }
 
-            // Swap permanentKey properties.
-            let ourPermanentKey = ourBrowser.permanentKey;
-            ourBrowser.permanentKey = aOtherBrowser.permanentKey;
-            aOtherBrowser.permanentKey = ourPermanentKey;
-            aOurTab.permanentKey = ourBrowser.permanentKey;
-            if (remoteBrowser) {
-              let otherTab = remoteBrowser.getTabForBrowser(aOtherBrowser);
-              if (otherTab) {
-                otherTab.permanentKey = aOtherBrowser.permanentKey;
+            if (!(aFlags & Ci.nsIBrowser.SWAP_KEEP_PERMANENT_KEY)) {
+              // Swap permanentKey properties.
+              let ourPermanentKey = ourBrowser.permanentKey;
+              ourBrowser.permanentKey = aOtherBrowser.permanentKey;
+              aOtherBrowser.permanentKey = ourPermanentKey;
+              aOurTab.permanentKey = ourBrowser.permanentKey;
+              if (remoteBrowser) {
+                let otherTab = remoteBrowser.getTabForBrowser(aOtherBrowser);
+                if (otherTab) {
+                  otherTab.permanentKey = aOtherBrowser.permanentKey;
+                }
               }
             }
 
             // Restore the progress listener
             tabListener = this.mTabProgressListener(aOurTab, ourBrowser, false, false);
             this._tabListeners.set(aOurTab, tabListener);
 
             const notifyAll = Ci.nsIWebProgress.NOTIFY_ALL;
@@ -2960,28 +2981,26 @@
         </body>
       </method>
 
       <method name="_swapRegisteredOpenURIs">
         <parameter name="aOurBrowser"/>
         <parameter name="aOtherBrowser"/>
         <body>
           <![CDATA[
-            // If the current URI is registered as open remove it from the list.
-            if (aOurBrowser.registeredOpenURI) {
-              this._unifiedComplete.unregisterOpenPage(aOurBrowser.registeredOpenURI,
-                                                       aOurBrowser.getAttribute("usercontextid") || 0);
-              delete aOurBrowser.registeredOpenURI;
-            }
-
-            // If the other/new URI is registered as open then copy it over.
+            // Swap the registeredOpenURI properties of the two browsers
+            let tmp = aOurBrowser.registeredOpenURI;
+            delete aOurBrowser.registeredOpenURI;
             if (aOtherBrowser.registeredOpenURI) {
               aOurBrowser.registeredOpenURI = aOtherBrowser.registeredOpenURI;
               delete aOtherBrowser.registeredOpenURI;
             }
+            if (tmp) {
+              aOtherBrowser.registeredOpenURI = tmp;
+            }
           ]]>
         </body>
       </method>
 
       <method name="reloadAllTabs">
         <body>
           <![CDATA[
             let tabs = this.visibleTabs;
--- a/browser/base/content/test/general/browser_aboutAccounts.js
+++ b/browser/base/content/test/general/browser_aboutAccounts.js
@@ -324,19 +324,20 @@ var gTests = [
   teardown: function() {
     gBrowser.removeCurrentTab();
   },
   run: function* () {
     setPref("identity.fxaccounts.remote.signup.uri", "https://example.com/");
     yield setSignedInUser();
     let tab = yield promiseNewTabLoadEvent("about:accounts");
     // sign the user out - the tab should have action=signin
+    let loadPromise = promiseOneMessage(tab, "test:document:load");
     yield signOut();
     // wait for the new load.
-    yield promiseOneMessage(tab, "test:document:load");
+    yield loadPromise;
     is(tab.linkedBrowser.contentDocument.location.href, "about:accounts?action=signin");
   }
 },
 {
   desc: "Test entrypoint query string, no action, no user logged in",
   teardown: () => gBrowser.removeCurrentTab(),
   run: function* () {
     // When this loads with no user logged-in, we expect the "normal" URL
@@ -430,39 +431,38 @@ function promiseOneMessage(tab, messageN
 }
 
 function promiseNewTabLoadEvent(aUrl)
 {
   let tab = gBrowser.selectedTab = gBrowser.addTab(aUrl);
   let browser = tab.linkedBrowser;
   let mm = browser.messageManager;
 
-  // give it an e10s-friendly content script to help with our tests.
-  mm.loadFrameScript(CHROME_BASE + "content_aboutAccounts.js", true);
+  // give it an e10s-friendly content script to help with our tests,
   // and wait for it to tell us about the load.
-  return promiseOneMessage(tab, "test:document:load").then(
-    () => tab
-  );
+  let promise = promiseOneMessage(tab, "test:document:load");
+  mm.loadFrameScript(CHROME_BASE + "content_aboutAccounts.js", true);
+  return promise.then(() => tab);
 }
 
 // Returns a promise which is resolved with the iframe's URL after a new
 // tab is created and the iframe in that tab loads.
 function promiseNewTabWithIframeLoadEvent(aUrl) {
   let deferred = Promise.defer();
   let tab = gBrowser.selectedTab = gBrowser.addTab(aUrl);
   let browser = tab.linkedBrowser;
   let mm = browser.messageManager;
 
-  // give it an e10s-friendly content script to help with our tests.
-  mm.loadFrameScript(CHROME_BASE + "content_aboutAccounts.js", true);
+  // give it an e10s-friendly content script to help with our tests,
   // and wait for it to tell us about the iframe load.
   mm.addMessageListener("test:iframe:load", function onFrameLoad(message) {
     mm.removeMessageListener("test:iframe:load", onFrameLoad);
     deferred.resolve([tab, message.data.url]);
   });
+  mm.loadFrameScript(CHROME_BASE + "content_aboutAccounts.js", true);
   return deferred.promise;
 }
 
 function checkVisibilities(tab, data) {
   let ids = Object.keys(data);
   let mm = tab.linkedBrowser.messageManager;
   let deferred = Promise.defer();
   mm.addMessageListener("test:check-visibilities-response", function onResponse(message) {
--- a/browser/base/content/test/general/browser_bug553455.js
+++ b/browser/base/content/test/general/browser_bug553455.js
@@ -73,16 +73,18 @@ function waitForProgressNotification(aPa
 
     info("Saw a notification");
     ok(PopupNotifications.isPanelOpen, "Panel should be open");
     is(PopupNotifications.panel.childNodes.length, aExpectedCount, "Should be the right number of notifications");
     if (PopupNotifications.panel.childNodes.length) {
       let nodes = Array.from(PopupNotifications.panel.childNodes);
       let notification = nodes.find(n => n.id == notificationId + "-notification");
       ok(notification, `Should have seen the right notification`);
+      ok(notification.button.hasAttribute("disabled"),
+         "The install button should be disabled");
     }
 
     return PopupNotifications.panel;
   });
 }
 
 function waitForNotification(aId, aExpectedCount = 1) {
   return Task.spawn(function* () {
@@ -111,23 +113,23 @@ function waitForNotification(aId, aExpec
         PopupNotifications.panel.removeEventListener("PanelUpdated", eventListener);
         resolve();
       });
     });
 
     yield observerPromise;
     yield panelEventPromise;
 
-    info("Saw a notification");
+    info("Saw a " + aId + " notification");
     ok(PopupNotifications.isPanelOpen, "Panel should be open");
     is(PopupNotifications.panel.childNodes.length, aExpectedCount, "Should be the right number of notifications");
     if (PopupNotifications.panel.childNodes.length) {
       let nodes = Array.from(PopupNotifications.panel.childNodes);
       let notification = nodes.find(n => n.id == aId + "-notification");
-      ok(notification, `Should have seen the right notification`);
+      ok(notification, "Should have seen the " + aId + " notification");
     }
 
     return PopupNotifications.panel;
   });
 }
 
 function waitForNotificationClose() {
   return new Promise(resolve => {
@@ -137,18 +139,18 @@ function waitForNotificationClose() {
       resolve();
     }, false);
   });
 }
 
 function waitForInstallDialog() {
   return Task.spawn(function* () {
     if (Preferences.get("xpinstall.customConfirmationUI", false)) {
-      yield waitForNotification("addon-install-confirmation");
-      return;
+      let panel = yield waitForNotification("addon-install-confirmation");
+      return panel.childNodes[0];
     }
 
     info("Waiting for install dialog");
 
     let window = yield new Promise(resolve => {
       Services.wm.addListener({
         onOpenWindow: function(aXULWindow) {
           Services.wm.removeListener(this);
@@ -171,39 +173,39 @@ function waitForInstallDialog() {
     });
     info("Saw install dialog");
     is(domwindow.document.location.href, XPINSTALL_URL, "Should have seen the right window open");
 
     // Override the countdown timer on the accept button
     let button = domwindow.document.documentElement.getButton("accept");
     button.disabled = false;
 
-    return;
+    return null;
   });
 }
 
 function removeTab() {
   return Promise.all([
     waitForNotificationClose(),
     BrowserTestUtils.removeTab(gBrowser.selectedTab)
   ]);
 }
 
-function acceptInstallDialog() {
+function acceptInstallDialog(installDialog) {
   if (Preferences.get("xpinstall.customConfirmationUI", false)) {
-    document.getElementById("addon-install-confirmation-accept").click();
+    installDialog.button.click();
   } else {
     let win = Services.wm.getMostRecentWindow("Addons:Install");
     win.document.documentElement.acceptDialog();
   }
 }
 
-function cancelInstallDialog() {
+function cancelInstallDialog(installDialog) {
   if (Preferences.get("xpinstall.customConfirmationUI", false)) {
-    document.getElementById("addon-install-confirmation-cancel").click();
+    installDialog.secondaryButton.click();
   } else {
     let win = Services.wm.getMostRecentWindow("Addons:Install");
     win.document.documentElement.cancelDialog();
   }
 }
 
 function waitForSingleNotification(aCallback) {
   return Task.spawn(function* () {
@@ -288,20 +290,20 @@ function test_blockedInstall() {
 
     let dialogPromise = waitForInstallDialog();
     // Click on Allow
     EventUtils.synthesizeMouse(notification.button, 20, 10, {});
     // Notification should have changed to progress notification
     ok(PopupNotifications.isPanelOpen, "Notification should still be open");
     notification = panel.childNodes[0];
     is(notification.id, "addon-progress-notification", "Should have seen the progress notification");
-    yield dialogPromise;
+    let installDialog = yield dialogPromise;
 
     notificationPromise = waitForNotification("addon-install-restart");
-    acceptInstallDialog();
+    acceptInstallDialog(installDialog);
     panel = yield notificationPromise;
 
     notification = panel.childNodes[0];
     is(notification.button.label, "Restart Now", "Should have seen the right button");
     is(notification.getAttribute("label"),
        "XPI Test will be installed after you restart " + gApp + ".",
        "Should have seen the right message");
 
@@ -323,24 +325,24 @@ function test_whitelistedInstall() {
     let progressPromise = waitForProgressNotification();
     let dialogPromise = waitForInstallDialog();
     let triggers = encodeURIComponent(JSON.stringify({
       "XPI": "amosigned.xpi"
     }));
     BrowserTestUtils.openNewForegroundTab(gBrowser, TESTROOT + "installtrigger.html?"
       + triggers).then(newTab => tab = newTab);
     yield progressPromise;
-    yield dialogPromise;
+    let installDialog = yield dialogPromise;
     yield BrowserTestUtils.waitForCondition(() => !!tab, "tab should be present");
 
     is(gBrowser.selectedTab, tab,
        "tab selected in response to the addon-install-confirmation notification");
 
     let notificationPromise = waitForNotification("addon-install-restart");
-    acceptInstallDialog();
+    acceptInstallDialog(installDialog);
     let panel = yield notificationPromise;
 
     let notification = panel.childNodes[0];
     is(notification.button.label, "Restart Now", "Should have seen the right button");
     is(notification.getAttribute("label"),
              "XPI Test will be installed after you restart " + gApp + ".",
              "Should have seen the right message");
 
@@ -435,20 +437,20 @@ function test_restartless() {
     let progressPromise = waitForProgressNotification();
     let dialogPromise = waitForInstallDialog();
     let triggers = encodeURIComponent(JSON.stringify({
       "XPI": "restartless.xpi"
     }));
     gBrowser.selectedTab = gBrowser.addTab();
     gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
     yield progressPromise;
-    yield dialogPromise;
+    let installDialog = yield dialogPromise;
 
     let notificationPromise = waitForNotification("addon-install-complete");
-    acceptInstallDialog();
+    acceptInstallDialog(installDialog);
     let panel = yield notificationPromise;
 
     let notification = panel.childNodes[0];
     is(notification.getAttribute("label"),
        "XPI Test has been installed successfully.",
        "Should have seen the right message");
 
     let installs = yield getInstalls();
@@ -477,20 +479,20 @@ function test_multiple() {
     let progressPromise = waitForProgressNotification();
     let dialogPromise = waitForInstallDialog();
     let triggers = encodeURIComponent(JSON.stringify({
       "Unsigned XPI": "amosigned.xpi",
       "Restartless XPI": "restartless.xpi"
     }));
     BrowserTestUtils.openNewForegroundTab(gBrowser, TESTROOT + "installtrigger.html?" + triggers);
     let panel = yield progressPromise;
-    yield dialogPromise;
+    let installDialog = yield dialogPromise;
 
     let notificationPromise = waitForNotification("addon-install-restart");
-    acceptInstallDialog();
+    acceptInstallDialog(installDialog);
     yield notificationPromise;
 
     let notification = panel.childNodes[0];
     is(notification.button.label, "Restart Now", "Should have seen the right button");
     is(notification.getAttribute("label"),
        "2 add-ons will be installed after you restart " + gApp + ".",
        "Should have seen the right message");
 
@@ -507,30 +509,32 @@ function test_multiple() {
     Services.perms.remove(makeURI("http://example.com/"), "install");
     yield removeTab();
   });
 },
 
 function test_sequential() {
   return Task.spawn(function* () {
     // This test is only relevant if using the new doorhanger UI
-    if (!Preferences.get("xpinstall.customConfirmationUI", false)) {
+    // TODO: this subtest is disabled until multiple notification prompts are
+    // reworked in bug 1188152
+    if (true || !Preferences.get("xpinstall.customConfirmationUI", false)) {
       return;
     }
     let pm = Services.perms;
     pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
 
     let progressPromise = waitForProgressNotification();
     let dialogPromise = waitForInstallDialog();
     let triggers = encodeURIComponent(JSON.stringify({
       "Restartless XPI": "restartless.xpi"
     }));
     BrowserTestUtils.openNewForegroundTab(gBrowser, TESTROOT + "installtrigger.html?" + triggers);
     yield progressPromise;
-    yield dialogPromise;
+    let installDialog = yield dialogPromise;
 
     // Should see the right add-on
     let container = document.getElementById("addon-install-confirmation-content");
     is(container.childNodes.length, 1, "Should be one item listed");
     is(container.childNodes[0].firstChild.getAttribute("value"), "XPI Test", "Should have the right add-on");
 
     progressPromise = waitForProgressNotification(true, 2);
     triggers = encodeURIComponent(JSON.stringify({
@@ -559,30 +563,30 @@ function test_sequential() {
     is(PopupNotifications.panel.childNodes.length, 1, "Should be the right number of notifications");
     is(PopupNotifications.panel.childNodes[0].id, "addon-install-confirmation-notification",
        "Should only be showing one install confirmation");
 
     // Should still have the right add-on in the confirmation notification
     is(container.childNodes.length, 1, "Should be one item listed");
     is(container.childNodes[0].firstChild.getAttribute("value"), "XPI Test", "Should have the right add-on");
 
-    cancelInstallDialog();
+    cancelInstallDialog(installDialog);
 
     ok(PopupNotifications.isPanelOpen, "Panel should still be open");
     is(PopupNotifications.panel.childNodes.length, 1, "Should be the right number of notifications");
     is(PopupNotifications.panel.childNodes[0].id, "addon-install-confirmation-notification",
        "Should still have an install confirmation open");
 
     // Should have the next add-on's confirmation dialog
     is(container.childNodes.length, 1, "Should be one item listed");
     is(container.childNodes[0].firstChild.getAttribute("value"), "Theme Test", "Should have the right add-on");
 
     Services.perms.remove(makeURI("http://example.com"), "install");
     let closePromise = waitForNotificationClose();
-    cancelInstallDialog();
+    cancelInstallDialog(installDialog);
     yield closePromise;
     yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
   });
 },
 
 function test_someUnverified() {
   return Task.spawn(function* () {
     // This test is only relevant if using the new doorhanger UI and allowing
@@ -598,34 +602,34 @@ function test_someUnverified() {
     let progressPromise = waitForProgressNotification();
     let dialogPromise = waitForInstallDialog();
     let triggers = encodeURIComponent(JSON.stringify({
       "Extension XPI": "restartless-unsigned.xpi",
       "Theme XPI": "theme.xpi"
     }));
     BrowserTestUtils.openNewForegroundTab(gBrowser, TESTROOT + "installtrigger.html?" + triggers);
     yield progressPromise;
-    yield dialogPromise;
+    let installDialog = yield dialogPromise;
 
     let notification = document.getElementById("addon-install-confirmation-notification");
     let message = notification.getAttribute("label");
     is(message, "Caution: This site would like to install 2 add-ons in " + gApp +
        ", some of which are unverified. Proceed at your own risk.",
        "Should see the right message");
 
     let container = document.getElementById("addon-install-confirmation-content");
     is(container.childNodes.length, 2, "Should be two items listed");
     is(container.childNodes[0].firstChild.getAttribute("value"), "XPI Test", "Should have the right add-on");
     is(container.childNodes[0].lastChild.getAttribute("class"),
        "addon-install-confirmation-unsigned", "Should have the unverified marker");
     is(container.childNodes[1].firstChild.getAttribute("value"), "Theme Test", "Should have the right add-on");
     is(container.childNodes[1].childNodes.length, 1, "Shouldn't have the unverified marker");
 
     let notificationPromise = waitForNotification("addon-install-restart");
-    acceptInstallDialog();
+    acceptInstallDialog(installDialog);
     yield notificationPromise;
 
     let [addon, theme] = yield new Promise(resolve => {
       AddonManager.getAddonsByIDs(["restartless-xpi@tests.mozilla.org",
                                   "theme-xpi@tests.mozilla.org"],
                                   function(addons) {
         resolve(addons);
       });
@@ -655,29 +659,29 @@ function test_allUnverified() {
 
     let progressPromise = waitForProgressNotification();
     let dialogPromise = waitForInstallDialog();
     let triggers = encodeURIComponent(JSON.stringify({
       "Extension XPI": "restartless-unsigned.xpi"
     }));
     BrowserTestUtils.openNewForegroundTab(gBrowser, TESTROOT + "installtrigger.html?" + triggers);
     yield progressPromise;
-    yield dialogPromise;
+    let installDialog = yield dialogPromise;
 
     let notification = document.getElementById("addon-install-confirmation-notification");
     let message = notification.getAttribute("label");
     is(message, "Caution: This site would like to install an unverified add-on in " + gApp + ". Proceed at your own risk.");
 
     let container = document.getElementById("addon-install-confirmation-content");
     is(container.childNodes.length, 1, "Should be one item listed");
     is(container.childNodes[0].firstChild.getAttribute("value"), "XPI Test", "Should have the right add-on");
     is(container.childNodes[0].childNodes.length, 1, "Shouldn't have the unverified marker");
 
     let notificationPromise = waitForNotification("addon-install-complete");
-    acceptInstallDialog();
+    acceptInstallDialog(installDialog);
     yield notificationPromise;
 
     let addon = yield new Promise(resolve => {
       AddonManager.getAddonByID("restartless-xpi@tests.mozilla.org", function(result) {
         resolve(result);
       });
     });
     addon.uninstall();
@@ -690,20 +694,20 @@ function test_allUnverified() {
 function test_url() {
   return Task.spawn(function* () {
     let progressPromise = waitForProgressNotification();
     let dialogPromise = waitForInstallDialog();
     gBrowser.selectedTab = gBrowser.addTab("about:blank");
     yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
     gBrowser.loadURI(TESTROOT + "amosigned.xpi");
     yield progressPromise;
-    yield dialogPromise;
+    let installDialog = yield dialogPromise;
 
     let notificationPromise = waitForNotification("addon-install-restart");
-    acceptInstallDialog();
+    acceptInstallDialog(installDialog);
     let panel = yield notificationPromise;
 
     let notification = panel.childNodes[0];
     is(notification.button.label, "Restart Now", "Should have seen the right button");
     is(notification.getAttribute("label"),
        "XPI Test will be installed after you restart " + gApp + ".",
        "Should have seen the right message");
 
@@ -858,20 +862,20 @@ function test_reload() {
 
     let progressPromise = waitForProgressNotification();
     let dialogPromise = waitForInstallDialog();
     let triggers = encodeURIComponent(JSON.stringify({
       "Unsigned XPI": "amosigned.xpi"
     }));
     BrowserTestUtils.openNewForegroundTab(gBrowser, TESTROOT + "installtrigger.html?" + triggers);
     yield progressPromise;
-    yield dialogPromise;
+    let installDialog = yield dialogPromise;
 
     let notificationPromise = waitForNotification("addon-install-restart");
-    acceptInstallDialog();
+    acceptInstallDialog(installDialog);
     let panel = yield notificationPromise;
 
     let notification = panel.childNodes[0];
     is(notification.button.label, "Restart Now", "Should have seen the right button");
     is(notification.getAttribute("label"),
        "XPI Test will be installed after you restart " + gApp + ".",
        "Should have seen the right message");
 
@@ -901,20 +905,20 @@ function test_theme() {
 
     let progressPromise = waitForProgressNotification();
     let dialogPromise = waitForInstallDialog();
     let triggers = encodeURIComponent(JSON.stringify({
       "Theme XPI": "theme.xpi"
     }));
     BrowserTestUtils.openNewForegroundTab(gBrowser, TESTROOT + "installtrigger.html?" + triggers);
     yield progressPromise;
-    yield dialogPromise;
+    let installDialog = yield dialogPromise;
 
     let notificationPromise = waitForNotification("addon-install-restart");
-    acceptInstallDialog();
+    acceptInstallDialog(installDialog);
     let panel = yield notificationPromise;
 
     let notification = panel.childNodes[0];
     is(notification.button.label, "Restart Now", "Should have seen the right button");
     is(notification.getAttribute("label"),
        "Theme Test will be installed after you restart " + gApp + ".",
        "Should have seen the right message");
 
@@ -981,42 +985,42 @@ function test_renotifyInstalled() {
 
     let progressPromise = waitForProgressNotification();
     let dialogPromise = waitForInstallDialog();
     let triggers = encodeURIComponent(JSON.stringify({
       "XPI": "amosigned.xpi"
     }));
     BrowserTestUtils.openNewForegroundTab(gBrowser, TESTROOT + "installtrigger.html?" + triggers);
     yield progressPromise;
-    yield dialogPromise;
+    let installDialog = yield dialogPromise;
 
     // Wait for the complete notification
     let notificationPromise = waitForNotification("addon-install-restart");
-    acceptInstallDialog();
+    acceptInstallDialog(installDialog);
     let panel = yield notificationPromise;
 
     let closePromise = waitForNotificationClose();
     // hide the panel (this simulates the user dismissing it)
     panel.hidePopup();
     yield closePromise;
 
     // Install another
     yield new Promise(resolve => executeSoon(resolve));
 
     progressPromise = waitForProgressNotification();
     dialogPromise = waitForInstallDialog();
     gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
     yield progressPromise;
-    yield dialogPromise;
+    installDialog = yield dialogPromise;
 
     info("Timeouts after this probably mean bug 589954 regressed");
 
     // Wait for the complete notification
     notificationPromise = waitForNotification("addon-install-restart");
-    acceptInstallDialog();
+    acceptInstallDialog(installDialog);
     yield notificationPromise;
 
     let installs = yield getInstalls();
     is(installs.length, 1, "Should be one pending installs");
     installs[0].cancel();
 
     Services.perms.remove(makeURI("http://example.com/"), "install");
     yield removeTab();
@@ -1041,29 +1045,28 @@ function test_cancel() {
     anchor.click();
     // Reopen the notification
     anchor.click();
 
     ok(PopupNotifications.isPanelOpen, "Notification should still be open");
     is(PopupNotifications.panel.childNodes.length, 1, "Should be only one notification");
     notification = panel.childNodes[0];
     is(notification.id, "addon-progress-notification", "Should have seen the progress notification");
-    let button = document.getElementById("addon-progress-cancel");
 
     // Cancel the download
     let install = notification.notification.options.installs[0];
     let cancelledPromise = new Promise(resolve => {
       install.addListener({
         onDownloadCancelled: function() {
           install.removeListener(this);
           resolve();
         }
       });
     });
-    EventUtils.synthesizeMouseAtCenter(button, {});
+    EventUtils.synthesizeMouseAtCenter(notification.secondaryButton, {});
     yield cancelledPromise;
 
     yield new Promise(resolve => executeSoon(resolve));
 
     ok(!PopupNotifications.isPanelOpen, "Notification should be closed");
 
     let installs = yield getInstalls();
     is(installs.length, 0, "Should be no pending install");
--- a/browser/base/content/test/popupNotifications/browser.ini
+++ b/browser/base/content/test/popupNotifications/browser.ini
@@ -7,12 +7,14 @@ skip-if = (os == "linux" && (debug || as
 [browser_popupNotification.js]
 skip-if = (os == "linux" && (debug || asan))
 [browser_popupNotification_2.js]
 skip-if = (os == "linux" && (debug || asan))
 [browser_popupNotification_3.js]
 skip-if = (os == "linux" && (debug || asan))
 [browser_popupNotification_4.js]
 skip-if = (os == "linux" && (debug || asan))
+[browser_popupNotification_5.js]
+skip-if = (os == "linux" && (debug || asan))
 [browser_popupNotification_checkbox.js]
 skip-if = (os == "linux" && (debug || asan))
 [browser_reshow_in_background.js]
 skip-if = (os == "linux" && (debug || asan))
--- a/browser/base/content/test/popupNotifications/browser_popupNotification.js
+++ b/browser/base/content/test/popupNotifications/browser_popupNotification.js
@@ -42,16 +42,60 @@ var tests = [
       triggerSecondaryCommand(popup, 0);
     },
     onHidden: function(popup) {
       ok(this.notifyObj.secondaryActionClicked, "secondaryAction was clicked");
       ok(!this.notifyObj.dismissalCallbackTriggered, "dismissal callback wasn't triggered");
       ok(this.notifyObj.removedCallbackTriggered, "removed callback triggered");
     }
   },
+  { id: "Test#2b",
+    run: function() {
+      this.notifyObj = new BasicNotification(this.id);
+      this.notifyObj.secondaryActions.push({
+        label: "Extra Secondary Action",
+        accessKey: "E",
+        callback: () => this.extraSecondaryActionClicked = true,
+      });
+      showNotification(this.notifyObj);
+    },
+    onShown: function(popup) {
+      checkPopup(popup, this.notifyObj);
+      triggerSecondaryCommand(popup, 1);
+    },
+    onHidden: function(popup) {
+      ok(this.extraSecondaryActionClicked, "extra secondary action was clicked");
+      ok(!this.notifyObj.dismissalCallbackTriggered, "dismissal callback wasn't triggered");
+      ok(this.notifyObj.removedCallbackTriggered, "removed callback triggered");
+    }
+  },
+  { id: "Test#2c",
+    run: function() {
+      this.notifyObj = new BasicNotification(this.id);
+      this.notifyObj.secondaryActions.push({
+        label: "Extra Secondary Action",
+        accessKey: "E",
+        callback: () => ok(false, "unexpected callback invocation"),
+      }, {
+        label: "Other Extra Secondary Action",
+        accessKey: "O",
+        callback: () => this.extraSecondaryActionClicked = true,
+      });
+      showNotification(this.notifyObj);
+    },
+    onShown: function(popup) {
+      checkPopup(popup, this.notifyObj);
+      triggerSecondaryCommand(popup, 2);
+    },
+    onHidden: function(popup) {
+      ok(this.extraSecondaryActionClicked, "extra secondary action was clicked");
+      ok(!this.notifyObj.dismissalCallbackTriggered, "dismissal callback wasn't triggered");
+      ok(this.notifyObj.removedCallbackTriggered, "removed callback triggered");
+    }
+  },
   { id: "Test#3",
     run: function() {
       this.notifyObj = new BasicNotification(this.id);
       this.notification = showNotification(this.notifyObj);
     },
     onShown: function(popup) {
       checkPopup(popup, this.notifyObj);
       dismissNotification(popup);
--- a/browser/base/content/test/popupNotifications/browser_popupNotification_2.js
+++ b/browser/base/content/test/popupNotifications/browser_popupNotification_2.js
@@ -182,32 +182,16 @@ var tests = [
       let promiseTopic = promiseTopicObserved("PopupNotifications-updateNotShowing");
       showNotification(notifyObj);
       yield promiseTopic;
       isnot(document.getElementById("geo-notification-icon").boxObject.width, 0,
             "geo anchor should be visible");
       goNext();
     }
   },
-  // Test notification "Not Now" menu item
-  { id: "Test#8",
-    run: function() {
-      this.notifyObj = new BasicNotification(this.id);
-      this.notification = showNotification(this.notifyObj);
-    },
-    onShown: function(popup) {
-      checkPopup(popup, this.notifyObj);
-      triggerSecondaryCommand(popup, 1);
-    },
-    onHidden: function(popup) {
-      ok(this.notifyObj.dismissalCallbackTriggered, "dismissal callback triggered");
-      this.notification.remove();
-      ok(this.notifyObj.removedCallbackTriggered, "removed callback triggered");
-    }
-  },
   // Test notification close button
   { id: "Test#9",
     run: function() {
       this.notifyObj = new BasicNotification(this.id);
       this.notification = showNotification(this.notifyObj);
     },
     onShown: function(popup) {
       checkPopup(popup, this.notifyObj);
--- a/browser/base/content/test/popupNotifications/browser_popupNotification_4.js
+++ b/browser/base/content/test/popupNotifications/browser_popupNotification_4.js
@@ -155,33 +155,16 @@ var tests = [
                                               "The browser should be active");
       let fm = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager);
       yield BrowserTestUtils.waitForCondition(() => fm.activeWindow == originalWindow,
                                               "The window should be active")
 
       goNext();
     }
   },
-  // the hideNotNow option
-  { id: "Test#7",
-    run: function() {
-      this.notifyObj = new BasicNotification(this.id);
-      this.notifyObj.options.hideNotNow = true;
-      this.notifyObj.mainAction.dismiss = true;
-      this.notification = showNotification(this.notifyObj);
-    },
-    onShown: function(popup) {
-      // checkPopup verifies that the Not Now item is hidden, and that no separator is added.
-      checkPopup(popup, this.notifyObj);
-      triggerMainCommand(popup);
-    },
-    onHidden: function(popup) {
-      this.notification.remove();
-    }
-  },
   // the main action callback can keep the notification.
   { id: "Test#8",
     run: function() {
       this.notifyObj = new BasicNotification(this.id);
       this.notifyObj.mainAction.dismiss = true;
       this.notification = showNotification(this.notifyObj);
     },
     onShown: function(popup) {
@@ -222,73 +205,10 @@ var tests = [
       };
 
       let notification = showNotification(notifyObj);
       ok(notifyObj.showingCallbackTriggered, "the showing callback was triggered");
       ok(!notifyObj.shownCallbackTriggered, "the shown callback wasn't triggered");
       notification.remove();
       goNext();
     }
-  },
-  // panel updates should fire the showing and shown callbacks again.
-  { id: "Test#11",
-    run: function() {
-      this.notifyObj = new BasicNotification(this.id);
-      this.notification = showNotification(this.notifyObj);
-    },
-    onShown: function(popup) {
-      checkPopup(popup, this.notifyObj);
-
-      this.notifyObj.showingCallbackTriggered = false;
-      this.notifyObj.shownCallbackTriggered = false;
-
-      // Force an update of the panel. This is typically called
-      // automatically when receiving 'activate' or 'TabSelect' events,
-      // but from a setTimeout, which is inconvenient for the test.
-      PopupNotifications._update();
-
-      checkPopup(popup, this.notifyObj);
-
-      this.notification.remove();
-    },
-    onHidden: function() { }
-  },
-  // A first dismissed notification shouldn't stop _update from showing a second notification
-  { id: "Test#12",
-    run: function() {
-      this.notifyObj1 = new BasicNotification(this.id);
-      this.notifyObj1.id += "_1";
-      this.notifyObj1.anchorID = "default-notification-icon";
-      this.notifyObj1.options.dismissed = true;
-      this.notification1 = showNotification(this.notifyObj1);
-
-      this.notifyObj2 = new BasicNotification(this.id);
-      this.notifyObj2.id += "_2";
-      this.notifyObj2.anchorID = "geo-notification-icon";
-      this.notifyObj2.options.dismissed = true;
-      this.notification2 = showNotification(this.notifyObj2);
-
-      this.notification2.dismissed = false;
-      PopupNotifications._update();
-    },
-    onShown: function(popup) {
-      checkPopup(popup, this.notifyObj2);
-      this.notification1.remove();
-      this.notification2.remove();
-    },
-    onHidden: function(popup) { }
-  },
-  // The anchor icon should be shown for notifications in background windows.
-  { id: "Test#13",
-    run: function() {
-      let notifyObj = new BasicNotification(this.id);
-      notifyObj.options.dismissed = true;
-      let win = gBrowser.replaceTabWithWindow(gBrowser.addTab("about:blank"));
-      whenDelayedStartupFinished(win, function() {
-        showNotification(notifyObj);
-        let anchor = document.getElementById("default-notification-icon");
-        is(anchor.getAttribute("showing"), "true", "the anchor is shown");
-        win.close();
-        goNext();
-      });
-    }
   }
 ];
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/popupNotifications/browser_popupNotification_5.js
@@ -0,0 +1,265 @@
+/* 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/. */
+
+function test() {
+  waitForExplicitFinish();
+
+  ok(PopupNotifications, "PopupNotifications object exists");
+  ok(PopupNotifications.panel, "PopupNotifications panel exists");
+
+  setup();
+  goNext();
+}
+
+var gNotification;
+
+var tests = [
+  // panel updates should fire the showing and shown callbacks again.
+  { id: "Test#1",
+    run: function() {
+      this.notifyObj = new BasicNotification(this.id);
+      this.notification = showNotification(this.notifyObj);
+    },
+    onShown: function(popup) {
+      checkPopup(popup, this.notifyObj);
+
+      this.notifyObj.showingCallbackTriggered = false;
+      this.notifyObj.shownCallbackTriggered = false;
+
+      // Force an update of the panel. This is typically called
+      // automatically when receiving 'activate' or 'TabSelect' events,
+      // but from a setTimeout, which is inconvenient for the test.
+      PopupNotifications._update();
+
+      checkPopup(popup, this.notifyObj);
+
+      this.notification.remove();
+    },
+    onHidden: function() { }
+  },
+  // A first dismissed notification shouldn't stop _update from showing a second notification
+  { id: "Test#2",
+    run: function() {
+      this.notifyObj1 = new BasicNotification(this.id);
+      this.notifyObj1.id += "_1";
+      this.notifyObj1.anchorID = "default-notification-icon";
+      this.notifyObj1.options.dismissed = true;
+      this.notification1 = showNotification(this.notifyObj1);
+
+      this.notifyObj2 = new BasicNotification(this.id);
+      this.notifyObj2.id += "_2";
+      this.notifyObj2.anchorID = "geo-notification-icon";
+      this.notifyObj2.options.dismissed = true;
+      this.notification2 = showNotification(this.notifyObj2);
+
+      this.notification2.dismissed = false;
+      PopupNotifications._update();
+    },
+    onShown: function(popup) {
+      checkPopup(popup, this.notifyObj2);
+      this.notification1.remove();
+      this.notification2.remove();
+    },
+    onHidden: function(popup) { }
+  },
+  // The anchor icon should be shown for notifications in background windows.
+  { id: "Test#3",
+    run: function() {
+      let notifyObj = new BasicNotification(this.id);
+      notifyObj.options.dismissed = true;
+      let win = gBrowser.replaceTabWithWindow(gBrowser.addTab("about:blank"));
+      whenDelayedStartupFinished(win, function() {
+        showNotification(notifyObj);
+        let anchor = document.getElementById("default-notification-icon");
+        is(anchor.getAttribute("showing"), "true", "the anchor is shown");
+        win.close();
+        goNext();
+      });
+    }
+  },
+  // Test that persistent doesn't allow the notification to persist after
+  // navigation.
+  { id: "Test#4",
+    run: function* () {
+      this.oldSelectedTab = gBrowser.selectedTab;
+      gBrowser.selectedTab = gBrowser.addTab("about:blank");
+      yield promiseTabLoadEvent(gBrowser.selectedTab, "http://example.com/");
+      this.notifyObj = new BasicNotification(this.id);
+      this.notifyObj.addOptions({
+        persistent: true
+      });
+      this.notification = showNotification(this.notifyObj);
+    },
+    onShown: function* (popup) {
+      this.complete = false;
+
+      yield promiseTabLoadEvent(gBrowser.selectedTab, "http://example.org/");
+      yield promiseTabLoadEvent(gBrowser.selectedTab, "http://example.com/");
+
+      // This code should not be executed.
+      ok(false, "Should have removed the notification after navigation");
+      // Properly dismiss and cleanup in case the unthinkable happens.
+      this.complete = true;
+      triggerSecondaryCommand(popup, 0);
+    },
+    onHidden: function(popup) {
+      ok(!this.complete, "Should have hidden the notification after navigation");
+      this.notification.remove();
+      gBrowser.removeTab(gBrowser.selectedTab);
+      gBrowser.selectedTab = this.oldSelectedTab;
+    }
+  },
+  // Test that persistent allows the notification to persist until explicitly
+  // dismissed.
+  { id: "Test#5",
+    run: function* () {
+      this.oldSelectedTab = gBrowser.selectedTab;
+      gBrowser.selectedTab = gBrowser.addTab("about:blank");
+      yield promiseTabLoadEvent(gBrowser.selectedTab, "http://example.com/");
+      this.notifyObj = new BasicNotification(this.id);
+      this.notifyObj.addOptions({
+        persistent: true
+      });
+      this.notification = showNotification(this.notifyObj);
+    },
+    onShown: function* (popup) {
+      this.complete = false;
+
+      // Notification should persist after attempt to dismiss by clicking on the
+      // content area.
+      let browser = gBrowser.selectedBrowser;
+      yield BrowserTestUtils.synthesizeMouseAtCenter("body", {}, browser)
+
+      // Notification should be hidden after dismissal via Don't Allow.
+      this.complete = true;
+      triggerSecondaryCommand(popup, 0);
+    },
+    onHidden: function(popup) {
+      ok(this.complete, "Should have hidden the notification after clicking Not Now");
+      this.notification.remove();
+      gBrowser.removeTab(gBrowser.selectedTab);
+      gBrowser.selectedTab = this.oldSelectedTab;
+    }
+  },
+  // Test that persistent panels are still open after switching to another tab
+  // and back.
+  { id: "Test#6a",
+    run: function* () {
+      this.notifyObj = new BasicNotification(this.id);
+      this.notifyObj.options.persistent = true;
+      gNotification = showNotification(this.notifyObj);
+    },
+    onShown: function* (popup) {
+      this.oldSelectedTab = gBrowser.selectedTab;
+      gBrowser.selectedTab = gBrowser.addTab("about:blank");
+      info("Waiting for the new tab to load.");
+      yield promiseTabLoadEvent(gBrowser.selectedTab, "http://example.com/");
+    },
+    onHidden: function(popup) {
+      ok(true, "Should have hidden the notification after tab switch");
+      gBrowser.removeTab(gBrowser.selectedTab);
+      gBrowser.selectedTab = this.oldSelectedTab;
+    }
+  },
+  // Second part of the previous test that compensates for the limitation in
+  // runNextTest that expects a single onShown/onHidden invocation per test.
+  { id: "Test#6b",
+    run: function* () {
+      let id = PopupNotifications.panel.firstChild.getAttribute("popupid");
+      ok(id.endsWith("Test#6a"), "Should have found the notification from Test6a");
+      ok(PopupNotifications.isPanelOpen, "Should have shown the popup again after getting back to the tab");
+      gNotification.remove();
+      gNotification = null;
+      goNext();
+    }
+  },
+  // Test that persistent panels are still open after switching to another
+  // window and back.
+  { id: "Test#7",
+    run: function* () {
+      this.oldSelectedTab = gBrowser.selectedTab;
+      gBrowser.selectedTab = gBrowser.addTab("about:blank");
+      let notifyObj = new BasicNotification(this.id);
+      notifyObj.options.persistent = true;
+      this.notification = showNotification(notifyObj);
+      let win = gBrowser.replaceTabWithWindow(gBrowser.addTab("about:blank"));
+      whenDelayedStartupFinished(win, () => {
+        ok(notifyObj.shownCallbackTriggered, "Should have triggered the shown callback");
+        let anchor = win.document.getElementById("default-notification-icon");
+        win.PopupNotifications._reshowNotifications(anchor);
+        ok(win.PopupNotifications.panel.childNodes.length == 0,
+           "no notification displayed in new window");
+        ok(PopupNotifications.isPanelOpen, "Should be still showing the popup in the first window");
+        win.close();
+        let id = PopupNotifications.panel.firstChild.getAttribute("popupid");
+        ok(id.endsWith("Test#7"), "Should have found the notification from Test7");
+        ok(PopupNotifications.isPanelOpen, "Should have shown the popup again after getting back to the window");
+        this.notification.remove();
+        gBrowser.removeTab(gBrowser.selectedTab);
+        gBrowser.selectedTab = this.oldSelectedTab;
+        goNext();
+      });
+    }
+  },
+  // Test that only the first persistent notification is shown on update
+  { id: "Test#8",
+    run: function() {
+      this.notifyObj1 = new BasicNotification(this.id);
+      this.notifyObj1.id += "_1";
+      this.notifyObj1.anchorID = "default-notification-icon";
+      this.notifyObj1.options.persistent = true;
+      this.notification1 = showNotification(this.notifyObj1);
+
+      this.notifyObj2 = new BasicNotification(this.id);
+      this.notifyObj2.id += "_2";
+      this.notifyObj2.anchorID = "geo-notification-icon";
+      this.notifyObj2.options.persistent = true;
+      this.notification2 = showNotification(this.notifyObj2);
+
+      PopupNotifications._update();
+    },
+    onShown: function(popup) {
+      checkPopup(popup, this.notifyObj1);
+      this.notification1.remove();
+      this.notification2.remove();
+    },
+    onHidden: function(popup) { }
+  },
+  // Test that persistent notifications are shown stacked by anchor on update
+  { id: "Test#9",
+    run: function() {
+      this.notifyObj1 = new BasicNotification(this.id);
+      this.notifyObj1.id += "_1";
+      this.notifyObj1.anchorID = "default-notification-icon";
+      this.notifyObj1.options.persistent = true;
+      this.notification1 = showNotification(this.notifyObj1);
+
+      this.notifyObj2 = new BasicNotification(this.id);
+      this.notifyObj2.id += "_2";
+      this.notifyObj2.anchorID = "geo-notification-icon";
+      this.notifyObj2.options.persistent = true;
+      this.notification2 = showNotification(this.notifyObj2);
+
+      this.notifyObj3 = new BasicNotification(this.id);
+      this.notifyObj3.id += "_3";
+      this.notifyObj3.anchorID = "default-notification-icon";
+      this.notifyObj3.options.persistent = true;
+      this.notification3 = showNotification(this.notifyObj3);
+
+      PopupNotifications._update();
+    },
+    onShown: function(popup) {
+      let notifications = popup.childNodes;
+      is(notifications.length, 2, "two notifications displayed");
+      let [notification1, notification2] = notifications;
+      is(notification1.id, this.notifyObj1.id + "-notification", "id 1 matches");
+      is(notification2.id, this.notifyObj3.id + "-notification", "id 2 matches");
+
+      this.notification1.remove();
+      this.notification2.remove();
+      this.notification3.remove();
+    },
+    onHidden: function(popup) { }
+  },
+];
--- a/browser/base/content/test/popupNotifications/head.js
+++ b/browser/base/content/test/popupNotifications/head.js
@@ -208,35 +208,34 @@ function checkPopup(popup, notifyObj) {
   is(notification.getAttribute("label"), notifyObj.message, "message matches");
   is(notification.id, notifyObj.id + "-notification", "id matches");
   if (notifyObj.mainAction) {
     is(notification.getAttribute("buttonlabel"), notifyObj.mainAction.label,
        "main action label matches");
     is(notification.getAttribute("buttonaccesskey"),
        notifyObj.mainAction.accessKey, "main action accesskey matches");
   }
-  let actualSecondaryActions =
-    Array.filter(notification.childNodes, child => child.nodeName == "menuitem");
-  let secondaryActions = notifyObj.secondaryActions || [];
-  let actualSecondaryActionsCount = actualSecondaryActions.length;
-  if (notifyObj.options.hideNotNow) {
-    is(notification.getAttribute("hidenotnow"), "true", "'Not Now' item hidden");
-    if (secondaryActions.length)
-      is(notification.lastChild.tagName, "menuitem", "no menuseparator");
+  if (notifyObj.secondaryActions && notifyObj.secondaryActions.length > 0) {
+    let secondaryAction = notifyObj.secondaryActions[0];
+    is(notification.getAttribute("secondarybuttonlabel"), secondaryAction.label,
+       "secondary action label matches");
+    is(notification.getAttribute("secondarybuttonaccesskey"),
+       secondaryAction.accessKey, "secondary action accesskey matches");
   }
-  else if (secondaryActions.length) {
-    is(notification.lastChild.tagName, "menuseparator", "menuseparator exists");
-  }
-  is(actualSecondaryActionsCount, secondaryActions.length,
-    actualSecondaryActions.length + " secondary actions");
-  secondaryActions.forEach(function(a, i) {
-    is(actualSecondaryActions[i].getAttribute("label"), a.label,
-       "label for secondary action " + i + " matches");
-    is(actualSecondaryActions[i].getAttribute("accesskey"), a.accessKey,
-       "accessKey for secondary action " + i + " matches");
+  // Additional secondary actions appear as menu items.
+  let actualExtraSecondaryActions =
+    Array.filter(notification.childNodes, child => child.nodeName == "menuitem");
+  let extraSecondaryActions = notifyObj.secondaryActions ? notifyObj.secondaryActions.slice(1) : [];
+  is(actualExtraSecondaryActions.length, extraSecondaryActions.length,
+     "number of extra secondary actions matches");
+  extraSecondaryActions.forEach(function(a, i) {
+    is(actualExtraSecondaryActions[i].getAttribute("label"), a.label,
+       "label for extra secondary action " + i + " matches");
+    is(actualExtraSecondaryActions[i].getAttribute("accesskey"), a.accessKey,
+       "accessKey for extra secondary action " + i + " matches");
   });
 }
 
 XPCOMUtils.defineLazyGetter(this, "gActiveListeners", () => {
   let listeners = new Map();
   registerCleanupFunction(() => {
     for (let [listener, eventName] of listeners) {
       PopupNotifications.panel.removeEventListener(eventName, listener, false);
@@ -258,36 +257,39 @@ function onPopupEvent(eventName, callbac
   PopupNotifications.panel.addEventListener(eventName, listener, false);
 }
 
 function triggerMainCommand(popup) {
   let notifications = popup.childNodes;
   ok(notifications.length > 0, "at least one notification displayed");
   let notification = notifications[0];
   info("Triggering main command for notification " + notification.id);
-  // 20, 10 so that the inner button is hit
-  EventUtils.synthesizeMouse(notification.button, 20, 10, {});
+  EventUtils.synthesizeMouseAtCenter(notification.button, {});
 }
 
 function triggerSecondaryCommand(popup, index) {
   let notifications = popup.childNodes;
   ok(notifications.length > 0, "at least one notification displayed");
   let notification = notifications[0];
   info("Triggering secondary command for notification " + notification.id);
-  // Cancel the arrow panel slide-in transition (bug 767133) such that
-  // it won't interfere with us interacting with the dropdown.
-  document.getAnonymousNodes(popup)[0].style.transition = "none";
 
-  notification.button.focus();
+  if (index == 0) {
+    EventUtils.synthesizeMouseAtCenter(notification.secondaryButton, {});
+    return;
+  }
+
+  // Extra secondary actions appear in a menu.
+  notification.secondaryButton.nextSibling.nextSibling.focus();
 
   popup.addEventListener("popupshown", function handle() {
     popup.removeEventListener("popupshown", handle, false);
     info("Command popup open for notification " + notification.id);
-    // Press down until the desired command is selected
-    for (let i = 0; i <= index; i++) {
+    // Press down until the desired command is selected. Decrease index by one
+    // since the secondary action was handled above.
+    for (let i = 0; i <= index - 1; i++) {
       EventUtils.synthesizeKey("VK_DOWN", {});
     }
     // Activate
     EventUtils.synthesizeKey("VK_RETURN", {});
   }, false);
 
   // One down event to open the popup
   info("Open the popup to trigger secondary command for notification " + notification.id);
--- a/browser/base/content/test/social/browser_social_activation.js
+++ b/browser/base/content/test/social/browser_social_activation.js
@@ -106,42 +106,54 @@ function clickAddonRemoveButton(tab, aCa
       executeSoon(function() { aCallback(addon); });
     });
 
     BrowserTestUtils.synthesizeMouseAtCenter(button, {}, tab.linkedBrowser);
   });
 }
 
 function activateOneProvider(manifest, finishActivation, aCallback) {
-  info("activating provider " + manifest.name);
-  let panel = document.getElementById("servicesInstall-notification");
-  BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popupshown").then(() => {
-    ok(!panel.hidden, "servicesInstall-notification panel opened");
-    if (finishActivation)
-      panel.button.click();
-    else
-      panel.closebutton.click();
-  });
-  BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popuphidden").then(() => {
-    ok(panel.hidden, "servicesInstall-notification panel hidden");
-    if (!finishActivation) {
-      ok(panel.hidden, "activation panel is not showing");
-      executeSoon(aCallback);
+  Task.spawn(function* () {
+    info("activating provider " + manifest.name);
+
+    // Wait for the helper callback and the popup shown event in any order.
+    let popupShown = BrowserTestUtils.waitForEvent(PopupNotifications.panel,
+                                                   "popupshown");
+    yield new Promise(resolve => activateProvider(manifest.origin, resolve));
+    yield popupShown;
+
+    info("servicesInstall-notification panel opened");
+
+    // Start waiting for the activation event before the click on the button.
+    let providerLoaded = finishActivation ?
+                         waitForProviderLoad(manifest.origin) : null;
+    let popupHidden = BrowserTestUtils.waitForEvent(PopupNotifications.panel,
+                                                    "popuphidden");
+
+    // We need to wait for PopupNotifications.jsm to place the element.
+    let notification;
+    yield BrowserTestUtils.waitForCondition(
+          () => (notification = PopupNotifications.panel.childNodes[0]));
+    is(notification.id, "servicesInstall-notification");
+
+    if (finishActivation) {
+      notification.button.click();
     } else {
-      waitForProviderLoad(manifest.origin).then(() => {
-        checkSocialUI();
-        executeSoon(aCallback);
-      });
+      notification.closebutton.click();
     }
-  });
+
+    yield providerLoaded;
+    yield popupHidden;
 
-  // the test will continue as the popup events fire...
-  activateProvider(manifest.origin, function() {
-    info("waiting on activation panel to open/close...");
-  });
+    info("servicesInstall-notification panel hidden");
+
+    if (finishActivation) {
+      checkSocialUI();
+    }
+  }).then(() => executeSoon(aCallback)).catch(ex => ok(false, ex));
 }
 
 var gTestDomains = ["https://example.com", "https://test1.example.com", "https://test2.example.com"];
 var gProviders = [
   {
     name: "provider 1",
     origin: "https://example.com",
     shareURL: "https://example.com/browser/browser/base/content/test/social/social_share.html?provider1",
--- a/browser/base/content/test/webrtc/browser_devices_get_user_media.js
+++ b/browser/base/content/test/webrtc/browser_devices_get_user_media.js
@@ -469,17 +469,17 @@ var gTests = [
     gIdentityHandler._identityPopup.hidden = true;
     yield expectNoObserverCalled();
 
     yield closeStream();
   }
 },
 
 {
-  desc: "'Always Allow' ignored and not shown on http pages",
+  desc: "'Always Allow' disabled on http pages",
   run: function* checkNoAlwaysOnHttp() {
     // Load an http page instead of the https version.
     let browser = gBrowser.selectedBrowser;
     browser.loadURI(browser.documentURI.spec.replace("https://", "http://"));
     yield BrowserTestUtils.browserLoaded(browser);
 
     // Initially set both permissions to 'allow'.
     let Perms = Services.perms;
@@ -489,26 +489,26 @@ var gTests = [
 
     // Request devices and expect a prompt despite the saved 'Allow' permission,
     // because the connection isn't secure.
     let promise = promisePopupNotificationShown("webRTC-shareDevices");
     yield promiseRequestDevice(true, true);
     yield promise;
     yield expectObserverCalled("getUserMedia:request");
 
-    // Ensure that the 'Always Allow' action isn't shown.
-    let alwaysLabel = gNavigatorBundle.getString("getUserMedia.always.label");
-    ok(!!alwaysLabel, "found the 'Always Allow' localized label");
-    let labels = [];
+    // Ensure that checking the 'Remember this decision' checkbox disables
+    // 'Allow'.
     let notification = PopupNotifications.panel.firstChild;
-    for (let node of notification.childNodes) {
-      if (node.localName == "menuitem")
-        labels.push(node.getAttribute("label"));
-    }
-    is(labels.indexOf(alwaysLabel), -1, "The 'Always Allow' item isn't shown");
+    let checkbox = notification.checkbox;
+    ok(!!checkbox, "checkbox is present");
+    ok(!checkbox.checked, "checkbox is not checked");
+    checkbox.click();
+    ok(checkbox.checked, "checkbox now checked");
+    ok(notification.button.disabled, "Allow button is disabled");
+    ok(!notification.hasAttribute("warninghidden"), "warning message is shown");
 
     // Cleanup.
     yield closeStream(true);
     Perms.remove(uri, "camera");
     Perms.remove(uri, "microphone");
   }
 }
 
--- a/browser/base/content/test/webrtc/head.js
+++ b/browser/base/content/test/webrtc/head.js
@@ -300,32 +300,27 @@ function promiseNoPopupNotification(aNam
 }
 
 const kActionAlways = 1;
 const kActionDeny = 2;
 const kActionNever = 3;
 
 function activateSecondaryAction(aAction) {
   let notification = PopupNotifications.panel.firstChild;
-  notification.button.focus();
-  let popup = notification.menupopup;
-  popup.addEventListener("popupshown", function() {
-    popup.removeEventListener("popupshown", arguments.callee, false);
-
-    // Press 'down' as many time as needed to select the requested action.
-    while (aAction--)
-      EventUtils.synthesizeKey("VK_DOWN", {});
-
-    // Activate
-    EventUtils.synthesizeKey("VK_RETURN", {});
-  }, false);
-
-  // One down event to open the popup
-  EventUtils.synthesizeKey("VK_DOWN",
-                           { altKey: !navigator.platform.includes("Mac") });
+  switch (aAction) {
+    case kActionNever:
+      notification.checkbox.setAttribute("checked", true); // fallthrough
+    case kActionDeny:
+      notification.secondaryButton.click();
+      break;
+    case kActionAlways:
+      notification.checkbox.setAttribute("checked", true);
+      notification.button.click();
+      break;
+  }
 }
 
 function getMediaCaptureState() {
   return new Promise(resolve => {
     let mm = _mm();
     mm.addMessageListener("Test:MediaCaptureState", ({data}) => {
       resolve(data);
     });
--- a/browser/components/contextualidentity/content/usercontext.css
+++ b/browser/components/contextualidentity/content/usercontext.css
@@ -78,14 +78,14 @@
   background-repeat: no-repeat;
 }
 
 .userContext-icon,
 .menuitem-iconic[data-usercontextid] > .menu-iconic-left > .menu-iconic-icon,
 .subviewbutton[usercontextid] > .toolbarbutton-icon,
 #userContext-indicator {
   background-image: var(--identity-icon);
-  filter: url(chrome://browser/skin/filters.svg#fill);
+  filter: url(chrome://global/skin/filters.svg#fill);
   fill: var(--identity-icon-color);
   background-size: contain;
   background-repeat: no-repeat;
   background-position: center center;
 }
--- a/browser/components/extensions/test/browser/browser_ext_browserAction_popup_preload.js
+++ b/browser/components/extensions/test/browser/browser_ext_browserAction_popup_preload.js
@@ -82,37 +82,43 @@ add_task(function* testBrowserActionDisa
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
       "browser_action": {
         "default_popup": "popup.html",
         "browser_style": true,
       },
     },
 
-    background() {
-      browser.browserAction.disable();
+    background: async function() {
+      await browser.browserAction.disable();
+      browser.test.sendMessage("browserAction-disabled");
     },
 
     files: {
       "popup.html": `<!DOCTYPE html><html><head><meta charset="utf-8"><script src="popup.js"></script></head></html>`,
       "popup.js"() {
         browser.test.fail("Should not get here");
       },
     },
   });
 
   yield extension.startup();
 
+  yield extension.awaitMessage("browserAction-disabled");
+
   const {GlobalManager, Management: {global: {browserActionFor}}} = Cu.import("resource://gre/modules/Extension.jsm", {});
 
   let ext = GlobalManager.extensionMap.get(extension.id);
   let browserAction = browserActionFor(ext);
 
   let widget = getBrowserActionWidget(extension).forWindow(window);
 
+  is(widget.node.getAttribute("disabled"), "true", "Button is disabled");
+  is(browserAction.pendingPopup, null, "Have no pending popup prior to click");
+
   // Test canceled click.
   EventUtils.synthesizeMouseAtCenter(widget.node, {type: "mousedown", button: 0}, window);
 
   is(browserAction.pendingPopup, null, "Have no pending popup");
   is(browserAction.pendingPopupTimeout, null, "Have no pending popup timeout");
 
   EventUtils.synthesizeMouseAtCenter(document.documentElement, {type: "mouseup", button: 0}, window);
 
--- a/browser/components/extensions/test/browser/browser_ext_windows_create_tabId.js
+++ b/browser/components/extensions/test/browser/browser_ext_windows_create_tabId.js
@@ -99,18 +99,16 @@ add_task(function* testWindowCreate() {
       let readyPromise = Promise.all([
         // tabs.onUpdated can be invoked between the call of windows.create and
         // the invocation of its callback/promise, so set up the listeners
         // before creating the window.
         promiseTabUpdated("http://example.com/"),
         promiseTabUpdated("http://example.org/"),
       ]);
 
-      await new Promise(resolve => setTimeout(resolve, 0));
-
       window = await browser.windows.create({url: ["http://example.com/", "http://example.org/"]});
       await readyPromise;
 
       browser.test.assertEq(2, window.tabs.length, "2 tabs were opened in new window");
       browser.test.assertEq("about:blank", window.tabs[0].url, "about:blank, page not loaded yet");
       browser.test.assertEq("about:blank", window.tabs[1].url, "about:blank, page not loaded yet");
 
       window = await browser.windows.get(window.id, {populate: true});
--- a/browser/components/extensions/test/browser/browser_ext_windows_events.js
+++ b/browser/components/extensions/test/browser/browser_ext_windows_events.js
@@ -79,16 +79,18 @@ add_task(function* testWindowsEvents() {
   let winId = yield extension.awaitMessage(`window-focus-changed`);
   is(winId, win1Id, "Got focus change event for the correct window ID.");
 
   info(`Create browser window 2`);
   let win2 = yield BrowserTestUtils.openNewBrowserWindow();
   let win2Id = yield extension.awaitMessage("window-created");
   info(`Window 2 ID: ${win2Id}`);
 
+  win2.focus();
+
   winId = yield extension.awaitMessage(`window-focus-changed`);
   is(winId, win2Id, "Got focus change event for the correct window ID.");
 
   info(`Focus browser window 1`);
   yield focusWindow(win1);
 
   winId = yield extension.awaitMessage(`window-focus-changed`);
   is(winId, win1Id, "Got focus change event for the correct window ID.");
--- a/browser/components/extensions/test/browser/head.js
+++ b/browser/components/extensions/test/browser/head.js
@@ -18,22 +18,20 @@
 var {AppConstants} = Cu.import("resource://gre/modules/AppConstants.jsm");
 var {CustomizableUI} = Cu.import("resource:///modules/CustomizableUI.jsm");
 
 // We run tests under two different configurations, from browser.ini and
 // browser-remote.ini. When running from browser-remote.ini, the tests are
 // copied to the sub-directory "test-oop-extensions", which we detect here, and
 // use to select our configuration.
 if (gTestPath.includes("test-oop-extensions")) {
-  add_task(() => {
-    return SpecialPowers.pushPrefEnv({set: [
-      ["dom.ipc.processCount", 1],
-      ["extensions.webextensions.remote", true],
-    ]});
-  });
+  SpecialPowers.pushPrefEnv({set: [
+    ["dom.ipc.processCount", 1],
+    ["extensions.webextensions.remote", true],
+  ]});
 }
 
 // Bug 1239884: Our tests occasionally hit a long GC pause at unpredictable
 // times in debug builds, which results in intermittent timeouts. Until we have
 // a better solution, we force a GC after certain strategic tests, which tend to
 // accumulate a high number of unreaped windows.
 function forceGC() {
   if (AppConstants.DEBUG) {
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -2849,18 +2849,18 @@ var E10SAccessibilityCheck = {
     let secondaryActions = [{
       label: win.gNavigatorBundle.getString("e10s.accessibilityNotice.enableAndRestart.label"),
       accessKey: win.gNavigatorBundle.getString("e10s.accessibilityNotice.enableAndRestart.accesskey"),
       callback: restartCallback,
     }];
     let options = {
       popupIconURL: "chrome://browser/skin/e10s-64@2x.png",
       learnMoreURL: Services.urlFormatter.formatURLPref("app.support.e10sAccessibilityUrl"),
+      persistent: true,
       persistWhileVisible: true,
-      hideNotNow: true,
     };
 
     notification =
       win.PopupNotifications.show(browser, "a11y_enabled_with_e10s",
                                   promptMessage, null, mainAction,
                                   secondaryActions, options);
   },
 };
--- a/browser/components/places/content/editBookmarkOverlay.js
+++ b/browser/components/places/content/editBookmarkOverlay.js
@@ -136,17 +136,27 @@ var gEditItemOverlay = {
       throw new Error("_initKeywordField called unexpectedly");
     }
 
     if (!newKeyword) {
       let entries = [];
       yield PlacesUtils.keywords.fetch({ url: this._paneInfo.uri.spec },
                                        e => entries.push(e));
       if (entries.length > 0) {
-        this._keyword = newKeyword = entries[0].keyword;
+        // We show an existing keyword if either POST data was not provided, or
+        // if the POST data is the same.
+        let existingKeyword = entries[0].keyword;
+        let postData = this._paneInfo.postData;
+        if (postData) {
+          let sameEntry = entries.find(e => e.postData === postData);
+          existingKeyword = sameEntry ? sameEntry.keyword : "";
+        }
+        if (existingKeyword) {
+          this._keyword = newKeyword = existingKeyword;
+        }
       }
     }
     this._initTextField(this._keywordField, newKeyword);
   }),
 
   _initLoadInSidebar: Task.async(function* () {
     if (!this._paneInfo.isBookmark)
       throw new Error("_initLoadInSidebar called unexpectedly");
--- a/browser/components/places/tests/browser/browser_bookmarkProperties_addKeywordForThisSearch.js
+++ b/browser/components/places/tests/browser/browser_bookmarkProperties_addKeywordForThisSearch.js
@@ -3,17 +3,17 @@
 const TEST_URL = "http://mochi.test:8888/browser/browser/components/places/tests/browser/keyword_form.html";
 
 add_task(function* () {
   yield BrowserTestUtils.withNewTab({
     gBrowser,
     url: TEST_URL,
   }, function* (browser) {
     // We must wait for the context menu code to build metadata.
-    yield openContextMenuForContentSelector(browser, 'form > input[name="search"]');
+    yield openContextMenuForContentSelector(browser, '#form1 > input[name="search"]');
 
     yield withBookmarksDialog(true, AddKeywordForSearchField, function* (dialogWin) {
       let acceptBtn = dialogWin.document.documentElement.getButton("accept");
       ok(acceptBtn.disabled, "Accept button is disabled");
 
       let promiseKeywordNotification = promiseBookmarksNotification(
         "onItemChanged", (itemId, prop, isAnno, val) => prop == "keyword" && val == "kw");
 
@@ -42,14 +42,69 @@ add_task(function* () {
       // Now check getShortcutOrURI.
       let data = yield getShortcutOrURIAndPostData("kw test");
       is(getPostDataString(data.postData), "accenti=\u00E0\u00E8\u00EC\u00F2\u00F9&search=test", "getShortcutOrURI POST data is correct");
       is(data.url, TEST_URL, "getShortcutOrURI URL is correct");
     });
   });
 });
 
+add_task(function* reopen_same_field() {
+  yield PlacesUtils.keywords.insert({
+    url: TEST_URL,
+    keyword: "kw",
+    postData: "accenti%3D%E0%E8%EC%F2%F9&search%3D%25s"
+  });
+  registerCleanupFunction(function* () {
+    yield PlacesUtils.keywords.remove("kw");
+  });
+  // Reopening on the same input field should show the existing keyword.
+  yield BrowserTestUtils.withNewTab({
+    gBrowser,
+    url: TEST_URL,
+  }, function* (browser) {
+    // We must wait for the context menu code to build metadata.
+    yield openContextMenuForContentSelector(browser, '#form1 > input[name="search"]');
+
+    yield withBookmarksDialog(true, AddKeywordForSearchField, function* (dialogWin) {
+      let acceptBtn = dialogWin.document.documentElement.getButton("accept");
+      ok(acceptBtn.disabled, "Accept button is disabled");
+
+      let elt = dialogWin.document.getElementById("editBMPanel_keywordField");
+      is(elt.value, "kw");
+    });
+  });
+});
+
+add_task(function* open_other_field() {
+  yield PlacesUtils.keywords.insert({
+    url: TEST_URL,
+    keyword: "kw2",
+    postData: "search%3D%25s"
+  });
+  registerCleanupFunction(function* () {
+    yield PlacesUtils.keywords.remove("kw2");
+  });
+  // Reopening on another field of the same page that has different postData
+  // should not show the existing keyword.
+  yield BrowserTestUtils.withNewTab({
+    gBrowser,
+    url: TEST_URL,
+  }, function* (browser) {
+    // We must wait for the context menu code to build metadata.
+    yield openContextMenuForContentSelector(browser, '#form2 > input[name="search"]');
+
+    yield withBookmarksDialog(true, AddKeywordForSearchField, function* (dialogWin) {
+      let acceptBtn = dialogWin.document.documentElement.getButton("accept");
+      ok(acceptBtn.disabled, "Accept button is disabled");
+
+      let elt = dialogWin.document.getElementById("editBMPanel_keywordField");
+      is(elt.value, "");
+    });
+  });
+});
+
 function getPostDataString(stream) {
   let sis = Cc["@mozilla.org/scriptableinputstream;1"]
               .createInstance(Ci.nsIScriptableInputStream);
   sis.init(stream);
   return sis.read(stream.available()).split("\n").pop();
 }
--- a/browser/components/places/tests/browser/keyword_form.html
+++ b/browser/components/places/tests/browser/keyword_form.html
@@ -1,13 +1,17 @@
 <!DOCTYPE HTML>
 
 <html lang="en">
 <head>
   <meta http-equiv="Content-Type" content="text/html;charset=windows-1252">
 </head>
 <body>
-  <form method="POST" action="keyword_form.html">
+  <form id="form1" method="POST" action="keyword_form.html">
     <input type="hidden" name="accenti" value="אטלעש">
     <input type="text" name="search">
   </form>
+  <form id="form2" method="POST" action="keyword_form.html">
+    <input type="hidden" name="accenti" value="שעלטא">
+    <input type="text" name="search">
+  </form>
 </body>
 </html>
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_geoprompt.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_geoprompt.js
@@ -19,19 +19,19 @@ add_task(function* test() {
       // Wait until the notification is available.
       while (!notification){
         yield new Promise(resolve => { executeSoon(resolve); });
         let notification = aWindow.PopupNotifications.getNotification("geolocation");
       }
 
       if (aPrivateMode) {
         // Make sure the notification is correctly displayed without a remember control
-        is(notification.secondaryActions.length, 0, "Secondary actions shouldn't exist (always/never remember)");
+        ok(!notification.options.checkbox.show, "Secondary actions should exist (always/never remember)");
       } else {
-        ok(notification.secondaryActions.length > 1, "Secondary actions should exist (always/never remember)");
+        ok(notification.options.checkbox.show, "Secondary actions should exist (always/never remember)");
       }
       notification.remove();
 
       aWindow.gBrowser.removeCurrentTab();
     });
   };
 
   let win = yield BrowserTestUtils.openNewBrowserWindow();
--- a/browser/components/sessionstore/SessionHistory.jsm
+++ b/browser/components/sessionstore/SessionHistory.jsm
@@ -25,18 +25,18 @@ function debug(msg) {
 /**
  * The external API exported by this module.
  */
 this.SessionHistory = Object.freeze({
   isEmpty: function (docShell) {
     return SessionHistoryInternal.isEmpty(docShell);
   },
 
-  collect: function (docShell) {
-    return SessionHistoryInternal.collect(docShell);
+  collect: function (docShell, aFromIdx = -1) {
+    return SessionHistoryInternal.collect(docShell, aFromIdx);
   },
 
   restore: function (docShell, tabData) {
     SessionHistoryInternal.restore(docShell, tabData);
   }
 });
 
 /**
@@ -64,21 +64,25 @@ var SessionHistoryInternal = {
     return uri == "about:blank" && history.count == 0;
   },
 
   /**
    * Collects session history data for a given docShell.
    *
    * @param docShell
    *        The docShell that owns the session history.
+   * @param aFromIdx
+   *        The starting local index to collect the history from.
+   * @return An object reprereseting a partial global history update.
    */
-  collect: function (docShell) {
+  collect: function (docShell, aFromIdx = -1) {
     let loadContext = docShell.QueryInterface(Ci.nsILoadContext);
     let webNavigation = docShell.QueryInterface(Ci.nsIWebNavigation);
     let history = webNavigation.sessionHistory.QueryInterface(Ci.nsISHistoryInternal);
+    let ihistory = history.QueryInterface(Ci.nsISHistory);
 
     let data = {entries: [], userContextId: loadContext.originAttributes.userContextId };
 
     if (history && history.count > 0) {
       // Loop over the transaction linked list directly so we can get the
       // persist property for each transaction.
       for (let txn = history.rootTransaction; txn; txn = txn.next) {
         let entry = this.serializeEntry(txn.sHEntry);
@@ -102,16 +106,33 @@ var SessionHistoryInternal = {
       // record it. For about:blank we explicitly want an empty array without
       // an 'index' property to denote that there are no history entries.
       if (uri != "about:blank" || (body && body.hasChildNodes())) {
         data.entries.push({ url: uri });
         data.index = 1;
       }
     }
 
+    // Check if we should discard some of the entries which didn't change
+    if (aFromIdx > -1) {
+      data.entries.splice(0, aFromIdx + 1);
+    }
+
+    // Transform the entries from local to global index space.
+    data.index += ihistory.globalIndexOffset;
+    data.fromIdx = aFromIdx + ihistory.globalIndexOffset;
+
+    // If we are not the most recent partialSHistory in our groupedSHistory, we
+    // need to make certain that we don't replace the entries from the following
+    // SHistories - so we replace only the number of entries which our SHistory
+    // takes up.
+    if (ihistory.globalIndexOffset + ihistory.count < ihistory.globalCount) {
+      data.toIdx = data.fromIdx + ihistory.count;
+    }
+
     return data;
   },
 
   /**
    * Get an object that is a serialized representation of a History entry.
    *
    * @param shEntry
    *        nsISHEntry instance
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -966,16 +966,28 @@ var SessionStoreInternal = {
         if (target.namespaceURI == NS_XUL &&
             target.localName == "browser" &&
             target.frameLoader &&
             target.permanentKey) {
           this._lastKnownFrameLoader.set(target.permanentKey, target.frameLoader);
           this.resetEpoch(target);
         }
         break;
+      case "BrowserWillChangeProcess":
+        let promise = TabStateFlusher.flush(target);
+        target.frameLoader.addProcessChangeBlockingPromise(promise);
+        break;
+      case "BrowserChangedProcess":
+        let newEpoch = 1 + Math.max(this.getCurrentEpoch(target),
+                                    this.getCurrentEpoch(aEvent.otherBrowser));
+        this.setCurrentEpoch(target, newEpoch);
+        target.messageManager.sendAsyncMessage("SessionStore:becomeActiveProcess", {
+          epoch: newEpoch
+        });
+        break;
       default:
         throw new Error(`unhandled event ${aEvent.type}?`);
     }
     this._clearRestoringWindows();
   },
 
   /**
    * Generate a unique window identifier
@@ -1032,16 +1044,18 @@ var SessionStoreInternal = {
     }
     // notification of tab add/remove/selection/show/hide
     TAB_EVENTS.forEach(function(aEvent) {
       tabbrowser.tabContainer.addEventListener(aEvent, this, true);
     }, this);
 
     // Keep track of a browser's latest frameLoader.
     aWindow.gBrowser.addEventListener("XULFrameLoaderCreated", this);
+    aWindow.gBrowser.addEventListener("BrowserChangedProcess", this);
+    aWindow.gBrowser.addEventListener("BrowserWillChangeProcess", this);
   },
 
   /**
    * Initializes a given window.
    *
    * Windows are registered as soon as they are created but we need to wait for
    * the session file to load, and the initial window's delayed startup to
    * finish before initializing a window, i.e. restoring data into it.
@@ -1286,16 +1300,18 @@ var SessionStoreInternal = {
 
     let browsers = Array.from(tabbrowser.browsers);
 
     TAB_EVENTS.forEach(function(aEvent) {
       tabbrowser.tabContainer.removeEventListener(aEvent, this, true);
     }, this);
 
     aWindow.gBrowser.removeEventListener("XULFrameLoaderCreated", this);
+    aWindow.gBrowser.removeEventListener("BrowserChangedProcess", this);
+    aWindow.gBrowser.removeEventListener("BrowserWillChangeProcess", this);
 
     let winData = this._windows[aWindow.__SSi];
 
     // Collect window data only when *not* closed during shutdown.
     if (RunState.isRunning) {
       // Grab the most recent window data. The tab data will be updated
       // once we finish flushing all of the messages from the tabs.
       let tabMap = this._collectWindowData(aWindow);
@@ -4408,16 +4424,24 @@ var SessionStoreInternal = {
    */
   startNextEpoch(browser) {
     let next = this.getCurrentEpoch(browser) + 1;
     this._browserEpochs.set(browser.permanentKey, next);
     return next;
   },
 
   /**
+   * Manually set the epoch to a given value.
+   */
+  setCurrentEpoch(aBrowser, aEpoch) {
+    this._browserEpochs.set(aBrowser.permanentKey, aEpoch);
+    return aEpoch;
+  },
+
+  /**
    * Returns the current epoch for the given <browser>. If we haven't assigned
    * a new epoch this will default to zero for new tabs.
    */
   getCurrentEpoch(browser) {
     return this._browserEpochs.get(browser.permanentKey) || 0;
   },
 
   /**
--- a/browser/components/sessionstore/TabStateCache.jsm
+++ b/browser/components/sessionstore/TabStateCache.jsm
@@ -108,25 +108,29 @@ var TabStateCacheInternal = {
   updatePartialHistoryChange: function (data, change) {
     const kLastIndex = Number.MAX_SAFE_INTEGER - 1;
 
     if (!data.history) {
       data.history = { entries: [] };
     }
 
     let history = data.history;
+    let toIdx = history.entries.length;
+    if ("toIdx" in change) {
+      toIdx = Math.min(toIdx, change.toIdx + 1);
+    }
+
     for (let key of Object.keys(change)) {
       if (key == "entries") {
         if (change.fromIdx != kLastIndex) {
-          history.entries.splice(change.fromIdx + 1);
-          while (change.entries.length) {
-            history.entries.push(change.entries.shift());
-          }
+          let start = change.fromIdx + 1;
+          history.entries.splice.apply(
+            history.entries, [start, toIdx - start].concat(change.entries));
         }
-      } else if (key != "fromIndex") {
+      } else if (key != "fromIdx" && key != "toIdx") {
         history[key] = change[key];
       }
     }
   },
 
   /**
    * Updates cached data for a given |tab| or associated |browser|.
    *
--- a/browser/components/sessionstore/content/content-sessionStore.js
+++ b/browser/components/sessionstore/content/content-sessionStore.js
@@ -109,16 +109,17 @@ var EventListener = {
  */
 var MessageListener = {
 
   MESSAGES: [
     "SessionStore:restoreHistory",
     "SessionStore:restoreTabContent",
     "SessionStore:resetRestore",
     "SessionStore:flush",
+    "SessionStore:becomeActiveProcess",
   ],
 
   init: function () {
     this.MESSAGES.forEach(m => addMessageListener(m, this));
   },
 
   receiveMessage: function ({name, data}) {
     // The docShell might be gone. Don't process messages,
@@ -143,16 +144,28 @@ var MessageListener = {
         this.restoreTabContent(data);
         break;
       case "SessionStore:resetRestore":
         gContentRestore.resetRestore();
         break;
       case "SessionStore:flush":
         this.flush(data);
         break;
+      case "SessionStore:becomeActiveProcess":
+        let shistory = docShell.QueryInterface(Ci.nsIWebNavigation).sessionHistory;
+        // Check if we are at the end of the current session history, if we are,
+        // it is safe for us to collect and transmit our session history, so
+        // transmit all of it. Otherwise, we only want to transmit our index changes,
+        // so collect from kLastIndex.
+        if (shistory.globalCount - shistory.globalIndexOffset == shistory.count) {
+          SessionHistoryListener.collect();
+        } else {
+          SessionHistoryListener.collectFrom(kLastIndex);
+        }
+        break;
       default:
         debug("received unknown message '" + name + "'");
         break;
     }
   },
 
   restoreHistory({epoch, tabData, loadArguments, isRemotenessUpdate}) {
     gContentRestore.restoreHistory(tabData, loadArguments, {
@@ -250,19 +263,22 @@ var SessionHistoryListener = {
   uninit: function () {
     let sessionHistory = docShell.QueryInterface(Ci.nsIWebNavigation).sessionHistory;
     if (sessionHistory) {
       sessionHistory.removeSHistoryListener(this);
     }
   },
 
   collect: function () {
-    this._fromIdx = kNoIndex;
+    // We want to send down a historychange even for full collects in case our
+    // session history is a partial session history, in which case we don't have
+    // enough information for a full update. collectFrom(-1) tells the collect
+    // function to collect all data avaliable in this process.
     if (docShell) {
-      MessageQueue.push("history", () => SessionHistory.collect(docShell));
+      this.collectFrom(-1);
     }
   },
 
   _fromIdx: kNoIndex,
 
   // History can grow relatively big with the nested elements, so if we don't have to, we
   // don't want to send the entire history all the time. For a simple optimization
   // we keep track of the smallest index from after any change has occured and we just send
@@ -281,25 +297,17 @@ var SessionHistoryListener = {
     }
 
     this._fromIdx = idx;
     MessageQueue.push("historychange", () => {
       if (this._fromIdx === kNoIndex) {
         return null;
       }
 
-      let history = SessionHistory.collect(docShell);
-      if (kLastIndex == idx) {
-        history.entries = [];
-      } else {
-        history.entries.splice(0, this._fromIdx + 1);
-      }
-
-      history.fromIdx = this._fromIdx;
-
+      let history = SessionHistory.collect(docShell, this._fromIdx);
       this._fromIdx = kNoIndex;
       return history;
     });
   },
 
   handleEvent(event) {
     this.collect();
   },
--- a/browser/components/sessionstore/test/browser.ini
+++ b/browser/components/sessionstore/test/browser.ini
@@ -233,8 +233,10 @@ run-if = e10s
 run-if = e10s
 [browser_background_tab_crash.js]
 run-if = e10s && crashreporter
 
 # Disabled on debug for frequent intermittent failures:
 [browser_undoCloseById.js]
 skip-if = debug
 [browser_docshell_uuid_consistency.js]
+[browser_grouped_session_store.js]
+skip-if = !e10s # GroupedSHistory is e10s-only
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/test/browser_grouped_session_store.js
@@ -0,0 +1,135 @@
+add_task(function* () {
+  const URIs = [
+    "data:text/html,1",
+    "data:text/html,2",
+    "data:text/html,3",
+    "data:text/html,4",
+    "data:text/html,5",
+  ];
+
+  const {TabStateCache} = Cu.import("resource:///modules/sessionstore/TabStateCache.jsm", {});
+  const {TabStateFlusher} = Cu.import("resource:///modules/sessionstore/TabStateFlusher.jsm", {});
+
+  yield SpecialPowers.pushPrefEnv({
+    set: [["browser.groupedhistory.enabled", true]]
+  });
+
+  // Check that the data stored in the TabStateCache is correct for the current state.
+  function* validate(browser, length, index) {
+    yield TabStateFlusher.flush(browser);
+    let {history} = TabStateCache.get(browser);
+    is(history.entries.length, length, "Lengths match");
+    for (let i = 0; i < length; ++i) {
+      is(history.entries[i].url, URIs[i], "URI at index " + i + " matches");
+    }
+    is(history.index, index, "Index matches");
+    yield ContentTask.spawn(browser, [index, length], function* ([index, length]) {
+      let webNav = content.window.QueryInterface(Ci.nsIInterfaceRequestor)
+            .getInterface(Ci.nsIWebNavigation);
+      is(webNav.canGoForward, index < length, "canGoForward is correct");
+      is(webNav.canGoBack, index > 1, "canGoBack is correct");
+    });
+  }
+
+  // Wait for a process change and then fulfil the promise.
+  function awaitProcessChange(browser) {
+    return new Promise(resolve => {
+      browser.addEventListener("BrowserChangedProcess", function bcp(e) {
+        browser.removeEventListener("BrowserChangedProcess", bcp);
+        ok(true, "The browser changed process!");
+        resolve();
+      });
+    });
+  }
+
+  // Order of events:
+  // Load [0], load [1], prerender [2], load [2], load [3]
+  // Back [2], Back [1], Forward [2], Back [0], Forward [3]
+  // Prerender [4], Back [0], Forward [2], Load [3'], Back [0].
+  yield BrowserTestUtils.withNewTab({ gBrowser, url: URIs[0] }, function* (browser1) {
+    yield* validate(browser1, 1, 1);
+
+    browser1.loadURI(URIs[1], null, null);
+    yield BrowserTestUtils.browserLoaded(browser1);
+    yield* validate(browser1, 2, 2);
+
+    // Create a new hidden prerendered tab to swap to.
+    let tab2 = gBrowser.loadOneTab(URIs[2], {
+      referrerPolicy: Ci.nsIHttpChannel.REFERRER_POLICY_DEFAULT,
+      allowThirdPartyFixup: true,
+      relatedToCurrent: true,
+      isPrerendered: true,
+    });
+    yield BrowserTestUtils.browserLoaded(tab2.linkedBrowser);
+    browser1.frameLoader.appendPartialSessionHistoryAndSwap(
+      tab2.linkedBrowser.frameLoader);
+    yield awaitProcessChange(browser1);
+    yield* validate(browser1, 3, 3);
+
+    browser1.loadURI(URIs[3], null, null);
+    yield BrowserTestUtils.browserLoaded(browser1);
+    yield* validate(browser1, 4, 4);
+
+    // In process navigate back.
+    let p = BrowserTestUtils.waitForContentEvent(browser1, "pageshow");
+    browser1.goBack();
+    yield p;
+    yield* validate(browser1, 4, 3);
+
+    // Cross process navigate back.
+    browser1.goBack();
+    yield awaitProcessChange(browser1);
+    yield* validate(browser1, 4, 2);
+
+    // Cross process navigate forward.
+    browser1.goForward();
+    yield awaitProcessChange(browser1);
+    yield* validate(browser1, 4, 3);
+
+    // Navigate across process to a page which was not recently loaded.
+    browser1.gotoIndex(0);
+    yield awaitProcessChange(browser1);
+    yield* validate(browser1, 4, 1);
+
+    // Navigate across process to a page which was not recently loaded in the other direction.
+    browser1.gotoIndex(3);
+    yield awaitProcessChange(browser1);
+    yield* validate(browser1, 4, 4);
+
+    // Create a new hidden prerendered tab to swap to
+    let tab3 = gBrowser.loadOneTab(URIs[4], {
+      referrerPolicy: Ci.nsIHttpChannel.REFERRER_POLICY_DEFAULT,
+      allowThirdPartyFixup: true,
+      relatedToCurrent: true,
+      isPrerendered: true,
+    });
+    yield BrowserTestUtils.browserLoaded(tab3.linkedBrowser);
+    browser1.frameLoader.appendPartialSessionHistoryAndSwap(
+      tab3.linkedBrowser.frameLoader);
+    yield awaitProcessChange(browser1);
+    yield* validate(browser1, 5, 5);
+
+    browser1.gotoIndex(0);
+    yield awaitProcessChange(browser1);
+    yield* validate(browser1, 5, 1);
+
+    browser1.gotoIndex(2);
+    yield awaitProcessChange(browser1);
+    yield* validate(browser1, 5, 3);
+
+    // Load a new page and make sure it throws out all of the following entries.
+    URIs[3] = "data:text/html,NEW";
+    browser1.loadURI(URIs[3]);
+    yield BrowserTestUtils.browserLoaded(browser1);
+    yield* validate(browser1, 4, 4);
+
+    browser1.gotoIndex(0);
+    yield awaitProcessChange(browser1);
+    yield* validate(browser1, 4, 1);
+
+    // XXX: This will be removed automatically by the owning tab closing in the
+    // future, but this is not supported yet.
+    gBrowser.removeTab(tab2);
+    gBrowser.removeTab(tab3);
+  });
+});
new file mode 100644
--- /dev/null
+++ b/browser/config/mozconfigs/linux64/stylo
@@ -0,0 +1,3 @@
+. "$topsrcdir/browser/config/mozconfigs/linux64/nightly"
+
+ac_add_options --enable-stylo
new file mode 100644
--- /dev/null
+++ b/browser/config/mozconfigs/linux64/stylo-debug
@@ -0,0 +1,3 @@
+. "$topsrcdir/browser/config/mozconfigs/linux64/debug"
+
+ac_add_options --enable-stylo
--- a/browser/extensions/flyweb/bootstrap.js
+++ b/browser/extensions/flyweb/bootstrap.js
@@ -153,17 +153,17 @@ const FlyWebPermissionPromptIntegration 
           notificationIcon.id = kAnchorID;
           notificationIcon.setAttribute("src",
                                         "chrome://flyweb/skin/icon-64.png");
           notificationIcon.classList.add("notification-anchor-icon");
           notificationIcon.setAttribute("role", "button");
           notificationIcon.setAttribute("aria-label",
                                         "View the publish-server request");
           notificationIcon.style.filter =
-            "url('chrome://browser/skin/filters.svg#fill')";
+            "url('chrome://global/skin/filters.svg#fill')";
           notificationIcon.style.fill = "currentcolor";
           notificationIcon.style.opacity = "0.4";
           notificationPopupBox.appendChild(notificationIcon);
         }
 
         return kAnchorID;
       },
       get message() {
--- a/browser/extensions/pdfjs/README.mozilla
+++ b/browser/extensions/pdfjs/README.mozilla
@@ -1,3 +1,3 @@
 This is the pdf.js project output, https://github.com/mozilla/pdf.js
 
-Current extension version is: 1.6.315
+Current extension version is: 1.6.329
--- a/browser/extensions/pdfjs/content/PdfJs.jsm
+++ b/browser/extensions/pdfjs/content/PdfJs.jsm
@@ -88,16 +88,17 @@ function initializeDefaultPreferences() 
   "disableRange": false,
   "disableStream": false,
   "disableAutoFetch": false,
   "disableFontFace": false,
   "disableTextLayer": false,
   "useOnlyCssZoom": false,
   "externalLinkTarget": 0,
   "enhanceTextSelection": false,
+  "renderer": "canvas",
   "renderInteractiveForms": false,
   "disablePageLabels": false
 }
 
 
   var defaultBranch = Services.prefs.getDefaultBranch(PREF_PREFIX + '.');
   var defaultValue;
   for (var key in DEFAULT_PREFERENCES) {
--- a/browser/extensions/pdfjs/content/PdfjsChromeUtils.jsm
+++ b/browser/extensions/pdfjs/content/PdfjsChromeUtils.jsm
@@ -46,16 +46,17 @@ var DEFAULT_PREFERENCES =
   "disableRange": false,
   "disableStream": false,
   "disableAutoFetch": false,
   "disableFontFace": false,
   "disableTextLayer": false,
   "useOnlyCssZoom": false,
   "externalLinkTarget": 0,
   "enhanceTextSelection": false,
+  "renderer": "canvas",
   "renderInteractiveForms": false,
   "disablePageLabels": false
 }
 
 
 var PdfjsChromeUtils = {
   // For security purposes when running remote, we restrict preferences
   // content can access.
--- a/browser/extensions/pdfjs/content/build/pdf.js
+++ b/browser/extensions/pdfjs/content/build/pdf.js
@@ -8,3530 +8,2936 @@
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 (function (root, factory) {
-  'use strict';
-  if (typeof define === 'function' && define.amd) {
-    define('pdfjs-dist/build/pdf', ['exports'], factory);
-  } else if (typeof exports !== 'undefined') {
-    factory(exports);
-  } else {
-    factory(root['pdfjsDistBuildPdf'] = {});
-  }
+ 'use strict';
+ if (typeof define === 'function' && define.amd) {
+  define('pdfjs-dist/build/pdf', ['exports'], factory);
+ } else if (typeof exports !== 'undefined') {
+  factory(exports);
+ } else {
+  factory(root['pdfjsDistBuildPdf'] = {});
+ }
 }(this, function (exports) {
-  // Use strict in our context only - users might not want it
-  'use strict';
-  var pdfjsVersion = '1.6.315';
-  var pdfjsBuild = 'a139c75';
-  var pdfjsFilePath = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : null;
-  var pdfjsLibs = {};
-  (function pdfjsWrapper() {
-    (function (root, factory) {
-      factory(root.pdfjsSharedUtil = {});
-    }(this, function (exports) {
-      var globalScope = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : this;
-      var FONT_IDENTITY_MATRIX = [
-        0.001,
-        0,
-        0,
-        0.001,
-        0,
-        0
-      ];
-      var TextRenderingMode = {
-        FILL: 0,
-        STROKE: 1,
-        FILL_STROKE: 2,
-        INVISIBLE: 3,
-        FILL_ADD_TO_PATH: 4,
-        STROKE_ADD_TO_PATH: 5,
-        FILL_STROKE_ADD_TO_PATH: 6,
-        ADD_TO_PATH: 7,
-        FILL_STROKE_MASK: 3,
-        ADD_TO_PATH_FLAG: 4
-      };
-      var ImageKind = {
-        GRAYSCALE_1BPP: 1,
-        RGB_24BPP: 2,
-        RGBA_32BPP: 3
-      };
-      var AnnotationType = {
-        TEXT: 1,
-        LINK: 2,
-        FREETEXT: 3,
-        LINE: 4,
-        SQUARE: 5,
-        CIRCLE: 6,
-        POLYGON: 7,
-        POLYLINE: 8,
-        HIGHLIGHT: 9,
-        UNDERLINE: 10,
-        SQUIGGLY: 11,
-        STRIKEOUT: 12,
-        STAMP: 13,
-        CARET: 14,
-        INK: 15,
-        POPUP: 16,
-        FILEATTACHMENT: 17,
-        SOUND: 18,
-        MOVIE: 19,
-        WIDGET: 20,
-        SCREEN: 21,
-        PRINTERMARK: 22,
-        TRAPNET: 23,
-        WATERMARK: 24,
-        THREED: 25,
-        REDACT: 26
-      };
-      var AnnotationFlag = {
-        INVISIBLE: 0x01,
-        HIDDEN: 0x02,
-        PRINT: 0x04,
-        NOZOOM: 0x08,
-        NOROTATE: 0x10,
-        NOVIEW: 0x20,
-        READONLY: 0x40,
-        LOCKED: 0x80,
-        TOGGLENOVIEW: 0x100,
-        LOCKEDCONTENTS: 0x200
-      };
-      var AnnotationFieldFlag = {
-        READONLY: 0x0000001,
-        REQUIRED: 0x0000002,
-        NOEXPORT: 0x0000004,
-        MULTILINE: 0x0001000,
-        PASSWORD: 0x0002000,
-        NOTOGGLETOOFF: 0x0004000,
-        RADIO: 0x0008000,
-        PUSHBUTTON: 0x0010000,
-        COMBO: 0x0020000,
-        EDIT: 0x0040000,
-        SORT: 0x0080000,
-        FILESELECT: 0x0100000,
-        MULTISELECT: 0x0200000,
-        DONOTSPELLCHECK: 0x0400000,
-        DONOTSCROLL: 0x0800000,
-        COMB: 0x1000000,
-        RICHTEXT: 0x2000000,
-        RADIOSINUNISON: 0x2000000,
-        COMMITONSELCHANGE: 0x4000000
-      };
-      var AnnotationBorderStyleType = {
-        SOLID: 1,
-        DASHED: 2,
-        BEVELED: 3,
-        INSET: 4,
-        UNDERLINE: 5
-      };
-      var StreamType = {
-        UNKNOWN: 0,
-        FLATE: 1,
-        LZW: 2,
-        DCT: 3,
-        JPX: 4,
-        JBIG: 5,
-        A85: 6,
-        AHX: 7,
-        CCF: 8,
-        RL: 9
-      };
-      var FontType = {
-        UNKNOWN: 0,
-        TYPE1: 1,
-        TYPE1C: 2,
-        CIDFONTTYPE0: 3,
-        CIDFONTTYPE0C: 4,
-        TRUETYPE: 5,
-        CIDFONTTYPE2: 6,
-        TYPE3: 7,
-        OPENTYPE: 8,
-        TYPE0: 9,
-        MMTYPE1: 10
-      };
-      var VERBOSITY_LEVELS = {
-        errors: 0,
-        warnings: 1,
-        infos: 5
-      };
-      // All the possible operations for an operator list.
-      var OPS = {
-        // Intentionally start from 1 so it is easy to spot bad operators that will be
-        // 0's.
-        dependency: 1,
-        setLineWidth: 2,
-        setLineCap: 3,
-        setLineJoin: 4,
-        setMiterLimit: 5,
-        setDash: 6,
-        setRenderingIntent: 7,
-        setFlatness: 8,
-        setGState: 9,
-        save: 10,
-        restore: 11,
-        transform: 12,
-        moveTo: 13,
-        lineTo: 14,
-        curveTo: 15,
-        curveTo2: 16,
-        curveTo3: 17,
-        closePath: 18,
-        rectangle: 19,
-        stroke: 20,
-        closeStroke: 21,
-        fill: 22,
-        eoFill: 23,
-        fillStroke: 24,
-        eoFillStroke: 25,
-        closeFillStroke: 26,
-        closeEOFillStroke: 27,
-        endPath: 28,
-        clip: 29,
-        eoClip: 30,
-        beginText: 31,
-        endText: 32,
-        setCharSpacing: 33,
-        setWordSpacing: 34,
-        setHScale: 35,
-        setLeading: 36,
-        setFont: 37,
-        setTextRenderingMode: 38,
-        setTextRise: 39,
-        moveText: 40,
-        setLeadingMoveText: 41,
-        setTextMatrix: 42,
-        nextLine: 43,
-        showText: 44,
-        showSpacedText: 45,
-        nextLineShowText: 46,
-        nextLineSetSpacingShowText: 47,
-        setCharWidth: 48,
-        setCharWidthAndBounds: 49,
-        setStrokeColorSpace: 50,
-        setFillColorSpace: 51,
-        setStrokeColor: 52,
-        setStrokeColorN: 53,
-        setFillColor: 54,
-        setFillColorN: 55,
-        setStrokeGray: 56,
-        setFillGray: 57,
-        setStrokeRGBColor: 58,
-        setFillRGBColor: 59,
-        setStrokeCMYKColor: 60,
-        setFillCMYKColor: 61,
-        shadingFill: 62,
-        beginInlineImage: 63,
-        beginImageData: 64,
-        endInlineImage: 65,
-        paintXObject: 66,
-        markPoint: 67,
-        markPointProps: 68,
-        beginMarkedContent: 69,
-        beginMarkedContentProps: 70,
-        endMarkedContent: 71,
-        beginCompat: 72,
-        endCompat: 73,
-        paintFormXObjectBegin: 74,
-        paintFormXObjectEnd: 75,
-        beginGroup: 76,
-        endGroup: 77,
-        beginAnnotations: 78,
-        endAnnotations: 79,
-        beginAnnotation: 80,
-        endAnnotation: 81,
-        paintJpegXObject: 82,
-        paintImageMaskXObject: 83,
-        paintImageMaskXObjectGroup: 84,
-        paintImageXObject: 85,
-        paintInlineImageXObject: 86,
-        paintInlineImageXObjectGroup: 87,
-        paintImageXObjectRepeat: 88,
-        paintImageMaskXObjectRepeat: 89,
-        paintSolidColorImageMask: 90,
-        constructPath: 91
+ 'use strict';
+ var pdfjsVersion = '1.6.329';
+ var pdfjsBuild = '9c3419d';
+ var pdfjsFilePath = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : null;
+ var pdfjsLibs = {};
+ (function pdfjsWrapper() {
+  (function (root, factory) {
+   factory(root.pdfjsSharedUtil = {});
+  }(this, function (exports) {
+   var globalScope = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : this;
+   var FONT_IDENTITY_MATRIX = [
+    0.001,
+    0,
+    0,
+    0.001,
+    0,
+    0
+   ];
+   var TextRenderingMode = {
+    FILL: 0,
+    STROKE: 1,
+    FILL_STROKE: 2,
+    INVISIBLE: 3,
+    FILL_ADD_TO_PATH: 4,
+    STROKE_ADD_TO_PATH: 5,
+    FILL_STROKE_ADD_TO_PATH: 6,
+    ADD_TO_PATH: 7,
+    FILL_STROKE_MASK: 3,
+    ADD_TO_PATH_FLAG: 4
+   };
+   var ImageKind = {
+    GRAYSCALE_1BPP: 1,
+    RGB_24BPP: 2,
+    RGBA_32BPP: 3
+   };
+   var AnnotationType = {
+    TEXT: 1,
+    LINK: 2,
+    FREETEXT: 3,
+    LINE: 4,
+    SQUARE: 5,
+    CIRCLE: 6,
+    POLYGON: 7,
+    POLYLINE: 8,
+    HIGHLIGHT: 9,
+    UNDERLINE: 10,
+    SQUIGGLY: 11,
+    STRIKEOUT: 12,
+    STAMP: 13,
+    CARET: 14,
+    INK: 15,
+    POPUP: 16,
+    FILEATTACHMENT: 17,
+    SOUND: 18,
+    MOVIE: 19,
+    WIDGET: 20,
+    SCREEN: 21,
+    PRINTERMARK: 22,
+    TRAPNET: 23,
+    WATERMARK: 24,
+    THREED: 25,
+    REDACT: 26
+   };
+   var AnnotationFlag = {
+    INVISIBLE: 0x01,
+    HIDDEN: 0x02,
+    PRINT: 0x04,
+    NOZOOM: 0x08,
+    NOROTATE: 0x10,
+    NOVIEW: 0x20,
+    READONLY: 0x40,
+    LOCKED: 0x80,
+    TOGGLENOVIEW: 0x100,
+    LOCKEDCONTENTS: 0x200
+   };
+   var AnnotationFieldFlag = {
+    READONLY: 0x0000001,
+    REQUIRED: 0x0000002,
+    NOEXPORT: 0x0000004,
+    MULTILINE: 0x0001000,
+    PASSWORD: 0x0002000,
+    NOTOGGLETOOFF: 0x0004000,
+    RADIO: 0x0008000,
+    PUSHBUTTON: 0x0010000,
+    COMBO: 0x0020000,
+    EDIT: 0x0040000,
+    SORT: 0x0080000,
+    FILESELECT: 0x0100000,
+    MULTISELECT: 0x0200000,
+    DONOTSPELLCHECK: 0x0400000,
+    DONOTSCROLL: 0x0800000,
+    COMB: 0x1000000,
+    RICHTEXT: 0x2000000,
+    RADIOSINUNISON: 0x2000000,
+    COMMITONSELCHANGE: 0x4000000
+   };
+   var AnnotationBorderStyleType = {
+    SOLID: 1,
+    DASHED: 2,
+    BEVELED: 3,
+    INSET: 4,
+    UNDERLINE: 5
+   };
+   var StreamType = {
+    UNKNOWN: 0,
+    FLATE: 1,
+    LZW: 2,
+    DCT: 3,
+    JPX: 4,
+    JBIG: 5,
+    A85: 6,
+    AHX: 7,
+    CCF: 8,
+    RL: 9
+   };
+   var FontType = {
+    UNKNOWN: 0,
+    TYPE1: 1,
+    TYPE1C: 2,
+    CIDFONTTYPE0: 3,
+    CIDFONTTYPE0C: 4,
+    TRUETYPE: 5,
+    CIDFONTTYPE2: 6,
+    TYPE3: 7,
+    OPENTYPE: 8,
+    TYPE0: 9,
+    MMTYPE1: 10
+   };
+   var VERBOSITY_LEVELS = {
+    errors: 0,
+    warnings: 1,
+    infos: 5
+   };
+   var OPS = {
+    dependency: 1,
+    setLineWidth: 2,
+    setLineCap: 3,
+    setLineJoin: 4,
+    setMiterLimit: 5,
+    setDash: 6,
+    setRenderingIntent: 7,
+    setFlatness: 8,
+    setGState: 9,
+    save: 10,
+    restore: 11,
+    transform: 12,
+    moveTo: 13,
+    lineTo: 14,
+    curveTo: 15,
+    curveTo2: 16,
+    curveTo3: 17,
+    closePath: 18,
+    rectangle: 19,
+    stroke: 20,
+    closeStroke: 21,
+    fill: 22,
+    eoFill: 23,
+    fillStroke: 24,
+    eoFillStroke: 25,
+    closeFillStroke: 26,
+    closeEOFillStroke: 27,
+    endPath: 28,
+    clip: 29,
+    eoClip: 30,
+    beginText: 31,
+    endText: 32,
+    setCharSpacing: 33,
+    setWordSpacing: 34,
+    setHScale: 35,
+    setLeading: 36,
+    setFont: 37,
+    setTextRenderingMode: 38,
+    setTextRise: 39,
+    moveText: 40,
+    setLeadingMoveText: 41,
+    setTextMatrix: 42,
+    nextLine: 43,
+    showText: 44,
+    showSpacedText: 45,
+    nextLineShowText: 46,
+    nextLineSetSpacingShowText: 47,
+    setCharWidth: 48,
+    setCharWidthAndBounds: 49,
+    setStrokeColorSpace: 50,
+    setFillColorSpace: 51,
+    setStrokeColor: 52,
+    setStrokeColorN: 53,
+    setFillColor: 54,
+    setFillColorN: 55,
+    setStrokeGray: 56,
+    setFillGray: 57,
+    setStrokeRGBColor: 58,
+    setFillRGBColor: 59,
+    setStrokeCMYKColor: 60,
+    setFillCMYKColor: 61,
+    shadingFill: 62,
+    beginInlineImage: 63,
+    beginImageData: 64,
+    endInlineImage: 65,
+    paintXObject: 66,
+    markPoint: 67,
+    markPointProps: 68,
+    beginMarkedContent: 69,
+    beginMarkedContentProps: 70,
+    endMarkedContent: 71,
+    beginCompat: 72,
+    endCompat: 73,
+    paintFormXObjectBegin: 74,
+    paintFormXObjectEnd: 75,
+    beginGroup: 76,
+    endGroup: 77,
+    beginAnnotations: 78,
+    endAnnotations: 79,
+    beginAnnotation: 80,
+    endAnnotation: 81,
+    paintJpegXObject: 82,
+    paintImageMaskXObject: 83,
+    paintImageMaskXObjectGroup: 84,
+    paintImageXObject: 85,
+    paintInlineImageXObject: 86,
+    paintInlineImageXObjectGroup: 87,
+    paintImageXObjectRepeat: 88,
+    paintImageMaskXObjectRepeat: 89,
+    paintSolidColorImageMask: 90,
+    constructPath: 91
+   };
+   var verbosity = VERBOSITY_LEVELS.warnings;
+   function setVerbosityLevel(level) {
+    verbosity = level;
+   }
+   function getVerbosityLevel() {
+    return verbosity;
+   }
+   function info(msg) {
+    if (verbosity >= VERBOSITY_LEVELS.infos) {
+     console.log('Info: ' + msg);
+    }
+   }
+   function warn(msg) {
+    if (verbosity >= VERBOSITY_LEVELS.warnings) {
+     console.log('Warning: ' + msg);
+    }
+   }
+   function deprecated(details) {
+    console.log('Deprecated API usage: ' + details);
+   }
+   function error(msg) {
+    if (verbosity >= VERBOSITY_LEVELS.errors) {
+     console.log('Error: ' + msg);
+     console.log(backtrace());
+    }
+    throw new Error(msg);
+   }
+   function backtrace() {
+    try {
+     throw new Error();
+    } catch (e) {
+     return e.stack ? e.stack.split('\n').slice(2).join('\n') : '';
+    }
+   }
+   function assert(cond, msg) {
+    if (!cond) {
+     error(msg);
+    }
+   }
+   var UNSUPPORTED_FEATURES = {
+    unknown: 'unknown',
+    forms: 'forms',
+    javaScript: 'javaScript',
+    smask: 'smask',
+    shadingPattern: 'shadingPattern',
+    font: 'font'
+   };
+   function isSameOrigin(baseUrl, otherUrl) {
+    try {
+     var base = new URL(baseUrl);
+     if (!base.origin || base.origin === 'null') {
+      return false;
+     }
+    } catch (e) {
+     return false;
+    }
+    var other = new URL(otherUrl, base);
+    return base.origin === other.origin;
+   }
+   function isValidProtocol(url) {
+    if (!url) {
+     return false;
+    }
+    switch (url.protocol) {
+    case 'http:':
+    case 'https:':
+    case 'ftp:':
+    case 'mailto:':
+    case 'tel:':
+     return true;
+    default:
+     return false;
+    }
+   }
+   function createValidAbsoluteUrl(url, baseUrl) {
+    if (!url) {
+     return null;
+    }
+    try {
+     var absoluteUrl = baseUrl ? new URL(url, baseUrl) : new URL(url);
+     if (isValidProtocol(absoluteUrl)) {
+      return absoluteUrl;
+     }
+    } catch (ex) {
+    }
+    return null;
+   }
+   function shadow(obj, prop, value) {
+    Object.defineProperty(obj, prop, {
+     value: value,
+     enumerable: true,
+     configurable: true,
+     writable: false
+    });
+    return value;
+   }
+   function getLookupTableFactory(initializer) {
+    var lookup;
+    return function () {
+     if (initializer) {
+      lookup = Object.create(null);
+      initializer(lookup);
+      initializer = null;
+     }
+     return lookup;
+    };
+   }
+   var PasswordResponses = {
+    NEED_PASSWORD: 1,
+    INCORRECT_PASSWORD: 2
+   };
+   var PasswordException = function PasswordExceptionClosure() {
+    function PasswordException(msg, code) {
+     this.name = 'PasswordException';
+     this.message = msg;
+     this.code = code;
+    }
+    PasswordException.prototype = new Error();
+    PasswordException.constructor = PasswordException;
+    return PasswordException;
+   }();
+   var UnknownErrorException = function UnknownErrorExceptionClosure() {
+    function UnknownErrorException(msg, details) {
+     this.name = 'UnknownErrorException';
+     this.message = msg;
+     this.details = details;
+    }
+    UnknownErrorException.prototype = new Error();
+    UnknownErrorException.constructor = UnknownErrorException;
+    return UnknownErrorException;
+   }();
+   var InvalidPDFException = function InvalidPDFExceptionClosure() {
+    function InvalidPDFException(msg) {
+     this.name = 'InvalidPDFException';
+     this.message = msg;
+    }
+    InvalidPDFException.prototype = new Error();
+    InvalidPDFException.constructor = InvalidPDFException;
+    return InvalidPDFException;
+   }();
+   var MissingPDFException = function MissingPDFExceptionClosure() {
+    function MissingPDFException(msg) {
+     this.name = 'MissingPDFException';
+     this.message = msg;
+    }
+    MissingPDFException.prototype = new Error();
+    MissingPDFException.constructor = MissingPDFException;
+    return MissingPDFException;
+   }();
+   var UnexpectedResponseException = function UnexpectedResponseExceptionClosure() {
+    function UnexpectedResponseException(msg, status) {
+     this.name = 'UnexpectedResponseException';
+     this.message = msg;
+     this.status = status;
+    }
+    UnexpectedResponseException.prototype = new Error();
+    UnexpectedResponseException.constructor = UnexpectedResponseException;
+    return UnexpectedResponseException;
+   }();
+   var NotImplementedException = function NotImplementedExceptionClosure() {
+    function NotImplementedException(msg) {
+     this.message = msg;
+    }
+    NotImplementedException.prototype = new Error();
+    NotImplementedException.prototype.name = 'NotImplementedException';
+    NotImplementedException.constructor = NotImplementedException;
+    return NotImplementedException;
+   }();
+   var MissingDataException = function MissingDataExceptionClosure() {
+    function MissingDataException(begin, end) {
+     this.begin = begin;
+     this.end = end;
+     this.message = 'Missing data [' + begin + ', ' + end + ')';
+    }
+    MissingDataException.prototype = new Error();
+    MissingDataException.prototype.name = 'MissingDataException';
+    MissingDataException.constructor = MissingDataException;
+    return MissingDataException;
+   }();
+   var XRefParseException = function XRefParseExceptionClosure() {
+    function XRefParseException(msg) {
+     this.message = msg;
+    }
+    XRefParseException.prototype = new Error();
+    XRefParseException.prototype.name = 'XRefParseException';
+    XRefParseException.constructor = XRefParseException;
+    return XRefParseException;
+   }();
+   var NullCharactersRegExp = /\x00/g;
+   function removeNullCharacters(str) {
+    if (typeof str !== 'string') {
+     warn('The argument for removeNullCharacters must be a string.');
+     return str;
+    }
+    return str.replace(NullCharactersRegExp, '');
+   }
+   function bytesToString(bytes) {
+    assert(bytes !== null && typeof bytes === 'object' && bytes.length !== undefined, 'Invalid argument for bytesToString');
+    var length = bytes.length;
+    var MAX_ARGUMENT_COUNT = 8192;
+    if (length < MAX_ARGUMENT_COUNT) {
+     return String.fromCharCode.apply(null, bytes);
+    }
+    var strBuf = [];
+    for (var i = 0; i < length; i += MAX_ARGUMENT_COUNT) {
+     var chunkEnd = Math.min(i + MAX_ARGUMENT_COUNT, length);
+     var chunk = bytes.subarray(i, chunkEnd);
+     strBuf.push(String.fromCharCode.apply(null, chunk));
+    }
+    return strBuf.join('');
+   }
+   function stringToBytes(str) {
+    assert(typeof str === 'string', 'Invalid argument for stringToBytes');
+    var length = str.length;
+    var bytes = new Uint8Array(length);
+    for (var i = 0; i < length; ++i) {
+     bytes[i] = str.charCodeAt(i) & 0xFF;
+    }
+    return bytes;
+   }
+   function arrayByteLength(arr) {
+    if (arr.length !== undefined) {
+     return arr.length;
+    }
+    assert(arr.byteLength !== undefined);
+    return arr.byteLength;
+   }
+   function arraysToBytes(arr) {
+    if (arr.length === 1 && arr[0] instanceof Uint8Array) {
+     return arr[0];
+    }
+    var resultLength = 0;
+    var i, ii = arr.length;
+    var item, itemLength;
+    for (i = 0; i < ii; i++) {
+     item = arr[i];
+     itemLength = arrayByteLength(item);
+     resultLength += itemLength;
+    }
+    var pos = 0;
+    var data = new Uint8Array(resultLength);
+    for (i = 0; i < ii; i++) {
+     item = arr[i];
+     if (!(item instanceof Uint8Array)) {
+      if (typeof item === 'string') {
+       item = stringToBytes(item);
+      } else {
+       item = new Uint8Array(item);
+      }
+     }
+     itemLength = item.byteLength;
+     data.set(item, pos);
+     pos += itemLength;
+    }
+    return data;
+   }
+   function string32(value) {
+    return String.fromCharCode(value >> 24 & 0xff, value >> 16 & 0xff, value >> 8 & 0xff, value & 0xff);
+   }
+   function log2(x) {
+    var n = 1, i = 0;
+    while (x > n) {
+     n <<= 1;
+     i++;
+    }
+    return i;
+   }
+   function readInt8(data, start) {
+    return data[start] << 24 >> 24;
+   }
+   function readUint16(data, offset) {
+    return data[offset] << 8 | data[offset + 1];
+   }
+   function readUint32(data, offset) {
+    return (data[offset] << 24 | data[offset + 1] << 16 | data[offset + 2] << 8 | data[offset + 3]) >>> 0;
+   }
+   function isLittleEndian() {
+    var buffer8 = new Uint8Array(2);
+    buffer8[0] = 1;
+    var buffer16 = new Uint16Array(buffer8.buffer);
+    return buffer16[0] === 1;
+   }
+   function isEvalSupported() {
+    try {
+     new Function('');
+     return true;
+    } catch (e) {
+     return false;
+    }
+   }
+   var IDENTITY_MATRIX = [
+    1,
+    0,
+    0,
+    1,
+    0,
+    0
+   ];
+   var Util = function UtilClosure() {
+    function Util() {
+    }
+    var rgbBuf = [
+     'rgb(',
+     0,
+     ',',
+     0,
+     ',',
+     0,
+     ')'
+    ];
+    Util.makeCssRgb = function Util_makeCssRgb(r, g, b) {
+     rgbBuf[1] = r;
+     rgbBuf[3] = g;
+     rgbBuf[5] = b;
+     return rgbBuf.join('');
+    };
+    Util.transform = function Util_transform(m1, m2) {
+     return [
+      m1[0] * m2[0] + m1[2] * m2[1],
+      m1[1] * m2[0] + m1[3] * m2[1],
+      m1[0] * m2[2] + m1[2] * m2[3],
+      m1[1] * m2[2] + m1[3] * m2[3],
+      m1[0] * m2[4] + m1[2] * m2[5] + m1[4],
+      m1[1] * m2[4] + m1[3] * m2[5] + m1[5]
+     ];
+    };
+    Util.applyTransform = function Util_applyTransform(p, m) {
+     var xt = p[0] * m[0] + p[1] * m[2] + m[4];
+     var yt = p[0] * m[1] + p[1] * m[3] + m[5];
+     return [
+      xt,
+      yt
+     ];
+    };
+    Util.applyInverseTransform = function Util_applyInverseTransform(p, m) {
+     var d = m[0] * m[3] - m[1] * m[2];
+     var xt = (p[0] * m[3] - p[1] * m[2] + m[2] * m[5] - m[4] * m[3]) / d;
+     var yt = (-p[0] * m[1] + p[1] * m[0] + m[4] * m[1] - m[5] * m[0]) / d;
+     return [
+      xt,
+      yt
+     ];
+    };
+    Util.getAxialAlignedBoundingBox = function Util_getAxialAlignedBoundingBox(r, m) {
+     var p1 = Util.applyTransform(r, m);
+     var p2 = Util.applyTransform(r.slice(2, 4), m);
+     var p3 = Util.applyTransform([
+      r[0],
+      r[3]
+     ], m);
+     var p4 = Util.applyTransform([
+      r[2],
+      r[1]
+     ], m);
+     return [
+      Math.min(p1[0], p2[0], p3[0], p4[0]),
+      Math.min(p1[1], p2[1], p3[1], p4[1]),
+      Math.max(p1[0], p2[0], p3[0], p4[0]),
+      Math.max(p1[1], p2[1], p3[1], p4[1])
+     ];
+    };
+    Util.inverseTransform = function Util_inverseTransform(m) {
+     var d = m[0] * m[3] - m[1] * m[2];
+     return [
+      m[3] / d,
+      -m[1] / d,
+      -m[2] / d,
+      m[0] / d,
+      (m[2] * m[5] - m[4] * m[3]) / d,
+      (m[4] * m[1] - m[5] * m[0]) / d
+     ];
+    };
+    Util.apply3dTransform = function Util_apply3dTransform(m, v) {
+     return [
+      m[0] * v[0] + m[1] * v[1] + m[2] * v[2],
+      m[3] * v[0] + m[4] * v[1] + m[5] * v[2],
+      m[6] * v[0] + m[7] * v[1] + m[8] * v[2]
+     ];
+    };
+    Util.singularValueDecompose2dScale = function Util_singularValueDecompose2dScale(m) {
+     var transpose = [
+      m[0],
+      m[2],
+      m[1],
+      m[3]
+     ];
+     var a = m[0] * transpose[0] + m[1] * transpose[2];
+     var b = m[0] * transpose[1] + m[1] * transpose[3];
+     var c = m[2] * transpose[0] + m[3] * transpose[2];
+     var d = m[2] * transpose[1] + m[3] * transpose[3];
+     var first = (a + d) / 2;
+     var second = Math.sqrt((a + d) * (a + d) - 4 * (a * d - c * b)) / 2;
+     var sx = first + second || 1;
+     var sy = first - second || 1;
+     return [
+      Math.sqrt(sx),
+      Math.sqrt(sy)
+     ];
+    };
+    Util.normalizeRect = function Util_normalizeRect(rect) {
+     var r = rect.slice(0);
+     if (rect[0] > rect[2]) {
+      r[0] = rect[2];
+      r[2] = rect[0];
+     }
+     if (rect[1] > rect[3]) {
+      r[1] = rect[3];
+      r[3] = rect[1];
+     }
+     return r;
+    };
+    Util.intersect = function Util_intersect(rect1, rect2) {
+     function compare(a, b) {
+      return a - b;
+     }
+     var orderedX = [
+       rect1[0],
+       rect1[2],
+       rect2[0],
+       rect2[2]
+      ].sort(compare), orderedY = [
+       rect1[1],
+       rect1[3],
+       rect2[1],
+       rect2[3]
+      ].sort(compare), result = [];
+     rect1 = Util.normalizeRect(rect1);
+     rect2 = Util.normalizeRect(rect2);
+     if (orderedX[0] === rect1[0] && orderedX[1] === rect2[0] || orderedX[0] === rect2[0] && orderedX[1] === rect1[0]) {
+      result[0] = orderedX[1];
+      result[2] = orderedX[2];
+     } else {
+      return false;
+     }
+     if (orderedY[0] === rect1[1] && orderedY[1] === rect2[1] || orderedY[0] === rect2[1] && orderedY[1] === rect1[1]) {
+      result[1] = orderedY[1];
+      result[3] = orderedY[2];
+     } else {
+      return false;
+     }
+     return result;
+    };
+    Util.sign = function Util_sign(num) {
+     return num < 0 ? -1 : 1;
+    };
+    var ROMAN_NUMBER_MAP = [
+     '',
+     'C',
+     'CC',
+     'CCC',
+     'CD',
+     'D',
+     'DC',
+     'DCC',
+     'DCCC',
+     'CM',
+     '',
+     'X',
+     'XX',
+     'XXX',
+     'XL',
+     'L',
+     'LX',
+     'LXX',
+     'LXXX',
+     'XC',
+     '',
+     'I',
+     'II',
+     'III',
+     'IV',
+     'V',
+     'VI',
+     'VII',
+     'VIII',
+     'IX'
+    ];
+    Util.toRoman = function Util_toRoman(number, lowerCase) {
+     assert(isInt(number) && number > 0, 'The number should be a positive integer.');
+     var pos, romanBuf = [];
+     while (number >= 1000) {
+      number -= 1000;
+      romanBuf.push('M');
+     }
+     pos = number / 100 | 0;
+     number %= 100;
+     romanBuf.push(ROMAN_NUMBER_MAP[pos]);
+     pos = number / 10 | 0;
+     number %= 10;
+     romanBuf.push(ROMAN_NUMBER_MAP[10 + pos]);
+     romanBuf.push(ROMAN_NUMBER_MAP[20 + number]);
+     var romanStr = romanBuf.join('');
+     return lowerCase ? romanStr.toLowerCase() : romanStr;
+    };
+    Util.appendToArray = function Util_appendToArray(arr1, arr2) {
+     Array.prototype.push.apply(arr1, arr2);
+    };
+    Util.prependToArray = function Util_prependToArray(arr1, arr2) {
+     Array.prototype.unshift.apply(arr1, arr2);
+    };
+    Util.extendObj = function extendObj(obj1, obj2) {
+     for (var key in obj2) {
+      obj1[key] = obj2[key];
+     }
+    };
+    Util.getInheritableProperty = function Util_getInheritableProperty(dict, name, getArray) {
+     while (dict && !dict.has(name)) {
+      dict = dict.get('Parent');
+     }
+     if (!dict) {
+      return null;
+     }
+     return getArray ? dict.getArray(name) : dict.get(name);
+    };
+    Util.inherit = function Util_inherit(sub, base, prototype) {
+     sub.prototype = Object.create(base.prototype);
+     sub.prototype.constructor = sub;
+     for (var prop in prototype) {
+      sub.prototype[prop] = prototype[prop];
+     }
+    };
+    Util.loadScript = function Util_loadScript(src, callback) {
+     var script = document.createElement('script');
+     var loaded = false;
+     script.setAttribute('src', src);
+     if (callback) {
+      script.onload = function () {
+       if (!loaded) {
+        callback();
+       }
+       loaded = true;
       };
-      var verbosity = VERBOSITY_LEVELS.warnings;
-      function setVerbosityLevel(level) {
-        verbosity = level;
-      }
-      function getVerbosityLevel() {
-        return verbosity;
-      }
-      // A notice for devs. These are good for things that are helpful to devs, such
-      // as warning that Workers were disabled, which is important to devs but not
-      // end users.
-      function info(msg) {
-        if (verbosity >= VERBOSITY_LEVELS.infos) {
-          console.log('Info: ' + msg);
-        }
-      }
-      // Non-fatal warnings.
-      function warn(msg) {
-        if (verbosity >= VERBOSITY_LEVELS.warnings) {
-          console.log('Warning: ' + msg);
-        }
-      }
-      // Deprecated API function -- display regardless of the PDFJS.verbosity setting.
-      function deprecated(details) {
-        console.log('Deprecated API usage: ' + details);
-      }
-      // Fatal errors that should trigger the fallback UI and halt execution by
-      // throwing an exception.
-      function error(msg) {
-        if (verbosity >= VERBOSITY_LEVELS.errors) {
-          console.log('Error: ' + msg);
-          console.log(backtrace());
-        }
-        throw new Error(msg);
-      }
-      function backtrace() {
-        try {
-          throw new Error();
-        } catch (e) {
-          return e.stack ? e.stack.split('\n').slice(2).join('\n') : '';
-        }
-      }
-      function assert(cond, msg) {
-        if (!cond) {
-          error(msg);
-        }
-      }
-      var UNSUPPORTED_FEATURES = {
-        unknown: 'unknown',
-        forms: 'forms',
-        javaScript: 'javaScript',
-        smask: 'smask',
-        shadingPattern: 'shadingPattern',
-        font: 'font'
-      };
-      // Checks if URLs have the same origin. For non-HTTP based URLs, returns false.
-      function isSameOrigin(baseUrl, otherUrl) {
-        try {
-          var base = new URL(baseUrl);
-          if (!base.origin || base.origin === 'null') {
-            return false;
-          }
-        } // non-HTTP url
-        catch (e) {
-          return false;
-        }
-        var other = new URL(otherUrl, base);
-        return base.origin === other.origin;
-      }
-      // Checks if URLs use one of the whitelisted protocols, e.g. to avoid XSS.
-      function isValidProtocol(url) {
-        if (!url) {
-          return false;
-        }
-        switch (url.protocol) {
-        case 'http:':
-        case 'https:':
-        case 'ftp:':
-        case 'mailto:':
-        case 'tel:':
-          return true;
-        default:
-          return false;
-        }
-      }
-      /**
-       * Attempts to create a valid absolute URL (utilizing `isValidProtocol`).
-       * @param {URL|string} url - An absolute, or relative, URL.
-       * @param {URL|string} baseUrl - An absolute URL.
-       * @returns Either a valid {URL}, or `null` otherwise.
-       */
-      function createValidAbsoluteUrl(url, baseUrl) {
-        if (!url) {
-          return null;
-        }
-        try {
-          var absoluteUrl = baseUrl ? new URL(url, baseUrl) : new URL(url);
-          if (isValidProtocol(absoluteUrl)) {
-            return absoluteUrl;
-          }
-        } catch (ex) {
-        }
-        return null;
-      }
-      function shadow(obj, prop, value) {
-        Object.defineProperty(obj, prop, {
-          value: value,
-          enumerable: true,
-          configurable: true,
-          writable: false
-        });
-        return value;
-      }
-      function getLookupTableFactory(initializer) {
-        var lookup;
-        return function () {
-          if (initializer) {
-            lookup = Object.create(null);
-            initializer(lookup);
-            initializer = null;
-          }
-          return lookup;
-        };
-      }
-      var PasswordResponses = {
-        NEED_PASSWORD: 1,
-        INCORRECT_PASSWORD: 2
-      };
-      var PasswordException = function PasswordExceptionClosure() {
-        function PasswordException(msg, code) {
-          this.name = 'PasswordException';
-          this.message = msg;
-          this.code = code;
-        }
-        PasswordException.prototype = new Error();
-        PasswordException.constructor = PasswordException;
-        return PasswordException;
-      }();
-      var UnknownErrorException = function UnknownErrorExceptionClosure() {
-        function UnknownErrorException(msg, details) {
-          this.name = 'UnknownErrorException';
-          this.message = msg;
-          this.details = details;
-        }
-        UnknownErrorException.prototype = new Error();
-        UnknownErrorException.constructor = UnknownErrorException;
-        return UnknownErrorException;
-      }();
-      var InvalidPDFException = function InvalidPDFExceptionClosure() {
-        function InvalidPDFException(msg) {
-          this.name = 'InvalidPDFException';
-          this.message = msg;
-        }
-        InvalidPDFException.prototype = new Error();
-        InvalidPDFException.constructor = InvalidPDFException;
-        return InvalidPDFException;
-      }();
-      var MissingPDFException = function MissingPDFExceptionClosure() {
-        function MissingPDFException(msg) {
-          this.name = 'MissingPDFException';
-          this.message = msg;
-        }
-        MissingPDFException.prototype = new Error();
-        MissingPDFException.constructor = MissingPDFException;
-        return MissingPDFException;
-      }();
-      var UnexpectedResponseException = function UnexpectedResponseExceptionClosure() {
-        function UnexpectedResponseException(msg, status) {
-          this.name = 'UnexpectedResponseException';
-          this.message = msg;
-          this.status = status;
-        }
-        UnexpectedResponseException.prototype = new Error();
-        UnexpectedResponseException.constructor = UnexpectedResponseException;
-        return UnexpectedResponseException;
-      }();
-      var NotImplementedException = function NotImplementedExceptionClosure() {
-        function NotImplementedException(msg) {
-          this.message = msg;
-        }
-        NotImplementedException.prototype = new Error();
-        NotImplementedException.prototype.name = 'NotImplementedException';
-        NotImplementedException.constructor = NotImplementedException;
-        return NotImplementedException;
-      }();
-      var MissingDataException = function MissingDataExceptionClosure() {
-        function MissingDataException(begin, end) {
-          this.begin = begin;
-          this.end = end;
-          this.message = 'Missing data [' + begin + ', ' + end + ')';
-        }
-        MissingDataException.prototype = new Error();
-        MissingDataException.prototype.name = 'MissingDataException';
-        MissingDataException.constructor = MissingDataException;
-        return MissingDataException;
-      }();
-      var XRefParseException = function XRefParseExceptionClosure() {
-        function XRefParseException(msg) {
-          this.message = msg;
-        }
-        XRefParseException.prototype = new Error();
-        XRefParseException.prototype.name = 'XRefParseException';
-        XRefParseException.constructor = XRefParseException;
-        return XRefParseException;
-      }();
-      var NullCharactersRegExp = /\x00/g;
-      function removeNullCharacters(str) {
-        if (typeof str !== 'string') {
-          warn('The argument for removeNullCharacters must be a string.');
-          return str;
-        }
-        return str.replace(NullCharactersRegExp, '');
-      }
-      function bytesToString(bytes) {
-        assert(bytes !== null && typeof bytes === 'object' && bytes.length !== undefined, 'Invalid argument for bytesToString');
-        var length = bytes.length;
-        var MAX_ARGUMENT_COUNT = 8192;
-        if (length < MAX_ARGUMENT_COUNT) {
-          return String.fromCharCode.apply(null, bytes);
-        }
-        var strBuf = [];
-        for (var i = 0; i < length; i += MAX_ARGUMENT_COUNT) {
-          var chunkEnd = Math.min(i + MAX_ARGUMENT_COUNT, length);
-          var chunk = bytes.subarray(i, chunkEnd);
-          strBuf.push(String.fromCharCode.apply(null, chunk));
-        }
-        return strBuf.join('');
-      }
-      function stringToBytes(str) {
-        assert(typeof str === 'string', 'Invalid argument for stringToBytes');
-        var length = str.length;
-        var bytes = new Uint8Array(length);
-        for (var i = 0; i < length; ++i) {
-          bytes[i] = str.charCodeAt(i) & 0xFF;
-        }
-        return bytes;
-      }
-      /**
-       * Gets length of the array (Array, Uint8Array, or string) in bytes.
-       * @param {Array|Uint8Array|string} arr
-       * @returns {number}
-       */
-      function arrayByteLength(arr) {
-        if (arr.length !== undefined) {
-          return arr.length;
-        }
-        assert(arr.byteLength !== undefined);
-        return arr.byteLength;
-      }
-      /**
-       * Combines array items (arrays) into single Uint8Array object.
-       * @param {Array} arr - the array of the arrays (Array, Uint8Array, or string).
-       * @returns {Uint8Array}
-       */
-      function arraysToBytes(arr) {
-        // Shortcut: if first and only item is Uint8Array, return it.
-        if (arr.length === 1 && arr[0] instanceof Uint8Array) {
-          return arr[0];
-        }
-        var resultLength = 0;
-        var i, ii = arr.length;
-        var item, itemLength;
-        for (i = 0; i < ii; i++) {
-          item = arr[i];
-          itemLength = arrayByteLength(item);
-          resultLength += itemLength;
-        }
-        var pos = 0;
-        var data = new Uint8Array(resultLength);
-        for (i = 0; i < ii; i++) {
-          item = arr[i];
-          if (!(item instanceof Uint8Array)) {
-            if (typeof item === 'string') {
-              item = stringToBytes(item);
-            } else {
-              item = new Uint8Array(item);
-            }
-          }
-          itemLength = item.byteLength;
-          data.set(item, pos);
-          pos += itemLength;
-        }
-        return data;
-      }
-      function string32(value) {
-        return String.fromCharCode(value >> 24 & 0xff, value >> 16 & 0xff, value >> 8 & 0xff, value & 0xff);
-      }
-      function log2(x) {
-        var n = 1, i = 0;
-        while (x > n) {
-          n <<= 1;
-          i++;
-        }
-        return i;
-      }
-      function readInt8(data, start) {
-        return data[start] << 24 >> 24;
-      }
-      function readUint16(data, offset) {
-        return data[offset] << 8 | data[offset + 1];
-      }
-      function readUint32(data, offset) {
-        return (data[offset] << 24 | data[offset + 1] << 16 | data[offset + 2] << 8 | data[offset + 3]) >>> 0;
-      }
-      // Lazy test the endianness of the platform
-      // NOTE: This will be 'true' for simulated TypedArrays
-      function isLittleEndian() {
-        var buffer8 = new Uint8Array(2);
-        buffer8[0] = 1;
-        var buffer16 = new Uint16Array(buffer8.buffer);
-        return buffer16[0] === 1;
-      }
-      // Checks if it's possible to eval JS expressions.
-      function isEvalSupported() {
-        try {
-          new Function('');
-          return true;
-        } catch (e) {
-          return false;
-        }
-      }
-      var IDENTITY_MATRIX = [
-        1,
-        0,
-        0,
-        1,
-        0,
-        0
+     }
+     document.getElementsByTagName('head')[0].appendChild(script);
+    };
+    return Util;
+   }();
+   var PageViewport = function PageViewportClosure() {
+    function PageViewport(viewBox, scale, rotation, offsetX, offsetY, dontFlip) {
+     this.viewBox = viewBox;
+     this.scale = scale;
+     this.rotation = rotation;
+     this.offsetX = offsetX;
+     this.offsetY = offsetY;
+     var centerX = (viewBox[2] + viewBox[0]) / 2;
+     var centerY = (viewBox[3] + viewBox[1]) / 2;
+     var rotateA, rotateB, rotateC, rotateD;
+     rotation = rotation % 360;
+     rotation = rotation < 0 ? rotation + 360 : rotation;
+     switch (rotation) {
+     case 180:
+      rotateA = -1;
+      rotateB = 0;
+      rotateC = 0;
+      rotateD = 1;
+      break;
+     case 90:
+      rotateA = 0;
+      rotateB = 1;
+      rotateC = 1;
+      rotateD = 0;
+      break;
+     case 270:
+      rotateA = 0;
+      rotateB = -1;
+      rotateC = -1;
+      rotateD = 0;
+      break;
+     default:
+      rotateA = 1;
+      rotateB = 0;
+      rotateC = 0;
+      rotateD = -1;
+      break;
+     }
+     if (dontFlip) {
+      rotateC = -rotateC;
+      rotateD = -rotateD;
+     }
+     var offsetCanvasX, offsetCanvasY;
+     var width, height;
+     if (rotateA === 0) {
+      offsetCanvasX = Math.abs(centerY - viewBox[1]) * scale + offsetX;
+      offsetCanvasY = Math.abs(centerX - viewBox[0]) * scale + offsetY;
+      width = Math.abs(viewBox[3] - viewBox[1]) * scale;
+      height = Math.abs(viewBox[2] - viewBox[0]) * scale;
+     } else {
+      offsetCanvasX = Math.abs(centerX - viewBox[0]) * scale + offsetX;
+      offsetCanvasY = Math.abs(centerY - viewBox[1]) * scale + offsetY;
+      width = Math.abs(viewBox[2] - viewBox[0]) * scale;
+      height = Math.abs(viewBox[3] - viewBox[1]) * scale;
+     }
+     this.transform = [
+      rotateA * scale,
+      rotateB * scale,
+      rotateC * scale,
+      rotateD * scale,
+      offsetCanvasX - rotateA * scale * centerX - rotateC * scale * centerY,
+      offsetCanvasY - rotateB * scale * centerX - rotateD * scale * centerY
+     ];
+     this.width = width;
+     this.height = height;
+     this.fontScale = scale;
+    }
+    PageViewport.prototype = {
+     clone: function PageViewPort_clone(args) {
+      args = args || {};
+      var scale = 'scale' in args ? args.scale : this.scale;
+      var rotation = 'rotation' in args ? args.rotation : this.rotation;
+      return new PageViewport(this.viewBox.slice(), scale, rotation, this.offsetX, this.offsetY, args.dontFlip);
+     },
+     convertToViewportPoint: function PageViewport_convertToViewportPoint(x, y) {
+      return Util.applyTransform([
+       x,
+       y
+      ], this.transform);
+     },
+     convertToViewportRectangle: function PageViewport_convertToViewportRectangle(rect) {
+      var tl = Util.applyTransform([
+       rect[0],
+       rect[1]
+      ], this.transform);
+      var br = Util.applyTransform([
+       rect[2],
+       rect[3]
+      ], this.transform);
+      return [
+       tl[0],
+       tl[1],
+       br[0],
+       br[1]
       ];
-      var Util = function UtilClosure() {
-        function Util() {
+     },
+     convertToPdfPoint: function PageViewport_convertToPdfPoint(x, y) {
+      return Util.applyInverseTransform([
+       x,
+       y
+      ], this.transform);
+     }
+    };
+    return PageViewport;
+   }();
+   var PDFStringTranslateTable = [
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0x2D8,
+    0x2C7,
+    0x2C6,
+    0x2D9,
+    0x2DD,
+    0x2DB,
+    0x2DA,
+    0x2DC,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0x2022,
+    0x2020,
+    0x2021,
+    0x2026,
+    0x2014,
+    0x2013,
+    0x192,
+    0x2044,
+    0x2039,
+    0x203A,
+    0x2212,
+    0x2030,
+    0x201E,
+    0x201C,
+    0x201D,
+    0x2018,
+    0x2019,
+    0x201A,
+    0x2122,
+    0xFB01,
+    0xFB02,
+    0x141,
+    0x152,
+    0x160,
+    0x178,
+    0x17D,
+    0x131,
+    0x142,
+    0x153,
+    0x161,
+    0x17E,
+    0,
+    0x20AC
+   ];
+   function stringToPDFString(str) {
+    var i, n = str.length, strBuf = [];
+    if (str[0] === '\xFE' && str[1] === '\xFF') {
+     for (i = 2; i < n; i += 2) {
+      strBuf.push(String.fromCharCode(str.charCodeAt(i) << 8 | str.charCodeAt(i + 1)));
+     }
+    } else {
+     for (i = 0; i < n; ++i) {
+      var code = PDFStringTranslateTable[str.charCodeAt(i)];
+      strBuf.push(code ? String.fromCharCode(code) : str.charAt(i));
+     }
+    }
+    return strBuf.join('');
+   }
+   function stringToUTF8String(str) {
+    return decodeURIComponent(escape(str));
+   }
+   function utf8StringToString(str) {
+    return unescape(encodeURIComponent(str));
+   }
+   function isEmptyObj(obj) {
+    for (var key in obj) {
+     return false;
+    }
+    return true;
+   }
+   function isBool(v) {
+    return typeof v === 'boolean';
+   }
+   function isInt(v) {
+    return typeof v === 'number' && (v | 0) === v;
+   }
+   function isNum(v) {
+    return typeof v === 'number';
+   }
+   function isString(v) {
+    return typeof v === 'string';
+   }
+   function isArray(v) {
+    return v instanceof Array;
+   }
+   function isArrayBuffer(v) {
+    return typeof v === 'object' && v !== null && v.byteLength !== undefined;
+   }
+   function isSpace(ch) {
+    return ch === 0x20 || ch === 0x09 || ch === 0x0D || ch === 0x0A;
+   }
+   function createPromiseCapability() {
+    var capability = {};
+    capability.promise = new Promise(function (resolve, reject) {
+     capability.resolve = resolve;
+     capability.reject = reject;
+    });
+    return capability;
+   }
+   (function PromiseClosure() {
+    if (globalScope.Promise) {
+     if (typeof globalScope.Promise.all !== 'function') {
+      globalScope.Promise.all = function (iterable) {
+       var count = 0, results = [], resolve, reject;
+       var promise = new globalScope.Promise(function (resolve_, reject_) {
+        resolve = resolve_;
+        reject = reject_;
+       });
+       iterable.forEach(function (p, i) {
+        count++;
+        p.then(function (result) {
+         results[i] = result;
+         count--;
+         if (count === 0) {
+          resolve(results);
+         }
+        }, reject);
+       });
+       if (count === 0) {
+        resolve(results);
+       }
+       return promise;
+      };
+     }
+     if (typeof globalScope.Promise.resolve !== 'function') {
+      globalScope.Promise.resolve = function (value) {
+       return new globalScope.Promise(function (resolve) {
+        resolve(value);
+       });
+      };
+     }
+     if (typeof globalScope.Promise.reject !== 'function') {
+      globalScope.Promise.reject = function (reason) {
+       return new globalScope.Promise(function (resolve, reject) {
+        reject(reason);
+       });
+      };
+     }
+     if (typeof globalScope.Promise.prototype.catch !== 'function') {
+      globalScope.Promise.prototype.catch = function (onReject) {
+       return globalScope.Promise.prototype.then(undefined, onReject);
+      };
+     }
+     return;
+    }
+    throw new Error('DOM Promise is not present');
+   }());
+   var StatTimer = function StatTimerClosure() {
+    function rpad(str, pad, length) {
+     while (str.length < length) {
+      str += pad;
+     }
+     return str;
+    }
+    function StatTimer() {
+     this.started = Object.create(null);
+     this.times = [];
+     this.enabled = true;
+    }
+    StatTimer.prototype = {
+     time: function StatTimer_time(name) {
+      if (!this.enabled) {
+       return;
+      }
+      if (name in this.started) {
+       warn('Timer is already running for ' + name);
+      }
+      this.started[name] = Date.now();
+     },
+     timeEnd: function StatTimer_timeEnd(name) {
+      if (!this.enabled) {
+       return;
+      }
+      if (!(name in this.started)) {
+       warn('Timer has not been started for ' + name);
+      }
+      this.times.push({
+       'name': name,
+       'start': this.started[name],
+       'end': Date.now()
+      });
+      delete this.started[name];
+     },
+     toString: function StatTimer_toString() {
+      var i, ii;
+      var times = this.times;
+      var out = '';
+      var longest = 0;
+      for (i = 0, ii = times.length; i < ii; ++i) {
+       var name = times[i]['name'];
+       if (name.length > longest) {
+        longest = name.length;
+       }
+      }
+      for (i = 0, ii = times.length; i < ii; ++i) {
+       var span = times[i];
+       var duration = span.end - span.start;
+       out += rpad(span['name'], ' ', longest) + ' ' + duration + 'ms\n';
+      }
+      return out;
+     }
+    };
+    return StatTimer;
+   }();
+   var createBlob = function createBlob(data, contentType) {
+    if (typeof Blob !== 'undefined') {
+     return new Blob([data], { type: contentType });
+    }
+    warn('The "Blob" constructor is not supported.');
+   };
+   var createObjectURL = function createObjectURLClosure() {
+    var digits = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
+    return function createObjectURL(data, contentType, forceDataSchema) {
+     if (!forceDataSchema && typeof URL !== 'undefined' && URL.createObjectURL) {
+      var blob = createBlob(data, contentType);
+      return URL.createObjectURL(blob);
+     }
+     var buffer = 'data:' + contentType + ';base64,';
+     for (var i = 0, ii = data.length; i < ii; i += 3) {
+      var b1 = data[i] & 0xFF;
+      var b2 = data[i + 1] & 0xFF;
+      var b3 = data[i + 2] & 0xFF;
+      var d1 = b1 >> 2, d2 = (b1 & 3) << 4 | b2 >> 4;
+      var d3 = i + 1 < ii ? (b2 & 0xF) << 2 | b3 >> 6 : 64;
+      var d4 = i + 2 < ii ? b3 & 0x3F : 64;
+      buffer += digits[d1] + digits[d2] + digits[d3] + digits[d4];
+     }
+     return buffer;
+    };
+   }();
+   function MessageHandler(sourceName, targetName, comObj) {
+    this.sourceName = sourceName;
+    this.targetName = targetName;
+    this.comObj = comObj;
+    this.callbackIndex = 1;
+    this.postMessageTransfers = true;
+    var callbacksCapabilities = this.callbacksCapabilities = Object.create(null);
+    var ah = this.actionHandler = Object.create(null);
+    this._onComObjOnMessage = function messageHandlerComObjOnMessage(event) {
+     var data = event.data;
+     if (data.targetName !== this.sourceName) {
+      return;
+     }
+     if (data.isReply) {
+      var callbackId = data.callbackId;
+      if (data.callbackId in callbacksCapabilities) {
+       var callback = callbacksCapabilities[callbackId];
+       delete callbacksCapabilities[callbackId];
+       if ('error' in data) {
+        callback.reject(data.error);
+       } else {
+        callback.resolve(data.data);
+       }
+      } else {
+       error('Cannot resolve callback ' + callbackId);
+      }
+     } else if (data.action in ah) {
+      var action = ah[data.action];
+      if (data.callbackId) {
+       var sourceName = this.sourceName;
+       var targetName = data.sourceName;
+       Promise.resolve().then(function () {
+        return action[0].call(action[1], data.data);
+       }).then(function (result) {
+        comObj.postMessage({
+         sourceName: sourceName,
+         targetName: targetName,
+         isReply: true,
+         callbackId: data.callbackId,
+         data: result
+        });
+       }, function (reason) {
+        if (reason instanceof Error) {
+         reason = reason + '';
         }
-        var rgbBuf = [
-          'rgb(',
-          0,
-          ',',
-          0,
-          ',',
-          0,
-          ')'
-        ];
-        // makeCssRgb() can be called thousands of times. Using |rgbBuf| avoids
-        // creating many intermediate strings.
-        Util.makeCssRgb = function Util_makeCssRgb(r, g, b) {
-          rgbBuf[1] = r;
-          rgbBuf[3] = g;
-          rgbBuf[5] = b;
-          return rgbBuf.join('');
-        };
-        // Concatenates two transformation matrices together and returns the result.
-        Util.transform = function Util_transform(m1, m2) {
-          return [
-            m1[0] * m2[0] + m1[2] * m2[1],
-            m1[1] * m2[0] + m1[3] * m2[1],
-            m1[0] * m2[2] + m1[2] * m2[3],
-            m1[1] * m2[2] + m1[3] * m2[3],
-            m1[0] * m2[4] + m1[2] * m2[5] + m1[4],
-            m1[1] * m2[4] + m1[3] * m2[5] + m1[5]
-          ];
-        };
-        // For 2d affine transforms
-        Util.applyTransform = function Util_applyTransform(p, m) {
-          var xt = p[0] * m[0] + p[1] * m[2] + m[4];
-          var yt = p[0] * m[1] + p[1] * m[3] + m[5];
-          return [
-            xt,
-            yt
-          ];
-        };
-        Util.applyInverseTransform = function Util_applyInverseTransform(p, m) {
-          var d = m[0] * m[3] - m[1] * m[2];
-          var xt = (p[0] * m[3] - p[1] * m[2] + m[2] * m[5] - m[4] * m[3]) / d;
-          var yt = (-p[0] * m[1] + p[1] * m[0] + m[4] * m[1] - m[5] * m[0]) / d;
-          return [
-            xt,
-            yt
-          ];
-        };
-        // Applies the transform to the rectangle and finds the minimum axially
-        // aligned bounding box.
-        Util.getAxialAlignedBoundingBox = function Util_getAxialAlignedBoundingBox(r, m) {
-          var p1 = Util.applyTransform(r, m);
-          var p2 = Util.applyTransform(r.slice(2, 4), m);
-          var p3 = Util.applyTransform([
-            r[0],
-            r[3]
-          ], m);
-          var p4 = Util.applyTransform([
-            r[2],
-            r[1]
-          ], m);
-          return [
-            Math.min(p1[0], p2[0], p3[0], p4[0]),
-            Math.min(p1[1], p2[1], p3[1], p4[1]),
-            Math.max(p1[0], p2[0], p3[0], p4[0]),
-            Math.max(p1[1], p2[1], p3[1], p4[1])
-          ];
-        };
-        Util.inverseTransform = function Util_inverseTransform(m) {
-          var d = m[0] * m[3] - m[1] * m[2];
-          return [
-            m[3] / d,
-            -m[1] / d,
-            -m[2] / d,
-            m[0] / d,
-            (m[2] * m[5] - m[4] * m[3]) / d,
-            (m[4] * m[1] - m[5] * m[0]) / d
-          ];
-        };
-        // Apply a generic 3d matrix M on a 3-vector v:
-        //   | a b c |   | X |
-        //   | d e f | x | Y |
-        //   | g h i |   | Z |
-        // M is assumed to be serialized as [a,b,c,d,e,f,g,h,i],
-        // with v as [X,Y,Z]
-        Util.apply3dTransform = function Util_apply3dTransform(m, v) {
-          return [
-            m[0] * v[0] + m[1] * v[1] + m[2] * v[2],
-            m[3] * v[0] + m[4] * v[1] + m[5] * v[2],
-            m[6] * v[0] + m[7] * v[1] + m[8] * v[2]
-          ];
-        };
-        // This calculation uses Singular Value Decomposition.
-        // The SVD can be represented with formula A = USV. We are interested in the
-        // matrix S here because it represents the scale values.
-        Util.singularValueDecompose2dScale = function Util_singularValueDecompose2dScale(m) {
-          var transpose = [
-            m[0],
-            m[2],
-            m[1],
-            m[3]
-          ];
-          // Multiply matrix m with its transpose.
-          var a = m[0] * transpose[0] + m[1] * transpose[2];
-          var b = m[0] * transpose[1] + m[1] * transpose[3];
-          var c = m[2] * transpose[0] + m[3] * transpose[2];
-          var d = m[2] * transpose[1] + m[3] * transpose[3];
-          // Solve the second degree polynomial to get roots.
-          var first = (a + d) / 2;
-          var second = Math.sqrt((a + d) * (a + d) - 4 * (a * d - c * b)) / 2;
-          var sx = first + second || 1;
-          var sy = first - second || 1;
-          // Scale values are the square roots of the eigenvalues.
-          return [
-            Math.sqrt(sx),
-            Math.sqrt(sy)
-          ];
-        };
-        // Normalize rectangle rect=[x1, y1, x2, y2] so that (x1,y1) < (x2,y2)
-        // For coordinate systems whose origin lies in the bottom-left, this
-        // means normalization to (BL,TR) ordering. For systems with origin in the
-        // top-left, this means (TL,BR) ordering.
-        Util.normalizeRect = function Util_normalizeRect(rect) {
-          var r = rect.slice(0);
-          // clone rect
-          if (rect[0] > rect[2]) {
-            r[0] = rect[2];
-            r[2] = rect[0];
-          }
-          if (rect[1] > rect[3]) {
-            r[1] = rect[3];
-            r[3] = rect[1];
+        comObj.postMessage({
+         sourceName: sourceName,
+         targetName: targetName,
+         isReply: true,
+         callbackId: data.callbackId,
+         error: reason
+        });
+       });
+      } else {
+       action[0].call(action[1], data.data);
+      }
+     } else {
+      error('Unknown action from worker: ' + data.action);
+     }
+    }.bind(this);
+    comObj.addEventListener('message', this._onComObjOnMessage);
+   }
+   MessageHandler.prototype = {
+    on: function messageHandlerOn(actionName, handler, scope) {
+     var ah = this.actionHandler;
+     if (ah[actionName]) {
+      error('There is already an actionName called "' + actionName + '"');
+     }
+     ah[actionName] = [
+      handler,
+      scope
+     ];
+    },
+    send: function messageHandlerSend(actionName, data, transfers) {
+     var message = {
+      sourceName: this.sourceName,
+      targetName: this.targetName,
+      action: actionName,
+      data: data
+     };
+     this.postMessage(message, transfers);
+    },
+    sendWithPromise: function messageHandlerSendWithPromise(actionName, data, transfers) {
+     var callbackId = this.callbackIndex++;
+     var message = {
+      sourceName: this.sourceName,
+      targetName: this.targetName,
+      action: actionName,
+      data: data,
+      callbackId: callbackId
+     };
+     var capability = createPromiseCapability();
+     this.callbacksCapabilities[callbackId] = capability;
+     try {
+      this.postMessage(message, transfers);
+     } catch (e) {
+      capability.reject(e);
+     }
+     return capability.promise;
+    },
+    postMessage: function (message, transfers) {
+     if (transfers && this.postMessageTransfers) {
+      this.comObj.postMessage(message, transfers);
+     } else {
+      this.comObj.postMessage(message);
+     }
+    },
+    destroy: function () {
+     this.comObj.removeEventListener('message', this._onComObjOnMessage);
+    }
+   };
+   function loadJpegStream(id, imageUrl, objs) {
+    var img = new Image();
+    img.onload = function loadJpegStream_onloadClosure() {
+     objs.resolve(id, img);
+    };
+    img.onerror = function loadJpegStream_onerrorClosure() {
+     objs.resolve(id, null);
+     warn('Error during JPEG image loading');
+    };
+    img.src = imageUrl;
+   }
+   exports.FONT_IDENTITY_MATRIX = FONT_IDENTITY_MATRIX;
+   exports.IDENTITY_MATRIX = IDENTITY_MATRIX;
+   exports.OPS = OPS;
+   exports.VERBOSITY_LEVELS = VERBOSITY_LEVELS;
+   exports.UNSUPPORTED_FEATURES = UNSUPPORTED_FEATURES;
+   exports.AnnotationBorderStyleType = AnnotationBorderStyleType;
+   exports.AnnotationFieldFlag = AnnotationFieldFlag;
+   exports.AnnotationFlag = AnnotationFlag;
+   exports.AnnotationType = AnnotationType;
+   exports.FontType = FontType;
+   exports.ImageKind = ImageKind;
+   exports.InvalidPDFException = InvalidPDFException;
+   exports.MessageHandler = MessageHandler;
+   exports.MissingDataException = MissingDataException;
+   exports.MissingPDFException = MissingPDFException;
+   exports.NotImplementedException = NotImplementedException;
+   exports.PageViewport = PageViewport;
+   exports.PasswordException = PasswordException;
+   exports.PasswordResponses = PasswordResponses;
+   exports.StatTimer = StatTimer;
+   exports.StreamType = StreamType;
+   exports.TextRenderingMode = TextRenderingMode;
+   exports.UnexpectedResponseException = UnexpectedResponseException;
+   exports.UnknownErrorException = UnknownErrorException;
+   exports.Util = Util;
+   exports.XRefParseException = XRefParseException;
+   exports.arrayByteLength = arrayByteLength;
+   exports.arraysToBytes = arraysToBytes;
+   exports.assert = assert;
+   exports.bytesToString = bytesToString;
+   exports.createBlob = createBlob;
+   exports.createPromiseCapability = createPromiseCapability;
+   exports.createObjectURL = createObjectURL;
+   exports.deprecated = deprecated;
+   exports.error = error;
+   exports.getLookupTableFactory = getLookupTableFactory;
+   exports.getVerbosityLevel = getVerbosityLevel;
+   exports.globalScope = globalScope;
+   exports.info = info;
+   exports.isArray = isArray;
+   exports.isArrayBuffer = isArrayBuffer;
+   exports.isBool = isBool;
+   exports.isEmptyObj = isEmptyObj;
+   exports.isInt = isInt;
+   exports.isNum = isNum;
+   exports.isString = isString;
+   exports.isSpace = isSpace;
+   exports.isSameOrigin = isSameOrigin;
+   exports.createValidAbsoluteUrl = createValidAbsoluteUrl;
+   exports.isLittleEndian = isLittleEndian;
+   exports.isEvalSupported = isEvalSupported;
+   exports.loadJpegStream = loadJpegStream;
+   exports.log2 = log2;
+   exports.readInt8 = readInt8;
+   exports.readUint16 = readUint16;
+   exports.readUint32 = readUint32;
+   exports.removeNullCharacters = removeNullCharacters;
+   exports.setVerbosityLevel = setVerbosityLevel;
+   exports.shadow = shadow;
+   exports.string32 = string32;
+   exports.stringToBytes = stringToBytes;
+   exports.stringToPDFString = stringToPDFString;
+   exports.stringToUTF8String = stringToUTF8String;
+   exports.utf8StringToString = utf8StringToString;
+   exports.warn = warn;
+  }));
+  (function (root, factory) {
+   factory(root.pdfjsDisplayDOMUtils = {}, root.pdfjsSharedUtil);
+  }(this, function (exports, sharedUtil) {
+   var removeNullCharacters = sharedUtil.removeNullCharacters;
+   var warn = sharedUtil.warn;
+   var deprecated = sharedUtil.deprecated;
+   var createValidAbsoluteUrl = sharedUtil.createValidAbsoluteUrl;
+   var CustomStyle = function CustomStyleClosure() {
+    var prefixes = [
+     'ms',
+     'Moz',
+     'Webkit',
+     'O'
+    ];
+    var _cache = Object.create(null);
+    function CustomStyle() {
+    }
+    CustomStyle.getProp = function get(propName, element) {
+     if (arguments.length === 1 && typeof _cache[propName] === 'string') {
+      return _cache[propName];
+     }
+     element = element || document.documentElement;
+     var style = element.style, prefixed, uPropName;
+     if (typeof style[propName] === 'string') {
+      return _cache[propName] = propName;
+     }
+     uPropName = propName.charAt(0).toUpperCase() + propName.slice(1);
+     for (var i = 0, l = prefixes.length; i < l; i++) {
+      prefixed = prefixes[i] + uPropName;
+      if (typeof style[prefixed] === 'string') {
+       return _cache[propName] = prefixed;
+      }
+     }
+     return _cache[propName] = 'undefined';
+    };
+    CustomStyle.setProp = function set(propName, element, str) {
+     var prop = this.getProp(propName);
+     if (prop !== 'undefined') {
+      element.style[prop] = str;
+     }
+    };
+    return CustomStyle;
+   }();
+   var hasCanvasTypedArrays;
+   hasCanvasTypedArrays = function () {
+    return true;
+   };
+   var LinkTarget = {
+    NONE: 0,
+    SELF: 1,
+    BLANK: 2,
+    PARENT: 3,
+    TOP: 4
+   };
+   var LinkTargetStringMap = [
+    '',
+    '_self',
+    '_blank',
+    '_parent',
+    '_top'
+   ];
+   function addLinkAttributes(link, params) {
+    var url = params && params.url;
+    link.href = link.title = url ? removeNullCharacters(url) : '';
+    if (url) {
+     var target = params.target;
+     if (typeof target === 'undefined') {
+      target = getDefaultSetting('externalLinkTarget');
+     }
+     link.target = LinkTargetStringMap[target];
+     var rel = params.rel;
+     if (typeof rel === 'undefined') {
+      rel = getDefaultSetting('externalLinkRel');
+     }
+     link.rel = rel;
+    }
+   }
+   function getFilenameFromUrl(url) {
+    var anchor = url.indexOf('#');
+    var query = url.indexOf('?');
+    var end = Math.min(anchor > 0 ? anchor : url.length, query > 0 ? query : url.length);
+    return url.substring(url.lastIndexOf('/', end) + 1, end);
+   }
+   function getDefaultSetting(id) {
+    var globalSettings = sharedUtil.globalScope.PDFJS;
+    switch (id) {
+    case 'pdfBug':
+     return globalSettings ? globalSettings.pdfBug : false;
+    case 'disableAutoFetch':
+     return globalSettings ? globalSettings.disableAutoFetch : false;
+    case 'disableStream':
+     return globalSettings ? globalSettings.disableStream : false;
+    case 'disableRange':
+     return globalSettings ? globalSettings.disableRange : false;
+    case 'disableFontFace':
+     return globalSettings ? globalSettings.disableFontFace : false;
+    case 'disableCreateObjectURL':
+     return globalSettings ? globalSettings.disableCreateObjectURL : false;
+    case 'disableWebGL':
+     return globalSettings ? globalSettings.disableWebGL : true;
+    case 'cMapUrl':
+     return globalSettings ? globalSettings.cMapUrl : null;
+    case 'cMapPacked':
+     return globalSettings ? globalSettings.cMapPacked : false;
+    case 'postMessageTransfers':
+     return globalSettings ? globalSettings.postMessageTransfers : true;
+    case 'workerSrc':
+     return globalSettings ? globalSettings.workerSrc : null;
+    case 'disableWorker':
+     return globalSettings ? globalSettings.disableWorker : false;
+    case 'maxImageSize':
+     return globalSettings ? globalSettings.maxImageSize : -1;
+    case 'imageResourcesPath':
+     return globalSettings ? globalSettings.imageResourcesPath : '';
+    case 'isEvalSupported':
+     return globalSettings ? globalSettings.isEvalSupported : true;
+    case 'externalLinkTarget':
+     if (!globalSettings) {
+      return LinkTarget.NONE;
+     }
+     switch (globalSettings.externalLinkTarget) {
+     case LinkTarget.NONE:
+     case LinkTarget.SELF:
+     case LinkTarget.BLANK:
+     case LinkTarget.PARENT:
+     case LinkTarget.TOP:
+      return globalSettings.externalLinkTarget;
+     }
+     warn('PDFJS.externalLinkTarget is invalid: ' + globalSettings.externalLinkTarget);
+     globalSettings.externalLinkTarget = LinkTarget.NONE;
+     return LinkTarget.NONE;
+    case 'externalLinkRel':
+     return globalSettings ? globalSettings.externalLinkRel : 'noreferrer';
+    case 'enableStats':
+     return !!(globalSettings && globalSettings.enableStats);
+    default:
+     throw new Error('Unknown default setting: ' + id);
+    }
+   }
+   function isExternalLinkTargetSet() {
+    var externalLinkTarget = getDefaultSetting('externalLinkTarget');
+    switch (externalLinkTarget) {
+    case LinkTarget.NONE:
+     return false;
+    case LinkTarget.SELF:
+    case LinkTarget.BLANK:
+    case LinkTarget.PARENT:
+    case LinkTarget.TOP:
+     return true;
+    }
+   }
+   function isValidUrl(url, allowRelative) {
+    deprecated('isValidUrl(), please use createValidAbsoluteUrl() instead.');
+    var baseUrl = allowRelative ? 'http://example.com' : null;
+    return createValidAbsoluteUrl(url, baseUrl) !== null;
+   }
+   exports.CustomStyle = CustomStyle;
+   exports.addLinkAttributes = addLinkAttributes;
+   exports.isExternalLinkTargetSet = isExternalLinkTargetSet;
+   exports.isValidUrl = isValidUrl;
+   exports.getFilenameFromUrl = getFilenameFromUrl;
+   exports.LinkTarget = LinkTarget;
+   exports.hasCanvasTypedArrays = hasCanvasTypedArrays;
+   exports.getDefaultSetting = getDefaultSetting;
+  }));
+  (function (root, factory) {
+   factory(root.pdfjsDisplayFontLoader = {}, root.pdfjsSharedUtil);
+  }(this, function (exports, sharedUtil) {
+   var assert = sharedUtil.assert;
+   var bytesToString = sharedUtil.bytesToString;
+   var string32 = sharedUtil.string32;
+   var shadow = sharedUtil.shadow;
+   var warn = sharedUtil.warn;
+   function FontLoader(docId) {
+    this.docId = docId;
+    this.styleElement = null;
+   }
+   FontLoader.prototype = {
+    insertRule: function fontLoaderInsertRule(rule) {
+     var styleElement = this.styleElement;
+     if (!styleElement) {
+      styleElement = this.styleElement = document.createElement('style');
+      styleElement.id = 'PDFJS_FONT_STYLE_TAG_' + this.docId;
+      document.documentElement.getElementsByTagName('head')[0].appendChild(styleElement);
+     }
+     var styleSheet = styleElement.sheet;
+     styleSheet.insertRule(rule, styleSheet.cssRules.length);
+    },
+    clear: function fontLoaderClear() {
+     var styleElement = this.styleElement;
+     if (styleElement) {
+      styleElement.parentNode.removeChild(styleElement);
+      styleElement = this.styleElement = null;
+     }
+    }
+   };
+   FontLoader.prototype.bind = function fontLoaderBind(fonts, callback) {
+    for (var i = 0, ii = fonts.length; i < ii; i++) {
+     var font = fonts[i];
+     if (font.attached) {
+      continue;
+     }
+     font.attached = true;
+     var rule = font.createFontFaceRule();
+     if (rule) {
+      this.insertRule(rule);
+     }
+    }
+    setTimeout(callback);
+   };
+   var IsEvalSupportedCached = {
+    get value() {
+     return shadow(this, 'value', sharedUtil.isEvalSupported());
+    }
+   };
+   var FontFaceObject = function FontFaceObjectClosure() {
+    function FontFaceObject(translatedData, options) {
+     this.compiledGlyphs = Object.create(null);
+     for (var i in translatedData) {
+      this[i] = translatedData[i];
+     }
+     this.options = options;
+    }
+    FontFaceObject.prototype = {
+     createNativeFontFace: function FontFaceObject_createNativeFontFace() {
+      throw new Error('Not implemented: createNativeFontFace');
+     },
+     createFontFaceRule: function FontFaceObject_createFontFaceRule() {
+      if (!this.data) {
+       return null;
+      }
+      if (this.options.disableFontFace) {
+       this.disableFontFace = true;
+       return null;
+      }
+      var data = bytesToString(new Uint8Array(this.data));
+      var fontName = this.loadedName;
+      var url = 'url(data:' + this.mimetype + ';base64,' + btoa(data) + ');';
+      var rule = '@font-face { font-family:"' + fontName + '";src:' + url + '}';
+      if (this.options.fontRegistry) {
+       this.options.fontRegistry.registerFont(this, url);
+      }
+      return rule;
+     },
+     getPathGenerator: function FontFaceObject_getPathGenerator(objs, character) {
+      if (!(character in this.compiledGlyphs)) {
+       var cmds = objs.get(this.loadedName + '_path_' + character);
+       var current, i, len;
+       if (this.options.isEvalSupported && IsEvalSupportedCached.value) {
+        var args, js = '';
+        for (i = 0, len = cmds.length; i < len; i++) {
+         current = cmds[i];
+         if (current.args !== undefined) {
+          args = current.args.join(',');
+         } else {
+          args = '';
+         }
+         js += 'c.' + current.cmd + '(' + args + ');\n';
+        }
+        this.compiledGlyphs[character] = new Function('c', 'size', js);
+       } else {
+        this.compiledGlyphs[character] = function (c, size) {
+         for (i = 0, len = cmds.length; i < len; i++) {
+          current = cmds[i];
+          if (current.cmd === 'scale') {
+           current.args = [
+            size,
+            -size
+           ];
           }
-          return r;
-        };
-        // Returns a rectangle [x1, y1, x2, y2] corresponding to the
-        // intersection of rect1 and rect2. If no intersection, returns 'false'
-        // The rectangle coordinates of rect1, rect2 should be [x1, y1, x2, y2]
-        Util.intersect = function Util_intersect(rect1, rect2) {
-          function compare(a, b) {
-            return a - b;
-          }
-          // Order points along the axes
-          var orderedX = [
-              rect1[0],
-              rect1[2],
-              rect2[0],
-              rect2[2]
-            ].sort(compare), orderedY = [
-              rect1[1],
-              rect1[3],
-              rect2[1],
-              rect2[3]
-            ].sort(compare), result = [];
-          rect1 = Util.normalizeRect(rect1);
-          rect2 = Util.normalizeRect(rect2);
-          // X: first and second points belong to different rectangles?
-          if (orderedX[0] === rect1[0] && orderedX[1] === rect2[0] || orderedX[0] === rect2[0] && orderedX[1] === rect1[0]) {
-            // Intersection must be between second and third points
-            result[0] = orderedX[1];
-            result[2] = orderedX[2];
-          } else {
-            return false;
-          }
-          // Y: first and second points belong to different rectangles?
-          if (orderedY[0] === rect1[1] && orderedY[1] === rect2[1] || orderedY[0] === rect2[1] && orderedY[1] === rect1[1]) {
-            // Intersection must be between second and third points
-            result[1] = orderedY[1];
-            result[3] = orderedY[2];
-          } else {
-            return false;
-          }
-          return result;
-        };
-        Util.sign = function Util_sign(num) {
-          return num < 0 ? -1 : 1;
-        };
-        var ROMAN_NUMBER_MAP = [
-          '',
-          'C',
-          'CC',
-          'CCC',
-          'CD',
-          'D',
-          'DC',
-          'DCC',
-          'DCCC',
-          'CM',
-          '',
-          'X',
-          'XX',
-          'XXX',
-          'XL',
-          'L',
-          'LX',
-          'LXX',
-          'LXXX',
-          'XC',
-          '',
-          'I',
-          'II',
-          'III',
-          'IV',
-          'V',
-          'VI',
-          'VII',
-          'VIII',
-          'IX'
-        ];
-        /**
-         * Converts positive integers to (upper case) Roman numerals.
-         * @param {integer} number - The number that should be converted.
-         * @param {boolean} lowerCase - Indicates if the result should be converted
-         *   to lower case letters. The default is false.
-         * @return {string} The resulting Roman number.
-         */
-        Util.toRoman = function Util_toRoman(number, lowerCase) {
-          assert(isInt(number) && number > 0, 'The number should be a positive integer.');
-          var pos, romanBuf = [];
-          // Thousands
-          while (number >= 1000) {
-            number -= 1000;
-            romanBuf.push('M');
-          }
-          // Hundreds
-          pos = number / 100 | 0;
-          number %= 100;
-          romanBuf.push(ROMAN_NUMBER_MAP[pos]);
-          // Tens
-          pos = number / 10 | 0;
-          number %= 10;
-          romanBuf.push(ROMAN_NUMBER_MAP[10 + pos]);
-          // Ones
-          romanBuf.push(ROMAN_NUMBER_MAP[20 + number]);
-          var romanStr = romanBuf.join('');
-          return lowerCase ? romanStr.toLowerCase() : romanStr;
-        };
-        Util.appendToArray = function Util_appendToArray(arr1, arr2) {
-          Array.prototype.push.apply(arr1, arr2);
-        };
-        Util.prependToArray = function Util_prependToArray(arr1, arr2) {
-          Array.prototype.unshift.apply(arr1, arr2);
-        };
-        Util.extendObj = function extendObj(obj1, obj2) {
-          for (var key in obj2) {
-            obj1[key] = obj2[key];
-          }
-        };
-        Util.getInheritableProperty = function Util_getInheritableProperty(dict, name, getArray) {
-          while (dict && !dict.has(name)) {
-            dict = dict.get('Parent');
-          }
-          if (!dict) {
-            return null;
-          }
-          return getArray ? dict.getArray(name) : dict.get(name);
-        };
-        Util.inherit = function Util_inherit(sub, base, prototype) {
-          sub.prototype = Object.create(base.prototype);
-          sub.prototype.constructor = sub;
-          for (var prop in prototype) {
-            sub.prototype[prop] = prototype[prop];
-          }
-        };
-        Util.loadScript = function Util_loadScript(src, callback) {
-          var script = document.createElement('script');
-          var loaded = false;
-          script.setAttribute('src', src);
-          if (callback) {
-            script.onload = function () {
-              if (!loaded) {
-                callback();
-              }
-              loaded = true;
-            };
-          }
-          document.getElementsByTagName('head')[0].appendChild(script);
+          c[current.cmd].apply(c, current.args);
+         }
         };
-        return Util;
-      }();
-      /**
-       * PDF page viewport created based on scale, rotation and offset.
-       * @class
-       * @alias PageViewport
-       */
-      var PageViewport = function PageViewportClosure() {
-        /**
-         * @constructor
-         * @private
-         * @param viewBox {Array} xMin, yMin, xMax and yMax coordinates.
-         * @param scale {number} scale of the viewport.
-         * @param rotation {number} rotations of the viewport in degrees.
-         * @param offsetX {number} offset X
-         * @param offsetY {number} offset Y
-         * @param dontFlip {boolean} if true, axis Y will not be flipped.
-         */
-        function PageViewport(viewBox, scale, rotation, offsetX, offsetY, dontFlip) {
-          this.viewBox = viewBox;
-          this.scale = scale;
-          this.rotation = rotation;
-          this.offsetX = offsetX;
-          this.offsetY = offsetY;
-          // creating transform to convert pdf coordinate system to the normal
-          // canvas like coordinates taking in account scale and rotation
-          var centerX = (viewBox[2] + viewBox[0]) / 2;
-          var centerY = (viewBox[3] + viewBox[1]) / 2;
-          var rotateA, rotateB, rotateC, rotateD;
-          rotation = rotation % 360;
-          rotation = rotation < 0 ? rotation + 360 : rotation;
-          switch (rotation) {
-          case 180:
-            rotateA = -1;
-            rotateB = 0;
-            rotateC = 0;
-            rotateD = 1;
-            break;
-          case 90:
-            rotateA = 0;
-            rotateB = 1;
-            rotateC = 1;
-            rotateD = 0;
-            break;
-          case 270:
-            rotateA = 0;
-            rotateB = -1;
-            rotateC = -1;
-            rotateD = 0;
-            break;
-          //case 0:
-          default:
-            rotateA = 1;
-            rotateB = 0;
-            rotateC = 0;
-            rotateD = -1;
-            break;
-          }
-          if (dontFlip) {
-            rotateC = -rotateC;
-            rotateD = -rotateD;
-          }
-          var offsetCanvasX, offsetCanvasY;
-          var width, height;
-          if (rotateA === 0) {
-            offsetCanvasX = Math.abs(centerY - viewBox[1]) * scale + offsetX;
-            offsetCanvasY = Math.abs(centerX - viewBox[0]) * scale + offsetY;
-            width = Math.abs(viewBox[3] - viewBox[1]) * scale;
-            height = Math.abs(viewBox[2] - viewBox[0]) * scale;
-          } else {
-            offsetCanvasX = Math.abs(centerX - viewBox[0]) * scale + offsetX;
-            offsetCanvasY = Math.abs(centerY - viewBox[1]) * scale + offsetY;
-            width = Math.abs(viewBox[2] - viewBox[0]) * scale;
-            height = Math.abs(viewBox[3] - viewBox[1]) * scale;
-          }
-          // creating transform for the following operations:
-          // translate(-centerX, -centerY), rotate and flip vertically,
-          // scale, and translate(offsetCanvasX, offsetCanvasY)
-          this.transform = [
-            rotateA * scale,
-            rotateB * scale,
-            rotateC * scale,
-            rotateD * scale,
-            offsetCanvasX - rotateA * scale * centerX - rotateC * scale * centerY,
-            offsetCanvasY - rotateB * scale * centerX - rotateD * scale * centerY
-          ];
-          this.width = width;
-          this.height = height;
-          this.fontScale = scale;
-        }
-        PageViewport.prototype = /** @lends PageViewport.prototype */
-        {
-          /**
-           * Clones viewport with additional properties.
-           * @param args {Object} (optional) If specified, may contain the 'scale' or
-           * 'rotation' properties to override the corresponding properties in
-           * the cloned viewport.
-           * @returns {PageViewport} Cloned viewport.
-           */
-          clone: function PageViewPort_clone(args) {
-            args = args || {};
-            var scale = 'scale' in args ? args.scale : this.scale;
-            var rotation = 'rotation' in args ? args.rotation : this.rotation;
-            return new PageViewport(this.viewBox.slice(), scale, rotation, this.offsetX, this.offsetY, args.dontFlip);
-          },
-          /**
-           * Converts PDF point to the viewport coordinates. For examples, useful for
-           * converting PDF location into canvas pixel coordinates.
-           * @param x {number} X coordinate.
-           * @param y {number} Y coordinate.
-           * @returns {Object} Object that contains 'x' and 'y' properties of the
-           * point in the viewport coordinate space.
-           * @see {@link convertToPdfPoint}
-           * @see {@link convertToViewportRectangle}
-           */
-          convertToViewportPoint: function PageViewport_convertToViewportPoint(x, y) {
-            return Util.applyTransform([
-              x,
-              y
-            ], this.transform);
-          },
-          /**
-           * Converts PDF rectangle to the viewport coordinates.
-           * @param rect {Array} xMin, yMin, xMax and yMax coordinates.
-           * @returns {Array} Contains corresponding coordinates of the rectangle
-           * in the viewport coordinate space.
-           * @see {@link convertToViewportPoint}
-           */
-          convertToViewportRectangle: function PageViewport_convertToViewportRectangle(rect) {
-            var tl = Util.applyTransform([
-              rect[0],
-              rect[1]
-            ], this.transform);
-            var br = Util.applyTransform([
-              rect[2],
-              rect[3]
-            ], this.transform);
-            return [
-              tl[0],
-              tl[1],
-              br[0],
-              br[1]
-            ];
-          },
-          /**
-           * Converts viewport coordinates to the PDF location. For examples, useful
-           * for converting canvas pixel location into PDF one.
-           * @param x {number} X coordinate.
-           * @param y {number} Y coordinate.
-           * @returns {Object} Object that contains 'x' and 'y' properties of the
-           * point in the PDF coordinate space.
-           * @see {@link convertToViewportPoint}
-           */
-          convertToPdfPoint: function PageViewport_convertToPdfPoint(x, y) {
-            return Util.applyInverseTransform([
-              x,
-              y
-            ], this.transform);
-          }
-        };
-        return PageViewport;
-      }();
-      var PDFStringTranslateTable = [
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0x2D8,
-        0x2C7,
-        0x2C6,
-        0x2D9,
-        0x2DD,
-        0x2DB,
-        0x2DA,
-        0x2DC,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
+       }
+      }
+      return this.compiledGlyphs[character];
+     }
+    };
+    return FontFaceObject;
+   }();
+   exports.FontFaceObject = FontFaceObject;
+   exports.FontLoader = FontLoader;
+  }));
+  (function (root, factory) {
+   factory(root.pdfjsDisplayMetadata = {}, root.pdfjsSharedUtil);
+  }(this, function (exports, sharedUtil) {
+   var error = sharedUtil.error;
+   function fixMetadata(meta) {
+    return meta.replace(/>\\376\\377([^<]+)/g, function (all, codes) {
+     var bytes = codes.replace(/\\([0-3])([0-7])([0-7])/g, function (code, d1, d2, d3) {
+      return String.fromCharCode(d1 * 64 + d2 * 8 + d3 * 1);
+     });
+     var chars = '';
+     for (var i = 0; i < bytes.length; i += 2) {
+      var code = bytes.charCodeAt(i) * 256 + bytes.charCodeAt(i + 1);
+      chars += code >= 32 && code < 127 && code !== 60 && code !== 62 && code !== 38 && false ? String.fromCharCode(code) : '&#x' + (0x10000 + code).toString(16).substring(1) + ';';
+     }
+     return '>' + chars;
+    });
+   }
+   function Metadata(meta) {
+    if (typeof meta === 'string') {
+     meta = fixMetadata(meta);
+     var parser = new DOMParser();
+     meta = parser.parseFromString(meta, 'application/xml');
+    } else if (!(meta instanceof Document)) {
+     error('Metadata: Invalid metadata object');
+    }
+    this.metaDocument = meta;
+    this.metadata = Object.create(null);
+    this.parse();
+   }
+   Metadata.prototype = {
+    parse: function Metadata_parse() {
+     var doc = this.metaDocument;
+     var rdf = doc.documentElement;
+     if (rdf.nodeName.toLowerCase() !== 'rdf:rdf') {
+      rdf = rdf.firstChild;
+      while (rdf && rdf.nodeName.toLowerCase() !== 'rdf:rdf') {
+       rdf = rdf.nextSibling;
+      }
+     }
+     var nodeName = rdf ? rdf.nodeName.toLowerCase() : null;
+     if (!rdf || nodeName !== 'rdf:rdf' || !rdf.hasChildNodes()) {
+      return;
+     }
+     var children = rdf.childNodes, desc, entry, name, i, ii, length, iLength;
+     for (i = 0, length = children.length; i < length; i++) {
+      desc = children[i];
+      if (desc.nodeName.toLowerCase() !== 'rdf:description') {
+       continue;
+      }
+      for (ii = 0, iLength = desc.childNodes.length; ii < iLength; ii++) {
+       if (desc.childNodes[ii].nodeName.toLowerCase() !== '#text') {
+        entry = desc.childNodes[ii];
+        name = entry.nodeName.toLowerCase();
+        this.metadata[name] = entry.textContent.trim();
+       }
+      }
+     }
+    },
+    get: function Metadata_get(name) {
+     return this.metadata[name] || null;
+    },
+    has: function Metadata_has(name) {
+     return typeof this.metadata[name] !== 'undefined';
+    }
+   };
+   exports.Metadata = Metadata;
+  }));
+  (function (root, factory) {
+   factory(root.pdfjsDisplaySVG = {}, root.pdfjsSharedUtil);
+  }(this, function (exports, sharedUtil) {
+  }));
+  (function (root, factory) {
+   factory(root.pdfjsDisplayAnnotationLayer = {}, root.pdfjsSharedUtil, root.pdfjsDisplayDOMUtils);
+  }(this, function (exports, sharedUtil, displayDOMUtils) {
+   var AnnotationBorderStyleType = sharedUtil.AnnotationBorderStyleType;
+   var AnnotationType = sharedUtil.AnnotationType;
+   var Util = sharedUtil.Util;
+   var addLinkAttributes = displayDOMUtils.addLinkAttributes;
+   var LinkTarget = displayDOMUtils.LinkTarget;
+   var getFilenameFromUrl = displayDOMUtils.getFilenameFromUrl;
+   var warn = sharedUtil.warn;
+   var CustomStyle = displayDOMUtils.CustomStyle;
+   var getDefaultSetting = displayDOMUtils.getDefaultSetting;
+   function AnnotationElementFactory() {
+   }
+   AnnotationElementFactory.prototype = {
+    create: function AnnotationElementFactory_create(parameters) {
+     var subtype = parameters.data.annotationType;
+     switch (subtype) {
+     case AnnotationType.LINK:
+      return new LinkAnnotationElement(parameters);
+     case AnnotationType.TEXT:
+      return new TextAnnotationElement(parameters);
+     case AnnotationType.WIDGET:
+      var fieldType = parameters.data.fieldType;
+      switch (fieldType) {
+      case 'Tx':
+       return new TextWidgetAnnotationElement(parameters);
+      case 'Ch':
+       return new ChoiceWidgetAnnotationElement(parameters);
+      }
+      return new WidgetAnnotationElement(parameters);
+     case AnnotationType.POPUP:
+      return new PopupAnnotationElement(parameters);
+     case AnnotationType.HIGHLIGHT:
+      return new HighlightAnnotationElement(parameters);
+     case AnnotationType.UNDERLINE:
+      return new UnderlineAnnotationElement(parameters);
+     case AnnotationType.SQUIGGLY:
+      return new SquigglyAnnotationElement(parameters);
+     case AnnotationType.STRIKEOUT:
+      return new StrikeOutAnnotationElement(parameters);
+     case AnnotationType.FILEATTACHMENT:
+      return new FileAttachmentAnnotationElement(parameters);
+     default:
+      return new AnnotationElement(parameters);
+     }
+    }
+   };
+   var AnnotationElement = function AnnotationElementClosure() {
+    function AnnotationElement(parameters, isRenderable) {
+     this.isRenderable = isRenderable || false;
+     this.data = parameters.data;
+     this.layer = parameters.layer;
+     this.page = parameters.page;
+     this.viewport = parameters.viewport;
+     this.linkService = parameters.linkService;
+     this.downloadManager = parameters.downloadManager;
+     this.imageResourcesPath = parameters.imageResourcesPath;
+     this.renderInteractiveForms = parameters.renderInteractiveForms;
+     if (isRenderable) {
+      this.container = this._createContainer();
+     }
+    }
+    AnnotationElement.prototype = {
+     _createContainer: function AnnotationElement_createContainer() {
+      var data = this.data, page = this.page, viewport = this.viewport;
+      var container = document.createElement('section');
+      var width = data.rect[2] - data.rect[0];
+      var height = data.rect[3] - data.rect[1];
+      container.setAttribute('data-annotation-id', data.id);
+      var rect = Util.normalizeRect([
+       data.rect[0],
+       page.view[3] - data.rect[1] + page.view[1],
+       data.rect[2],
+       page.view[3] - data.rect[3] + page.view[1]
+      ]);
+      CustomStyle.setProp('transform', container, 'matrix(' + viewport.transform.join(',') + ')');
+      CustomStyle.setProp('transformOrigin', container, -rect[0] + 'px ' + -rect[1] + 'px');
+      if (data.borderStyle.width > 0) {
+       container.style.borderWidth = data.borderStyle.width + 'px';
+       if (data.borderStyle.style !== AnnotationBorderStyleType.UNDERLINE) {
+        width = width - 2 * data.borderStyle.width;
+        height = height - 2 * data.borderStyle.width;
+       }
+       var horizontalRadius = data.borderStyle.horizontalCornerRadius;
+       var verticalRadius = data.borderStyle.verticalCornerRadius;
+       if (horizontalRadius > 0 || verticalRadius > 0) {
+        var radius = horizontalRadius + 'px / ' + verticalRadius + 'px';
+        CustomStyle.setProp('borderRadius', container, radius);
+       }
+       switch (data.borderStyle.style) {
+       case AnnotationBorderStyleType.SOLID:
+        container.style.borderStyle = 'solid';
+        break;
+       case AnnotationBorderStyleType.DASHED:
+        container.style.borderStyle = 'dashed';
+        break;
+       case AnnotationBorderStyleType.BEVELED:
+        warn('Unimplemented border style: beveled');
+        break;
+       case AnnotationBorderStyleType.INSET:
+        warn('Unimplemented border style: inset');
+        break;
+       case AnnotationBorderStyleType.UNDERLINE:
+        container.style.borderBottomStyle = 'solid';
+        break;
+       default:
+        break;
+       }
+       if (data.color) {
+        container.style.borderColor = Util.makeCssRgb(data.color[0] | 0, data.color[1] | 0, data.color[2] | 0);
+       } else {
+        container.style.borderWidth = 0;
+       }
+      }
+      container.style.left = rect[0] + 'px';
+      container.style.top = rect[1] + 'px';
+      container.style.width = width + 'px';
+      container.style.height = height + 'px';
+      return container;
+     },
+     _createPopup: function AnnotationElement_createPopup(container, trigger, data) {
+      if (!trigger) {
+       trigger = document.createElement('div');
+       trigger.style.height = container.style.height;
+       trigger.style.width = container.style.width;
+       container.appendChild(trigger);
+      }
+      var popupElement = new PopupElement({
+       container: container,
+       trigger: trigger,
+       color: data.color,
+       title: data.title,
+       contents: data.contents,
+       hideWrapper: true
+      });
+      var popup = popupElement.render();
+      popup.style.left = container.style.width;
+      container.appendChild(popup);
+     },
+     render: function AnnotationElement_render() {
+      throw new Error('Abstract method AnnotationElement.render called');
+     }
+    };
+    return AnnotationElement;
+   }();
+   var LinkAnnotationElement = function LinkAnnotationElementClosure() {
+    function LinkAnnotationElement(parameters) {
+     AnnotationElement.call(this, parameters, true);
+    }
+    Util.inherit(LinkAnnotationElement, AnnotationElement, {
+     render: function LinkAnnotationElement_render() {
+      this.container.className = 'linkAnnotation';
+      var link = document.createElement('a');
+      addLinkAttributes(link, {
+       url: this.data.url,
+       target: this.data.newWindow ? LinkTarget.BLANK : undefined
+      });
+      if (!this.data.url) {
+       if (this.data.action) {
+        this._bindNamedAction(link, this.data.action);
+       } else {
+        this._bindLink(link, this.data.dest);
+       }
+      }
+      this.container.appendChild(link);
+      return this.container;
+     },
+     _bindLink: function LinkAnnotationElement_bindLink(link, destination) {
+      var self = this;
+      link.href = this.linkService.getDestinationHash(destination);
+      link.onclick = function () {
+       if (destination) {
+        self.linkService.navigateTo(destination);
+       }
+       return false;
+      };
+      if (destination) {
+       link.className = 'internalLink';
+      }
+     },
+     _bindNamedAction: function LinkAnnotationElement_bindNamedAction(link, action) {
+      var self = this;
+      link.href = this.linkService.getAnchorUrl('');
+      link.onclick = function () {
+       self.linkService.executeNamedAction(action);
+       return false;
+      };
+      link.className = 'internalLink';
+     }
+    });
+    return LinkAnnotationElement;
+   }();
+   var TextAnnotationElement = function TextAnnotationElementClosure() {
+    function TextAnnotationElement(parameters) {
+     var isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents);
+     AnnotationElement.call(this, parameters, isRenderable);
+    }
+    Util.inherit(TextAnnotationElement, AnnotationElement, {
+     render: function TextAnnotationElement_render() {
+      this.container.className = 'textAnnotation';
+      var image = document.createElement('img');
+      image.style.height = this.container.style.height;
+      image.style.width = this.container.style.width;
+      image.src = this.imageResourcesPath + 'annotation-' + this.data.name.toLowerCase() + '.svg';
+      image.alt = '[{{type}} Annotation]';
+      image.dataset.l10nId = 'text_annotation_type';
+      image.dataset.l10nArgs = JSON.stringify({ type: this.data.name });
+      if (!this.data.hasPopup) {
+       this._createPopup(this.container, image, this.data);
+      }
+      this.container.appendChild(image);
+      return this.container;
+     }
+    });
+    return TextAnnotationElement;
+   }();
+   var WidgetAnnotationElement = function WidgetAnnotationElementClosure() {
+    function WidgetAnnotationElement(parameters, isRenderable) {
+     AnnotationElement.call(this, parameters, isRenderable);
+    }
+    Util.inherit(WidgetAnnotationElement, AnnotationElement, {
+     render: function WidgetAnnotationElement_render() {
+      return this.container;
+     }
+    });
+    return WidgetAnnotationElement;
+   }();
+   var TextWidgetAnnotationElement = function TextWidgetAnnotationElementClosure() {
+    var TEXT_ALIGNMENT = [
+     'left',
+     'center',
+     'right'
+    ];
+    function TextWidgetAnnotationElement(parameters) {
+     var isRenderable = parameters.renderInteractiveForms || !parameters.data.hasAppearance && !!parameters.data.fieldValue;
+     WidgetAnnotationElement.call(this, parameters, isRenderable);
+    }
+    Util.inherit(TextWidgetAnnotationElement, WidgetAnnotationElement, {
+     render: function TextWidgetAnnotationElement_render() {
+      this.container.className = 'textWidgetAnnotation';
+      var element = null;
+      if (this.renderInteractiveForms) {
+       if (this.data.multiLine) {
+        element = document.createElement('textarea');
+        element.textContent = this.data.fieldValue;
+       } else {
+        element = document.createElement('input');
+        element.type = 'text';
+        element.setAttribute('value', this.data.fieldValue);
+       }
+       element.disabled = this.data.readOnly;
+       if (this.data.maxLen !== null) {
+        element.maxLength = this.data.maxLen;
+       }
+       if (this.data.comb) {
+        var fieldWidth = this.data.rect[2] - this.data.rect[0];
+        var combWidth = fieldWidth / this.data.maxLen;
+        element.classList.add('comb');
+        element.style.letterSpacing = 'calc(' + combWidth + 'px - 1ch)';
+       }
+      } else {
+       element = document.createElement('div');
+       element.textContent = this.data.fieldValue;
+       element.style.verticalAlign = 'middle';
+       element.style.display = 'table-cell';
+       var font = null;
+       if (this.data.fontRefName) {
+        font = this.page.commonObjs.getData(this.data.fontRefName);
+       }
+       this._setTextStyle(element, font);
+      }
+      if (this.data.textAlignment !== null) {
+       element.style.textAlign = TEXT_ALIGNMENT[this.data.textAlignment];
+      }
+      this.container.appendChild(element);
+      return this.container;
+     },
+     _setTextStyle: function TextWidgetAnnotationElement_setTextStyle(element, font) {
+      var style = element.style;
+      style.fontSize = this.data.fontSize + 'px';
+      style.direction = this.data.fontDirection < 0 ? 'rtl' : 'ltr';
+      if (!font) {
+       return;
+      }
+      style.fontWeight = font.black ? font.bold ? '900' : 'bold' : font.bold ? 'bold' : 'normal';
+      style.fontStyle = font.italic ? 'italic' : 'normal';
+      var fontFamily = font.loadedName ? '"' + font.loadedName + '", ' : '';
+      var fallbackName = font.fallbackName || 'Helvetica, sans-serif';
+      style.fontFamily = fontFamily + fallbackName;
+     }
+    });
+    return TextWidgetAnnotationElement;
+   }();
+   var ChoiceWidgetAnnotationElement = function ChoiceWidgetAnnotationElementClosure() {
+    function ChoiceWidgetAnnotationElement(parameters) {
+     WidgetAnnotationElement.call(this, parameters, parameters.renderInteractiveForms);
+    }
+    Util.inherit(ChoiceWidgetAnnotationElement, WidgetAnnotationElement, {
+     render: function ChoiceWidgetAnnotationElement_render() {
+      this.container.className = 'choiceWidgetAnnotation';
+      var selectElement = document.createElement('select');
+      selectElement.disabled = this.data.readOnly;
+      if (!this.data.combo) {
+       selectElement.size = this.data.options.length;
+       if (this.data.multiSelect) {
+        selectElement.multiple = true;
+       }
+      }
+      for (var i = 0, ii = this.data.options.length; i < ii; i++) {
+       var option = this.data.options[i];
+       var optionElement = document.createElement('option');
+       optionElement.textContent = option.displayValue;
+       optionElement.value = option.exportValue;
+       if (this.data.fieldValue.indexOf(option.displayValue) >= 0) {
+        optionElement.setAttribute('selected', true);
+       }
+       selectElement.appendChild(optionElement);
+      }
+      this.container.appendChild(selectElement);
+      return this.container;
+     }
+    });
+    return ChoiceWidgetAnnotationElement;
+   }();
+   var PopupAnnotationElement = function PopupAnnotationElementClosure() {
+    function PopupAnnotationElement(parameters) {
+     var isRenderable = !!(parameters.data.title || parameters.data.contents);
+     AnnotationElement.call(this, parameters, isRenderable);
+    }
+    Util.inherit(PopupAnnotationElement, AnnotationElement, {
+     render: function PopupAnnotationElement_render() {
+      this.container.className = 'popupAnnotation';
+      var selector = '[data-annotation-id="' + this.data.parentId + '"]';
+      var parentElement = this.layer.querySelector(selector);
+      if (!parentElement) {
+       return this.container;
+      }
+      var popup = new PopupElement({
+       container: this.container,
+       trigger: parentElement,
+       color: this.data.color,
+       title: this.data.title,
+       contents: this.data.contents
+      });
+      var parentLeft = parseFloat(parentElement.style.left);
+      var parentWidth = parseFloat(parentElement.style.width);
+      CustomStyle.setProp('transformOrigin', this.container, -(parentLeft + parentWidth) + 'px -' + parentElement.style.top);
+      this.container.style.left = parentLeft + parentWidth + 'px';
+      this.container.appendChild(popup.render());
+      return this.container;
+     }
+    });
+    return PopupAnnotationElement;
+   }();
+   var PopupElement = function PopupElementClosure() {
+    var BACKGROUND_ENLIGHT = 0.7;
+    function PopupElement(parameters) {
+     this.container = parameters.container;
+     this.trigger = parameters.trigger;
+     this.color = parameters.color;
+     this.title = parameters.title;
+     this.contents = parameters.contents;
+     this.hideWrapper = parameters.hideWrapper || false;
+     this.pinned = false;
+    }
+    PopupElement.prototype = {
+     render: function PopupElement_render() {
+      var wrapper = document.createElement('div');
+      wrapper.className = 'popupWrapper';
+      this.hideElement = this.hideWrapper ? wrapper : this.container;
+      this.hideElement.setAttribute('hidden', true);
+      var popup = document.createElement('div');
+      popup.className = 'popup';
+      var color = this.color;
+      if (color) {
+       var r = BACKGROUND_ENLIGHT * (255 - color[0]) + color[0];
+       var g = BACKGROUND_ENLIGHT * (255 - color[1]) + color[1];
+       var b = BACKGROUND_ENLIGHT * (255 - color[2]) + color[2];
+       popup.style.backgroundColor = Util.makeCssRgb(r | 0, g | 0, b | 0);
+      }
+      var contents = this._formatContents(this.contents);
+      var title = document.createElement('h1');
+      title.textContent = this.title;
+      this.trigger.addEventListener('click', this._toggle.bind(this));
+      this.trigger.addEventListener('mouseover', this._show.bind(this, false));
+      this.trigger.addEventListener('mouseout', this._hide.bind(this, false));
+      popup.addEventListener('click', this._hide.bind(this, true));
+      popup.appendChild(title);
+      popup.appendChild(contents);
+      wrapper.appendChild(popup);
+      return wrapper;
+     },
+     _formatContents: function PopupElement_formatContents(contents) {
+      var p = document.createElement('p');
+      var lines = contents.split(/(?:\r\n?|\n)/);
+      for (var i = 0, ii = lines.length; i < ii; ++i) {
+       var line = lines[i];
+       p.appendChild(document.createTextNode(line));
+       if (i < ii - 1) {
+        p.appen