Merge mozilla-inbound to mozilla-central. a=merge
authorAndreea Pavel <apavel@mozilla.com>
Wed, 20 Jun 2018 14:28:15 +0300
changeset 479792 681eb7dfa324dd50403c382888929ea8b8b11b00
parent 479791 76a4a96da17c8ffa1bd57b84af81652a3b12870e (current diff)
parent 479784 b6f1b6284f474a08953cd3b58709c14c5cd5e684 (diff)
child 479793 e7553bb78c5992a62dd1c1881d52cadf69c9b243
child 479807 dadc58a65c2eb3347a6427ec29df1fa183f3b8e7
child 479892 b84a55905d0e1c6bc6d863878d1e81243e0500fa
push id1757
push userffxbld-merge
push dateFri, 24 Aug 2018 17:02:43 +0000
treeherdermozilla-release@736023aebdb1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone62.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 mozilla-inbound to mozilla-central. a=merge
devtools/client/inspector/computed/computed.js
modules/libpref/init/StaticPrefList.h
testing/web-platform/meta/2dcontext/fill-and-stroke-styles/2d.gradient.interpolate.overlap.html.ini
testing/web-platform/meta/2dcontext/path-objects/2d.path.arc.selfintersect.1.html.ini
testing/web-platform/meta/2dcontext/path-objects/2d.path.arc.shape.3.html.ini
testing/web-platform/meta/2dcontext/path-objects/2d.path.rect.zero.3.html.ini
testing/web-platform/meta/2dcontext/path-objects/2d.path.stroke.scale2.html.ini
toolkit/mozapps/extensions/test/Makefile.in
toolkit/mozapps/extensions/test/addons/bootstrap_globals/bootstrap.js
toolkit/mozapps/extensions/test/addons/bootstrap_globals/install.rdf
toolkit/mozapps/extensions/test/addons/langpack_1/browser/localization/und/browser.ftl
toolkit/mozapps/extensions/test/addons/langpack_1/chrome/und/locale/und/global/test.properties
toolkit/mozapps/extensions/test/addons/langpack_1/localization/und/toolkit_test.ftl
toolkit/mozapps/extensions/test/addons/langpack_1/manifest.json
toolkit/mozapps/extensions/test/addons/test_bootstrap1_1/bootstrap.js
toolkit/mozapps/extensions/test/addons/test_bootstrap1_1/install.rdf
toolkit/mozapps/extensions/test/addons/test_bootstrap1_1/version.jsm
toolkit/mozapps/extensions/test/addons/test_bootstrap1_2/bootstrap.js
toolkit/mozapps/extensions/test/addons/test_bootstrap1_2/install.rdf
toolkit/mozapps/extensions/test/addons/test_bootstrap1_2/version.jsm
toolkit/mozapps/extensions/test/addons/test_bootstrap_const/bootstrap.js
toolkit/mozapps/extensions/test/addons/test_bootstrap_const/install.rdf
toolkit/mozapps/extensions/test/addons/test_bug567173/install.rdf
toolkit/mozapps/extensions/test/addons/test_cache_certdb/bootstrap.js
toolkit/mozapps/extensions/test/addons/test_cache_certdb/install.rdf
toolkit/mozapps/extensions/test/addons/test_delay_update_complete_v2/bootstrap.js
toolkit/mozapps/extensions/test/addons/test_delay_update_complete_v2/install.rdf
toolkit/mozapps/extensions/test/addons/test_delay_update_complete_webextension_v2/manifest.json
toolkit/mozapps/extensions/test/addons/test_delay_update_defer_v2/bootstrap.js
toolkit/mozapps/extensions/test/addons/test_delay_update_defer_v2/install.rdf
toolkit/mozapps/extensions/test/addons/test_delay_update_defer_webextension_v2/manifest.json
toolkit/mozapps/extensions/test/addons/test_delay_update_ignore_v2/bootstrap.js
toolkit/mozapps/extensions/test/addons/test_delay_update_ignore_v2/install.rdf
toolkit/mozapps/extensions/test/addons/test_delay_update_ignore_webextension_v2/manifest.json
toolkit/mozapps/extensions/test/addons/test_dictionary/chrome.manifest
toolkit/mozapps/extensions/test/addons/test_dictionary/dictionaries/ab-CD.dic
toolkit/mozapps/extensions/test/addons/test_dictionary/install.rdf
toolkit/mozapps/extensions/test/addons/test_dictionary_3/install.rdf
toolkit/mozapps/extensions/test/addons/test_dictionary_4/install.rdf
toolkit/mozapps/extensions/test/addons/test_dictionary_5/install.rdf
toolkit/mozapps/extensions/test/addons/test_distribution1_2/install.rdf
toolkit/mozapps/extensions/test/addons/test_getresource/icon.png
toolkit/mozapps/extensions/test/addons/test_getresource/install.rdf
toolkit/mozapps/extensions/test/addons/test_getresource/subdir/subfile.txt
toolkit/mozapps/extensions/test/addons/test_install1/icon.png
toolkit/mozapps/extensions/test/addons/test_install1/icon64.png
toolkit/mozapps/extensions/test/addons/test_install1/install.rdf
toolkit/mozapps/extensions/test/addons/test_locale/install.rdf
toolkit/mozapps/extensions/test/addons/test_symbol/bootstrap.js
toolkit/mozapps/extensions/test/addons/test_symbol/install.rdf
toolkit/mozapps/extensions/test/addons/test_undoincompatible/bootstrap.js
toolkit/mozapps/extensions/test/addons/test_undoincompatible/install.rdf
toolkit/mozapps/extensions/test/addons/test_undouninstall1/bootstrap.js
toolkit/mozapps/extensions/test/addons/test_undouninstall1/install.rdf
toolkit/mozapps/extensions/test/addons/test_updateid1/bootstrap.js
toolkit/mozapps/extensions/test/addons/test_updateid1/install.rdf
toolkit/mozapps/extensions/test/addons/test_updateid2/bootstrap.js
toolkit/mozapps/extensions/test/addons/test_updateid2/install.rdf
toolkit/mozapps/extensions/test/addons/webextension_1/chrome.manifest
toolkit/mozapps/extensions/test/addons/webextension_1/manifest.json
toolkit/mozapps/extensions/test/addons/webextension_3/_locales/en/messages.json
toolkit/mozapps/extensions/test/addons/webextension_3/_locales/fr/messages.json
toolkit/mozapps/extensions/test/addons/webextension_3/manifest.json
toolkit/mozapps/extensions/test/xpcshell/test_pass_symbol.js
--- a/browser/config/mozconfigs/win32/mingw32
+++ b/browser/config/mozconfigs/win32/mingw32
@@ -9,17 +9,16 @@
 unset MAKECAB
 
 # Sets:
 #  build/mozconfig.common
 #    AUTOCLOBBER=1
 #    --enable-crashreporter
 #    --enable-release
 #    LLVM_CONFIG
-#    MOZ_ADDON_SIGNING
 #    MOZ_REQUIRE_SIGNING
 #    --enable-js-shell
 #  build/mozconfig.automation
 #    MOZ_AUTOMATION_ flags
 #  build/mozconfig.rust
 #    TOOLTOOL_DIR
 #    RUSTC
 #    CARGO
--- a/browser/confvars.sh
+++ b/browser/confvars.sh
@@ -62,13 +62,10 @@ fi
 # ASan reporter builds should have different channel ids
 if [ "${MOZ_ASAN_REPORTER}" = "1" ]; then
     ACCEPTED_MAR_CHANNEL_IDS="${ACCEPTED_MAR_CHANNEL_IDS}-asan"
     MAR_CHANNEL_ID="${MAR_CHANNEL_ID}-asan"
 fi
 
 MOZ_PROFILE_MIGRATOR=1
 
-# Enable checking that add-ons are signed by the trusted root
-MOZ_ADDON_SIGNING=1
-
 # Include the DevTools client, not just the server (which is the default)
 MOZ_DEVTOOLS=all
--- a/build/mozconfig.common
+++ b/build/mozconfig.common
@@ -9,18 +9,16 @@
 # architectures, though note that if you want to override options set in
 # another mozconfig file, you'll need to use mozconfig.common.override instead
 # of this file.
 
 mk_add_options AUTOCLOBBER=1
 
 ac_add_options --enable-crashreporter
 
-# Enable checking that add-ons are signed by the trusted root
-MOZ_ADDON_SIGNING=${MOZ_ADDON_SIGNING-1}
 # Disable enforcing that add-ons are signed by the trusted root
 MOZ_REQUIRE_SIGNING=${MOZ_REQUIRE_SIGNING-0}
 
 ac_add_options --enable-js-shell
 
 . "$topsrcdir/build/mozconfig.automation"
 . "$topsrcdir/build/mozconfig.rust"
 . "$topsrcdir/build/mozconfig.cache"
--- a/devtools/client/accessibility/accessibility.css
+++ b/devtools/client/accessibility/accessibility.css
@@ -118,16 +118,19 @@ body {
 /* TreeView Customization */
 .split-box:not(.horz) .main-panel {
   height: calc(100vh - var(--accessibility-toolbar-height));
 }
 
 .treeTable > thead {
   position: sticky;
   top: 0;
+  /* Bug 1466806 - fix expander arrow for expanding treeview rows rendering over the
+     thead */
+  z-index: 1;
 }
 
 .split-box:not(.horz) .treeTable {
   /* To compenstate for 1px splitter between the tree and sidebar. */
   width: var(--accessibility-full-length-minus-splitter);
 }
 
 .split-box.horz .treeTable {
--- a/devtools/client/shared/components/tree/TreeView.css
+++ b/devtools/client/shared/components/tree/TreeView.css
@@ -52,16 +52,20 @@
   transition: all 0.2s ease-in-out;
 }
 
 .treeTable .treeLabelCell {
   cursor: default;
   overflow: hidden;
   padding-inline-start: 4px;
   white-space: nowrap;
+}
+
+.treeTable .treeValueCell > [aria-labelledby],
+.treeTable .treeLabelCell > .treeLabel {
   unicode-bidi: plaintext;
 }
 
 /* No padding if there is actually no label */
 .treeTable .treeLabel:empty {
   padding-inline-start: 0;
 }
 
--- a/dom/base/CustomElementRegistry.cpp
+++ b/dom/base/CustomElementRegistry.cpp
@@ -976,16 +976,55 @@ CustomElementRegistry::SetElementCreatio
     return;
   }
 
   RefPtr<CustomElementCreationCallback> callback = &aCallback;
   mElementCreationCallbacks.Put(nameAtom, callback.forget());
   return;
 }
 
+static void
+TryUpgrade(nsINode& aNode)
+{
+  Element* element = aNode.IsElement() ? aNode.AsElement() : nullptr;
+  if (element) {
+    CustomElementData* ceData = element->GetCustomElementData();
+    if (ceData) {
+      NodeInfo* nodeInfo = element->NodeInfo();
+      nsAtom* typeAtom = ceData->GetCustomElementType();
+      CustomElementDefinition* definition =
+        nsContentUtils::LookupCustomElementDefinition(nodeInfo->GetDocument(),
+                                                      nodeInfo->NameAtom(),
+                                                      nodeInfo->NamespaceID(),
+                                                      typeAtom);
+      if (definition) {
+        nsContentUtils::EnqueueUpgradeReaction(element, definition);
+      }
+    }
+
+    if (ShadowRoot* root = element->GetShadowRoot()) {
+      for (Element* child = root->GetFirstElementChild(); child;
+           child = child->GetNextElementSibling()) {
+        TryUpgrade(*child);
+      }
+    }
+  }
+
+  for (Element* child = aNode.GetFirstElementChild(); child;
+       child = child->GetNextElementSibling()) {
+    TryUpgrade(*child);
+  }
+}
+
+void
+CustomElementRegistry::Upgrade(nsINode& aRoot)
+{
+  TryUpgrade(aRoot);
+}
+
 void
 CustomElementRegistry::Get(JSContext* aCx, const nsAString& aName,
                            JS::MutableHandle<JS::Value> aRetVal)
 {
   RefPtr<nsAtom> nameAtom(NS_Atomize(aName));
   CustomElementDefinition* data = mCustomDefinitions.GetWeak(nameAtom);
 
   if (!data) {
--- a/dom/base/CustomElementRegistry.h
+++ b/dom/base/CustomElementRegistry.h
@@ -549,16 +549,18 @@ public:
   void Get(JSContext* cx, const nsAString& name,
            JS::MutableHandle<JS::Value> aRetVal);
 
   already_AddRefed<Promise> WhenDefined(const nsAString& aName, ErrorResult& aRv);
 
   // Chrome-only method that give JS an opportunity to only load the custom
   // element definition script when needed.
   void SetElementCreationCallback(const nsAString& aName, CustomElementCreationCallback& aCallback, ErrorResult& aRv);
+
+  void Upgrade(nsINode& aRoot);
 };
 
 class MOZ_RAII AutoCEReaction final {
   public:
     // JSContext is allowed to be a nullptr if we are guaranteeing that we're
     // not doing something that might throw but not finish reporting a JS
     // exception during the lifetime of the AutoCEReaction.
     AutoCEReaction(CustomElementReactionsStack* aReactionsStack, JSContext* aCx)
--- a/dom/base/nsContentIterator.cpp
+++ b/dom/base/nsContentIterator.cpp
@@ -888,19 +888,17 @@ protected:
   // no copy's or assigns  FIX ME
   nsContentSubtreeIterator(const nsContentSubtreeIterator&);
   nsContentSubtreeIterator& operator=(const nsContentSubtreeIterator&);
 
   virtual void LastRelease() override;
 
   RefPtr<nsRange> mRange;
 
-  // these arrays all typically are used and have elements
   AutoTArray<nsIContent*, 8> mEndNodes;
-  AutoTArray<int32_t, 8>     mEndOffsets;
 };
 
 NS_IMPL_ADDREF_INHERITED(nsContentSubtreeIterator, nsContentIterator)
 NS_IMPL_RELEASE_INHERITED(nsContentSubtreeIterator, nsContentIterator)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsContentSubtreeIterator)
 NS_INTERFACE_MAP_END_INHERITING(nsContentIterator)
 
@@ -1010,31 +1008,37 @@ nsContentSubtreeIterator::InitWithRange(
     if (!child || startOffset == endOffset) {
       // Text node, empty container, or collapsed
       MakeEmpty();
       return NS_OK;
     }
   }
 
   // cache ancestors
-  nsContentUtils::GetAncestorsAndOffsets(endContainer, endOffset,
-                                         &mEndNodes, &mEndOffsets);
+  mEndNodes.Clear();
+  nsIContent* endNode =
+    endContainer->IsContent() ? endContainer->AsContent() : nullptr;
+  while (endNode) {
+    mEndNodes.AppendElement(endNode);
+    endNode = endNode->GetParent();
+  }
 
   nsIContent* firstCandidate = nullptr;
   nsIContent* lastCandidate = nullptr;
 
   // find first node in range
   int32_t offset = mRange->StartOffset();
 
   nsINode* node = nullptr;
   if (!startContainer->GetChildCount()) {
     // no children, start at the node itself
     node = startContainer;
   } else {
-    nsIContent* child = startContainer->GetChildAt_Deprecated(offset);
+    nsIContent* child = mRange->GetChildAtStartOffset();
+    MOZ_ASSERT(child == startContainer->GetChildAt_Deprecated(offset));
     if (!child) {
       // offset after last child
       node = startContainer;
     } else {
       firstCandidate = child;
     }
   }
 
@@ -1073,17 +1077,18 @@ nsContentSubtreeIterator::InitWithRange(
 
   if (offset > numChildren) {
     // Can happen for text nodes
     offset = numChildren;
   }
   if (!offset || !numChildren) {
     node = endContainer;
   } else {
-    lastCandidate = endContainer->GetChildAt_Deprecated(--offset);
+    lastCandidate = mRange->EndRef().Ref();
+    MOZ_ASSERT(lastCandidate == endContainer->GetChildAt_Deprecated(--offset));
     NS_ASSERTION(lastCandidate,
                  "tree traversal trouble in nsContentSubtreeIterator::Init");
   }
 
   if (!lastCandidate) {
     // then lastCandidate is prev node before node
     lastCandidate = GetPrevSibling(node);
   }
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -9879,21 +9879,21 @@ nsContentUtils::NewXULOrHTMLElement(Elem
       } else {
         isCustomElementName = nsContentUtils::IsCustomElementName(name, kNameSpaceID_XUL);
       }
     } else {
       isCustomElementName = nsContentUtils::IsCustomElementName(name, kNameSpaceID_XUL);
     }
   }
 
-  RefPtr<nsAtom> tagAtom = nodeInfo->NameAtom();
-  RefPtr<nsAtom> typeAtom;
+  nsAtom* tagAtom = nodeInfo->NameAtom();
+  nsAtom* typeAtom = nullptr;
   bool isCustomElement = isCustomElementName || aIsAtom;
   if (isCustomElement) {
-    typeAtom = isCustomElementName ? tagAtom.get() : aIsAtom;
+    typeAtom = isCustomElementName ? tagAtom : aIsAtom;
   }
 
   MOZ_ASSERT_IF(aDefinition, isCustomElement);
 
   bool customElementEnabled = CustomElementRegistry::IsCustomElementEnabled(nodeInfo->GetDocument());
 
   // https://dom.spec.whatwg.org/#concept-create-element
   // We only handle the "synchronous custom elements flag is set" now.
--- a/dom/base/nsFocusManager.cpp
+++ b/dom/base/nsFocusManager.cpp
@@ -3481,20 +3481,21 @@ nsFocusManager::GetNextTabbableContent(n
                                       aForDocumentNavigation,
                                       true /* aSkipOwner */);
       if (contentToFocus) {
         NS_ADDREF(*aResultContent = contentToFocus);
         return NS_OK;
       }
     }
 
-    // If aStartContent is not in scope owned by aRootContent
-    // (e.g., aStartContent is already in shadow DOM),
+    // If aStartContent is not in a scope owned by the root element
+    // (i.e. aStartContent is already in shadow DOM),
     // search from scope including aStartContent
-    if (aRootContent != FindOwner(aStartContent)) {
+    nsIContent* rootElement = aRootContent->OwnerDoc()->GetRootElement();
+    if (rootElement != FindOwner(aStartContent)) {
       nsIContent* contentToFocus =
         GetNextTabbableContentInAncestorScopes(&aStartContent,
                                                aOriginalStartContent,
                                                aForward,
                                                &aCurrentTabIndex,
                                                aIgnoreTabIndex,
                                                aForDocumentNavigation);
       if (contentToFocus) {
--- a/dom/media/test/crashtests/crashtests.list
+++ b/dom/media/test/crashtests/crashtests.list
@@ -11,106 +11,106 @@ load 495794-1.html
 load 497734-1.xhtml
 load 497734-2.html
 load 576612-1.html
 load 752784-1.html
 skip-if(Android) load 789075-1.html # bug 1374405 for android
 skip-if(Android&&AndroidVersion=='22') HTTP load 795892-1.html # bug 1358718
 load 844563.html
 load 846612.html
-load 852838.html
+skip-if(verify&&isDebugBuild&&gtkWidget) load 852838.html
 load 865537-1.html
-load 868504.html
-load 874869.html
-load 874915.html
-load 874934.html
-load 874952.html
-load 875144.html
-load 875596.html
+skip-if(verify&&isDebugBuild&&gtkWidget) load 868504.html
+skip-if(verify&&isDebugBuild&&gtkWidget) load 874869.html
+skip-if(verify&&isDebugBuild&&gtkWidget) load 874915.html
+skip-if(verify&&isDebugBuild&&gtkWidget) load 874934.html
+skip-if(verify&&isDebugBuild&&gtkWidget) load 874952.html
+skip-if(verify&&isDebugBuild&&gtkWidget) load 875144.html
+skip-if(verify&&isDebugBuild&&gtkWidget) load 875596.html
 load 875911.html
 load 876024-1.html
-load 876024-2.html
-load 876118.html
+skip-if(verify&&isDebugBuild&&gtkWidget) load 876024-2.html
+skip-if(verify&&isDebugBuild&&gtkWidget) load 876118.html
 load 876207.html
 load 876215.html
-load 876249.html
-load 876252.html
+skip-if(verify&&isDebugBuild&&gtkWidget) load 876249.html
+skip-if(verify&&isDebugBuild&&gtkWidget) load 876252.html
 load 876834.html
 load 877527.html
 load 877820.html
 load 878014.html
 load 878328.html
-load 878407.html
-load 878478.html
+skip-if(verify&&isDebugBuild&&gtkWidget) load 878407.html
+skip-if(verify&&isDebugBuild&&gtkWidget) load 878478.html
 load 880129.html
 load 880202.html
 load 880342-1.html
 load 880342-2.html
 load 880384.html
 load 880404.html
-load 880724.html
+skip-if(verify&&isDebugBuild&&gtkWidget) load 880724.html
 load 881775.html
 load 882549.html
 load 882956.html
 load 884459.html
-load 889042.html
+skip-if(verify&&isDebugBuild&&gtkWidget) load 889042.html
 load 894104.html
 load 907986-1.html
 load 907986-2.html
 load 907986-3.html
 load 907986-4.html
 load 910171-1.html
-load 920987.html
+skip-if(verify&&isDebugBuild&&gtkWidget) load 920987.html
 load 925619-1.html
 load 925619-2.html
-load 926619.html
-load 933151.html
-load 933156.html
+skip-if(verify&&isDebugBuild&&gtkWidget) load 926619.html
+skip-if(verify&&isDebugBuild&&gtkWidget) load 933151.html
+skip-if(verify&&isDebugBuild&&gtkWidget) load 933156.html
 load 944851.html
-load 952756.html
-load 966636.html
-load 986901.html
-load 990794.html
-load 995289.html
-load 1012609.html
+skip-if(verify&&isDebugBuild&&gtkWidget) load 952756.html
+skip-if(verify&&isDebugBuild&&gtkWidget) load 966636.html
+skip-if(verify&&isDebugBuild&&gtkWidget) load 986901.html
+skip-if(verify&&isDebugBuild&&gtkWidget) load 990794.html
+skip-if(verify&&isDebugBuild&&gtkWidget) load 995289.html
+skip-if(verify&&isDebugBuild&&gtkWidget) load 1012609.html
 load 1015662.html
-load 1020205.html
+skip-if(verify&&isDebugBuild&&gtkWidget) load 1020205.html
 skip-if(Android) test-pref(media.navigator.permission.disabled,true) load 1028458.html # bug 1048863
-load 1041466.html
-load 1045650.html
+skip-if(verify&&isDebugBuild&&gtkWidget) load 1041466.html
+skip-if(verify&&isDebugBuild&&gtkWidget) load 1045650.html
 load 1080986.html
 skip-if(Android&&AndroidVersion=='21') load 1180881.html # bug 1409365
 load 1197935.html
-load 1122218.html
+skip-if(verify&&isDebugBuild&&gtkWidget) load 1122218.html
 load 1127188.html
-load 1157994.html
+skip-if(verify&&isDebugBuild&&gtkWidget) load 1157994.html
 load 1158427.html
-load 1185176.html
+skip-if(verify&&isDebugBuild&&gtkWidget) load 1185176.html
 load 1185192.html
 load 1257700.html
 load 1267263.html
 load 1270303.html
 load 1304948.html
 load 1319486.html
-load 1368490.html
-load 1291702.html
+skip-if(verify&&isDebugBuild&&gtkWidget) load 1368490.html
+skip-if(verify&&isDebugBuild&&gtkWidget) load 1291702.html
 load 1378826.html
 load 1384248.html
 load 1389304.html
 load 1393272.webm
 load 1411322.html
 load disconnect-wrong-destination.html
 load analyser-channels-1.html
-load audiocontext-double-suspend.html
+skip-if(verify&&isDebugBuild&&gtkWidget) load audiocontext-double-suspend.html
 load buffer-source-duration-1.html
-load buffer-source-ended-1.html
+skip-if(verify&&isDebugBuild&&gtkWidget) load buffer-source-ended-1.html
 load buffer-source-resampling-start-1.html
 load buffer-source-slow-resampling-1.html
-load doppler-1.html
-HTTP load media-element-source-seek-1.html
-load offline-buffer-source-ended-1.html
+skip-if(verify&&isDebugBuild&&gtkWidget) load doppler-1.html
+skip-if(verify&&isDebugBuild&&gtkWidget) HTTP load media-element-source-seek-1.html
+skip-if(verify&&isDebugBuild&&gtkWidget) load offline-buffer-source-ended-1.html
 load oscillator-ended-1.html
 load oscillator-ended-2.html
 skip-if(Android&&AndroidVersion=='22') load video-replay-after-audio-end.html # bug 1315125, bug 1358876
 # This needs to run at the end to avoid leaking busted state into other tests.
 skip-if(Android) load 691096-1.html # Bug 1365451
 load 1236639.html
 test-pref(media.navigator.permission.disabled,true) load 1388372.html
--- a/dom/media/tests/crashtests/crashtests.list
+++ b/dom/media/tests/crashtests/crashtests.list
@@ -9,17 +9,17 @@ load 802982.html
 load 812785.html
 load 834100.html
 load 836349.html
 load 837324.html
 load 855796.html
 load 860143.html
 load 861958.html
 load 863929.html
-load 1185191.html
+skip-if(verify&&gtkWidget) load 1185191.html
 load 1281695.html
 load 1306476.html
 load 1348381.html
 load 1367930_1.html
 load 1367930_2.html
 pref(browser.link.open_newwindow,2) load 1429507_1.html # window.open() in tab doesn't work for crashtest in e10s, this opens a new window instead
 pref(browser.link.open_newwindow,2) load 1429507_2.html # window.open() in tab doesn't work for crashtest in e10s, this opens a new window instead
 load 1453030.html
--- a/dom/serviceworkers/ServiceWorkerUtils.cpp
+++ b/dom/serviceworkers/ServiceWorkerUtils.cpp
@@ -6,30 +6,27 @@
 
 #include "ServiceWorkerUtils.h"
 
 #include "mozilla/Preferences.h"
 
 namespace mozilla {
 namespace dom {
 
+
 bool
 ServiceWorkerParentInterceptEnabled()
 {
-  static bool sInit = false;
   static Atomic<bool> sEnabled;
-
-  if (!sInit) {
-    MOZ_ASSERT(NS_IsMainThread());
-    Preferences::AddAtomicBoolVarCache(&sEnabled,
-                                       "dom.serviceWorkers.parent_intercept",
-                                       false);
-    sInit = true;
+  static Atomic<bool> sInitialized;
+  if (!sInitialized) {
+    AssertIsOnMainThread();
+    sInitialized = true;
+    sEnabled = Preferences::GetBool("dom.serviceWorkers.parent_intercept", false);
   }
-
   return sEnabled;
 }
 
 bool
 ServiceWorkerRegistrationDataIsValid(const ServiceWorkerRegistrationData& aData)
 {
   return !aData.scope().IsEmpty() &&
          !aData.currentWorkerURL().IsEmpty() &&
--- a/dom/webidl/CustomElementRegistry.webidl
+++ b/dom/webidl/CustomElementRegistry.webidl
@@ -8,15 +8,16 @@ interface CustomElementRegistry {
   [CEReactions, Throws]
   void define(DOMString name, Function functionConstructor,
               optional ElementDefinitionOptions options);
   [ChromeOnly, Throws]
   void setElementCreationCallback(DOMString name, CustomElementCreationCallback callback);
   any get(DOMString name);
   [Throws]
   Promise<void> whenDefined(DOMString name);
+  [CEReactions] void upgrade(Node root);
 };
 
 dictionary ElementDefinitionOptions {
   DOMString extends;
 };
 
 callback CustomElementCreationCallback = void (DOMString name);
--- a/dom/workers/ScriptLoader.cpp
+++ b/dom/workers/ScriptLoader.cpp
@@ -600,36 +600,32 @@ private:
   ~LoaderListener() {}
 
   RefPtr<ScriptLoaderRunnable> mRunnable;
   uint32_t mIndex;
 };
 
 NS_IMPL_ISUPPORTS(LoaderListener, nsIStreamLoaderObserver, nsIRequestObserver)
 
-class ScriptLoaderHolder;
-
 class ScriptLoaderRunnable final : public nsIRunnable,
                                    public nsINamed
 {
   friend class ScriptExecutorRunnable;
-  friend class ScriptLoaderHolder;
   friend class CachePromiseHandler;
   friend class CacheScriptLoader;
   friend class LoaderListener;
 
   WorkerPrivate* mWorkerPrivate;
   nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
   nsTArray<ScriptLoadInfo> mLoadInfos;
   RefPtr<CacheCreator> mCacheCreator;
   Maybe<ClientInfo> mClientInfo;
   Maybe<ServiceWorkerDescriptor> mController;
   bool mIsMainScript;
   WorkerScriptType mWorkerScriptType;
-  bool mCanceled;
   bool mCanceledMainThread;
   ErrorResult& mRv;
 
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
 
   ScriptLoaderRunnable(WorkerPrivate* aWorkerPrivate,
                        nsIEventTarget* aSyncLoopTarget,
@@ -637,25 +633,31 @@ public:
                        const Maybe<ClientInfo>& aClientInfo,
                        const Maybe<ServiceWorkerDescriptor>& aController,
                        bool aIsMainScript,
                        WorkerScriptType aWorkerScriptType,
                        ErrorResult& aRv)
   : mWorkerPrivate(aWorkerPrivate), mSyncLoopTarget(aSyncLoopTarget),
     mClientInfo(aClientInfo), mController(aController),
     mIsMainScript(aIsMainScript), mWorkerScriptType(aWorkerScriptType),
-    mCanceled(false), mCanceledMainThread(false), mRv(aRv)
+    mCanceledMainThread(false), mRv(aRv)
   {
     aWorkerPrivate->AssertIsOnWorkerThread();
     MOZ_ASSERT(aSyncLoopTarget);
     MOZ_ASSERT_IF(aIsMainScript, aLoadInfos.Length() == 1);
 
     mLoadInfos.SwapElements(aLoadInfos);
   }
 
+  void
+  CancelMainThreadWithBindingAborted()
+  {
+    CancelMainThread(NS_BINDING_ABORTED);
+  }
+
 private:
   ~ScriptLoaderRunnable()
   { }
 
   NS_IMETHOD
   Run() override
   {
     AssertIsOnMainThread();
@@ -809,51 +811,28 @@ private:
 
     loadInfo.mCachePromise.swap(cachePromise);
     loadInfo.mCacheStatus = ScriptLoadInfo::WritingToCache;
 
     return NS_OK;
   }
 
   bool
-  Notify(WorkerStatus aStatus)
-  {
-    mWorkerPrivate->AssertIsOnWorkerThread();
-
-    if (aStatus >= Terminating && !mCanceled) {
-      mCanceled = true;
-
-      MOZ_ALWAYS_SUCCEEDS(
-        NS_DispatchToMainThread(NewRunnableMethod("ScriptLoaderRunnable::CancelMainThreadWithBindingAborted",
-                                                  this,
-                                                  &ScriptLoaderRunnable::CancelMainThreadWithBindingAborted)));
-    }
-
-    return true;
-  }
-
-  bool
   IsMainWorkerScript() const
   {
     return mIsMainScript && mWorkerScriptType == WorkerScript;
   }
 
   bool
   IsDebuggerScript() const
   {
     return mWorkerScriptType == DebuggerScript;
   }
 
   void
-  CancelMainThreadWithBindingAborted()
-  {
-    CancelMainThread(NS_BINDING_ABORTED);
-  }
-
-  void
   CancelMainThread(nsresult aCancelResult)
   {
     AssertIsOnMainThread();
 
     if (mCanceledMainThread) {
       return;
     }
 
@@ -1469,37 +1448,16 @@ private:
         MOZ_ASSERT(false, "This should never fail!");
       }
     }
   }
 };
 
 NS_IMPL_ISUPPORTS(ScriptLoaderRunnable, nsIRunnable, nsINamed)
 
-class MOZ_STACK_CLASS ScriptLoaderHolder final : public WorkerHolder
-{
-  // Raw pointer because this holder object follows the mRunnable life-time.
-  ScriptLoaderRunnable* mRunnable;
-
-public:
-  explicit ScriptLoaderHolder(ScriptLoaderRunnable* aRunnable)
-    : WorkerHolder("ScriptLoaderHolder")
-    , mRunnable(aRunnable)
-  {
-    MOZ_ASSERT(aRunnable);
-  }
-
-  virtual bool
-  Notify(WorkerStatus aStatus) override
-  {
-    mRunnable->Notify(aStatus);
-    return true;
-  }
-};
-
 NS_IMETHODIMP
 LoaderListener::OnStreamComplete(nsIStreamLoader* aLoader, nsISupports* aContext,
                                  nsresult aStatus, uint32_t aStringLen,
                                  const uint8_t* aString)
 {
   return mRunnable->OnStreamComplete(aLoader, mIndex, aStatus, aStringLen, aString);
 }
 
@@ -2272,19 +2230,24 @@ LoadAllScripts(WorkerPrivate* aWorkerPri
 
   RefPtr<ScriptLoaderRunnable> loader =
     new ScriptLoaderRunnable(aWorkerPrivate, syncLoopTarget, aLoadInfos,
                              clientInfo, controller,
                              aIsMainScript, aWorkerScriptType, aRv);
 
   NS_ASSERTION(aLoadInfos.IsEmpty(), "Should have swapped!");
 
-  ScriptLoaderHolder workerHolder(loader);
-
-  if (NS_WARN_IF(!workerHolder.HoldWorker(aWorkerPrivate, Terminating))) {
+  RefPtr<StrongWorkerRef> workerRef =
+    StrongWorkerRef::Create(aWorkerPrivate, "ScriptLoader", [loader]() {
+      NS_DispatchToMainThread(NewRunnableMethod("ScriptLoader::CancelMainThreadWithBindingAborted",
+                                                loader,
+                                                &ScriptLoaderRunnable::CancelMainThreadWithBindingAborted));
+    });
+
+  if (NS_WARN_IF(!workerRef)) {
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
 
   if (NS_FAILED(NS_DispatchToMainThread(loader))) {
     NS_ERROR("Failed to dispatch!");
     aRv.Throw(NS_ERROR_FAILURE);
     return;
--- a/editor/reftests/reftest.list
+++ b/editor/reftests/reftest.list
@@ -129,9 +129,9 @@ needs-focus == spellcheck-contenteditabl
 == spellcheck-contenteditable-property-dynamic-inherit.html spellcheck-contenteditable-disabled-ref.html
 == spellcheck-contenteditable-attr-dynamic-override.html spellcheck-contenteditable-disabled-ref.html
 == spellcheck-contenteditable-attr-dynamic-override-inherit.html spellcheck-contenteditable-disabled-ref.html
 == spellcheck-contenteditable-property-dynamic-override.html spellcheck-contenteditable-disabled-ref.html
 == spellcheck-contenteditable-property-dynamic-override-inherit.html spellcheck-contenteditable-disabled-ref.html
 == 911201.html 911201-ref.html
 needs-focus == 969773.html 969773-ref.html
 fuzzy-if(skiaContent,1,220) == 997805.html 997805-ref.html
-fuzzy-if(skiaContent,1,220) == 1088158.html 1088158-ref.html
+fuzzy-if(skiaContent,1,220) skip-if(verify&&OSX) == 1088158.html 1088158-ref.html
--- a/js/src/builtin/BigInt.cpp
+++ b/js/src/builtin/BigInt.cpp
@@ -184,18 +184,19 @@ const ClassSpec BigIntObject::classSpec_
     GenericCreateConstructor<BigIntConstructor, 1, gc::AllocKind::FUNCTION>,
     CreateBigIntPrototype,
     nullptr,
     nullptr,
     BigIntObject::methods,
     BigIntObject::properties
 };
 
+// The class is named "Object" as a workaround for bug 1277801.
 const Class BigIntObject::class_ = {
-    "BigInt",
+    "Object",
     JSCLASS_HAS_CACHED_PROTO(JSProto_BigInt) |
     JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS),
     JS_NULL_CLASS_OPS,
     &BigIntObject::classSpec_
 };
 
 const JSPropertySpec BigIntObject::properties[] = {
     // BigInt proposal section 5.3.5
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -469,21 +469,16 @@ GetBuiltinTagSlow(JSContext* cx, HandleO
         builtinTag.set(cx->names().objectNumber);
         return true;
       case ESClass::Date:
         builtinTag.set(cx->names().objectDate);
         return true;
       case ESClass::RegExp:
         builtinTag.set(cx->names().objectRegExp);
         return true;
-#ifdef ENABLE_BIGINT
-      case ESClass::BigInt:
-        builtinTag.set(cx->names().objectBigInt);
-        return true;
-#endif
       default:
         if (obj->isCallable()) {
             // Non-standard: Prevent <object> from showing up as Function.
             RootedObject unwrapped(cx, CheckedUnwrap(obj));
             if (!unwrapped || !unwrapped->getClass()->isDOMClass()) {
                 builtinTag.set(cx->names().objectFunction);
                 return true;
             }
@@ -533,21 +528,16 @@ GetBuiltinTagFast(JSObject* obj, const C
     if (obj->is<ErrorObject>())
         return cx->names().objectError;
 
     if (obj->isCallable() && !obj->getClass()->isDOMClass()) {
         // Non-standard: Prevent <object> from showing up as Function.
         return cx->names().objectFunction;
     }
 
-#ifdef ENABLE_BIGINT
-    if (obj->is<BigIntObject>())
-        return cx->names().objectBigInt;
-#endif
-
     return nullptr;
 }
 
 // ES6 19.1.3.6
 bool
 js::obj_toString(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
--- a/layout/generic/crashtests/crashtests.list
+++ b/layout/generic/crashtests/crashtests.list
@@ -581,17 +581,17 @@ asserts(11) asserts-if(Android,274) load
 pref(font.size.inflation.minTwips,200) load 1032450.html
 load 1032613-1.svg
 load 1032613-2.html
 load 1037903.html
 load 1039454-1.html
 load 1042489.html
 load 1054010-1.html
 load 1058954-1.html
-pref(dom.webcomponents.shadowdom.enabled,true) pref(dom.webcomponents.customelements.enabled,true) load 1059138-1.html
+skip-if(verify&&isDebugBuild&&(gtkWidget||OSX)) pref(dom.webcomponents.shadowdom.enabled,true) pref(dom.webcomponents.customelements.enabled,true) load 1059138-1.html
 load 1134531.html
 load 1134667.html
 load 1137723-1.html
 load 1137723-2.html
 load 1140268-1.html
 load 1145768.html
 load 1145931.html
 load 1146103.html
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1874,17 +1874,17 @@ fails-if(webrender) == 1081185-1.html 10
 fuzzy-if(d2d,36,304) fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu)&&d2d,139,701) == 1116480-1-fakeitalic-overflow.html 1116480-1-fakeitalic-overflow-ref.html
 == 1111753-1.html about:blank
 == 1114526-1.html 1114526-1-ref.html
 fuzzy-if(skiaContent,1,800000) == 1119117-1a.html 1119117-1-ref.html
 fuzzy-if(skiaContent,1,800000) == 1119117-1b.html 1119117-1-ref.html
 == 1120431-1.html 1120431-1-ref.html
 == 1120431-2.html 1120431-2-ref.html
 == 1121748-1.html 1121748-1-ref.html
-== 1121748-2.html 1121748-2-ref.html
+skip-if(verify) == 1121748-2.html 1121748-2-ref.html
 == 1127107-1a-nowrap.html 1127107-1-ref.html
 == 1127107-1b-pre.html 1127107-1-ref.html
 == 1127107-2-capitalize.html 1127107-2-capitalize-ref.html
 == 1127679-1a-inline-flex-relpos.html 1127679-1b-inline-flex-relpos.html
 == 1128354-1.html 1128354-1-ref.html
 == 1130231-1-button-padding-rtl.html 1130231-1-button-padding-rtl-ref.html
 == 1130231-2-button-padding-rtl.html 1130231-2-button-padding-rtl-ref.html
 # The 1133905-*.html reftests only make sense on platforms where both APZ and
--- a/layout/reftests/canvas/reftest.list
+++ b/layout/reftests/canvas/reftest.list
@@ -72,17 +72,17 @@ random != text-emoji.html text-emoji-not
 == dash-sanity.html data:text/html,<body>Pass
 fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu)||azureSkia||skiaContent,9,470) random-if(Android) fails-if(webrender) == dash-1.html dash-1-ref.svg  # Bug 668412 (really is android-specific, not IPC-specific)
 
 == ctm-sanity.html data:text/html,<body>Pass
 == ctm-singular-sanity.html data:text/html,<body>Pass
 == ctm-1.html ctm-1-ref.html
 
 == 672646-alpha-radial-gradient.html 672646-alpha-radial-gradient-ref.html
-== 674003-alpha-radial-gradient-superlum.html 674003-alpha-radial-gradient-superlum-ref.html
+skip-if(verify&&/^Windows\x20NT\x2010\.0/.test(http.oscpu)) == 674003-alpha-radial-gradient-superlum.html 674003-alpha-radial-gradient-superlum-ref.html
 
 != 693610-1.html 693610-1-notref.html # bug 693610: multiple glyph runs should not be overprinted
 
 == 726951-shadow-clips.html 726951-shadow-clips-ref.html
 
 == transformed-clip.html transformed-clip-ref.html
 fuzzy-if(azureSkia,1,15) fuzzy-if(skiaContent,1,20) == transformed-gradient.html transformed-gradient-ref.html
 == transformed-path.html transformed-path.html
--- a/layout/reftests/css-break/reftest.list
+++ b/layout/reftests/css-break/reftest.list
@@ -1,13 +1,13 @@
 default-preferences pref(layout.css.box-decoration-break.enabled,true)
 
 == box-decoration-break-1.html box-decoration-break-1-ref.html
 fuzzy(1,20) fuzzy-if(skiaContent,1,700) fuzzy-if(webrender,21-26,8910-12357) == box-decoration-break-with-inset-box-shadow-1.html box-decoration-break-with-inset-box-shadow-1-ref.html
-fuzzy(45,460) fuzzy-if(skiaContent,57,439) fuzzy-if(Android,57,1330) == box-decoration-break-with-outset-box-shadow-1.html box-decoration-break-with-outset-box-shadow-1-ref.html # Bug 1386543
+skip-if(verify) fuzzy(45,460) fuzzy-if(skiaContent,57,439) fuzzy-if(Android,57,1330) == box-decoration-break-with-outset-box-shadow-1.html box-decoration-break-with-outset-box-shadow-1-ref.html # Bug 1386543
 random-if(!gtkWidget) == box-decoration-break-border-image.html box-decoration-break-border-image-ref.html
 == box-decoration-break-block-border-padding.html box-decoration-break-block-border-padding-ref.html
 == box-decoration-break-block-margin.html box-decoration-break-block-margin-ref.html
 fuzzy-if(!Android,1,62) fuzzy-if(Android,8,6627) random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == box-decoration-break-first-letter.html box-decoration-break-first-letter-ref.html #Bug 1313773 # Bug 1392106
 == box-decoration-break-with-bidi.html box-decoration-break-with-bidi-ref.html
 random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == box-decoration-break-bug-1235152.html box-decoration-break-bug-1235152-ref.html # Bug 1392106
 random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == box-decoration-break-bug-1249913.html box-decoration-break-bug-1249913-ref.html # Bug 1392106
 == vertical-wm-001.html vertical-wm-001-ref.html
--- a/layout/reftests/css-grid/reftest.list
+++ b/layout/reftests/css-grid/reftest.list
@@ -131,17 +131,17 @@ skip-if(Android) == grid-item-video-stre
 skip-if(Android) == grid-item-video-stretch-002.html grid-item-video-stretch-002-ref.html # Huh, Android doesn't have webm support?
 == grid-item-input-stretch-001.html grid-item-input-stretch-001-ref.html
 == grid-item-self-baseline-001.html grid-item-self-baseline-001-ref.html
 random-if(http.oscpu!="Linux\u0020i686") == grid-item-content-baseline-001.html grid-item-content-baseline-001-ref.html # depends on exact Ahem baseline font metrics which seems to differ between platforms: bug 1310792
 random-if(http.oscpu!="Linux\u0020i686") == grid-item-content-baseline-002.html grid-item-content-baseline-002-ref.html # ditto
 random-if(http.oscpu!="Linux\u0020i686") == grid-item-mixed-baseline-001.html grid-item-mixed-baseline-001-ref.html # ditto
 random-if(http.oscpu!="Linux\u0020i686") == grid-item-mixed-baseline-002.html grid-item-mixed-baseline-002-ref.html # ditto
 random-if(http.oscpu!="Linux\u0020i686") == grid-item-mixed-baseline-003.html grid-item-mixed-baseline-003-ref.html # ditto
-skip-if(!gtkWidget) == grid-item-mixed-baseline-004.html grid-item-mixed-baseline-004-ref.html # ditto.
+skip-if(!gtkWidget) skip-if(verify) == grid-item-mixed-baseline-004.html grid-item-mixed-baseline-004-ref.html # ditto.
 == grid-align-content-001.html grid-align-content-001-ref.html
 == grid-justify-content-001.html grid-justify-content-001-ref.html
 skip-if(Android&&isDebugBuild) == grid-justify-content-002.html grid-justify-content-002-ref.html # Bug 1245884 - slow
 skip-if(Android&&isDebugBuild) == grid-justify-content-003.html grid-justify-content-003-ref.html # Bug 1245884 - slow
 skip-if(!gtkWidget) == grid-container-baselines-001.html grid-container-baselines-001-ref.html
 skip-if(!gtkWidget) == grid-container-baselines-002.html grid-container-baselines-002-ref.html
 skip-if(!gtkWidget) == grid-container-baselines-003.html grid-container-baselines-003-ref.html
 == grid-container-baselines-004.html grid-container-baselines-004-ref.html
--- a/layout/reftests/text-overflow/reftest.list
+++ b/layout/reftests/text-overflow/reftest.list
@@ -1,16 +1,16 @@
 == ellipsis-font-fallback.html ellipsis-font-fallback-ref.html
 == line-clipping.html line-clipping-ref.html
 fuzzy-if(Android,16,244) fuzzy-if(webrender,47,6) == marker-basic.html marker-basic-ref.html  # Bug 1128229
 == marker-string.html marker-string-ref.html
 skip-if(Android) fuzzy-if(webrender,47,18) == bidi-simple.html bidi-simple-ref.html # Fails on Android due to anti-aliasing
 skip-if(!gtkWidget) fuzzy-if(gtkWidget,2,289) == bidi-simple-scrolled.html bidi-simple-scrolled-ref.html # Fails on Windows and OSX due to anti-aliasing
 fuzzy-if(Android,24,4000) fuzzy-if(cocoaWidget,1,40) fuzzy-if(asyncPan&&!layersGPUAccelerated,149,1836) == scroll-rounding.html scroll-rounding-ref.html # bug 760264
-fuzzy(16,454) fails-if(gtkWidget) fuzzy-if(webrender&&!gtkWidget,50-85,459-499) == anonymous-block.html anonymous-block-ref.html # gtkWidget:bug 1309103, fuzzy: subpixel aa
+fuzzy(16,454) fails-if(gtkWidget) fuzzy-if(webrender&&!gtkWidget,50-85,459-499) skip-if(OSX&&!isDebugBuild&&verify) == anonymous-block.html anonymous-block-ref.html # gtkWidget:bug 1309103, fuzzy: subpixel aa
 fuzzy-if(webrender,47,3) == false-marker-overlap.html false-marker-overlap-ref.html
 == visibility-hidden.html visibility-hidden-ref.html
 fuzzy-if(asyncPan&&!layersGPUAccelerated,102,1724) fuzzy-if(gtkWidget,10,8) fuzzy-if(webrender,47,24) == block-padding.html block-padding-ref.html
 fuzzy-if(webrender,3,825) == quirks-decorations.html quirks-decorations-ref.html
 == quirks-line-height.html quirks-line-height-ref.html
 == standards-decorations.html standards-decorations-ref.html
 == standards-line-height.html standards-line-height-ref.html
 fuzzy-if(skiaContent,1,4200) fuzzy-if(webrender,47,6) == selection.html selection-ref.html
--- a/mobile/android/confvars.sh
+++ b/mobile/android/confvars.sh
@@ -34,11 +34,8 @@ MOZ_XULRUNNER=
 
 MOZ_CAPTURE=1
 MOZ_RAW=1
 
 # use custom widget for html:select
 MOZ_USE_NATIVE_POPUP_WINDOWS=1
 
 MOZ_APP_ID={aa3c5121-dab2-40e2-81ca-7ea25febc110}
-
-# Enable checking that add-ons are signed by the trusted root
-MOZ_ADDON_SIGNING=1
--- a/modules/libpref/init/StaticPrefList.h
+++ b/modules/libpref/init/StaticPrefList.h
@@ -106,16 +106,21 @@ VARCACHE_PREF(
 )
 
 VARCACHE_PREF(
   "dom.performance.enable_scheduler_timing",
   dom_performance_enable_scheduler_timing,
   RelaxedAtomicBool, false
 )
 
+// If true. then the service worker interception and the ServiceWorkerManager
+// will live in the parent process.  This only takes effect on browser start.
+// Note, this is not currently safe to use for normal browsing yet.
+PREF("dom.serviceWorkers.parent_intercept", bool, false)
+
 //---------------------------------------------------------------------------
 // Full-screen prefs
 //---------------------------------------------------------------------------
 
 #ifdef RELEASE_OR_BETA
 # define PREF_VALUE false
 #else
 # define PREF_VALUE true
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1765,18 +1765,22 @@ pref("network.http.rcwn.min_wait_before_
 pref("network.http.rcwn.max_wait_before_racing_ms", 500);
 
 // The ratio of the transaction count for the focused window and the count of
 // all available active connections.
 pref("network.http.focused_window_transaction_ratio", "0.9");
 
 // Whether or not we give more priority to active tab.
 // Note that this requires restart for changes to take effect.
+#ifdef ANDROID
+// disabled because of bug 1382274
+pref("network.http.active_tab_priority", false);
+#else
 pref("network.http.active_tab_priority", true);
-// </http>
+#endif
 
 // default values for FTP
 // in a DSCP environment this should be 40 (0x28, or AF11), per RFC-4594,
 // Section 4.8 "High-Throughput Data Service Class", and 80 (0x50, or AF22)
 // per Section 4.7 "Low-Latency Data Service Class".
 pref("network.ftp.data.qos", 0);
 pref("network.ftp.control.qos", 0);
 pref("network.ftp.enabled", true);
@@ -2118,17 +2122,22 @@ pref("network.generic-ntlm-auth.workstat
 // credentials, those will be sent automatically in Private Browsing windows.
 //
 // This preference has no effect when the browser is set to "Never Remember History",
 // in that case default credentials will always be used.
 pref("network.auth.private-browsing-sso", false);
 
 // Control how throttling of http responses works - number of ms that each
 // suspend and resume period lasts (prefs named appropriately)
+#ifdef ANDROID
+// disabled because of bug 1382274
+pref("network.http.throttle.enable", false);
+#else
 pref("network.http.throttle.enable", true);
+#endif
 pref("network.http.throttle.version", 2);
 
 // V1 prefs
 pref("network.http.throttle.suspend-for", 900);
 pref("network.http.throttle.resume-for", 100);
 
 // V2 prefs
 pref("network.http.throttle.read-limit-bytes", 8000);
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -9300,16 +9300,44 @@ nsHttpChannel::SetWarningReporter(HttpCh
 
 HttpChannelSecurityWarningReporter*
 nsHttpChannel::GetWarningReporter()
 {
     LOG(("nsHttpChannel [this=%p] GetWarningReporter [%p]", this, mWarningReporter.get()));
     return mWarningReporter.get();
 }
 
+namespace {
+
+class CopyNonDefaultHeaderVisitor final : public nsIHttpHeaderVisitor
+{
+  nsCOMPtr<nsIHttpChannel> mTarget;
+
+  ~CopyNonDefaultHeaderVisitor() = default;
+
+  NS_IMETHOD
+  VisitHeader(const nsACString& aHeader, const nsACString& aValue) override
+  {
+    return mTarget->SetRequestHeader(aHeader, aValue, false /* merge */);
+  }
+
+public:
+  explicit CopyNonDefaultHeaderVisitor(nsIHttpChannel* aTarget)
+    : mTarget(aTarget)
+  {
+    MOZ_DIAGNOSTIC_ASSERT(mTarget);
+  }
+
+  NS_DECL_ISUPPORTS
+};
+
+NS_IMPL_ISUPPORTS(CopyNonDefaultHeaderVisitor, nsIHttpHeaderVisitor)
+
+} // anonymous namespace
+
 nsresult
 nsHttpChannel::RedirectToInterceptedChannel()
 {
     nsCOMPtr<nsINetworkInterceptController> controller;
     GetCallback(controller);
 
     RefPtr<InterceptedHttpChannel> intercepted =
       InterceptedHttpChannel::CreateForInterception(mChannelCreationTime,
@@ -9323,16 +9351,33 @@ nsHttpChannel::RedirectToInterceptedChan
     nsCOMPtr<nsILoadInfo> redirectLoadInfo =
       CloneLoadInfoForRedirect(mURI, nsIChannelEventSink::REDIRECT_INTERNAL);
     intercepted->SetLoadInfo(redirectLoadInfo);
 
     rv = SetupReplacementChannel(mURI, intercepted, true,
                                  nsIChannelEventSink::REDIRECT_INTERNAL);
     NS_ENSURE_SUCCESS(rv, rv);
 
+    // Some APIs, like fetch(), allow content to set non-standard headers.
+    // Normally these APIs are responsible for copying these headers across
+    // redirects.  In the e10s parent-side intercept case, though, we currently
+    // "hide" the internal redirect to the InterceptedHttpChannel.  So the
+    // fetch() API does not have the opportunity to move headers over.
+    // Therefore, we do it automatically here.
+    //
+    // Once child-side interception is removed and the internal redirect no
+    // longer needs to be "hidden", then this header copying code can be
+    // removed.
+    if (ServiceWorkerParentInterceptEnabled()) {
+      nsCOMPtr<nsIHttpHeaderVisitor> visitor =
+        new CopyNonDefaultHeaderVisitor(intercepted);
+      rv = VisitNonDefaultRequestHeaders(visitor);
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+
     mRedirectChannel = intercepted;
 
     PushRedirectAsyncFunc(
         &nsHttpChannel::ContinueAsyncRedirectChannelToURI);
 
     rv = gHttpHandler->AsyncOnChannelRedirect(this, intercepted,
                                               nsIChannelEventSink::REDIRECT_INTERNAL);
 
--- a/old-configure.in
+++ b/old-configure.in
@@ -4115,20 +4115,16 @@ AC_SUBST_LIST(MOZ_FIX_LINK_PATHS)
 
 AC_SUBST(MOZ_POST_PROGRAM_COMMAND)
 AC_SUBST(MOZ_LINKER_EXTRACT)
 
 if test -n "$MOZ_BINARY_EXTENSIONS"; then
   AC_DEFINE(MOZ_BINARY_EXTENSIONS)
 fi
 
-AC_SUBST(MOZ_ADDON_SIGNING)
-if test "$MOZ_ADDON_SIGNING" = 1; then
-  AC_DEFINE(MOZ_ADDON_SIGNING)
-fi
 AC_SUBST(MOZ_REQUIRE_SIGNING)
 if test "$MOZ_REQUIRE_SIGNING" = 1; then
   AC_DEFINE(MOZ_REQUIRE_SIGNING)
 fi
 
 dnl ========================================================
 dnl = Mac bundle name prefix
 dnl ========================================================
--- a/python/mozbuild/mozbuild/mozinfo.py
+++ b/python/mozbuild/mozbuild/mozinfo.py
@@ -86,17 +86,16 @@ def build_dict(config, env=os.environ):
     # FIXME(emilio): We need to update a lot of WPT expectations before removing this.
     d['stylo'] = True
     d['asan'] = substs.get('MOZ_ASAN') == '1'
     d['tsan'] = substs.get('MOZ_TSAN') == '1'
     d['ubsan'] = substs.get('MOZ_UBSAN') == '1'
     d['telemetry'] = substs.get('MOZ_TELEMETRY_REPORTING') == '1'
     d['tests_enabled'] = substs.get('ENABLE_TESTS') == "1"
     d['bin_suffix'] = substs.get('BIN_SUFFIX', '')
-    d['addon_signing'] = substs.get('MOZ_ADDON_SIGNING') == '1'
     d['require_signing'] = substs.get('MOZ_REQUIRE_SIGNING') == '1'
     d['allow_legacy_extensions'] = substs.get('MOZ_ALLOW_LEGACY_EXTENSIONS') == '1'
     d['official'] = bool(substs.get('MOZILLA_OFFICIAL'))
     d['updater'] = substs.get('MOZ_UPDATER') == '1'
     d['artifact'] = substs.get('MOZ_ARTIFACT_BUILDS') == '1'
     d['ccov'] = substs.get('MOZ_CODE_COVERAGE') == '1'
     d['cc_type'] = substs.get('CC_TYPE')
 
--- a/taskcluster/ci/build/windows.yml
+++ b/taskcluster/ci/build/windows.yml
@@ -31,16 +31,22 @@ win32/opt:
     description: "Win32 Opt"
     index:
         product: firefox
         job-name: win32-opt
     treeherder:
         platform: windows2012-32/opt
         symbol: B
         tier: 1
+    stub-installer:
+        # Beta and release use a stub-requiring update channel
+        by-project:
+            default: false
+            mozilla-beta: true
+            mozilla-release: true
     worker-type: aws-provisioner-v1/gecko-{level}-b-win2012
     worker:
         max-run-time: 7200
         env:
             TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/win32/releng.manifest"
     run:
         using: mozharness
         options: [append-env-variables-from-configs]
--- a/taskcluster/ci/test/web-platform.yml
+++ b/taskcluster/ci/test/web-platform.yml
@@ -14,16 +14,17 @@ job-defaults:
                     - remove_executables.py
 
 web-platform-tests:
     description: "Web platform test run"
     suite: web-platform-tests
     treeherder-symbol: W(wpt)
     chunks:
         by-test-platform:
+            linux.*/debug: 18
             macosx64/opt: 5
             macosx64/debug: 10
             windows10-64-ccov/debug: 15
             default: 12
     max-run-time:
         by-test-platform:
             windows10-64-ccov/debug: 10800
             default: 7200
--- a/taskcluster/taskgraph/transforms/build.py
+++ b/taskcluster/taskgraph/transforms/build.py
@@ -31,24 +31,25 @@ def set_defaults(config, jobs):
             worker['chain-of-trust'] = True
 
         yield job
 
 
 @transforms.add
 def stub_installer(config, jobs):
     for job in jobs:
+        resolve_keyed_by(
+            job, 'stub-installer', item_name=job['name'], project=config.params['project']
+        )
         job.setdefault('attributes', {})
         if job.get('stub-installer'):
-            resolve_keyed_by(
-                job, 'stub-installer', item_name=job['name'], project=config.params['project']
-            )
             job['attributes']['stub-installer'] = job['stub-installer']
+            job['worker']['env'].update({"USE_STUB_INSTALLER": "1"})
+        if 'stub-installer' in job:
             del job['stub-installer']
-            job['worker']['env'].update({"USE_STUB_INSTALLER": "1"})
         yield job
 
 
 @transforms.add
 def set_env(config, jobs):
     """Set extra environment variables from try command line."""
     env = []
     if config.params['try_mode'] == 'try_option_syntax':
--- a/testing/mozharness/configs/merge_day/release_to_esr.py
+++ b/testing/mozharness/configs/merge_day/release_to_esr.py
@@ -14,22 +14,16 @@ config = {
         # File, from, to
         ("browser/confvars.sh",
          "ACCEPTED_MAR_CHANNEL_IDS=firefox-mozilla-release",
          "ACCEPTED_MAR_CHANNEL_IDS=firefox-mozilla-esr"),
         ("browser/confvars.sh",
          "MAR_CHANNEL_ID=firefox-mozilla-release",
          "MAR_CHANNEL_ID=firefox-mozilla-esr"),
         ("build/mozconfig.common",
-         "# Enable checking that add-ons are signed by the trusted root",
-         "# Disable checking that add-ons are signed by the trusted root"),
-        ("build/mozconfig.common",
-         "MOZ_ADDON_SIGNING=${MOZ_ADDON_SIGNING-1}",
-         "MOZ_ADDON_SIGNING=${MOZ_ADDON_SIGNING-0}"),
-        ("build/mozconfig.common",
          "# Enable enforcing that add-ons are signed by the trusted root",
          "# Disable enforcing that add-ons are signed by the trusted root"),
         ("build/mozconfig.common",
          "MOZ_REQUIRE_SIGNING=${MOZ_REQUIRE_SIGNING-1}",
          "MOZ_REQUIRE_SIGNING=${MOZ_REQUIRE_SIGNING-0}"),
     ],
     "vcs_share_base": os.path.join(ABS_WORK_DIR, 'hg-shared'),
     # "hg_share_base": None,
--- a/testing/web-platform/meta/2dcontext/drawing-paths-to-the-canvas/drawFocusIfNeeded_004.html.ini
+++ b/testing/web-platform/meta/2dcontext/drawing-paths-to-the-canvas/drawFocusIfNeeded_004.html.ini
@@ -1,9 +1,7 @@
 [drawFocusIfNeeded_004.html]
   disabled:
     if os == "win": https://bugzilla.mozilla.org/show_bug.cgi?id=1092458
   [drawFocusIfNeeded does draw a focus ring if the element is in focus.]
     expected:
-      if not debug and (os == "win") and (version == "6.2.9200") and (processor == "x86_64") and (bits == 64): FAIL
-      if not debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): FAIL
       if not debug and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
 
deleted file mode 100644
--- a/testing/web-platform/meta/2dcontext/fill-and-stroke-styles/2d.gradient.interpolate.overlap.html.ini
+++ /dev/null
@@ -1,8 +0,0 @@
-[2d.gradient.interpolate.overlap.html]
-  [Canvas test: 2d.gradient.interpolate.overlap]
-    expected:
-      if not debug and (os == "win") and (version == "6.2.9200") and (processor == "x86_64") and (bits == 64): FAIL
-      if debug and (os == "win") and (version == "6.2.9200") and (processor == "x86_64") and (bits == 64): FAIL
-      if debug and (os == "win") and (version == "10.0.10240") and (processor == "x86_64") and (bits == 64): FAIL
-      if not debug and (os == "win") and (version == "10.0.10240") and (processor == "x86_64") and (bits == 64): FAIL
-
--- a/testing/web-platform/meta/2dcontext/fill-and-stroke-styles/2d.pattern.image.broken.html.ini
+++ b/testing/web-platform/meta/2dcontext/fill-and-stroke-styles/2d.pattern.image.broken.html.ini
@@ -1,5 +1,3 @@
 [2d.pattern.image.broken.html]
   [Canvas test: 2d.pattern.image.broken]
     disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1016482
-    expected:
-      if not debug and (os == "win") and (version == "6.2.9200") and (processor == "x86_64") and (bits == 64): FAIL
deleted file mode 100644
--- a/testing/web-platform/meta/2dcontext/path-objects/2d.path.arc.selfintersect.1.html.ini
+++ /dev/null
@@ -1,7 +0,0 @@
-[2d.path.arc.selfintersect.1.html]
-  [arc() with lineWidth > 2*radius is drawn sensibly]
-    expected:
-      if (os == "win") and (version == "6.2.9200") and (processor == "x86_64") and (bits == 64): FAIL
-      if debug and (os == "win") and (version == "10.0.10240") and (processor == "x86_64") and (bits == 64): FAIL
-      if not debug and (os == "win") and (version == "10.0.10240") and (processor == "x86_64") and (bits == 64): FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/2dcontext/path-objects/2d.path.arc.shape.3.html.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[2d.path.arc.shape.3.html]
-  [arc() from 0 to -pi/2 does not draw anything in the wrong quadrant]
-    expected:
-      if (os == "win") and (version == "6.2.9200") and (processor == "x86_64") and (bits == 64): FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/2dcontext/path-objects/2d.path.rect.zero.3.html.ini
+++ /dev/null
@@ -1,8 +0,0 @@
-[2d.path.rect.zero.3.html]
-  [Canvas test: 2d.path.rect.zero.3]
-    expected:
-      if not debug and (os == "win") and (version == "6.2.9200") and (processor == "x86_64") and (bits == 64): FAIL
-      if debug and (os == "win") and (version == "6.2.9200") and (processor == "x86_64") and (bits == 64): FAIL
-      if debug and (os == "win") and (version == "10.0.10240") and (processor == "x86_64") and (bits == 64): FAIL
-      if not debug and (os == "win") and (version == "10.0.10240") and (processor == "x86_64") and (bits == 64): FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/2dcontext/path-objects/2d.path.stroke.scale2.html.ini
+++ /dev/null
@@ -1,8 +0,0 @@
-[2d.path.stroke.scale2.html]
-  [Stroke line widths are scaled by the current transformation matrix]
-    expected:
-      if not debug and (os == "win") and (version == "6.2.9200") and (processor == "x86_64") and (bits == 64): FAIL
-      if debug and (os == "win") and (version == "6.2.9200") and (processor == "x86_64") and (bits == 64): FAIL
-      if debug and (os == "win") and (version == "10.0.10240") and (processor == "x86_64") and (bits == 64): FAIL
-      if not debug and (os == "win") and (version == "10.0.10240") and (processor == "x86_64") and (bits == 64): FAIL
-
--- a/testing/web-platform/meta/custom-elements/custom-element-registry/upgrade.html.ini
+++ b/testing/web-platform/meta/custom-elements/custom-element-registry/upgrade.html.ini
@@ -1,16 +1,2 @@
 [upgrade.html]
-  [Upgrading an element directly (example from the spec)]
-    expected: FAIL
-
-  [Two elements as children of the upgraded node]
-    expected: FAIL
-
-  [Two elements as descendants of the upgraded node]
-    expected: FAIL
-
-  [Two elements as shadow-including descendants (and not descendants) of the upgraded node]
-    expected: FAIL
-
-  [Elements inside a template contents DocumentFragment node]
-    expected: FAIL
-
+  prefs: [dom.webcomponents.shadowdom.enabled:true]
--- a/testing/web-platform/meta/html/dom/interfaces.https.html.ini
+++ b/testing/web-platform/meta/html/dom/interfaces.https.html.ini
@@ -1398,11 +1398,8 @@
     expected: FAIL
 
   [HTMLFrameSetElement interface: document.createElement("frameset") must inherit property "onrejectionhandled" with the proper type]
     expected: FAIL
 
   [HTMLFrameSetElement interface: document.createElement("frameset") must inherit property "onunhandledrejection" with the proper type]
     expected: FAIL
 
-  [CustomElementRegistry interface: operation upgrade(Node)]
-    expected: FAIL
-
--- a/toolkit/components/extensions/ExtensionTestCommon.jsm
+++ b/toolkit/components/extensions/ExtensionTestCommon.jsm
@@ -152,17 +152,37 @@ class MockExtension {
     return this._extensionPromise.then(extension => {
       return extension.broadcast("Extension:FlushJarCache", {path: this.file.path});
     }).then(() => {
       return OS.File.remove(this.file.path);
     });
   }
 }
 
+function provide(obj, keys, value, override = false) {
+  if (keys.length == 1) {
+    if (!(keys[0] in obj) || override) {
+      obj[keys[0]] = value;
+    }
+  } else {
+    if (!(keys[0] in obj)) {
+      obj[keys[0]] = {};
+    }
+    provide(obj[keys[0]], keys.slice(1), value, override);
+  }
+}
+
 var ExtensionTestCommon = class ExtensionTestCommon {
+  static generateManifest(manifest) {
+    provide(manifest, ["name"], "Generated extension");
+    provide(manifest, ["manifest_version"], 2);
+    provide(manifest, ["version"], "1.0");
+    return manifest;
+  }
+
   /**
    * This code is designed to make it easy to test a WebExtension
    * without creating a bunch of files. Everything is contained in a
    * single JSON blob.
    *
    * Properties:
    *   "background": "<JS code>"
    *     A script to be loaded as the background script.
@@ -189,29 +209,16 @@ var ExtensionTestCommon = class Extensio
   static generateXPI(data) {
     let manifest = data.manifest;
     if (!manifest) {
       manifest = {};
     }
 
     let files = Object.assign({}, data.files);
 
-    function provide(obj, keys, value, override = false) {
-      if (keys.length == 1) {
-        if (!(keys[0] in obj) || override) {
-          obj[keys[0]] = value;
-        }
-      } else {
-        if (!(keys[0] in obj)) {
-          obj[keys[0]] = {};
-        }
-        provide(obj[keys[0]], keys.slice(1), value, override);
-      }
-    }
-
     provide(manifest, ["name"], "Generated extension");
     provide(manifest, ["manifest_version"], 2);
     provide(manifest, ["version"], "1.0");
 
     if (data.background) {
       let bgScript = uuidGen.generateUUID().number + ".js";
 
       provide(manifest, ["background", "scripts"], [bgScript], true);
--- a/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js
@@ -672,17 +672,17 @@ function checkSystemSection(data) {
           Assert.equal(typeof product, "string", "Each element of " + f + " must be a string");
         }
       }
     }
   }
 }
 
 function checkActiveAddon(data, partialRecord) {
-  let signedState = mozinfo.addon_signing ? "number" : "undefined";
+  let signedState = "number";
   // system add-ons have an undefined signState
   if (data.isSystem)
     signedState = "undefined";
 
   const EXPECTED_ADDON_FIELDS_TYPES = {
     version: "string",
     scope: "number",
     type: "string",
@@ -1213,17 +1213,17 @@ add_task(async function test_addonsAndPl
     appDisabled: false,
     version: "1.0",
     scope: 1,
     type: "extension",
     foreignInstall: false,
     hasBinaryComponents: false,
     installDay: ADDON_INSTALL_DATE,
     updateDay: ADDON_INSTALL_DATE,
-    signedState: mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_PRIVILEGED : AddonManager.SIGNEDSTATE_NOT_REQUIRED,
+    signedState: AddonManager.SIGNEDSTATE_PRIVILEGED,
     isSystem: false,
     isWebExtension: true,
     multiprocessCompatible: true,
   };
   const SYSTEM_ADDON_ID = "tel-system-xpi@tests.mozilla.org";
   const EXPECTED_SYSTEM_ADDON_DATA = {
     blocklisted: false,
     description: "A system addon which is shipped with Firefox.",
@@ -1253,17 +1253,17 @@ add_task(async function test_addonsAndPl
     appDisabled: false,
     version: "1.0",
     scope: 1,
     type: "extension",
     foreignInstall: false,
     hasBinaryComponents: false,
     installDay: WEBEXTENSION_ADDON_INSTALL_DATE,
     updateDay: WEBEXTENSION_ADDON_INSTALL_DATE,
-    signedState: mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_PRIVILEGED : AddonManager.SIGNEDSTATE_NOT_REQUIRED,
+    signedState: AddonManager.SIGNEDSTATE_PRIVILEGED,
     isSystem: false,
     isWebExtension: true,
     multiprocessCompatible: true,
   };
 
   const EXPECTED_PLUGIN_DATA = {
     name: FLASH_PLUGIN_NAME,
     version: FLASH_PLUGIN_VERSION,
@@ -1370,17 +1370,17 @@ add_task(async function test_signedAddon
     appDisabled: false,
     version: "1.0",
     scope: 1,
     type: "extension",
     foreignInstall: false,
     hasBinaryComponents: false,
     installDay: ADDON_INSTALL_DATE,
     updateDay: ADDON_INSTALL_DATE,
-    signedState: mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED,
+    signedState: AddonManager.SIGNEDSTATE_SIGNED,
   };
 
   let deferred = PromiseUtils.defer();
   TelemetryEnvironment.registerChangeListener("test_signedAddon", deferred.resolve);
 
   // Install the addon.
   let addon = await installXPIFromURL(ADDON_INSTALL_URL);
 
@@ -1455,18 +1455,17 @@ add_task(async function test_collectionW
     appDisabled: false,
     version: "1.0",
     scope: 1,
     type: "extension",
     foreignInstall: false,
     hasBinaryComponents: false,
     installDay: ADDON_INSTALL_DATE,
     updateDay: ADDON_INSTALL_DATE,
-    signedState: mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_MISSING :
-                                         AddonManager.SIGNEDSTATE_NOT_REQUIRED,
+    signedState: AddonManager.SIGNEDSTATE_MISSING,
   };
 
   let receivedNotifications = 0;
 
   let registerCheckpointPromise = (aExpected) => {
     return new Promise(resolve => TelemetryEnvironment.registerChangeListener(
       "testBrokenAddon_collection" + aExpected, (reason, data) => {
         Assert.equal(reason, "addons-changed");
--- a/toolkit/modules/AppConstants.jsm
+++ b/toolkit/modules/AppConstants.jsm
@@ -217,23 +217,16 @@ this.AppConstants = Object.freeze({
 
   MOZ_PLACES:
 #ifdef MOZ_PLACES
   true,
 #else
   false,
 #endif
 
-  MOZ_ADDON_SIGNING:
-#ifdef MOZ_ADDON_SIGNING
-  true,
-#else
-  false,
-#endif
-
   MOZ_REQUIRE_SIGNING:
 #ifdef MOZ_REQUIRE_SIGNING
   true,
 #else
   false,
 #endif
 
   MOZ_ALLOW_LEGACY_EXTENSIONS:
--- a/toolkit/mozapps/extensions/AddonManager.jsm
+++ b/toolkit/mozapps/extensions/AddonManager.jsm
@@ -2048,43 +2048,16 @@ var AddonManagerInternal = {
     if (!(aFile instanceof Ci.nsIFile))
       throw Components.Exception("aFile must be a nsIFile",
                                  Cr.NS_ERROR_INVALID_ARG);
 
     return AddonManagerInternal._getProviderByName("XPIProvider")
                                .installTemporaryAddon(aFile);
   },
 
-  /**
-   * Returns an Addon corresponding to an instance ID.
-   * @param aInstanceID
-   *        An Addon Instance ID symbol
-   * @return {Promise}
-   * @resolves The found Addon or null if no such add-on exists.
-   * @rejects  Never
-   * @throws if the aInstanceID argument is not specified
-   *         or the AddonManager is not initialized
-   */
-   async getAddonByInstanceID(aInstanceID) {
-     return this.syncGetAddonByInstanceID(aInstanceID);
-   },
-
-   syncGetAddonByInstanceID(aInstanceID) {
-     if (!gStarted)
-       throw Components.Exception("AddonManager is not initialized",
-                                  Cr.NS_ERROR_NOT_INITIALIZED);
-
-     if (!aInstanceID || typeof aInstanceID != "symbol")
-       throw Components.Exception("aInstanceID must be a Symbol()",
-                                  Cr.NS_ERROR_INVALID_ARG);
-
-     return AddonManagerInternal._getProviderByName("XPIProvider")
-                                .getAddonByInstanceID(aInstanceID);
-   },
-
    syncGetAddonIDByInstanceID(aInstanceID) {
      if (!gStarted)
        throw Components.Exception("AddonManager is not initialized",
                                   Cr.NS_ERROR_NOT_INITIALIZED);
 
      if (!aInstanceID || typeof aInstanceID != "symbol")
        throw Components.Exception("aInstanceID must be a Symbol()",
                                   Cr.NS_ERROR_INVALID_ARG);
@@ -3366,20 +3339,16 @@ var AddonManager = {
   installAddonFromAOM(aBrowser, aUri, aInstall) {
     AddonManagerInternal.installAddonFromAOM(aBrowser, aUri, aInstall);
   },
 
   installTemporaryAddon(aDirectory) {
     return AddonManagerInternal.installTemporaryAddon(aDirectory);
   },
 
-  getAddonByInstanceID(aInstanceID) {
-    return AddonManagerInternal.getAddonByInstanceID(aInstanceID);
-  },
-
   addManagerListener(aListener) {
     AddonManagerInternal.addManagerListener(aListener);
   },
 
   removeManagerListener(aListener) {
     AddonManagerInternal.removeManagerListener(aListener);
   },
 
--- a/toolkit/mozapps/extensions/internal/AddonSettings.jsm
+++ b/toolkit/mozapps/extensions/internal/AddonSettings.jsm
@@ -21,18 +21,16 @@ function makeConstant(name, value) {
   Object.defineProperty(AddonSettings, name, {
     configurable: false,
     enumerable: false,
     writable: false,
     value,
   });
 }
 
-makeConstant("ADDON_SIGNING", AppConstants.MOZ_ADDON_SIGNING);
-
 if (AppConstants.MOZ_REQUIRE_SIGNING && !Cu.isInAutomation) {
   makeConstant("REQUIRE_SIGNING", true);
   makeConstant("LANGPACKS_REQUIRE_SIGNING", true);
 } else {
   XPCOMUtils.defineLazyPreferenceGetter(AddonSettings, "REQUIRE_SIGNING",
                                         PREF_SIGNATURES_REQUIRED, false);
   XPCOMUtils.defineLazyPreferenceGetter(AddonSettings, "LANGPACKS_REQUIRE_SIGNING",
                                         PREF_LANGPACK_SIGNATURES, false);
--- a/toolkit/mozapps/extensions/internal/AddonTestUtils.jsm
+++ b/toolkit/mozapps/extensions/internal/AddonTestUtils.jsm
@@ -744,16 +744,29 @@ var AddonTestUtils = {
 
 
     if (newVersion)
       this.appInfo.version = newVersion;
 
     let XPIScope = ChromeUtils.import("resource://gre/modules/addons/XPIProvider.jsm", null);
     XPIScope.AsyncShutdown = MockAsyncShutdown;
 
+    XPIScope.XPIInternal.BootstrapScope.prototype
+      ._beforeCallBootstrapMethod = (method, params, reason) => {
+        try {
+          this.emit("bootstrap-method", {method, params, reason});
+        } catch (e) {
+          try {
+            this.testScope.do_throw(e);
+          } catch (e) {
+            // Le sigh.
+          }
+        }
+      };
+
     this.addonIntegrationService = Cc["@mozilla.org/addons/integration;1"]
           .getService(Ci.nsIObserver);
 
     this.addonIntegrationService.observe(null, "addons-startup", null);
 
     this.emit("addon-manager-started");
 
     // Load the add-ons list as it was after extension registration
@@ -876,17 +889,17 @@ var AddonTestUtils = {
     rdf += "</RDF>\n";
 
     return rdf;
   },
 
   _writeProps(obj, props, indent = "  ") {
     let items = [];
     for (let prop of props) {
-      if (prop in obj)
+      if (obj[prop] !== undefined)
         items.push(escaped`${indent}<em:${prop}>${obj[prop]}</em:${prop}>\n`);
     }
     return items.join("");
   },
 
   _writeArrayProps(obj, props, indent = "  ") {
     let items = [];
     for (let prop of props) {
@@ -901,22 +914,37 @@ var AddonTestUtils = {
 
     items.push(this._writeProps(data, ["name", "description", "creator", "homepageURL"]));
     items.push(this._writeArrayProps(data, ["developer", "translator", "contributor"]));
 
     return items.join("");
   },
 
   createInstallRDF(data) {
+    let defaults = {
+      bootstrap: true,
+      version: "1.0",
+      name: `Test Extension ${data.id}`,
+      targetApplications: [
+        {
+          "id": "xpcshell@tests.mozilla.org",
+          "minVersion": "1",
+          "maxVersion": "64.*",
+        },
+      ],
+    };
+
     var rdf = '<?xml version="1.0"?>\n';
     rdf += '<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"\n' +
            '     xmlns:em="http://www.mozilla.org/2004/em-rdf#">\n';
 
     rdf += '<Description about="urn:mozilla:install-manifest">\n';
 
+    data = Object.assign({}, defaults, data);
+
     let props = ["id", "version", "type", "internalName", "updateURL",
                  "optionsURL", "optionsType", "aboutURL", "iconURL", "icon64URL",
                  "skinnable", "bootstrap", "strictCompatibility",
                  "hasEmbeddedWebExtension"];
     rdf += this._writeProps(data, props);
 
     rdf += this._writeLocaleStrings(data);
 
@@ -1009,16 +1037,18 @@ var AddonTestUtils = {
 
       // Create parent directories, if necessary.
       let dirPath = dir;
       for (let subDir of path) {
         dirPath = OS.Path.join(dirPath, subDir);
         await OS.File.makeDir(dirPath, {ignoreExisting: true});
       }
 
+      if (typeof data == "object" && ChromeUtils.getClassName(data) == "Object")
+        data = JSON.stringify(data);
       if (typeof data == "string")
         data = new TextEncoder("utf-8").encode(data);
 
       await OS.File.writeAtomic(OS.Path.join(dirPath, leafName), data);
     }
 
     return nsFile(dir);
   },
--- a/toolkit/mozapps/extensions/internal/XPIDatabase.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIDatabase.jsm
@@ -1676,20 +1676,16 @@ this.XPIDatabase = {
     return this.asyncLoadDB()
       .then(addonDB => getRepositoryAddon(_findAddon(addonDB, aFilter)))
       .catch(
         error => {
           logger.error("getAddon failed", error);
         });
   },
 
-  syncGetAddon(aFilter) {
-    return _findAddon(this.addonDB, aFilter);
-  },
-
   /**
    * Asynchronously gets an add-on with a particular ID in a particular
    * install location.
    *
    * @param {string} aId
    *        The ID of the add-on to retrieve
    * @param {string} aLocation
    *        The name of the install location
@@ -1717,20 +1713,16 @@ this.XPIDatabase = {
    * @param {string} aId
    *        The ID of the add-on to retrieve
    * @returns {Promise<AddonInternal?>}
    */
   getVisibleAddonForID(aId) {
     return this.getAddon(aAddon => ((aAddon.id == aId) && aAddon.visible));
   },
 
-  syncGetVisibleAddonForID(aId) {
-    return this.syncGetAddon(aAddon => ((aAddon.id == aId) && aAddon.visible));
-  },
-
   /**
    * Asynchronously gets the visible add-ons, optionally restricting by type.
    *
    * @param {Set<string>?} aTypes
    *        An array of types to include or null to include all types
    * @returns {Promise<Array<AddonInternal>>}
    */
   getVisibleAddons(aTypes) {
@@ -1796,36 +1788,16 @@ this.XPIDatabase = {
    * @returns {Addon?}
    */
   async getAddonByID(aId) {
     let aAddon = await this.getVisibleAddonForID(aId);
     return aAddon ? aAddon.wrapper : null;
   },
 
   /**
-   * Synchronously returns the Addon object for the add-on with the
-   * given ID.
-   *
-   * *DO NOT USE THIS IF YOU CAN AT ALL AVOID IT*
-   *
-   * This will always return null if the add-on database has not been
-   * loaded, and the resulting Addon object may not yet include a
-   * reference to its corresponding repository add-on object.
-   *
-   * @param {string} aId
-   *        The ID of the add-on to return.
-   * @returns {DBAddonInternal?}
-   *        The Addon object, if available.
-   */
-  syncGetAddonByID(aId) {
-    let aAddon = this.syncGetVisibleAddonForID(aId);
-    return aAddon ? aAddon.wrapper : null;
-  },
-
-  /**
    * Obtain an Addon having the specified Sync GUID.
    *
    * @param {string} aGUID
    *        String GUID of add-on to retrieve
    * @returns {Addon?}
    */
   async getAddonBySyncGUID(aGUID) {
     let addon = await this.getAddon(aAddon => aAddon.syncGUID == aGUID);
@@ -2565,17 +2537,16 @@ this.XPIDatabaseReconcile = {
    *        the addon manifests. Default to false.
    * @returns {AddonInternal}
    *        The new addon.
    */
   updateCompatibility(aLocation, aOldAddon, aAddonState, aReloadMetadata) {
     logger.debug(`Updating compatibility for add-on ${aOldAddon.id} in ${aLocation.name}`);
 
     let checkSigning = (aOldAddon.signedState === undefined &&
-                        AddonSettings.ADDON_SIGNING &&
                         SIGNED_TYPES.has(aOldAddon.type));
 
     let manifest = null;
     if (checkSigning || aReloadMetadata) {
       try {
         let file = new nsIFile(aAddonState.path);
         manifest = XPIInstall.syncLoadManifestFromFile(file, aLocation);
       } catch (err) {
--- a/toolkit/mozapps/extensions/internal/XPIInstall.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIInstall.jsm
@@ -28,17 +28,16 @@ var EXPORTED_SYMBOLS = [
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/AddonManager.jsm");
 
 XPCOMUtils.defineLazyGlobalGetters(this, ["TextDecoder", "TextEncoder", "fetch"]);
 
 XPCOMUtils.defineLazyModuleGetters(this, {
   AddonRepository: "resource://gre/modules/addons/AddonRepository.jsm",
-  AddonSettings: "resource://gre/modules/addons/AddonSettings.jsm",
   AppConstants: "resource://gre/modules/AppConstants.jsm",
   CertUtils: "resource://gre/modules/CertUtils.jsm",
   ExtensionData: "resource://gre/modules/Extension.jsm",
   FileUtils: "resource://gre/modules/FileUtils.jsm",
   NetUtil: "resource://gre/modules/NetUtil.jsm",
   OS: "resource://gre/modules/osfile.jsm",
   ProductAddonChecker: "resource://gre/modules/addons/ProductAddonChecker.jsm",
   UpdateUtils: "resource://gre/modules/UpdateUtils.jsm",
@@ -955,19 +954,19 @@ function shouldVerifySignedState(aAddon)
   // Updated system add-ons should always have their signature checked
   if (aAddon.location.name == KEY_APP_SYSTEM_ADDONS)
     return true;
 
   // We don't care about signatures for default system add-ons
   if (aAddon.location.name == KEY_APP_SYSTEM_DEFAULTS)
     return false;
 
-  // Otherwise only check signatures if signing is enabled and the add-on is one
-  // of the signed types.
-  return AddonSettings.ADDON_SIGNING && XPIDatabase.SIGNED_TYPES.has(aAddon.type);
+  // Otherwise only check signatures if the add-on is one of the signed
+  // types.
+  return XPIDatabase.SIGNED_TYPES.has(aAddon.type);
 }
 
 /**
  * Verifies that a bundle's contents are all correctly signed by an
  * AMO-issued certificate
  *
  * @param {nsIFile} aBundle
  *        The nsIFile for the bundle to check, either a directory or zip file.
--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
@@ -187,17 +187,16 @@ function awaitPromise(promise) {
     result = val;
   });
 
   Services.tm.spinEventLoopUntil(() => success !== undefined);
 
   if (!success)
     throw result;
   return result;
-
 }
 
 /**
  * Returns a nsIFile instance for the given path, relative to the given
  * base file, if provided.
  *
  * @param {string} path
  *        The (possibly relative) path of the file.
@@ -827,17 +826,16 @@ class DirectoryLocation extends XPIState
    * @param {integer} scope
    *        The scope of add-ons installed in this location.
    * @param {boolean} [locked = true]
    *        If false, the location accepts new add-on installs.
    */
   constructor(name, dir, scope, locked = true) {
     super(name, dir, scope);
     this.locked = locked;
-    this.initialized = false;
   }
 
   makeInstaller() {
     if (this.locked) {
       return null;
     }
     return new XPIInstall.DirectoryInstaller(this);
   }
@@ -906,17 +904,16 @@ class DirectoryLocation extends XPIState
    *        A map of add-ons present in this location.
    */
   readAddons() {
     let addons = new Map();
 
     if (!this.dir) {
       return addons;
     }
-    this.initialized = true;
 
     // Use a snapshot of the directory contents to avoid possible issues with
     // iterating over a directory while removing files from it (the YAFFS2
     // embedded filesystem has this issue, see bug 772238).
     for (let entry of Array.from(iterDirectory(this.dir))) {
       let id = getExpectedID(entry);
       if (!id) {
         if (![DIR_STAGE, DIR_TRASH].includes(entry.leafName))
@@ -1580,16 +1577,18 @@ class BootstrapScope {
       }
 
       let result;
       if (!method) {
         logger.warn(`Add-on ${addon.id} is missing bootstrap method ${aMethod}`);
       } else {
         logger.debug(`Calling bootstrap method ${aMethod} on ${addon.id} version ${addon.version}`);
 
+        this._beforeCallBootstrapMethod(aMethod, params, aReason);
+
         try {
           result = method.call(scope, params, aReason);
         } catch (e) {
           logger.warn(`Exception running bootstrap method ${aMethod} on ${addon.id}`, e);
         }
       }
       return result;
     } finally {
@@ -1603,16 +1602,19 @@ class BootstrapScope {
       if (addon.type == "extension" && aMethod == "shutdown" &&
           aReason != BOOTSTRAP_REASONS.APP_SHUTDOWN) {
         logger.debug(`Removing manifest for ${this.file.path}`);
         Components.manager.removeBootstrappedManifestLocation(this.file);
       }
     }
   }
 
+  // No-op method to be overridden by tests.
+  _beforeCallBootstrapMethod() {}
+
   /**
    * Loads a bootstrapped add-on's bootstrap.js into a sandbox and the reason
    * values as constants in the scope.
    *
    * @param {integer?} [aReason]
    *        The reason this bootstrap is being loaded, as passed to a
    *        bootstrap method.
    */
@@ -1849,18 +1851,16 @@ var XPIProvider = {
   get name() {
     return "XPIProvider";
   },
 
   BOOTSTRAP_REASONS: Object.freeze(BOOTSTRAP_REASONS),
 
   // A Map of active addons to their bootstrapScope by ID
   activeAddons: new Map(),
-  // True if the platform could have activated extensions
-  extensionsActive: false,
   // Per-addon telemetry information
   _telemetryDetails: {},
   // Have we started shutting down bootstrap add-ons?
   _closing: false,
 
   // Check if the XPIDatabase has been loaded (without actually
   // triggering unwanted imports or I/O)
   get isDBLoaded() {
@@ -2229,18 +2229,16 @@ var XPIProvider = {
         };
         for (let event of EVENTS) {
           Services.obs.addObserver(observer, event);
         }
       }
 
       AddonManagerPrivate.recordTimestamp("XPI_startup_end");
 
-      this.extensionsActive = true;
-
       timerManager.registerTimer("xpi-signature-verification", () => {
         XPIDatabase.verifySignatures();
       }, XPI_SIGNATURE_CHECK_PERIOD);
     } catch (e) {
       logger.error("startup failed", e);
       AddonManagerPrivate.recordException("XPI", "startup failed", e);
     }
   },
@@ -2275,19 +2273,16 @@ var XPIProvider = {
 
     // Ugh, if we reach this point without loading the xpi database,
     // we need to load it know, otherwise the telemetry shutdown blocker
     // will never resolve.
     if (!XPIDatabase.initialized) {
       await XPIDatabase.asyncLoadDB();
     }
 
-    // This is needed to allow xpcshell tests to simulate a restart
-    this.extensionsActive = false;
-
     await XPIDatabase.shutdown();
   },
 
   cleanupTemporaryAddons() {
     let promises = [];
     let tempLocation = TemporaryInstallLocation;
     for (let [id, addon] of tempLocation.entries()) {
       tempLocation.delete(id);
@@ -2574,17 +2569,17 @@ var XPIProvider = {
       // We detected changes. Update the database to account for them.
       await XPIDatabase.asyncLoadDB(false);
       XPIDatabaseReconcile.processFileChanges({}, false);
       XPIDatabase.updateActiveAddons();
     }
 
     let addons = await Promise.all(
       Array.from(XPIStates.sideLoadedAddons.keys(),
-                 id => AddonManager.getAddonByID(id)));
+                 id => this.getAddonByID(id)));
 
     return addons.filter(addon => (addon.seen === false &&
                                    addon.permissions & AddonManager.PERM_CAN_ENABLE));
   },
 
   /**
    * Called to test whether this provider supports installing a particular
    * mimetype.
@@ -2615,35 +2610,16 @@ var XPIProvider = {
    *        The data to store.  Must be JSON serializable.
    */
   setStartupData(aID, aData) {
     let state = XPIStates.findAddon(aID);
     state.startupData = aData;
     XPIStates.save();
   },
 
-  /**
-   * Returns an Addon corresponding to an instance ID.
-   *
-   * @param {Symbol} aInstanceID
-   *        An Addon Instance ID
-   *
-   * @returns {AddonInternal?}
-   *
-   * @throws if the aInstanceID argument is not valid.
-   */
-   getAddonByInstanceID(aInstanceID) {
-     let id = this.getAddonIDByInstanceID(aInstanceID);
-     if (id) {
-       return XPIDatabase.syncGetAddonByID(id);
-     }
-
-     return null;
-   },
-
    getAddonIDByInstanceID(aInstanceID) {
      if (!aInstanceID || typeof aInstanceID != "symbol")
        throw Components.Exception("aInstanceID must be a Symbol()",
                                   Cr.NS_ERROR_INVALID_ARG);
 
      for (let [id, val] of this.activeAddons) {
        if (aInstanceID == val.instanceID) {
          return id;
@@ -2665,39 +2641,29 @@ var XPIProvider = {
    *
    * @param {Array<string>?} aTypes
    *        An array of types to fetch. Can be null to get all types.
    * @returns {Promise<Array<Addon>>}
    */
   async getActiveAddons(aTypes) {
     // If we already have the database loaded, returning full info is fast.
     if (this.isDBLoaded) {
-      let addons = await XPIProvider.getAddonsByTypes(aTypes);
+      let addons = await this.getAddonsByTypes(aTypes);
       return {
         addons: addons.filter(addon => addon.isActive),
         fullData: true,
       };
     }
 
-    // Construct addon-like objects with the information we already
-    // have in memory.
-    if (!XPIStates.db) {
-      throw new Error("XPIStates not yet initialized");
-    }
-
     let result = [];
     for (let addon of XPIStates.enabledAddons()) {
       if (aTypes && !aTypes.includes(addon.type)) {
         continue;
       }
-      let {location} = addon;
-      let scope, isSystem;
-      if (location) {
-        ({scope, isSystem} = location);
-      }
+      let {scope, isSystem} = addon.location;
       result.push({
         id: addon.id,
         version: addon.version,
         type: addon.type,
         updateDate: addon.lastModifiedTime,
         scope,
         isSystem,
         isWebExtension: isWebExtension(addon),
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/Makefile.in
+++ /dev/null
@@ -1,20 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-ADDONSRC = $(srcdir)/addons
-TESTROOT = $(CURDIR)/$(DEPTH)/_tests/xpcshell/$(relativesrcdir)
-TESTXPI = $(TESTROOT)/xpcshell/addons
-
-include $(topsrcdir)/config/rules.mk
-
-libs::
-	rm -rf $(TESTXPI)
-	$(NSINSTALL) -D $(TESTXPI)
-	if [ -d $(ADDONSRC) ]; then \
-		$(EXIT_ON_ERROR) \
-		for dir in $(ADDONSRC)/*; do \
-			base=`basename $$dir` ; \
-			(cd $$dir && zip -qr $(TESTXPI)/$$base.xpi *) \
-		done \
-	fi
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/bootstrap_globals/bootstrap.js
+++ /dev/null
@@ -1,30 +0,0 @@
-/* exported startup, shutdown, install, uninstall */
-ChromeUtils.import("resource://gre/modules/Services.jsm");
-
-var seenGlobals = new Set();
-var scope = this;
-function checkGlobal(name, type) {
-  if (scope[name] && typeof(scope[name]) == type)
-    seenGlobals.add(name);
-}
-
-var wrapped = {};
-Services.obs.notifyObservers({ wrappedJSObject: wrapped }, "bootstrap-request-globals");
-for (let [name, type] of wrapped.expectedGlobals) {
-  checkGlobal(name, type);
-}
-
-function install(data, reason) {
-}
-
-function startup(data, reason) {
-  Services.obs.notifyObservers({
-    wrappedJSObject: seenGlobals
-  }, "bootstrap-seen-globals");
-}
-
-function shutdown(data, reason) {
-}
-
-function uninstall(data, reason) {
-}
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/bootstrap_globals/install.rdf
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0"?>
-
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-
-  <Description about="urn:mozilla:install-manifest">
-    <em:id>bootstrap_globals@tests.mozilla.org</em:id>
-    <em:version>1.0</em:version>
-    <em:bootstrap>true</em:bootstrap>
-
-    <!-- Front End MetaData -->
-    <em:name>Test Bootstrap Globals</em:name>
-
-    <em:targetApplication>
-      <Description>
-        <em:id>xpcshell@tests.mozilla.org</em:id>
-        <em:minVersion>1</em:minVersion>
-        <em:maxVersion>1</em:maxVersion>
-      </Description>
-    </em:targetApplication>
-
-  </Description>
-</RDF>
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/langpack_1/browser/localization/und/browser.ftl
+++ /dev/null
@@ -1,1 +0,0 @@
-message-browser = Value from Browser
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/langpack_1/chrome/und/locale/und/global/test.properties
+++ /dev/null
@@ -1,1 +0,0 @@
-message = Value from .properties
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/langpack_1/localization/und/toolkit_test.ftl
+++ /dev/null
@@ -1,1 +0,0 @@
-message-id1 = Value 1
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/langpack_1/manifest.json
+++ /dev/null
@@ -1,29 +0,0 @@
-{
-  "name": "und Language Pack",
-  "version": "1.0",
-  "manifest_version": 2,
-  "applications": {
-    "gecko": {
-      "id": "langpack-und@test.mozilla.org",
-      "strict_min_version": "58.0",
-      "strict_max_version": "58.*"
-    }
-  },
-  "sources": {
-    "browser": {
-      "base_path": "browser/"
-    }
-  },
-  "langpack_id": "und",
-  "version": "1",
-  "languages": {
-    "und": {
-      "chrome_resources": {
-        "global": "chrome/und/locale/und/global/"
-      },
-      "version": "20171001190118"
-    }
-  },
-  "author": "Mozilla Localization Task Force",
-  "description": "Language pack for Testy for und"
-}
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_bootstrap1_1/bootstrap.js
+++ /dev/null
@@ -1,1 +0,0 @@
-ChromeUtils.import("resource://xpcshell-data/BootstrapMonitor.jsm").monitor(this);
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_bootstrap1_1/install.rdf
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0"?>
-
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-
-  <Description about="urn:mozilla:install-manifest">
-    <em:id>bootstrap1@tests.mozilla.org</em:id>
-    <em:version>1.0</em:version>
-    <em:bootstrap>true</em:bootstrap>
-    <em:multiprocessCompatible>true</em:multiprocessCompatible>
-
-    <!-- Front End MetaData -->
-    <em:name>Test Bootstrap 1</em:name>
-    <em:description>Test Description</em:description>
-
-    <em:iconURL>chrome://foo/skin/icon.png</em:iconURL>
-    <em:aboutURL>chrome://foo/content/about.xul</em:aboutURL>
-    <em:optionsURL>chrome://foo/content/options.xul</em:optionsURL>
-
-    <em:targetApplication>
-      <Description>
-        <em:id>xpcshell@tests.mozilla.org</em:id>
-        <em:minVersion>1</em:minVersion>
-        <em:maxVersion>1</em:maxVersion>
-      </Description>
-    </em:targetApplication>
-
-  </Description>
-</RDF>
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_bootstrap1_1/version.jsm
+++ /dev/null
@@ -1,3 +0,0 @@
-var EXPORTED_SYMBOLS = ["VERSION"];
-
-var VERSION = 1;
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_bootstrap1_2/bootstrap.js
+++ /dev/null
@@ -1,1 +0,0 @@
-ChromeUtils.import("resource://xpcshell-data/BootstrapMonitor.jsm").monitor(this);
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_bootstrap1_2/install.rdf
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0"?>
-
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-
-  <Description about="urn:mozilla:install-manifest">
-    <em:id>bootstrap1@tests.mozilla.org</em:id>
-    <em:version>2.0</em:version>
-    <em:bootstrap>true</em:bootstrap>
-
-    <!-- Front End MetaData -->
-    <em:name>Test Bootstrap 1</em:name>
-    <em:description>Test Description</em:description>
-
-    <em:targetApplication>
-      <Description>
-        <em:id>xpcshell@tests.mozilla.org</em:id>
-        <em:minVersion>1</em:minVersion>
-        <em:maxVersion>1</em:maxVersion>
-      </Description>
-    </em:targetApplication>
-
-  </Description>
-</RDF>
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_bootstrap1_2/version.jsm
+++ /dev/null
@@ -1,3 +0,0 @@
-var EXPORTED_SYMBOLS = ["VERSION"];
-
-var VERSION = 2;
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_bootstrap_const/bootstrap.js
+++ /dev/null
@@ -1,6 +0,0 @@
-/* exported install */
-ChromeUtils.import("resource://gre/modules/Services.jsm");
-
-const install = function() {
-  Services.obs.notifyObservers(null, "addon-install");
-};
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_bootstrap_const/install.rdf
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0"?>
-
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-
-  <Description about="urn:mozilla:install-manifest">
-    <em:id>bootstrap@tests.mozilla.org</em:id>
-    <em:version>1.0</em:version>
-    <em:bootstrap>true</em:bootstrap>
-
-    <!-- Front End MetaData -->
-    <em:name>Test Bootstrap</em:name>
-    <em:description>Test Description</em:description>
-
-    <em:targetApplication>
-      <Description>
-        <em:id>xpcshell@tests.mozilla.org</em:id>
-        <em:minVersion>1</em:minVersion>
-        <em:maxVersion>1</em:maxVersion>
-      </Description>
-    </em:targetApplication>
-
-  </Description>
-</RDF>
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_bug567173/install.rdf
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0"?>
-
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-
-  <Description about="urn:mozilla:install-manifest">
-    <em:id>bug567173</em:id>
-    <em:version>1.0</em:version>
-
-    <em:targetApplication>
-      <Description>
-        <em:id>xpcshell@tests.mozilla.org</em:id>
-        <em:minVersion>1</em:minVersion>
-        <em:maxVersion>1</em:maxVersion>
-      </Description>
-    </em:targetApplication>
-    
-    <!-- Front End MetaData -->
-    <em:name>Test Bug 567173</em:name>
-
-  </Description>
-</RDF>
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_cache_certdb/bootstrap.js
+++ /dev/null
@@ -1,70 +0,0 @@
-const CERTDB_CONTRACTID = "@mozilla.org/security/x509certdb;1";
-const CERTDB_CID = Components.ID("{fb0bbc5c-452e-4783-b32c-80124693d871}");
-
-ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
-
-const CERT = `MIIDITCCAgmgAwIBAgIJALAv8fydd6nBMA0GCSqGSIb3DQEBBQUAMCcxJTAjBgNV
-BAMMHGJvb3RzdHJhcDFAdGVzdHMubW96aWxsYS5vcmcwHhcNMTYwMjAyMjMxNjUy
-WhcNMjYwMTMwMjMxNjUyWjAnMSUwIwYDVQQDDBxib290c3RyYXAxQHRlc3RzLm1v
-emlsbGEub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5caNuLTu
-H8dEqNntLlhKi4y09hrgcF3cb6n5Xx9DIHA8CKiZxt9qGXKeeiDwEiiQ8ibJYzdc
-jLkbzJUyPVUaH9ygrWynSpSTOvv/Ys3+ERrCo9W7Zuzwdmzt6TTEjFMS4lVx06us
-3uUqkdp3JMgCqCEbOFZiztICiSKrp8QFJkAfApZzBqmJOPOWH0yZ2CRRzvbQZ6af
-hqQDUalJQjWfsenyUWphhbREqExetxHJFR3OrmJt/shXVyz6dD7TBuE3PPUh1RpE
-3ejVufcTzjV3XmK79PxsKLM9V2+ww9e9V3OET57kyvn+bpSWdUYm3X4DA8dxNW6+
-kTFWRnQNZ+zQVQIDAQABo1AwTjAdBgNVHQ4EFgQUac36ccv+99N5HxYa8dCDYRaF
-HNQwHwYDVR0jBBgwFoAUac36ccv+99N5HxYa8dCDYRaFHNQwDAYDVR0TBAUwAwEB
-/zANBgkqhkiG9w0BAQUFAAOCAQEAFfu3MN8EtY5wcxOFdGShOmGQPm2MJJVE6MG+
-p4RqHrukHZSgKOyWjkRk7t6NXzNcnHco9HFv7FQRAXSJ5zObmyu+TMZlu4jHHCav
-GMcV3C/4SUGtlipZbgNe00UAIm6tM3Wh8dr38W7VYg4KGAwXou5XhQ9gCAnSn90o
-H/42NqHTjJsR4v18izX2aO25ARQdMby7Lsr5j9RqweHywiSlPusFcKRseqOnIP0d
-JT3+qh78LeMbNBO2mYD3SP/zu0TAmkAVNcj2KPw0+a0kVZ15rvslPC/K3xn9msMk
-fQthv3rDAcsWvi9YO7T+vylgZBgJfn1ZqpQqy58xN96uh6nPOw==`;
-
-function overrideCertDB() {
-  // Unregister the real database.
-  let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
-  let factory = registrar.getClassObject(CERTDB_CID, Ci.nsIFactory);
-  registrar.unregisterFactory(CERTDB_CID, factory);
-
-  // Get the real DB
-  let realCertDB = factory.createInstance(null, Ci.nsIX509CertDB);
-
-  let fakeCert = realCertDB.constructX509FromBase64(CERT.replace(/\n/g, ""));
-
-  let fakeCertDB = {
-    openSignedAppFileAsync(root, file, callback) {
-      callback.openSignedAppFileFinished(Cr.NS_OK, null, fakeCert);
-    },
-
-    QueryInterface: ChromeUtils.generateQI([Ci.nsIX509CertDB])
-  };
-
-  for (let property of Object.keys(realCertDB)) {
-    if (property in fakeCertDB) {
-      continue;
-    }
-
-    if (typeof realCertDB[property] == "function") {
-      fakeCertDB[property] = realCertDB[property].bind(realCertDB);
-    }
-  }
-
-  let certDBFactory = {
-    createInstance(outer, iid) {
-      if (outer != null) {
-        throw Cr.NS_ERROR_NO_AGGREGATION;
-      }
-      return fakeCertDB.QueryInterface(iid);
-    }
-  };
-  registrar.registerFactory(CERTDB_CID, "CertDB",
-                            CERTDB_CONTRACTID, certDBFactory);
-
-  const scope = ChromeUtils.import("resource://gre/modules/addons/XPIProvider.jsm", {});
-  scope.gCertDB = fakeCertDB;
-}
-
-function install() { // eslint-disable-line no-unused-vars
-  overrideCertDB();
-}
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_cache_certdb/install.rdf
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0"?>
-
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-
-  <Description about="urn:mozilla:install-manifest">
-    <em:id>bootstrap1@tests.mozilla.org</em:id>
-    <em:version>1.0</em:version>
-    <em:bootstrap>true</em:bootstrap>
-    <em:multiprocessCompatible>true</em:multiprocessCompatible>
-
-    <!-- Front End MetaData -->
-    <em:name>Test Bootstrap 1</em:name>
-    <em:description>Test Description</em:description>
-
-    <em:targetApplication>
-      <Description>
-        <em:id>xpcshell@tests.mozilla.org</em:id>
-        <em:minVersion>1</em:minVersion>
-        <em:maxVersion>1</em:maxVersion>
-      </Description>
-    </em:targetApplication>
-
-  </Description>
-</RDF>
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_delay_update_complete_v2/bootstrap.js
+++ /dev/null
@@ -1,11 +0,0 @@
-/* exported startup, shutdown, install, ADDON_ID */
-ChromeUtils.import("resource://gre/modules/Services.jsm");
-ChromeUtils.import("resource://gre/modules/AddonManager.jsm");
-
-const ADDON_ID = "test_delay_update_complete@tests.mozilla.org";
-
-function install(data, reason) {}
-
-function startup(data, reason) {}
-
-function shutdown(data, reason) {}
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_delay_update_complete_v2/install.rdf
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0"?>
-
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-
-  <Description about="urn:mozilla:install-manifest">
-    <em:id>test_delay_update_complete@tests.mozilla.org</em:id>
-    <em:version>2.0</em:version>
-    <em:bootstrap>true</em:bootstrap>
-
-    <!-- Front End MetaData -->
-    <em:name>Test Delay Update Complete</em:name>
-    <em:description>Test Description</em:description>
-
-    <em:iconURL>chrome://foo/skin/icon.png</em:iconURL>
-    <em:aboutURL>chrome://foo/content/about.xul</em:aboutURL>
-    <em:optionsURL>chrome://foo/content/options.xul</em:optionsURL>
-
-    <em:targetApplication>
-      <Description>
-        <em:id>xpcshell@tests.mozilla.org</em:id>
-        <em:minVersion>1</em:minVersion>
-        <em:maxVersion>1</em:maxVersion>
-      </Description>
-    </em:targetApplication>
-
-  </Description>
-</RDF>
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_delay_update_complete_webextension_v2/manifest.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-  "manifest_version": 2,
-  "name": "Delay Upgrade",
-  "version": "2.0",
-  "applications": {
-    "gecko": {
-      "id": "test_delay_update_complete_webext@tests.mozilla.org"
-    }
-  }
-}
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_delay_update_defer_v2/bootstrap.js
+++ /dev/null
@@ -1,11 +0,0 @@
-/* exported startup, shutdown, install, ADDON_ID */
-ChromeUtils.import("resource://gre/modules/Services.jsm");
-ChromeUtils.import("resource://gre/modules/AddonManager.jsm");
-
-const ADDON_ID = "test_delay_update_defer@tests.mozilla.org";
-
-function install(data, reason) {}
-
-function startup(data, reason) {}
-
-function shutdown(data, reason) {}
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_delay_update_defer_v2/install.rdf
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0"?>
-
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-
-  <Description about="urn:mozilla:install-manifest">
-    <em:id>test_delay_update_defer@tests.mozilla.org</em:id>
-    <em:version>2.0</em:version>
-    <em:bootstrap>true</em:bootstrap>
-
-    <!-- Front End MetaData -->
-    <em:name>Test Delay Update Defer</em:name>
-    <em:description>Test Description</em:description>
-
-    <em:iconURL>chrome://foo/skin/icon.png</em:iconURL>
-    <em:aboutURL>chrome://foo/content/about.xul</em:aboutURL>
-    <em:optionsURL>chrome://foo/content/options.xul</em:optionsURL>
-
-    <em:targetApplication>
-      <Description>
-        <em:id>xpcshell@tests.mozilla.org</em:id>
-        <em:minVersion>1</em:minVersion>
-        <em:maxVersion>1</em:maxVersion>
-      </Description>
-    </em:targetApplication>
-
-  </Description>
-</RDF>
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_delay_update_defer_webextension_v2/manifest.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-  "manifest_version": 2,
-  "name": "Delay Upgrade",
-  "version": "2.0",
-  "applications": {
-    "gecko": {
-      "id": "test_delay_update_defer_webext@tests.mozilla.org"
-    }
-  }
-}
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_delay_update_ignore_v2/bootstrap.js
+++ /dev/null
@@ -1,9 +0,0 @@
-/* exported startup, shutdown, install */
-ChromeUtils.import("resource://gre/modules/Services.jsm");
-ChromeUtils.import("resource://gre/modules/AddonManager.jsm");
-
-function install(data, reason) {}
-
-function startup(data, reason) {}
-
-function shutdown(data, reason) {}
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_delay_update_ignore_v2/install.rdf
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0"?>
-
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-
-  <Description about="urn:mozilla:install-manifest">
-    <em:id>test_delay_update_ignore@tests.mozilla.org</em:id>
-    <em:version>2.0</em:version>
-    <em:bootstrap>true</em:bootstrap>
-
-    <!-- Front End MetaData -->
-    <em:name>Test Delay Update Ignore</em:name>
-    <em:description>Test Description</em:description>
-
-    <em:iconURL>chrome://foo/skin/icon.png</em:iconURL>
-    <em:aboutURL>chrome://foo/content/about.xul</em:aboutURL>
-    <em:optionsURL>chrome://foo/content/options.xul</em:optionsURL>
-    <em:updateURL>http://localhost:4444/data/test_delay_updates_ignore.rdf</em:updateURL>
-
-    <em:targetApplication>
-      <Description>
-        <em:id>xpcshell@tests.mozilla.org</em:id>
-        <em:minVersion>1</em:minVersion>
-        <em:maxVersion>1</em:maxVersion>
-      </Description>
-    </em:targetApplication>
-
-  </Description>
-</RDF>
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_delay_update_ignore_webextension_v2/manifest.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-  "manifest_version": 2,
-  "name": "Delay Upgrade",
-  "version": "2.0",
-  "applications": {
-    "gecko": {
-      "id": "test_delay_update_ignore_webext@tests.mozilla.org"
-    }
-  }
-}
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_dictionary/chrome.manifest
+++ /dev/null
@@ -1,1 +0,0 @@
-content dict ./
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_dictionary/dictionaries/ab-CD.dic
+++ /dev/null
@@ -1,2 +0,0 @@
-1
-test1
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_dictionary/install.rdf
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0"?>
-
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-
-  <Description about="urn:mozilla:install-manifest">
-    <em:id>ab-CD@dictionaries.addons.mozilla.org</em:id>
-    <em:version>1.0</em:version>
-    <em:type>64</em:type>
-    <em:unpack>true</em:unpack>
-
-    <!-- Front End MetaData -->
-    <em:name>Test Dictionary</em:name>
-    <em:description>Test Description</em:description>
-
-    <em:targetApplication>
-      <Description>
-        <em:id>xpcshell@tests.mozilla.org</em:id>
-        <em:minVersion>1</em:minVersion>
-        <em:maxVersion>1</em:maxVersion>
-      </Description>
-    </em:targetApplication>
-
-  </Description>
-</RDF>
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_dictionary_3/install.rdf
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0"?>
-
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-
-  <Description about="urn:mozilla:install-manifest">
-    <em:id>ab-CD@dictionaries.addons.mozilla.org</em:id>
-    <em:version>2.0</em:version>
-    <em:type>64</em:type>
-    <em:unpack>true</em:unpack>
-
-    <!-- Front End MetaData -->
-    <em:name>Test Dictionary</em:name>
-    <em:description>Test Description</em:description>
-
-    <em:targetApplication>
-      <Description>
-        <em:id>xpcshell@tests.mozilla.org</em:id>
-        <em:minVersion>1</em:minVersion>
-        <em:maxVersion>1</em:maxVersion>
-      </Description>
-    </em:targetApplication>
-
-  </Description>
-</RDF>
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_dictionary_4/install.rdf
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0"?>
-
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-
-  <Description about="urn:mozilla:install-manifest">
-    <em:id>ef@dictionaries.addons.mozilla.org</em:id>
-    <em:version>2.0</em:version>
-    <em:unpack>true</em:unpack>
-
-    <!-- Front End MetaData -->
-    <em:name>Test Dictionary ef</em:name>
-    <em:description>Test Description</em:description>
-
-    <em:targetApplication>
-      <Description>
-        <em:id>xpcshell@tests.mozilla.org</em:id>
-        <em:minVersion>1</em:minVersion>
-        <em:maxVersion>1</em:maxVersion>
-      </Description>
-    </em:targetApplication>
-
-  </Description>
-</RDF>
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_dictionary_5/install.rdf
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0"?>
-
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-
-  <Description about="urn:mozilla:install-manifest">
-    <em:id>gh@dictionaries.addons.mozilla.org</em:id>
-    <em:version>2.0</em:version>
-    <em:type>64</em:type>
-    <em:unpack>true</em:unpack>
-
-    <!-- Front End MetaData -->
-    <em:name>Test Dictionary gh</em:name>
-    <em:description>Test Description</em:description>
-
-    <em:targetApplication>
-      <Description>
-        <em:id>xpcshell@tests.mozilla.org</em:id>
-        <em:minVersion>1</em:minVersion>
-        <em:maxVersion>1</em:maxVersion>
-      </Description>
-    </em:targetApplication>
-
-  </Description>
-</RDF>
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_distribution1_2/install.rdf
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0"?>
-
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-
-  <Description about="urn:mozilla:install-manifest">
-    <em:id>addon1@tests.mozilla.org</em:id>
-    <em:version>2.0</em:version>
-
-    <!-- Front End MetaData -->
-    <em:name>Distributed add-ons test</em:name>
-    <em:bootstrap>true</em:bootstrap>
-
-    <em:targetApplication>
-      <Description>
-        <em:id>xpcshell@tests.mozilla.org</em:id>
-        <em:minVersion>1</em:minVersion>
-        <em:maxVersion>5</em:maxVersion>
-      </Description>
-    </em:targetApplication>
-
-  </Description>
-</RDF>
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_getresource/icon.png
+++ /dev/null
@@ -1,1 +0,0 @@
-Dummy icon file
\ No newline at end of file
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_getresource/install.rdf
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0"?>
-
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-
-  <Description about="urn:mozilla:install-manifest">
-    <em:id>addon1@tests.mozilla.org</em:id>
-    <em:version>1.0</em:version>
-
-    <!-- Front End MetaData -->
-    <em:name>Test 1</em:name>
-    <em:description>Test Description</em:description>
-    <em:bootstrap>true</em:bootstrap>
-    <em:targetApplication>
-      <Description>
-        <em:id>xpcshell@tests.mozilla.org</em:id>
-        <em:minVersion>1</em:minVersion>
-        <em:maxVersion>1</em:maxVersion>
-      </Description>
-    </em:targetApplication>
-
-  </Description>
-</RDF>
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_getresource/subdir/subfile.txt
+++ /dev/null
@@ -1,1 +0,0 @@
-Dummy file in subdirectory
\ No newline at end of file
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_install1/icon.png
+++ /dev/null
@@ -1,1 +0,0 @@
-Fake icon image
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_install1/icon64.png
+++ /dev/null
@@ -1,1 +0,0 @@
-Fake icon image
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_install1/install.rdf
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0"?>
-
-<!-- An extension that is compatible with the XPCShell test suite -->
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-
-  <Description about="urn:mozilla:install-manifest">
-    <em:id>addon1@tests.mozilla.org</em:id>
-    <em:version>1.0</em:version>
-
-    <!-- Front End MetaData -->
-    <em:name>Test 1</em:name>
-    <em:description>Test Description</em:description>
-
-    <em:targetApplication>
-      <Description>
-        <em:id>xpcshell@tests.mozilla.org</em:id>
-        <em:minVersion>1</em:minVersion>
-        <em:maxVersion>1</em:maxVersion>
-      </Description>
-    </em:targetApplication>
-
-  </Description>
-</RDF>
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_locale/install.rdf
+++ /dev/null
@@ -1,62 +0,0 @@
-<?xml version="1.0"?>
-
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-
-  <Description about="urn:mozilla:install-manifest">
-    <em:id>addon1@tests.mozilla.org</em:id>
-    <em:version>1.0</em:version>
-    <em:bootstrap>true</em:bootstrap>
-
-    <em:targetApplication>
-      <Description>
-        <em:id>xpcshell@tests.mozilla.org</em:id>
-        <em:minVersion>1</em:minVersion>
-        <em:maxVersion>1</em:maxVersion>
-      </Description>
-    </em:targetApplication>
-
-    <em:localized>
-      <Description em:locale="fr-FR">
-        <em:locale/>                    <!-- Should be ignored and not fail -->
-        <em:name>fr-FR Name</em:name>
-        <em:description>fr-FR Description</em:description>
-        <em:contributor>Fr Contributor 1</em:contributor>
-        <em:contributor>Fr Contributor 2</em:contributor>
-        <em:contributor>Fr Contributor 3</em:contributor>
-      </Description>
-    </em:localized>
-
-    <em:localized>
-      <Description em:locale="de-DE">
-        <em:name>de-DE Name</em:name>
-      </Description>
-    </em:localized>
-
-    <em:localized>
-      <Description em:locale="es-ES">
-        <em:name>es-ES Name</em:name>
-        <em:description>es-ES Description</em:description>
-      </Description>
-    </em:localized>
-
-    <!-- Subsequent definitions for the same locale should be ignored -->
-    <em:localized>
-      <Description em:locale="fr-FR">
-        <em:name>Repeated locale</em:name>
-      </Description>
-    </em:localized>
-
-    <!-- Properties with no listed locale should be ignored -->
-    <em:localized>
-      <Description>
-        <em:name>Missing locale</em:name>
-      </Description>
-    </em:localized>
-
-    <!-- Front End MetaData -->
-    <em:name>Fallback Name</em:name>
-    <em:description>Fallback Description</em:description>
-
-  </Description>
-</RDF>
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_symbol/bootstrap.js
+++ /dev/null
@@ -1,63 +0,0 @@
-/* exported startup, shutdown, install, uninstall, ADDON_ID */
-ChromeUtils.import("resource://gre/modules/Services.jsm");
-ChromeUtils.import("resource://gre/modules/AddonManager.jsm");
-
-const PASS_PREF = "symboltest.instanceid.pass";
-const FAIL_BOGUS_PREF = "symboltest.instanceid.fail_bogus";
-const FAIL_ID_PREF = "symboltest.instanceid.fail_bogus";
-const ADDON_ID = "test_symbol@tests.mozilla.org";
-
-function install(data, reason) {}
-
-// normally we would use BootstrapMonitor here, but we need a reference to
-// the symbol inside `XPIProvider.jsm`.
-function startup(data, reason) {
-  Services.prefs.setBoolPref(PASS_PREF, false);
-  Services.prefs.setBoolPref(FAIL_BOGUS_PREF, false);
-  Services.prefs.setBoolPref(FAIL_ID_PREF, false);
-
-  // test with the correct symbol
-  if (data.hasOwnProperty("instanceID") && data.instanceID) {
-    AddonManager.getAddonByInstanceID(data.instanceID)
-      .then(addon => {
-        if (addon.id == ADDON_ID) {
-          Services.prefs.setBoolPref(PASS_PREF, true);
-        }
-      }).catch(err => {
-        throw Error("no addon found for symbol");
-      });
-
-  }
-
-  // test with a totally bogus symbol
-  AddonManager.getAddonByInstanceID(Symbol("bad symbol"))
-    .then(addon => {
-      // there is no symbol by this name, so null should be returned
-      if (addon == null) {
-        Services.prefs.setBoolPref(FAIL_BOGUS_PREF, true);
-      } else {
-        throw Error("bad symbol should not match:", addon);
-      }
-    }).catch(err => {
-      throw Error("promise should not have rejected: " + err);
-    });
-
-  // try to make a matching symbol - this should fail because it's not a
-  // reference to the same symbol stored inside the addons manager.
-  AddonManager.getAddonByInstanceID(Symbol(ADDON_ID))
-    .then(addon => {
-      // there is no symbol by this name, so null should be returned
-      if (addon == null) {
-        Services.prefs.setBoolPref(FAIL_ID_PREF, true);
-      } else {
-        throw Error("bad symbol should not match:", addon);
-      }
-    }).catch(err => {
-      throw Error("promise should not have rejected: " + err);
-    });
-
-}
-
-function shutdown(data, reason) {}
-
-function uninstall(data, reason) {}
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_symbol/install.rdf
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0"?>
-
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-
-  <Description about="urn:mozilla:install-manifest">
-    <em:id>test_symbol@tests.mozilla.org</em:id>
-    <em:version>1.0</em:version>
-    <em:bootstrap>true</em:bootstrap>
-
-    <!-- Front End MetaData -->
-    <em:name>Test Symbol</em:name>
-    <em:description>Test Description</em:description>
-
-    <em:iconURL>chrome://foo/skin/icon.png</em:iconURL>
-    <em:aboutURL>chrome://foo/content/about.xul</em:aboutURL>
-    <em:optionsURL>chrome://foo/content/options.xul</em:optionsURL>
-
-    <em:targetApplication>
-      <Description>
-        <em:id>xpcshell@tests.mozilla.org</em:id>
-        <em:minVersion>1</em:minVersion>
-        <em:maxVersion>1</em:maxVersion>
-      </Description>
-    </em:targetApplication>
-
-  </Description>
-</RDF>
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_undoincompatible/bootstrap.js
+++ /dev/null
@@ -1,1 +0,0 @@
-ChromeUtils.import("resource://xpcshell-data/BootstrapMonitor.jsm").monitor(this);
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_undoincompatible/install.rdf
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0"?>
-
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-
-  <Description about="urn:mozilla:install-manifest">
-    <em:id>incompatible@tests.mozilla.org</em:id>
-    <em:version>1.0</em:version>
-    <em:bootstrap>true</em:bootstrap>
-
-    <!-- Front End MetaData -->
-    <em:name>Incompatible Addon</em:name>
-    <em:description>I am incompatible</em:description>
-
-    <em:iconURL>chrome://foo/skin/icon.png</em:iconURL>
-    <em:aboutURL>chrome://foo/content/about.xul</em:aboutURL>
-    <em:optionsURL>chrome://foo/content/options.xul</em:optionsURL>
-
-    <em:targetApplication>
-      <Description>
-        <em:id>xpcshell@tests.mozilla.org</em:id>
-        <em:minVersion>2</em:minVersion>
-        <em:maxVersion>2</em:maxVersion>
-      </Description>
-    </em:targetApplication>
-
-  </Description>
-</RDF>
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_undouninstall1/bootstrap.js
+++ /dev/null
@@ -1,1 +0,0 @@
-ChromeUtils.import("resource://xpcshell-data/BootstrapMonitor.jsm").monitor(this);
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_undouninstall1/install.rdf
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0"?>
-
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-
-  <Description about="urn:mozilla:install-manifest">
-    <em:id>undouninstall1@tests.mozilla.org</em:id>
-    <em:version>1.0</em:version>
-    <em:bootstrap>true</em:bootstrap>
-
-    <!-- Front End MetaData -->
-    <em:name>Test Bootstrap 1</em:name>
-    <em:description>Test Description</em:description>
-
-    <em:iconURL>chrome://foo/skin/icon.png</em:iconURL>
-    <em:aboutURL>chrome://foo/content/about.xul</em:aboutURL>
-    <em:optionsURL>chrome://foo/content/options.xul</em:optionsURL>
-
-    <em:targetApplication>
-      <Description>
-        <em:id>xpcshell@tests.mozilla.org</em:id>
-        <em:minVersion>1</em:minVersion>
-        <em:maxVersion>1</em:maxVersion>
-      </Description>
-    </em:targetApplication>
-
-  </Description>
-</RDF>
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_updateid1/bootstrap.js
+++ /dev/null
@@ -1,6 +0,0 @@
-/* exported startup, shutdown, install, uninstall */
-
-function install(data, reason) {}
-function startup(data, reason) {}
-function shutdown(data, reason) {}
-function uninstall(data, reason) {}
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_updateid1/install.rdf
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0"?>
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-<Description about="urn:mozilla:install-manifest">
-  <em:id>addon1@tests.mozilla.org</em:id>
-  <em:version>1.0</em:version>
-  <em:updateURL>http://example.com/data/test_updateid.rdf</em:updateURL>
-  <em:bootstrap>true</em:bootstrap>
-  <em:name>Test Addon 1</em:name>
-<em:targetApplication><Description>
-  <em:id>xpcshell@tests.mozilla.org</em:id>
-  <em:minVersion>1</em:minVersion>
-  <em:maxVersion>1</em:maxVersion>
-</Description></em:targetApplication>
-</Description>
-</RDF>
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_updateid2/bootstrap.js
+++ /dev/null
@@ -1,6 +0,0 @@
-/* exported startup, shutdown, install, uninstall */
-
-function install(data, reason) {}
-function startup(data, reason) {}
-function shutdown(data, reason) {}
-function uninstall(data, reason) {}
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_updateid2/install.rdf
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0"?>
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-<Description about="urn:mozilla:install-manifest">
-  <em:id>addon1.changed@tests.mozilla.org</em:id>
-  <em:version>2.0</em:version>
-  <em:updateURL>http://localhost:4444/data/test_updateid.rdf</em:updateURL>
-  <em:bootstrap>true</em:bootstrap>
-  <em:name>Test Addon 1</em:name>
-<em:targetApplication><Description>
-  <em:id>xpcshell@tests.mozilla.org</em:id>
-  <em:minVersion>1</em:minVersion>
-  <em:maxVersion>1</em:maxVersion>
-</Description></em:targetApplication>
-</Description>
-</RDF>
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/webextension_1/chrome.manifest
+++ /dev/null
@@ -1,1 +0,0 @@
-content webex ./
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/webextension_1/manifest.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-  "name": "Web Extension Name",
-  "version": "1.0",
-  "manifest_version": 2,
-  "applications": {
-    "gecko": {
-      "id": "webextension1@tests.mozilla.org"
-    }
-  },
-  "icons": {
-    "48": "icon48.png",
-    "64": "icon64.png"
-  }
-}
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/webextension_3/_locales/en/messages.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-  "name": {
-    "message": "foo ☹",
-    "description": "foo"
-  },
-  "desc": {
-    "message": "bar ☹",
-    "description": "bar"
-  }
-}
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/webextension_3/_locales/fr/messages.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-  "name": {
-    "message": "le foo ☺",
-    "description": "foo"
-  },
-  "desc": {
-    "message": "le bar ☺",
-    "description": "bar"
-  }
-}
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/webextension_3/manifest.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
-  "name": "Web Extensiøn __MSG_name__",
-  "description": "Descriptïon __MSG_desc__ of add-on",
-  "version": "1.0",
-  "manifest_version": 2,
-  "default_locale": "en",
-  "applications": {
-    "gecko": {
-      "id": "webextension3@tests.mozilla.org"
-    }
-  }
-}
--- a/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
@@ -98,16 +98,29 @@ const {
 } = AddonTestUtils;
 
 // WebExtension wrapper for ease of testing
 ExtensionTestUtils.init(this);
 
 AddonTestUtils.init(this);
 AddonTestUtils.overrideCertDB();
 
+XPCOMUtils.defineLazyGetter(this, "BOOTSTRAP_REASONS",
+                            () => AddonManagerPrivate.BOOTSTRAP_REASONS);
+
+function getReasonName(reason) {
+  for (let key of Object.keys(BOOTSTRAP_REASONS)) {
+    if (BOOTSTRAP_REASONS[key] == reason) {
+      return key;
+    }
+  }
+  throw new Error("This shouldn't happen.");
+}
+
+
 Object.defineProperty(this, "gAppInfo", {
   get() {
     return AddonTestUtils.appInfo;
   },
 });
 
 Object.defineProperty(this, "gAddonStartup", {
   get() {
@@ -184,16 +197,28 @@ function isManifestRegistered(file) {
     }
 
     if (manifest.equals(file))
       return true;
   }
   return false;
 }
 
+const BOOTSTRAP_MONITOR_BOOTSTRAP_JS = `
+  ChromeUtils.import("resource://xpcshell-data/BootstrapMonitor.jsm").monitor(this);
+`;
+
+
+const EMPTY_BOOTSTRAP_JS = `
+  function startup() {}
+  function shutdown() {}
+  function install() {}
+  function uninstall() {}
+`;
+
 // Listens to messages from bootstrap.js telling us what add-ons were started
 // and stopped etc. and performs some sanity checks that only installed add-ons
 // are started etc.
 this.BootstrapMonitor = {
   inited: false,
 
   // Contain the current state of add-ons in the system
   installed: new Map(),
@@ -346,16 +371,142 @@ this.BootstrapMonitor = {
         resolve();
       this.startupPromises = [];
     }
   }
 };
 
 AddonTestUtils.on("addon-manager-shutdown", () => BootstrapMonitor.shutdownCheck());
 
+var SlightlyLessDodgyBootstrapMonitor = {
+  started: new Map(),
+  stopped: new Map(),
+  installed: new Map(),
+  uninstalled: new Map(),
+
+  init() {
+    this.onEvent = this.onEvent.bind(this);
+
+    AddonTestUtils.on("addon-manager-shutdown", this.onEvent);
+    AddonTestUtils.on("bootstrap-method", this.onEvent);
+  },
+
+  shutdownCheck() {
+    equal(this.started.size, 0,
+          "Should have no add-ons that were started but not shutdown");
+  },
+
+  onEvent(msg, data) {
+    switch (msg) {
+      case "addon-manager-shutdown":
+        this.shutdownCheck();
+        break;
+      case "bootstrap-method":
+        this.onBootstrapMethod(data.method, data.params, data.reason);
+        break;
+    }
+  },
+
+  onBootstrapMethod(method, params, reason) {
+    let {id} = params;
+
+    info(`Bootstrap method ${method} for ${params.id} version ${params.version}`);
+
+    if (method !== "install") {
+      this.checkInstalled(id);
+    }
+
+    switch (method) {
+      case "install":
+        this.checkNotInstalled(id);
+        this.installed.set(id, {reason, params});
+        this.uninstalled.delete(id);
+        break;
+      case "startup":
+        this.checkNotStarted(id);
+        this.started.set(id, {reason, params});
+        this.stopped.delete(id);
+        break;
+      case "shutdown":
+        this.checkMatches("shutdown", "startup", params, this.started.get(id));
+        this.checkStarted(id);
+        this.stopped.set(id, {reason, params});
+        this.started.delete(id);
+        break;
+      case "uninstall":
+        this.checkMatches("uninstall", "install", params, this.installed.get(id));
+        this.uninstalled.set(id, {reason, params});
+        this.installed.delete(id);
+        break;
+      case "update":
+        this.checkMatches("update", "install", params, this.installed.get(id));
+        this.installed.set(id, {reason, params});
+        break;
+    }
+  },
+
+  clear(id) {
+    this.installed.delete(id);
+    this.started.delete(id);
+    this.stopped.delete(id);
+    this.uninstalled.delete(id);
+  },
+
+  checkMatches(method, lastMethod, params, {params: lastParams} = {}) {
+    ok(lastParams,
+       `Expecting matching ${lastMethod} call for add-on ${params.id} ${method} call`);
+
+    if (method == "update") {
+      equal(params.oldVersion, lastParams.version,
+            "params.version should match last call");
+    } else {
+      equal(params.version, lastParams.version,
+            "params.version should match last call");
+    }
+
+    if (method !== "update" && method !== "uninstall") {
+      equal(params.installPath.path, lastParams.installPath.path,
+            `params.installPath should match last call`);
+
+      ok(params.resourceURI.equals(lastParams.resourceURI),
+         `params.resourceURI should match: "${params.resourceURI.spec}" == "${lastParams.resourceURI.spec}"`);
+    }
+  },
+
+  checkStarted(id, version = undefined) {
+    let started = this.started.get(id);
+    ok(started, `Should have seen startup method call for ${id}`);
+
+    if (version !== undefined)
+      equal(started.params.version, version,
+            "Expected version number");
+  },
+
+  checkNotStarted(id) {
+    ok(!this.started.has(id),
+       `Should not have seen startup method call for ${id}`);
+  },
+
+  checkInstalled(id, version = undefined) {
+    const installed = this.installed.get(id);
+    ok(installed, `Should have seen install call for ${id}`);
+
+    if (version !== undefined)
+      equal(installed.params.version, version,
+            "Expected version number");
+
+    return installed;
+  },
+
+  checkNotInstalled(id) {
+    ok(!this.installed.has(id),
+       `Should not have seen install method call for ${id}`);
+  },
+};
+
 function isNightlyChannel() {
   var channel = Services.prefs.getCharPref("app.update.channel", "default");
 
   return channel != "aurora" && channel != "beta" && channel != "release" && channel != "esr";
 }
 
 
 async function restartWithLocales(locales) {
@@ -453,32 +604,16 @@ function do_check_not_in_crash_annotatio
     Assert.ok(true);
     return;
   }
 
   let addons = gAppInfo.annotations["Add-ons"].split(",");
   Assert.ok(!addons.includes(`${encodeURIComponent(aId)}:${encodeURIComponent(aVersion)}`));
 }
 
-/**
- * Returns a testcase xpi
- *
- * @param  aName
- *         The name of the testcase (without extension)
- * @return an nsIFile pointing to the testcase xpi
- */
-function do_get_addon(aName) {
-  return do_get_file("addons/" + aName + ".xpi");
-}
-
-function do_get_addon_hash(aName, aAlgorithm) {
-  let file = do_get_addon(aName);
-  return do_get_file_hash(file);
-}
-
 function do_get_file_hash(aFile, aAlgorithm) {
   if (!aAlgorithm)
     aAlgorithm = "sha1";
 
   let crypto = Cc["@mozilla.org/security/hash;1"].
                createInstance(Ci.nsICryptoHash);
   crypto.initWithString(aAlgorithm);
   let fis = Cc["@mozilla.org/network/file-input-stream;1"].
--- a/toolkit/mozapps/extensions/test/xpcshell/test_XPIStates.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_XPIStates.js
@@ -5,51 +5,47 @@
 // Test that we only check manifest age for disabled extensions
 
 
 createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
 
 const profileDir = gProfD.clone();
 profileDir.append("extensions");
 
+const ADDONS = {
+  test_bootstrap1_1: {
+    "install.rdf": {
+      "id": "bootstrap1@tests.mozilla.org",
+      "name": "Test Bootstrap 1",
+    },
+    "bootstrap.js": BOOTSTRAP_MONITOR_BOOTSTRAP_JS
+  },
+};
+
 /* We want one add-on installed packed, and one installed unpacked
  */
 
 function run_test() {
   // Shut down the add-on manager after all tests run.
   registerCleanupFunction(promiseShutdownManager);
   // Kick off the task-based tests...
   run_next_test();
 }
 
 // Use bootstrap extensions so the changes will be immediate.
 // A packed extension, to be enabled
 add_task(async function setup() {
   await promiseWriteInstallRDFToXPI({
     id: "packed-enabled@tests.mozilla.org",
-    version: "1.0",
-    bootstrap: true,
-    targetApplications: [{
-      id: "xpcshell@tests.mozilla.org",
-      minVersion: "1",
-      maxVersion: "1"
-    }],
     name: "Packed, Enabled",
   }, profileDir);
 
   // Packed, will be disabled
   await promiseWriteInstallRDFToXPI({
     id: "packed-disabled@tests.mozilla.org",
-    version: "1.0",
-    bootstrap: true,
-    targetApplications: [{
-      id: "xpcshell@tests.mozilla.org",
-      minVersion: "1",
-      maxVersion: "1"
-    }],
     name: "Packed, Disabled",
   }, profileDir);
 });
 
 // Keep track of the last time stamp we've used, so that we can keep moving
 // it forward (if we touch two different files in the same add-on with the same
 // timestamp we may not consider the change significant)
 var lastTimestamp = Date.now();
@@ -132,18 +128,16 @@ add_task(async function uninstall_bootst
 });
 
 /*
  * Installing a restartless add-on should immediately add it to XPIState
  */
 add_task(async function install_bootstrap() {
   let XS = getXS();
 
-  let installer = await promiseInstallFile(
-    do_get_addon("test_bootstrap1_1"));
+  let {addon} = await AddonTestUtils.promiseInstallXPI(ADDONS.test_bootstrap1_1);
 
-  let newAddon = installer.addon;
-  let xState = XS.getAddon("app-profile", newAddon.id);
+  let xState = XS.getAddon("app-profile", addon.id);
   Assert.ok(!!xState);
   Assert.ok(xState.enabled);
-  Assert.equal(xState.mtime, newAddon.updateDate.getTime());
-  await newAddon.uninstall();
+  Assert.equal(xState.mtime, addon.updateDate.getTime());
+  await addon.uninstall();
 });
--- a/toolkit/mozapps/extensions/test/xpcshell/test_backgroundupdate.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_backgroundupdate.js
@@ -9,17 +9,16 @@ Services.prefs.setBoolPref(PREF_EM_CHECK
 
 var testserver = AddonTestUtils.createHttpServer({hosts: ["example.com"]});
 const profileDir = gProfD.clone();
 profileDir.append("extensions");
 
 add_task(async function setup() {
   createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
 
-  testserver.registerDirectory("/addons/", do_get_file("addons"));
   testserver.registerDirectory("/data/", do_get_file("data"));
 
   await promiseStartupManager();
 
   do_test_pending();
   run_test_1();
 });
 
--- a/toolkit/mozapps/extensions/test/xpcshell/test_badschema.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_badschema.js
@@ -2,17 +2,16 @@
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 // Checks that we rebuild something sensible from a database with a bad schema
 
 var testserver = AddonTestUtils.createHttpServer({hosts: ["example.com"]});
 
 // register files with server
-testserver.registerDirectory("/addons/", do_get_file("addons"));
 testserver.registerDirectory("/data/", do_get_file("data"));
 
 // The test extension uses an insecure update url.
 Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
 Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, false);
 
 const ADDONS = {
   "addon1@tests.mozilla.org": {
--- a/toolkit/mozapps/extensions/test/xpcshell/test_bootstrap.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bootstrap.js
@@ -28,94 +28,57 @@ createAppInfo("xpcshell@tests.mozilla.or
 const profileDir = gProfD.clone();
 profileDir.append("extensions");
 const userExtDir = gProfD.clone();
 userExtDir.append("extensions2");
 userExtDir.append(gAppInfo.ID);
 registerDirectory("XREUSysExt", userExtDir.parent);
 
 
-const BOOTSTRAP = `
-  ChromeUtils.import("resource://xpcshell-data/BootstrapMonitor.jsm").monitor(this);
-`;
-
 const ADDONS = {
   test_bootstrap1_1: {
     "install.rdf": {
       id: "bootstrap1@tests.mozilla.org",
-      version: "1.0",
-      bootstrap: "true",
-      multiprocessCompatible: "true",
 
       name: "Test Bootstrap 1",
-      description: "Test Description",
 
       iconURL: "chrome://foo/skin/icon.png",
       aboutURL: "chrome://foo/content/about.xul",
       optionsURL: "chrome://foo/content/options.xul",
-
-      targetApplications: [{
-        id: "xpcshell@tests.mozilla.org",
-        minVersion: "1",
-        maxVersion: "1"}],
     },
-    "bootstrap.js": BOOTSTRAP,
+    "bootstrap.js": BOOTSTRAP_MONITOR_BOOTSTRAP_JS,
   },
   test_bootstrap1_2: {
     "install.rdf": {
       id: "bootstrap1@tests.mozilla.org",
       version: "2.0",
-      bootstrap: "true",
 
       name: "Test Bootstrap 1",
-      description: "Test Description",
-
-      targetApplications: [{
-        id: "xpcshell@tests.mozilla.org",
-        minVersion: "1",
-        maxVersion: "1"}],
     },
-    "bootstrap.js": BOOTSTRAP,
+    "bootstrap.js": BOOTSTRAP_MONITOR_BOOTSTRAP_JS,
   },
   test_bootstrap1_3: {
     "install.rdf": {
       id: "bootstrap1@tests.mozilla.org",
       version: "3.0",
-      bootstrap: "true",
 
       name: "Test Bootstrap 1",
-      description: "Test Description",
 
       targetApplications: [{
         id: "undefined",
         minVersion: "1",
         maxVersion: "1"}],
     },
-    "bootstrap.js": BOOTSTRAP,
+    "bootstrap.js": BOOTSTRAP_MONITOR_BOOTSTRAP_JS,
   },
   test_bootstrap2_1: {
     "install.rdf": {
       id: "bootstrap2@tests.mozilla.org",
-      version: "1.0",
-      bootstrap: "true",
-      multiprocessCompatible: "true",
-
-      name: "Test Bootstrap 2",
-      description: "Test Description",
-
-      iconURL: "chrome://foo/skin/icon.png",
-      aboutURL: "chrome://foo/content/about.xul",
-      optionsURL: "chrome://foo/content/options.xul",
-
-      targetApplications: [{
-        id: "xpcshell@tests.mozilla.org",
-        minVersion: "1",
-        maxVersion: "1"}],
     },
-    "bootstrap.js": BOOTSTRAP,
+    "bootstrap.js": BOOTSTRAP_MONITOR_BOOTSTRAP_JS,
   },
 };
 
 var testserver = AddonTestUtils.createHttpServer({hosts: ["example.com"]});
 
 const XPIS = {};
 for (let [name, addon] of Object.entries(ADDONS)) {
   XPIS[name] = AddonTestUtils.createTempXPIFile(addon);
--- a/toolkit/mozapps/extensions/test/xpcshell/test_bootstrap_const.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bootstrap_const.js
@@ -1,18 +1,27 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
 
+const ADDONS = {
+  test_bootstrap_const: {
+    "install.rdf": {
+      "id": "bootstrap@tests.mozilla.org",
+    },
+    "bootstrap.js": "ChromeUtils.import(\"resource://gre/modules/Services.jsm\");\n\nconst install = function() {\n  Services.obs.notifyObservers(null, \"addon-install\");\n};\n"
+  },
+};
+
 add_task(async function() {
   await promiseStartupManager();
 
   let sawInstall = false;
   Services.obs.addObserver(function() {
     sawInstall = true;
   }, "addon-install");
 
-  await promiseInstallAllFiles([do_get_addon("test_bootstrap_const")]);
+  await AddonTestUtils.promiseInstallXPI(ADDONS.test_bootstrap_const);
 
   ok(sawInstall);
 });
--- a/toolkit/mozapps/extensions/test/xpcshell/test_bootstrap_globals.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bootstrap_globals.js
@@ -2,16 +2,48 @@
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 // This verifies that bootstrap.js has the expected globals defined
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
 
+const ADDONS = {
+  bootstrap_globals: {
+    "install.rdf": {
+      "id": "bootstrap_globals@tests.mozilla.org",
+    },
+    "bootstrap.js": String.raw`ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+var seenGlobals = new Set();
+var scope = this;
+function checkGlobal(name, type) {
+  if (scope[name] && typeof(scope[name]) == type)
+    seenGlobals.add(name);
+}
+
+var wrapped = {};
+Services.obs.notifyObservers({ wrappedJSObject: wrapped }, "bootstrap-request-globals");
+for (let [name, type] of wrapped.expectedGlobals) {
+  checkGlobal(name, type);
+}
+
+function startup(data, reason) {
+  Services.obs.notifyObservers({ wrappedJSObject: seenGlobals }, "bootstrap-seen-globals");
+}
+
+function install(data, reason) {}
+function shutdown(data, reason) {}
+function uninstall(data, reason) {}
+`,
+  },
+};
+
+
 const EXPECTED_GLOBALS = [
   ["console", "object"]
 ];
 
 async function run_test() {
   do_test_pending();
   await promiseStartupManager();
   let sawGlobals = false;
@@ -22,13 +54,13 @@ async function run_test() {
 
   Services.obs.addObserver(function({ wrappedJSObject: seenGlobals }) {
     for (let [name, ] of EXPECTED_GLOBALS)
       Assert.ok(seenGlobals.has(name));
 
     sawGlobals = true;
   }, "bootstrap-seen-globals");
 
-  await promiseInstallAllFiles([do_get_addon("bootstrap_globals")]);
+  await AddonTestUtils.promiseInstallXPI(ADDONS.bootstrap_globals);
   Assert.ok(sawGlobals);
   await promiseShutdownManager();
   do_test_finished();
 }
--- a/toolkit/mozapps/extensions/test/xpcshell/test_bootstrapped_chrome_manifest.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bootstrapped_chrome_manifest.js
@@ -1,26 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 const ADDON = {
   "install.rdf": {
     "id": "bug675371@tests.mozilla.org",
-    "version": "1.0",
-    "bootstrap": "true",
-    "name": "Bug 675371 Test",
-    "description": "Test Description",
-    "targetApplications": [
-      {
-        "id": "xpcshell@tests.mozilla.org",
-        "minVersion": "1",
-        "maxVersion": "1"
-      }
-    ]
   },
   "chrome.manifest": `content bug675371 .`,
   "test.js": `var active = true;`,
 };
 
 add_task(async function run_test() {
   createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
   await promiseStartupManager();
--- a/toolkit/mozapps/extensions/test/xpcshell/test_cache_certdb.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_cache_certdb.js
@@ -6,20 +6,100 @@
 Services.prefs.setBoolPref(PREF_XPI_SIGNATURES_REQUIRED, true);
 gUseRealCertChecks = true;
 
 const profileDir = gProfD.clone();
 profileDir.append("extensions");
 
 const ID = "bootstrap1@tests.mozilla.org";
 
+const ADDONS = {
+  test_cache_certdb: {
+    "install.rdf": {
+      "id": ID,
+    },
+    "bootstrap.js": String.raw`const CERTDB_CONTRACTID = "@mozilla.org/security/x509certdb;1";
+const CERTDB_CID = Components.ID("{fb0bbc5c-452e-4783-b32c-80124693d871}");
+
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+const CERT = "MIIDITCCAgmgAwIBAgIJALAv8fydd6nBMA0GCSqGSIb3DQEBBQUAMCcxJTAjBgNV\
+BAMMHGJvb3RzdHJhcDFAdGVzdHMubW96aWxsYS5vcmcwHhcNMTYwMjAyMjMxNjUy\
+WhcNMjYwMTMwMjMxNjUyWjAnMSUwIwYDVQQDDBxib290c3RyYXAxQHRlc3RzLm1v\
+emlsbGEub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5caNuLTu\
+H8dEqNntLlhKi4y09hrgcF3cb6n5Xx9DIHA8CKiZxt9qGXKeeiDwEiiQ8ibJYzdc\
+jLkbzJUyPVUaH9ygrWynSpSTOvv/Ys3+ERrCo9W7Zuzwdmzt6TTEjFMS4lVx06us\
+3uUqkdp3JMgCqCEbOFZiztICiSKrp8QFJkAfApZzBqmJOPOWH0yZ2CRRzvbQZ6af\
+hqQDUalJQjWfsenyUWphhbREqExetxHJFR3OrmJt/shXVyz6dD7TBuE3PPUh1RpE\
+3ejVufcTzjV3XmK79PxsKLM9V2+ww9e9V3OET57kyvn+bpSWdUYm3X4DA8dxNW6+\
+kTFWRnQNZ+zQVQIDAQABo1AwTjAdBgNVHQ4EFgQUac36ccv+99N5HxYa8dCDYRaF\
+HNQwHwYDVR0jBBgwFoAUac36ccv+99N5HxYa8dCDYRaFHNQwDAYDVR0TBAUwAwEB\
+/zANBgkqhkiG9w0BAQUFAAOCAQEAFfu3MN8EtY5wcxOFdGShOmGQPm2MJJVE6MG+\
+p4RqHrukHZSgKOyWjkRk7t6NXzNcnHco9HFv7FQRAXSJ5zObmyu+TMZlu4jHHCav\
+GMcV3C/4SUGtlipZbgNe00UAIm6tM3Wh8dr38W7VYg4KGAwXou5XhQ9gCAnSn90o\
+H/42NqHTjJsR4v18izX2aO25ARQdMby7Lsr5j9RqweHywiSlPusFcKRseqOnIP0d\
+JT3+qh78LeMbNBO2mYD3SP/zu0TAmkAVNcj2KPw0+a0kVZ15rvslPC/K3xn9msMk\
+fQthv3rDAcsWvi9YO7T+vylgZBgJfn1ZqpQqy58xN96uh6nPOw==";
+
+function overrideCertDB() {
+  // Unregister the real database.
+  let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
+  let factory = registrar.getClassObject(CERTDB_CID, Ci.nsIFactory);
+  registrar.unregisterFactory(CERTDB_CID, factory);
+
+  // Get the real DB
+  let realCertDB = factory.createInstance(null, Ci.nsIX509CertDB);
+
+  let fakeCert = realCertDB.constructX509FromBase64(CERT.replace(/\n/g, ""));
+
+  let fakeCertDB = {
+    openSignedAppFileAsync(root, file, callback) {
+      callback.openSignedAppFileFinished(Cr.NS_OK, null, fakeCert);
+    },
+
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIX509CertDB])
+  };
+
+  for (let property of Object.keys(realCertDB)) {
+    if (property in fakeCertDB) {
+      continue;
+    }
+
+    if (typeof realCertDB[property] == "function") {
+      fakeCertDB[property] = realCertDB[property].bind(realCertDB);
+    }
+  }
+
+  let certDBFactory = {
+    createInstance(outer, iid) {
+      if (outer != null) {
+        throw Cr.NS_ERROR_NO_AGGREGATION;
+      }
+      return fakeCertDB.QueryInterface(iid);
+    }
+  };
+  registrar.registerFactory(CERTDB_CID, "CertDB",
+                            CERTDB_CONTRACTID, certDBFactory);
+
+  const scope = ChromeUtils.import("resource://gre/modules/addons/XPIProvider.jsm", {});
+  scope.gCertDB = fakeCertDB;
+}
+
+function install() { // eslint-disable-line no-unused-vars
+  overrideCertDB();
+}
+`,
+  },
+};
+
+
 add_task(async function() {
   createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
 
-  AddonTestUtils.manuallyInstall(do_get_addon("test_cache_certdb"), profileDir, ID);
+  AddonTestUtils.manuallyInstall(AddonTestUtils.createTempXPIFile(ADDONS.test_cache_certdb), profileDir, ID);
 
   await promiseStartupManager();
 
   // Force a rescan of signatures
   const { XPIDatabase } = ChromeUtils.import("resource://gre/modules/addons/XPIDatabase.jsm", {});
   await XPIDatabase.verifySignatures();
 
   let addon = await AddonManager.getAddonByID(ID);
--- a/toolkit/mozapps/extensions/test/xpcshell/test_cacheflush.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_cacheflush.js
@@ -25,22 +25,16 @@ var CacheFlushObserver = {
 };
 
 const ADDONS = [
   {
     id: "addon2@tests.mozilla.org",
     version: "2.0",
 
     name: "Cache Flush Test",
-    bootstrap: true,
-
-    targetApplications: [{
-      id: "xpcshell@tests.mozilla.org",
-      minVersion: "1",
-      maxVersion: "1" }],
   },
 ];
 
 const XPIS = ADDONS.map(addon => createTempXPIFile(addon));
 
 add_task(async function setup() {
   Services.obs.addObserver(CacheFlushObserver, "flush-cache-entry");
   createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "2");
--- a/toolkit/mozapps/extensions/test/xpcshell/test_corrupt.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_corrupt.js
@@ -4,37 +4,29 @@
 
 // Checks that we rebuild something sensible from a corrupt database
 
 
 // Create and configure the HTTP server.
 var testserver = AddonTestUtils.createHttpServer({hosts: ["example.com"]});
 
 // register files with server
-testserver.registerDirectory("/addons/", do_get_file("addons"));
 testserver.registerDirectory("/data/", do_get_file("data"));
 
 // The test extension uses an insecure update url.
 Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
 Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, false);
 
 const ADDONS = {
   // Will get a compatibility update and stay enabled
   "addon3@tests.mozilla.org": {
     "install.rdf": {
       id: "addon3@tests.mozilla.org",
-      version: "1.0",
       name: "Test 3",
-      bootstrap: true,
       updateURL: "http://example.com/data/test_corrupt.json",
-      targetApplications: [{
-        id: "xpcshell@tests.mozilla.org",
-        minVersion: "1",
-        maxVersion: "1"
-      }]
     },
     findUpdates: true,
     desiredState: {
       isActive: true,
       userDisabled: false,
       appDisabled: false,
       pendingOperations: 0,
     },
@@ -43,25 +35,18 @@ const ADDONS = {
     afterCorruption: {},
     afterSecondRestart: {},
   },
 
   // Will get a compatibility update and be enabled
   "addon4@tests.mozilla.org": {
     "install.rdf": {
       id: "addon4@tests.mozilla.org",
-      version: "1.0",
       name: "Test 4",
-      bootstrap: true,
       updateURL: "http://example.com/data/test_corrupt.json",
-      targetApplications: [{
-        id: "xpcshell@tests.mozilla.org",
-        minVersion: "1",
-        maxVersion: "1"
-      }]
     },
     initialState: {
       userDisabled: true,
     },
     findUpdates: true,
     desiredState: {
       isActive: false,
       userDisabled: true,
@@ -75,42 +60,33 @@ const ADDONS = {
     afterCorruption: {},
     afterSecondRestart: {},
   },
 
   // Would stay incompatible with strict compat
   "addon5@tests.mozilla.org": {
     "install.rdf": {
       id: "addon5@tests.mozilla.org",
-      version: "1.0",
       name: "Test 5",
-      bootstrap: true,
-      targetApplications: [{
-        id: "xpcshell@tests.mozilla.org",
-        minVersion: "1",
-        maxVersion: "1"
-      }]
     },
     desiredState: {
       isActive: true,
       userDisabled: false,
       appDisabled: false,
       pendingOperations: 0,
     },
     afterCorruption: {},
     afterSecondRestart: {},
   },
 
   // Enabled bootstrapped
   "addon6@tests.mozilla.org": {
     "install.rdf": {
       id: "addon6@tests.mozilla.org",
-      version: "1.0",
       name: "Test 6",
-      bootstrap: "true",
       targetApplications: [{
         id: "xpcshell@tests.mozilla.org",
         minVersion: "2",
         maxVersion: "2"
       }]
     },
     desiredState: {
       isActive: true,
@@ -121,19 +97,17 @@ const ADDONS = {
     afterCorruption: {},
     afterSecondRestart: {},
   },
 
   // Disabled bootstrapped
   "addon7@tests.mozilla.org": {
     "install.rdf": {
       id: "addon7@tests.mozilla.org",
-      version: "1.0",
       name: "Test 7",
-      bootstrap: "true",
       targetApplications: [{
         id: "xpcshell@tests.mozilla.org",
         minVersion: "2",
         maxVersion: "2"
       }]
     },
     initialState: {
       userDisabled: true,
--- a/toolkit/mozapps/extensions/test/xpcshell/test_delay_update.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_delay_update.js
@@ -20,17 +20,47 @@ const TEST_IGNORE_PREF = "delaytest.igno
 // the objects in `data` to be serializable, and we need a real reference to the
 // `instanceID` symbol to test.
 
 createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42");
 
 // Create and configure the HTTP server.
 var testserver = AddonTestUtils.createHttpServer({hosts: ["example.com"]});
 testserver.registerDirectory("/data/", do_get_file("data"));
-testserver.registerDirectory("/addons/", do_get_file("addons"));
+
+
+const ADDONS = {
+  test_delay_update_complete_v2: {
+    "install.rdf": {
+      "id": "test_delay_update_complete@tests.mozilla.org",
+      "version": "2.0",
+      "name": "Test Delay Update Complete",
+    },
+  },
+  test_delay_update_defer_v2: {
+    "install.rdf": {
+      "id": "test_delay_update_defer@tests.mozilla.org",
+      "version": "2.0",
+      "name": "Test Delay Update Defer",
+    },
+  },
+  test_delay_update_ignore_v2: {
+    "install.rdf": {
+      "id": "test_delay_update_ignore@tests.mozilla.org",
+      "version": "2.0",
+      "name": "Test Delay Update Ignore",
+    },
+  },
+};
+
+const XPIS = {};
+for (let [name, files] of Object.entries(ADDONS)) {
+  XPIS[name] = AddonTestUtils.createTempXPIFile(files);
+  testserver.registerFile(`/addons/${name}.xpi`, XPIS[name]);
+}
 
 async function createIgnoreAddon() {
   await promiseWriteInstallRDFToXPI({
     id: IGNORE_ID,
     version: "1.0",
     bootstrap: true,
     unpack: true,
     updateURL: `http://example.com/data/test_delay_updates_ignore_legacy.json`,
--- a/toolkit/mozapps/extensions/test/xpcshell/test_delay_update_webextension.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_delay_update_webextension.js
@@ -28,20 +28,58 @@ stageDir.append("staged");
 const IGNORE_ID = "test_delay_update_ignore_webext@tests.mozilla.org";
 const COMPLETE_ID = "test_delay_update_complete_webext@tests.mozilla.org";
 const DEFER_ID = "test_delay_update_defer_webext@tests.mozilla.org";
 const NOUPDATE_ID = "test_no_update_webext@tests.mozilla.org";
 
 // Create and configure the HTTP server.
 var testserver = AddonTestUtils.createHttpServer({hosts: ["example.com"]});
 testserver.registerDirectory("/data/", do_get_file("data"));
-testserver.registerDirectory("/addons/", do_get_file("addons"));
 
 createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "42", "42");
 
+const ADDONS = {
+  test_delay_update_complete_webextension_v2: {
+    "manifest.json": {
+      "manifest_version": 2,
+      "name": "Delay Upgrade",
+      "version": "2.0",
+      "applications": {
+        "gecko": {id: COMPLETE_ID}
+      }
+    }
+  },
+  test_delay_update_defer_webextension_v2: {
+    "manifest.json": {
+      "manifest_version": 2,
+      "name": "Delay Upgrade",
+      "version": "2.0",
+      "applications": {
+        "gecko": {id: DEFER_ID}
+      }
+    }
+  },
+  test_delay_update_ignore_webextension_v2: {
+    "manifest.json": {
+      "manifest_version": 2,
+      "name": "Delay Upgrade",
+      "version": "2.0",
+      "applications": {
+        "gecko": {id: IGNORE_ID}
+      }
+    }
+  },
+};
+
+const XPIS = {};
+for (let [name, files] of Object.entries(ADDONS)) {
+  XPIS[name] = AddonTestUtils.createTempXPIFile(files);
+  testserver.registerFile(`/addons/${name}.xpi`, XPIS[name]);
+}
+
 // add-on registers upgrade listener, and ignores update.
 add_task(async function delay_updates_ignore() {
   await promiseStartupManager();
 
   let extension = ExtensionTestUtils.loadExtension({
     useAddonManager: "permanent",
     manifest: {
       "version": "1.0",
--- a/toolkit/mozapps/extensions/test/xpcshell/test_dictionary.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_dictionary.js
@@ -12,37 +12,77 @@ PromiseTestUtils.whitelistRejectionsGlob
 
 // Enable loading extensions from the user scopes
 Services.prefs.setIntPref("extensions.enabledScopes",
                           AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_USER);
 
 // The test extension uses an insecure update url.
 Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
 
-const ID_DICT = "ab-CD@dictionaries.addons.mozilla.org";
-const XPI_DICT = do_get_addon("test_dictionary");
-
 createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
 
 const profileDir = gProfD.clone();
 profileDir.append("extensions");
 
 const userExtDir = gProfD.clone();
 userExtDir.append("extensions2");
 userExtDir.append(gAppInfo.ID);
 
 registerDirectory("XREUSysExt", userExtDir.parent);
 
 // Create and configure the HTTP server.
 var testserver = AddonTestUtils.createHttpServer({hosts: ["example.com"]});
 
 // register files with server
-testserver.registerDirectory("/addons/", do_get_file("addons"));
 testserver.registerDirectory("/data/", do_get_file("data"));
 
+const ADDONS = {
+  test_dictionary: {
+    "install.rdf": {
+      "id": "ab-CD@dictionaries.addons.mozilla.org",
+      "type": "64",
+      "name": "Test Dictionary",
+    },
+    "dictionaries/ab-CD.dic": "1\ntest1\n",
+    "chrome.manifest": "content dict ./\n"
+  },
+  test_dictionary_3: {
+    "install.rdf": {
+      "id": "ab-CD@dictionaries.addons.mozilla.org",
+      "version": "2.0",
+      "type": "64",
+      "name": "Test Dictionary",
+    }
+  },
+  test_dictionary_4: {
+    "install.rdf": {
+      "id": "ef@dictionaries.addons.mozilla.org",
+      "version": "2.0",
+      "name": "Test Dictionary ef",
+    }
+  },
+  test_dictionary_5: {
+    "install.rdf": {
+      "id": "gh@dictionaries.addons.mozilla.org",
+      "version": "2.0",
+      "type": "64",
+      "name": "Test Dictionary gh",
+    }
+  },
+};
+
+const ID_DICT = "ab-CD@dictionaries.addons.mozilla.org";
+const XPI_DICT = AddonTestUtils.createTempXPIFile(ADDONS.test_dictionary);
+
+const XPIS = {};
+for (let [name, files] of Object.entries(ADDONS)) {
+  XPIS[name] = AddonTestUtils.createTempXPIFile(files);
+  testserver.registerFile(`/addons/${name}.xpi`, XPIS[name]);
+}
+
 /**
  * This object is both a factory and an mozISpellCheckingEngine implementation (so, it
  * is de-facto a service). It's also an interface requestor that gives out
  * itself when asked for mozISpellCheckingEngine.
  */
 var HunspellEngine = {
   dictionaryURIs: new Map(),
   listener: null,
@@ -121,18 +161,17 @@ add_task(async function setup() {
 // Tests that installing doesn't require a restart
 add_task(async function test_1() {
   prepare_test({ }, [
     "onNewInstall"
   ]);
 
   HunspellEngine.activate();
 
-  let install = await AddonManager.getInstallForFile(
-    do_get_addon("test_dictionary"));
+  let install = await AddonManager.getInstallForFile(XPI_DICT);
   ensure_test_completed();
 
   notEqual(install, null);
   equal(install.type, "dictionary");
   equal(install.version, "1.0");
   equal(install.name, "Test Dictionary");
   equal(install.state, AddonManager.STATE_DOWNLOADED);
   equal(install.addon.operationsRequiringRestart &
@@ -358,17 +397,17 @@ add_task(async function test_12() {
 
   await addon.uninstall();
 });
 
 
 // Tests that bootstrapped extensions don't get loaded when in safe mode
 add_task(async function test_16() {
   await promiseRestartManager();
-  await promiseInstallFile(do_get_addon("test_dictionary"));
+  await promiseInstallFile(XPI_DICT);
 
   let addon = await AddonManager.getAddonByID(ID_DICT);
   // Should have installed and started
   ok(HunspellEngine.isDictionaryEnabled("ab-CD.dic"));
 
   await promiseShutdownManager();
 
   // We don't unregister dictionaries at app shutdown, so the dictionary
--- a/toolkit/mozapps/extensions/test/xpcshell/test_distribution.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_distribution.js
@@ -12,16 +12,26 @@ createAppInfo("xpcshell@tests.mozilla.or
 
 const profileDir = gProfD.clone();
 profileDir.append("extensions");
 const distroDir = gProfD.clone();
 distroDir.append("distribution");
 distroDir.append("extensions");
 registerDirectory("XREAppDist", distroDir.parent);
 
+const ADDONS = {
+  test_distribution1_2: {
+    "install.rdf": {
+      "id": "addon1@tests.mozilla.org",
+      "version": "2.0",
+      "name": "Distributed add-ons test",
+    }
+  },
+};
+
 var addon1_1 = {
   id: "addon1@tests.mozilla.org",
   version: "1.0",
   name: "Test version 1",
   bootstrap: true,
   targetApplications: [{
     id: "xpcshell@tests.mozilla.org",
     minVersion: "1",
@@ -170,17 +180,17 @@ async function run_test_6() {
   executeSoon(run_test_7);
 }
 
 // Tests that a pending install of a newer version of a distributed add-on
 // at app change still gets applied
 async function run_test_7() {
   Services.prefs.clearUserPref("extensions.installedDistroAddon.addon1@tests.mozilla.org");
 
-  await promiseInstallAllFiles([do_get_addon("test_distribution1_2")]);
+  await AddonTestUtils.promiseInstallXPI(ADDONS.test_distribution1_2);
   await promiseRestartManager(2);
 
   let a1 = await AddonManager.getAddonByID("addon1@tests.mozilla.org");
   Assert.notEqual(a1, null);
   Assert.equal(a1.version, "2.0");
   Assert.ok(a1.isActive);
   Assert.equal(a1.scope, AddonManager.SCOPE_PROFILE);
 
@@ -190,17 +200,17 @@ async function run_test_7() {
 
 // Tests that a pending install of a older version of a distributed add-on
 // at app change gets replaced by the distributed version
 async function run_test_8() {
   await promiseRestartManager();
 
   await promiseWriteInstallRDFForExtension(addon1_3, distroDir);
 
-  await promiseInstallAllFiles([do_get_addon("test_distribution1_2")]);
+  await AddonTestUtils.promiseInstallXPI(ADDONS.test_distribution1_2);
   await promiseRestartManager(3);
 
   let a1 = await AddonManager.getAddonByID("addon1@tests.mozilla.org");
   Assert.notEqual(a1, null);
   Assert.equal(a1.version, "3.0");
   Assert.ok(a1.isActive);
   Assert.equal(a1.scope, AddonManager.SCOPE_PROFILE);
 
--- a/toolkit/mozapps/extensions/test/xpcshell/test_error.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_error.js
@@ -4,16 +4,24 @@
 
 // Tests that various error conditions are handled correctly
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 const profileDir = gProfD.clone();
 profileDir.append("extensions");
 
+const ADDONS = {
+  test_bug567173: {
+    "install.rdf": {
+      "id": "bug567173",
+    }
+  },
+};
+
 async function run_test() {
   do_test_pending();
   createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
 
   await promiseStartupManager();
 
   run_test_1();
 }
@@ -70,15 +78,16 @@ async function run_test_5() {
   Assert.equal(install.state, AddonManager.STATE_DOWNLOAD_FAILED);
   Assert.equal(install.error, AddonManager.ERROR_NETWORK_FAILURE);
 
   run_test_6();
 }
 
 // Checks that an add-on with an illegal ID shows an error
 async function run_test_6() {
-  let install = await AddonManager.getInstallForFile(do_get_addon("test_bug567173"));
+  let xpi = await AddonTestUtils.createTempXPIFile(ADDONS.test_bug567173);
+  let install = await AddonManager.getInstallForFile(xpi);
   Assert.notEqual(install, null);
   Assert.equal(install.state, AddonManager.STATE_DOWNLOAD_FAILED);
   Assert.equal(install.error, AddonManager.ERROR_CORRUPT_FILE);
 
   executeSoon(do_test_finished);
 }
--- a/toolkit/mozapps/extensions/test/xpcshell/test_filepointer.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_filepointer.js
@@ -45,17 +45,16 @@ const profileDir = gProfD.clone();
 profileDir.append("extensions");
 profileDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755);
 
 const sourceDir = gProfD.clone();
 sourceDir.append("source");
 
 var testserver = AddonTestUtils.createHttpServer({hosts: ["example.com"]});
 testserver.registerDirectory("/data/", do_get_file("data"));
-testserver.registerDirectory("/addons/", do_get_file("addons"));
 gPort = testserver.identity.primaryPort;
 
 function promiseWritePointer(aId, aName) {
   let path = OS.Path.join(profileDir.path, aName || aId);
 
   let target = OS.Path.join(sourceDir.path,
                             do_get_expected_addon_name(aId));
 
--- a/toolkit/mozapps/extensions/test/xpcshell/test_getresource.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_getresource.js
@@ -1,23 +1,35 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 // This verifies the functionality of getResourceURI
 // There are two cases - with a filename it returns an nsIFileURL to the filename
 // and with no parameters, it returns an nsIFileURL to the root of the addon
 
+const ADDONS = {
+  test_getresource: {
+    "install.rdf": {
+      "id": "addon1@tests.mozilla.org",
+      "name": "Test 1",
+    },
+    "icon.png": "Dummy icon file",
+    "subdir/subfile.txt": "Dummy file in subdirectory"
+  },
+};
+
 async function run_test() {
   do_test_pending();
   createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
 
   await promiseStartupManager();
 
-  let aInstall = await AddonManager.getInstallForFile(do_get_addon("test_getresource"));
+  let xpi = AddonTestUtils.createTempXPIFile(ADDONS.test_getresource);
+  let aInstall = await AddonManager.getInstallForFile(xpi);
   Assert.equal(aInstall.addon.getResourceURI().spec, aInstall.sourceURI.spec);
 
   Assert.equal(aInstall.addon.getResourceURI("icon.png").spec,
                "jar:" + aInstall.sourceURI.spec + "!/icon.png");
 
   Assert.equal(aInstall.addon.getResourceURI("subdir/subfile.txt").spec,
                "jar:" + aInstall.sourceURI.spec + "!/subdir/subfile.txt");
 
--- a/toolkit/mozapps/extensions/test/xpcshell/test_install.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_install.js
@@ -10,85 +10,54 @@ var testserver = AddonTestUtils.createHt
 var gInstallDate;
 
 const ADDONS = {
   test_install1: {
     "install.rdf": {
       id: "addon1@tests.mozilla.org",
       version: "1.0",
       name: "Test 1",
-      description: "Test Description",
-      bootstrap: true,
-
-      targetApplications: [{
-          id: "xpcshell@tests.mozilla.org",
-          minVersion: "1",
-          maxVersion: "1"}],
     },
     "icon.png": "Fake icon image",
     "icon64.png": "Fake icon image",
   },
   test_install2_1: {
     "install.rdf": {
       id: "addon2@tests.mozilla.org",
       version: "2.0",
       name: "Real Test 2",
-      description: "Test Description",
-      bootstrap: true,
-
-      targetApplications: [{
-          id: "xpcshell@tests.mozilla.org",
-          minVersion: "1",
-          maxVersion: "1"}],
-
     },
     "icon.png": "Fake icon image",
   },
   test_install2_2: {
     "install.rdf": {
       id: "addon2@tests.mozilla.org",
       version: "3.0",
       name: "Real Test 3",
-      description: "Test Description",
-      bootstrap: true,
-
-      targetApplications: [{
-          id: "xpcshell@tests.mozilla.org",
-          minVersion: "1",
-          maxVersion: "1"}],
     },
   },
   test_install3: {
     "install.rdf": {
       id: "addon3@tests.mozilla.org",
       version: "1.0",
       name: "Real Test 4",
-      description: "Test Description",
-      bootstrap: true,
 
       updateURL: "http://example.com/data/test_install.rdf",
 
       targetApplications: [{
           id: "xpcshell@tests.mozilla.org",
           minVersion: "0",
           maxVersion: "0"}],
     },
   },
   test_install6: {
     "install.rdf": {
       id: "addon6@tests.mozilla.org",
       version: "1.0",
       name: "Addon Test 6",
-      description: "Test Description",
-      bootstrap: true,
-
-      targetApplications: [{
-          id: "xpcshell@tests.mozilla.org",
-          minVersion: "1",
-          maxVersion: "1"}],
     },
   },
   test_install7: {
     "install.rdf": {
       type: "32",
     },
   },
 };
--- a/toolkit/mozapps/extensions/test/xpcshell/test_invalid_install_rdf.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_invalid_install_rdf.js
@@ -64,16 +64,17 @@ add_task(async function() {
     "chrome.manifest": `
       content foo ./
     `,
   });
 
   await promiseWriteInstallRDFToXPI({
     id: "foo-legacy-legacy@addons.mozilla.org",
     version: "1.0",
+    bootstrap: false,
     targetApplications: [{
       id: "xpcshell@tests.mozilla.org",
       minVersion: "1",
       maxVersion: "1"
     }],
     name: "Invalid install.rdf extension",
   }, userExtensions, undefined, {
     "chrome.manifest": `
--- a/toolkit/mozapps/extensions/test/xpcshell/test_locale.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_locale.js
@@ -1,23 +1,71 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
+const ADDONS = {
+  test_locale: {
+    "install.rdf": {
+      "id": "addon1@tests.mozilla.org",
+      "name": "Fallback Name",
+      "description": "Fallback Description",
+      "localized": [
+        {
+          "name": "fr-FR Name",
+          "description": "fr-FR Description",
+          "locale": [
+            "fr-FR",
+            ""
+          ],
+          "contributor": [
+            "Fr Contributor 1",
+            "Fr Contributor 2",
+            "Fr Contributor 3"
+          ]
+        },
+        {
+          "name": "de-DE Name",
+          "locale": [
+            "de-DE"
+          ]
+        },
+        {
+          "name": "es-ES Name",
+          "description": "es-ES Description",
+          "locale": [
+            "es-ES"
+          ]
+        },
+        {
+          "name": "Repeated locale",
+          "locale": [
+            "fr-FR"
+          ]
+        },
+        {
+          "name": "Missing locale"
+        }
+      ]
+    }
+  },
+};
+
 add_task(async function setup() {
   // Setup for test
   createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
   await promiseStartupManager();
 });
 
 // Tests that the localized properties are visible before installation
 add_task(async function test_1() {
   await restartWithLocales(["fr-FR"]);
 
-  let install = await AddonManager.getInstallForFile(do_get_addon("test_locale"));
+  let xpi = AddonTestUtils.createTempXPIFile(ADDONS.test_locale);
+  let install = await AddonManager.getInstallForFile(xpi);
   Assert.equal(install.addon.name, "fr-FR Name");
   Assert.equal(install.addon.description, "fr-FR Description");
 
   await new Promise(resolve => {
     prepare_test({
       "addon1@tests.mozilla.org": [
         ["onInstalling", false],
         ["onInstalled", false],
--- a/toolkit/mozapps/extensions/test/xpcshell/test_moved_extension_metadata.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_moved_extension_metadata.js
@@ -44,19 +44,17 @@ var addon1 = {
     maxVersion: "1"
   }]
 };
 
 const ADDONS = [
   {
     "install.rdf": {
       id: "addon2@tests.mozilla.org",
-      version: "1.0",
       name: "Test 2",
-      bootstrap: true,
 
       targetApplications: [{
         id: "xpcshell@tests.mozilla.org",
         minVersion: "2",
         maxVersion: "2"}],
     },
     "bootstrap.js": `
       /* exported startup, shutdown */
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/xpcshell/test_pass_symbol.js
+++ /dev/null
@@ -1,46 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
-
-const PASS_PREF = "symboltest.instanceid.pass";
-const FAIL_BOGUS_PREF = "symboltest.instanceid.fail_bogus";
-const FAIL_ID_PREF = "symboltest.instanceid.fail_bogus";
-const ADDON_ID = "test_symbol@tests.mozilla.org";
-
-createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42");
-
-BootstrapMonitor.init();
-
-// symbol is passed when add-on is installed
-add_task(async function() {
-  await promiseStartupManager();
-
-  PromiseTestUtils.expectUncaughtRejection(/no addon found for symbol/);
-
-  for (let pref of [PASS_PREF, FAIL_BOGUS_PREF, FAIL_ID_PREF])
-    Services.prefs.clearUserPref(pref);
-
-  await promiseInstallAllFiles([do_get_addon("test_symbol")], true);
-
-  let addon = await promiseAddonByID(ADDON_ID);
-
-  Assert.notEqual(addon, null);
-  Assert.equal(addon.version, "1.0");
-  Assert.equal(addon.name, "Test Symbol");
-  Assert.ok(addon.isCompatible);
-  Assert.ok(!addon.appDisabled);
-  Assert.ok(addon.isActive);
-  Assert.equal(addon.type, "extension");
-
-  // most of the test is in bootstrap.js in the addon because BootstrapMonitor
-  // currently requires the objects in `data` to be serializable, and we
-  // need a real reference to the symbol to test this.
-  executeSoon(function() {
-    // give the startup time to run
-    Assert.ok(Services.prefs.getBoolPref(PASS_PREF));
-    Assert.ok(Services.prefs.getBoolPref(FAIL_BOGUS_PREF));
-    Assert.ok(Services.prefs.getBoolPref(FAIL_ID_PREF));
-  });
-
-  await promiseRestartManager();
-});
--- a/toolkit/mozapps/extensions/test/xpcshell/test_proxy.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_proxy.js
@@ -3,18 +3,16 @@
  */
 
 const ID = "proxy1@tests.mozilla.org";
 
 createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42");
 
 BootstrapMonitor.init();
 
-const BOOTSTRAP_JS = `ChromeUtils.import("resource://xpcshell-data/BootstrapMonitor.jsm").monitor(this);`;
-
 // Ensure that a proxy file to an add-on with a valid manifest works.
 add_task(async function() {
   Services.prefs.setBoolPref(PREF_XPI_SIGNATURES_REQUIRED, false);
 
   await promiseStartupManager();
 
   let tempdir = gTmpD.clone();
   let unpackedAddon = await promiseWriteInstallRDFToDir({
@@ -24,17 +22,17 @@ add_task(async function() {
     unpack: true,
     targetApplications: [{
       id: "xpcshell@tests.mozilla.org",
       minVersion: "1",
       maxVersion: "1"
     }],
     name: "Test Bootstrap 1 (proxy)",
   }, tempdir, ID, {
-    "bootstrap.js": BOOTSTRAP_JS,
+    "bootstrap.js": BOOTSTRAP_MONITOR_BOOTSTRAP_JS,
   });
 
   // create proxy file in profile/extensions dir
   let extensionsDir = gProfD.clone();
   extensionsDir.append("extensions");
   let proxyFile = await promiseWriteProxyFileToDir(extensionsDir, unpackedAddon, ID);
 
   await promiseRestartManager();
@@ -52,17 +50,17 @@ add_task(async function() {
 
     Assert.notEqual(addon, null);
     Assert.equal(addon.version, "1.0");
     Assert.equal(addon.name, "Test Bootstrap 1 (proxy)");
     Assert.ok(addon.isCompatible);
     Assert.ok(!addon.appDisabled);
     Assert.ok(addon.isActive);
     Assert.equal(addon.type, "extension");
-    Assert.equal(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_UNKNOWN : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
+    Assert.equal(addon.signedState, AddonManager.SIGNEDSTATE_UNKNOWN);
 
     Assert.ok(proxyFile.exists());
 
     await addon.uninstall();
   }
   unpackedAddon.remove(true);
 
   await promiseRestartManager();
@@ -82,17 +80,17 @@ add_task(async function() {
     unpack: true,
     targetApplications: [{
       id: "xpcshell@tests.mozilla.org",
       minVersion: "1",
       maxVersion: "1"
     }],
     name: "Test Bootstrap 1 (proxy)",
   }, tempdir, ID, {
-    "bootstrap.js": BOOTSTRAP_JS,
+    "bootstrap.js": BOOTSTRAP_MONITOR_BOOTSTRAP_JS,
   });
 
   // create proxy file in profile/extensions dir
   let extensionsDir = gProfD.clone();
   extensionsDir.append("extensions");
   let proxyFile = await promiseWriteProxyFileToDir(extensionsDir, unpackedAddon, ID);
 
   await promiseRestartManager();
--- a/toolkit/mozapps/extensions/test/xpcshell/test_reload.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_reload.js
@@ -1,72 +1,84 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
 
 createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42");
 
-const sampleAddon = {
-  id: "webextension1@tests.mozilla.org",
-  name: "webextension_1",
+const ID = "webextension1@tests.mozilla.org";
+
+const ADDONS = {
+  webextension_1: {
+    "manifest.json": {
+      "name": "Web Extension Name",
+      "version": "1.0",
+      "manifest_version": 2,
+      "applications": {
+        "gecko": {
+          "id": ID
+        }
+      },
+      "icons": {
+        "48": "icon48.png",
+        "64": "icon64.png"
+      }
+    },
+    "chrome.manifest": "content webex ./\n"
+  },
 };
 
 const manifestSample = {
   id: "bootstrap1@tests.mozilla.org",
   version: "1.0",
   bootstrap: true,
   targetApplications: [{
     id: "xpcshell@tests.mozilla.org",
     minVersion: "1",
     maxVersion: "1"
   }],
 };
 
-async function installAddon(fixtureName, addonID) {
-  await promiseInstallAllFiles([do_get_addon(fixtureName)]);
-  return promiseAddonByID(addonID);
-}
-
 async function tearDownAddon(addon) {
   await addon.uninstall();
   await promiseShutdownManager();
 }
 
 add_task(async function test_reloading_a_temp_addon() {
   if (AppConstants.MOZ_APP_NAME == "thunderbird")
     return;
   await promiseRestartManager();
-  await AddonManager.installTemporaryAddon(do_get_addon(sampleAddon.name));
-  const addon = await promiseAddonByID(sampleAddon.id);
+  let xpi = AddonTestUtils.createTempXPIFile(ADDONS.webextension_1);
+  const addon = await AddonManager.installTemporaryAddon(xpi);
 
   var receivedOnUninstalled = false;
   var receivedOnUninstalling = false;
   var receivedOnInstalled = false;
   var receivedOnInstalling = false;
 
   const onReload = new Promise(resolve => {
     const listener = {
       onUninstalling: (addonObj) => {
-        if (addonObj.id === sampleAddon.id) {
+        if (addonObj.id === ID) {
           receivedOnUninstalling = true;
         }
       },
       onUninstalled: (addonObj) => {
-        if (addonObj.id === sampleAddon.id) {
+        if (addonObj.id === ID) {
           receivedOnUninstalled = true;
         }
       },
       onInstalling: (addonObj) => {
         receivedOnInstalling = true;
-        equal(addonObj.id, sampleAddon.id);
+        equal(addonObj.id, ID);
       },
       onInstalled: (addonObj) => {
         receivedOnInstalled = true;
-        equal(addonObj.id, sampleAddon.id);
+        equal(addonObj.id, ID);
         // This should be the last event called.
         AddonManager.removeAddonListener(listener);
         resolve();
       },
     };
     AddonManager.addAddonListener(listener);
   });
 
@@ -81,17 +93,17 @@ add_task(async function test_reloading_a
   equal(receivedOnInstalling, true, "reload should trigger onInstalling");
   equal(receivedOnInstalled, true, "reload should trigger onInstalled");
 
   await tearDownAddon(addon);
 });
 
 add_task(async function test_can_reload_permanent_addon() {
   await promiseRestartManager();
-  const addon = await installAddon(sampleAddon.name, sampleAddon.id);
+  const {addon} = await AddonTestUtils.promiseInstallXPI(ADDONS.webextension_1);
 
   let disabledCalled = false;
   let enabledCalled = false;
   AddonManager.addAddonListener({
     onDisabled: (aAddon) => {
       Assert.ok(!enabledCalled);
       disabledCalled = true;
     },
--- a/toolkit/mozapps/extensions/test/xpcshell/test_seen.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_seen.js
@@ -9,37 +9,61 @@ profileDir.append("extensions");
 
 // By default disable add-ons from the profile and the system-wide scope
 const SCOPES = AddonManager.SCOPE_PROFILE | AddonManager.SCOPE_SYSTEM;
 Services.prefs.setIntPref("extensions.enabledScopes", SCOPES);
 Services.prefs.setIntPref("extensions.autoDisableScopes", SCOPES);
 
 createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
 
+const ADDONS = {
+  test_bootstrap1_1: {
+    "install.rdf": {
+      "id": "bootstrap1@tests.mozilla.org",
+      "name": "Test Bootstrap 1",
+    },
+    "bootstrap.js": BOOTSTRAP_MONITOR_BOOTSTRAP_JS
+  },
+  test_bootstrap1_2: {
+    "install.rdf": {
+      "id": "bootstrap1@tests.mozilla.org",
+      "version": "2.0",
+      "name": "Test Bootstrap 1",
+    },
+    "bootstrap.js": BOOTSTRAP_MONITOR_BOOTSTRAP_JS
+  },
+};
+
+const XPIS = {};
+for (let [name, files] of Object.entries(ADDONS)) {
+  XPIS[name] = AddonTestUtils.createTempXPIFile(files);
+}
+
+
 // Installing an add-on through the API should mark it as seen
 add_task(async function() {
   await promiseStartupManager();
 
-  let install = await promiseInstallFile(do_get_addon("test_bootstrap1_1"));
+  let install = await AddonTestUtils.promiseInstallXPI(ADDONS.test_bootstrap1_1);
   Assert.equal(install.state, AddonManager.STATE_INSTALLED);
   Assert.ok(!hasFlag(install.addon.pendingOperations, AddonManager.PENDING_INSTALL));
 
   let addon = install.addon;
   Assert.equal(addon.version, "1.0");
   Assert.ok(!addon.foreignInstall);
   Assert.ok(addon.seen);
 
   await promiseRestartManager();
 
   addon = await promiseAddonByID(ID);
   Assert.ok(!addon.foreignInstall);
   Assert.ok(addon.seen);
 
   // Installing an update should retain that
-  install = await promiseInstallFile(do_get_addon("test_bootstrap1_2"));
+  install = await AddonTestUtils.promiseInstallXPI(ADDONS.test_bootstrap1_2);
   Assert.equal(install.state, AddonManager.STATE_INSTALLED);
   Assert.ok(!hasFlag(install.addon.pendingOperations, AddonManager.PENDING_INSTALL));
 
   addon = install.addon;
   Assert.equal(addon.version, "2.0");
   Assert.ok(!addon.foreignInstall);
   Assert.ok(addon.seen);
 
@@ -64,17 +88,17 @@ add_task(async function() {
   registerDirectory("XRESysSExtPD", systemParentDir.clone());
   registerCleanupFunction(() => {
     systemParentDir.remove(true);
   });
 
   let systemDir = systemParentDir.clone();
   systemDir.append(Services.appinfo.ID);
 
-  let path = await manuallyInstall(do_get_addon("test_bootstrap1_1"), systemDir, ID);
+  let path = await manuallyInstall(XPIS.test_bootstrap1_1, systemDir, ID);
   // Make sure the startup code will detect sideloaded updates
   setExtensionModifiedTime(path, Date.now() - 10000);
 
   await promiseStartupManager();
   await AddonManagerPrivate.getNewSideloads();
 
   let addon = await promiseAddonByID(ID);
   Assert.equal(addon.version, "1.0");
@@ -92,17 +116,17 @@ add_task(async function() {
   path.remove(true);
 
   Services.prefs.setIntPref("extensions.startupScanScopes", savedStartupScanScopes);
 });
 
 // Sideloading an add-on in the profile should mark it as unseen and it should
 // remain unseen after an update is sideloaded.
 add_task(async function() {
-  let path = await manuallyInstall(do_get_addon("test_bootstrap1_1"), profileDir, ID);
+  let path = await manuallyInstall(XPIS.test_bootstrap1_1, profileDir, ID);
   // Make sure the startup code will detect sideloaded updates
   setExtensionModifiedTime(path, Date.now() - 10000);
 
   await promiseStartupManager();
 
   let addon = await promiseAddonByID(ID);
   Assert.equal(addon.version, "1.0");
   Assert.ok(addon.foreignInstall);
@@ -113,34 +137,34 @@ add_task(async function() {
   addon = await promiseAddonByID(ID);
   Assert.ok(addon.foreignInstall);
   Assert.ok(!addon.seen);
 
   await promiseShutdownManager();
 
   // Sideloading an update shouldn't change the state
   manuallyUninstall(profileDir, ID);
-  await manuallyInstall(do_get_addon("test_bootstrap1_2"), profileDir, ID);
+  await manuallyInstall(XPIS.test_bootstrap1_2, profileDir, ID);
   setExtensionModifiedTime(path, Date.now());
 
   await promiseStartupManager();
 
   addon = await promiseAddonByID(ID);
   Assert.equal(addon.version, "2.0");
   Assert.ok(addon.foreignInstall);
   Assert.ok(!addon.seen);
 
   await addon.uninstall();
   await promiseShutdownManager();
 });
 
 // Sideloading an add-on in the profile should mark it as unseen and it should
 // remain unseen after a regular update.
 add_task(async function() {
-  let path = await manuallyInstall(do_get_addon("test_bootstrap1_1"), profileDir, ID);
+  let path = await manuallyInstall(XPIS.test_bootstrap1_1, profileDir, ID);
   // Make sure the startup code will detect sideloaded updates
   setExtensionModifiedTime(path, Date.now() - 10000);
 
   await promiseStartupManager();
 
   let addon = await promiseAddonByID(ID);
   Assert.equal(addon.version, "1.0");
   Assert.ok(addon.foreignInstall);
@@ -148,17 +172,17 @@ add_task(async function() {
 
   await promiseRestartManager();
 
   addon = await promiseAddonByID(ID);
   Assert.ok(addon.foreignInstall);
   Assert.ok(!addon.seen);
 
   // Updating through the API shouldn't change the state
-  let install = await promiseInstallFile(do_get_addon("test_bootstrap1_2"));
+  let install = await AddonTestUtils.promiseInstallXPI(ADDONS.test_bootstrap1_2);
   Assert.equal(install.state, AddonManager.STATE_INSTALLED);
   Assert.ok(!hasFlag(install.addon.pendingOperations, AddonManager.PENDING_INSTALL));
 
   addon = install.addon;
   Assert.ok(addon.foreignInstall);
   Assert.ok(!addon.seen);
 
   await promiseRestartManager();
@@ -170,17 +194,17 @@ add_task(async function() {
 
   await addon.uninstall();
   await promiseShutdownManager();
 });
 
 // After a sideloaded addon has been seen, sideloading an update should
 // not reset it to unseen.
 add_task(async function() {
-  let path = await manuallyInstall(do_get_addon("test_bootstrap1_1"), profileDir, ID);
+  let path = await manuallyInstall(XPIS.test_bootstrap1_1, profileDir, ID);
   // Make sure the startup code will detect sideloaded updates
   setExtensionModifiedTime(path, Date.now() - 10000);
 
   await promiseStartupManager();
 
   let addon = await promiseAddonByID(ID);
   Assert.equal(addon.version, "1.0");
   Assert.ok(addon.foreignInstall);
@@ -193,34 +217,34 @@ add_task(async function() {
   addon = await promiseAddonByID(ID);
   Assert.ok(addon.foreignInstall);
   Assert.ok(addon.seen);
 
   await promiseShutdownManager();
 
   // Sideloading an update shouldn't change the state
   manuallyUninstall(profileDir, ID);
-  await manuallyInstall(do_get_addon("test_bootstrap1_2"), profileDir, ID);
+  await manuallyInstall(XPIS.test_bootstrap1_2, profileDir, ID);
   setExtensionModifiedTime(path, Date.now());
 
   await promiseStartupManager();
 
   addon = await promiseAddonByID(ID);
   Assert.equal(addon.version, "2.0");
   Assert.ok(addon.foreignInstall);
   Assert.ok(addon.seen);
 
   await addon.uninstall();
   await promiseShutdownManager();
 });
 
 // After a sideloaded addon has been seen, manually applying an update should
 // not reset it to unseen.
 add_task(async function() {
-  let path = await manuallyInstall(do_get_addon("test_bootstrap1_1"), profileDir, ID);
+  let path = await manuallyInstall(XPIS.test_bootstrap1_1, profileDir, ID);
   // Make sure the startup code will detect sideloaded updates
   setExtensionModifiedTime(path, Date.now() - 10000);
 
   await promiseStartupManager();
 
   let addon = await promiseAddonByID(ID);
   Assert.equal(addon.version, "1.0");
   Assert.ok(addon.foreignInstall);
@@ -230,17 +254,17 @@ add_task(async function() {
 
   await promiseRestartManager();
 
   addon = await promiseAddonByID(ID);
   Assert.ok(addon.foreignInstall);
   Assert.ok(addon.seen);
 
   // Updating through the API shouldn't change the state
-  let install = await promiseInstallFile(do_get_addon("test_bootstrap1_2"));
+  let install = await AddonTestUtils.promiseInstallXPI(ADDONS.test_bootstrap1_2);
   Assert.equal(install.state, AddonManager.STATE_INSTALLED);
   Assert.ok(!hasFlag(install.addon.pendingOperations, AddonManager.PENDING_INSTALL));
 
   addon = install.addon;
   Assert.ok(addon.foreignInstall);
   Assert.ok(addon.seen);
 
   await promiseRestartManager();
--- a/toolkit/mozapps/extensions/test/xpcshell/test_startup.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_startup.js
@@ -63,16 +63,17 @@ var addon4 = {
     minVersion: "1",
     maxVersion: "1"
   }]
 };
 
 // Should be ignored because it has no version
 var addon5 = {
   id: "addon5@tests.mozilla.org",
+  version: undefined,
   name: "Test 5",
   bootstrap: true,
   targetApplications: [{
     id: "xpcshell@tests.mozilla.org",
     minVersion: "1",
     maxVersion: "1"
   }]
 };
--- a/toolkit/mozapps/extensions/test/xpcshell/test_switch_os.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_switch_os.js
@@ -8,20 +8,29 @@ const ID = "bootstrap1@tests.mozilla.org
 
 BootstrapMonitor.init();
 
 const profileDir = gProfD.clone();
 profileDir.append("extensions");
 
 createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
 
+const ADDONS = {
+  test_bootstrap1_1: {
+    "install.rdf": {
+      "id": "bootstrap1@tests.mozilla.org",
+    },
+    "bootstrap.js": BOOTSTRAP_MONITOR_BOOTSTRAP_JS
+  },
+};
+
 add_task(async function() {
   await promiseStartupManager();
 
-  await promiseInstallFile(do_get_addon("test_bootstrap1_1"));
+  await AddonTestUtils.promiseInstallXPI(ADDONS.test_bootstrap1_1);
 
   let addon = await promiseAddonByID(ID);
   Assert.notEqual(addon, null);
 
   BootstrapMonitor.checkAddonStarted(ID);
   Assert.ok(!addon.userDisabled);
   Assert.ok(addon.isActive);
 
--- a/toolkit/mozapps/extensions/test/xpcshell/test_syncGUID.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_syncGUID.js
@@ -18,37 +18,22 @@ add_task(async function setup() {
 
   createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
   await promiseStartupManager();
 });
 
 const ADDONS = [
   {
     id: "addon1@tests.mozilla.org",
-    version: "1.0",
     name: "Test 1",
-    bootstrap: true,
-    description: "Test Description",
-
-    targetApplications: [{
-      id: "xpcshell@tests.mozilla.org",
-      minVersion: "1",
-      maxVersion: "1"}],
   },
   {
     id: "addon2@tests.mozilla.org",
     version: "2.0",
     name: "Real Test 2",
-    bootstrap: true,
-    description: "Test Description",
-
-    targetApplications: [{
-      id: "xpcshell@tests.mozilla.org",
-      minVersion: "1",
-      maxVersion: "1"}],
   },
 ];
 
 const XPIS = ADDONS.map(addon => createTempXPIFile(addon));
 
 const UUID_PATTERN = /^\{[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\}$/i;
 
 add_test(async function test_getter_and_setter() {
--- a/toolkit/mozapps/extensions/test/xpcshell/test_temporary.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_temporary.js
@@ -1,62 +1,73 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
 
 const ID = "bootstrap1@tests.mozilla.org";
-const sampleRDFManifest = {
-  id: ID,
-  version: "1.0",
-  bootstrap: true,
-  targetApplications: [{
-    id: "xpcshell@tests.mozilla.org",
-    minVersion: "1",
-    maxVersion: "1"
-  }],
-  name: "Test Bootstrap 1 (temporary)",
-};
 
 createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42");
 
-BootstrapMonitor.init();
+const ADDONS = {
+  test_bootstrap1_1: {
+    "install.rdf": {
+      "id": ID,
+      "name": "Test Bootstrap 1",
+    },
+    "bootstrap.js": EMPTY_BOOTSTRAP_JS
+  },
+  test_bootstrap1_2: {
+    "install.rdf": {
+      "id": "bootstrap1@tests.mozilla.org",
+      "version": "2.0",
+      "name": "Test Bootstrap 1",
+    },
+    "bootstrap.js": EMPTY_BOOTSTRAP_JS
+  },
+};
 
-// Partial list of bootstrap reasons from XPIProvider.jsm
-const BOOTSTRAP_REASONS = {
-  APP_STARTUP: 1,
-  ADDON_INSTALL: 5,
-  ADDON_UNINSTALL: 6,
-  ADDON_UPGRADE: 7,
-  ADDON_DOWNGRADE: 8,
-};
+const XPIS = {};
+for (let [name, files] of Object.entries(ADDONS)) {
+  XPIS[name] = AddonTestUtils.createTempXPIFile(files);
+}
 
 function waitForBootstrapEvent(expectedEvent, addonId) {
   return new Promise(resolve => {
-    const observer = {
-      observe: (subject, topic, data) => {
-        const info = JSON.parse(data);
-        const targetAddonId = info.data.id;
-        if (targetAddonId === addonId && info.event === expectedEvent) {
-          resolve(info);
-          Services.obs.removeObserver(observer, "bootstrapmonitor-event");
-        } else {
-          info(
-            `Ignoring bootstrap event: ${info.event} for ${targetAddonId}`);
-        }
-      },
-    };
-    Services.obs.addObserver(observer, "bootstrapmonitor-event");
+    function listener(msg, {method, params, reason}) {
+      if (params.id === addonId && method === expectedEvent) {
+        resolve({params, method, reason});
+        AddonTestUtils.off("bootstrap-method", listener);
+      } else {
+        info(`Ignoring bootstrap event: ${method} for ${params.id}`);
+      }
+    }
+    AddonTestUtils.on("bootstrap-method", listener);
   });
 }
 
+async function checkEvent(promise, {reason, params}) {
+  let event = await promise;
+  info(`Checking bootstrap event ${event.method} for ${event.params.id}`);
+
+  equal(event.reason, reason,
+        `Expected bootstrap reason ${getReasonName(reason)} got ${getReasonName(event.reason)}`);
+
+  for (let [param, value] of Object.entries(params)) {
+    equal(event.params[param], value, `Expected value for params.${param}`);
+  }
+}
+
+let Monitor = SlightlyLessDodgyBootstrapMonitor;
+Monitor.init();
+
 // Install a temporary add-on with no existing add-on present.
 // Restart and make sure it has gone away.
-add_task(async function() {
+add_task(async function test_new_temporary() {
   await promiseStartupManager();
 
   let extInstallCalled = false;
   AddonManager.addInstallListener({
     onExternalInstall: (aInstall) => {
       Assert.equal(aInstall.id, ID);
       Assert.equal(aInstall.version, "1.0");
       extInstallCalled = true;
@@ -76,100 +87,97 @@ add_task(async function() {
       Assert.equal(aInstall.version, "1.0");
       installedCalled = true;
     },
     onInstallStarted: (aInstall) => {
       do_throw("onInstallStarted called unexpectedly");
     }
   });
 
-  await AddonManager.installTemporaryAddon(do_get_addon("test_bootstrap1_1"));
+  await AddonManager.installTemporaryAddon(XPIS.test_bootstrap1_1);
 
   Assert.ok(extInstallCalled);
   Assert.ok(installingCalled);
   Assert.ok(installedCalled);
 
-  const install = BootstrapMonitor.checkAddonInstalled(ID, "1.0");
+  const install = Monitor.checkInstalled(ID, "1.0");
   equal(install.reason, BOOTSTRAP_REASONS.ADDON_INSTALL);
-  BootstrapMonitor.checkAddonStarted(ID, "1.0");
 
-  let info = BootstrapMonitor.started.get(ID);
+  Monitor.checkStarted(ID, "1.0");
+
+  let info = Monitor.started.get(ID);
   Assert.equal(info.reason, BOOTSTRAP_REASONS.ADDON_INSTALL);
 
   let addon = await promiseAddonByID(ID);
 
-  Assert.notEqual(addon, null);
-  Assert.equal(addon.version, "1.0");
-  Assert.equal(addon.name, "Test Bootstrap 1");
-  Assert.ok(addon.isCompatible);
-  Assert.ok(!addon.appDisabled);
-  Assert.ok(addon.isActive);
-  Assert.equal(addon.type, "extension");
-  Assert.equal(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_PRIVILEGED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
+  checkAddon(ID, addon, {
+    version: "1.0",
+    name: "Test Bootstrap 1",
+    isCompatible: true,
+    appDisabled: false,
+    isActive: true,
+    type: "extension",
+    signedState: AddonManager.SIGNEDSTATE_PRIVILEGED,
+    temporarilyInstalled: true,
+  });
 
   let onShutdown = waitForBootstrapEvent("shutdown", ID);
   let onUninstall = waitForBootstrapEvent("uninstall", ID);
 
   await promiseRestartManager();
 
   let shutdown = await onShutdown;
   equal(shutdown.reason, BOOTSTRAP_REASONS.ADDON_UNINSTALL);
 
   let uninstall = await onUninstall;
   equal(uninstall.reason, BOOTSTRAP_REASONS.ADDON_UNINSTALL);
 
-  BootstrapMonitor.checkAddonNotInstalled(ID);
-  BootstrapMonitor.checkAddonNotStarted(ID);
+  Monitor.checkNotInstalled(ID);
+  Monitor.checkNotStarted(ID);
 
   addon = await promiseAddonByID(ID);
   Assert.equal(addon, null);
 
   await promiseRestartManager();
 });
 
 // Install a temporary add-on over the top of an existing add-on.
 // Restart and make sure the existing add-on comes back.
-add_task(async function() {
-  await promiseInstallAllFiles([do_get_addon("test_bootstrap1_2")], true);
+add_task(async function test_replace_temporary() {
+  let {addon} = await AddonTestUtils.promiseInstallXPI(ADDONS.test_bootstrap1_2);
 
-  BootstrapMonitor.checkAddonInstalled(ID, "2.0");
-  BootstrapMonitor.checkAddonStarted(ID, "2.0");
-
-  let addon = await promiseAddonByID(ID);
+  Monitor.checkInstalled(ID, "2.0");
+  Monitor.checkStarted(ID, "2.0");
 
-  Assert.notEqual(addon, null);
-  Assert.equal(addon.version, "2.0");
-  Assert.equal(addon.name, "Test Bootstrap 1");
-  Assert.ok(addon.isCompatible);
-  Assert.ok(!addon.appDisabled);
-  Assert.ok(addon.isActive);
-  Assert.equal(addon.type, "extension");
-  Assert.equal(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_PRIVILEGED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
+  checkAddon(ID, addon, {
+    version: "2.0",
+    name: "Test Bootstrap 1",
+    isCompatible: true,
+    appDisabled: false,
+    isActive: true,
+    type: "extension",
+    signedState: AddonManager.SIGNEDSTATE_PRIVILEGED,
+    temporarilyInstalled: false,
+  });
 
   let tempdir = gTmpD.clone();
 
   let bootstrapJS = await OS.File.read("data/test_temporary/bootstrap.js", {encoding: "utf-8"});
 
   for (let newversion of ["1.0", "3.0"]) {
     for (let packed of [false, true]) {
       // ugh, file locking issues with xpis on windows
       if (packed && AppConstants.platform == "win") {
         continue;
       }
 
       let files = {
         "install.rdf": AddonTestUtils.createInstallRDF({
           id: ID,
           version: newversion,
-          bootstrap: true,
-          targetApplications: [{
-            id: "xpcshell@tests.mozilla.org",
-            minVersion: "1",
-            maxVersion: "1"
-          }],
           name: "Test Bootstrap 1 (temporary)",
         }),
         "bootstrap.js": bootstrapJS,
       };
 
       let target;
       if (!packed) {
         // Unpacked extensions don't support signing, which means that
@@ -197,107 +205,135 @@ add_task(async function() {
       let onStartup = waitForBootstrapEvent("startup", ID);
 
       await AddonManager.installTemporaryAddon(target);
 
       let reason = Services.vc.compare(newversion, "2.0") < 0 ?
                    BOOTSTRAP_REASONS.ADDON_DOWNGRADE :
                    BOOTSTRAP_REASONS.ADDON_UPGRADE;
 
-      let shutdown = await onShutdown;
-      equal(shutdown.data.version, "2.0");
-      equal(shutdown.reason, reason);
+      await checkEvent(onShutdown, {
+        reason,
+        params: {
+          version: "2.0",
+        },
+      });
 
-      let uninstall = await onUninstall;
-      equal(uninstall.data.version, "2.0");
-      equal(uninstall.reason, reason);
+      await checkEvent(onUninstall, {
+        reason,
+        params: {
+          version: "2.0",
+        },
+      });
 
-      let install = await onInstall;
-      equal(install.data.version, newversion);
-      equal(install.reason, reason);
-      equal(install.data.oldVersion, "2.0");
+      await checkEvent(onInstall, {
+        reason,
+        params: {
+          version: newversion,
+          oldVersion: "2.0",
+        },
+      });
 
-      let startup = await onStartup;
-      equal(startup.data.version, newversion);
-      equal(startup.reason, reason);
-      equal(startup.data.oldVersion, "2.0");
+      await checkEvent(onStartup, {
+        reason,
+        params: {
+          version: newversion,
+          oldVersion: "2.0",
+        },
+      });
 
       addon = await promiseAddonByID(ID);
 
       let signedState = packed ? AddonManager.SIGNEDSTATE_PRIVILEGED : AddonManager.SIGNEDSTATE_UNKNOWN;
 
       // temporary add-on is installed and started
-      Assert.notEqual(addon, null);
-      Assert.equal(addon.version, newversion);
-      Assert.equal(addon.name, "Test Bootstrap 1 (temporary)");
-      Assert.ok(addon.isCompatible);
-      Assert.ok(!addon.appDisabled);
-      Assert.ok(addon.isActive);
-      Assert.equal(addon.type, "extension");
-      Assert.equal(addon.signedState, mozinfo.addon_signing ? signedState : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
+      checkAddon(ID, addon, {
+        version: newversion,
+        name: "Test Bootstrap 1 (temporary)",
+        isCompatible: true,
+        appDisabled: false,
+        isActive: true,
+        type: "extension",
+        signedState,
+        temporarilyInstalled: true,
+      });
 
       // Now restart, the temporary addon will go away which should
       // be the opposite action (ie, if the temporary addon was an
       // upgrade, then removing it is a downgrade and vice versa)
       reason = reason == BOOTSTRAP_REASONS.ADDON_UPGRADE ?
                BOOTSTRAP_REASONS.ADDON_DOWNGRADE :
                BOOTSTRAP_REASONS.ADDON_UPGRADE;
 
       onShutdown = waitForBootstrapEvent("shutdown", ID);
       onUninstall = waitForBootstrapEvent("uninstall", ID);
       onInstall = waitForBootstrapEvent("install", ID);
       onStartup = waitForBootstrapEvent("startup", ID);
 
       await promiseRestartManager();
 
-      shutdown = await onShutdown;
-      equal(shutdown.data.version, newversion);
-      equal(shutdown.reason, reason);
+      await checkEvent(onShutdown, {
+        reason,
+        params: {
+          version: newversion,
+        },
+      });
 
-      uninstall = await onUninstall;
-      equal(uninstall.data.version, newversion);
-      equal(uninstall.reason, reason);
+      await checkEvent(onUninstall, {
+        reason,
+        params: {
+          version: newversion,
+        },
+      });
 
-      install = await onInstall;
-      equal(install.data.version, "2.0");
-      equal(install.reason, reason);
-      equal(install.data.oldVersion, newversion);
+      await checkEvent(onInstall, {
+        reason,
+        params: {
+          version: "2.0",
+          oldVersion: newversion,
+        },
+      });
 
-      startup = await onStartup;
-      equal(startup.data.version, "2.0");
-      // We don't actually propagate the upgrade/downgrade reason across
-      // the browser restart when a temporary addon is removed.  See
-      // bug 1359558 for detailed reasoning.
-      equal(startup.reason, BOOTSTRAP_REASONS.APP_STARTUP);
+      await checkEvent(onStartup, {
+        // We don't actually propagate the upgrade/downgrade reason across
+        // the browser restart when a temporary addon is removed.  See
+        // bug 1359558 for detailed reasoning.
+        reason: BOOTSTRAP_REASONS.APP_STARTUP,
+        params: {
+          version: "2.0",
+        },
+      });
 
-      BootstrapMonitor.checkAddonInstalled(ID, "2.0");
-      BootstrapMonitor.checkAddonStarted(ID, "2.0");
+      Monitor.checkInstalled(ID, "2.0");
+      Monitor.checkStarted(ID, "2.0");
 
       addon = await promiseAddonByID(ID);
 
       // existing add-on is back
-      Assert.notEqual(addon, null);
-      Assert.equal(addon.version, "2.0");
-      Assert.equal(addon.name, "Test Bootstrap 1");
-      Assert.ok(addon.isCompatible);
-      Assert.ok(!addon.appDisabled);
-      Assert.ok(addon.isActive);
-      Assert.equal(addon.type, "extension");
-      Assert.equal(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_PRIVILEGED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
+      checkAddon(ID, addon, {
+        version: "2.0",
+        name: "Test Bootstrap 1",
+        isCompatible: true,
+        appDisabled: false,
+        isActive: true,
+        type: "extension",
+        signedState: AddonManager.SIGNEDSTATE_PRIVILEGED,
+        temporarilyInstalled: false,
+      });
 
       Services.obs.notifyObservers(target, "flush-cache-entry");
       target.remove(true);
     }
   }
 
   // remove original add-on
   await addon.uninstall();
 
-  BootstrapMonitor.checkAddonNotInstalled(ID);
-  BootstrapMonitor.checkAddonNotStarted(ID);
+  Monitor.checkNotInstalled(ID);
+  Monitor.checkNotStarted(ID);
 
   await promiseRestartManager();
 });
 
 // Test that loading from the same path multiple times work
 add_task(async function test_samefile() {
   // File locking issues on Windows, ugh
   if (AppConstants.platform == "win") {
@@ -312,89 +348,90 @@ add_task(async function test_samefile() 
       applications: {
         gecko: {
           id: ID
         }
       }
     }
   });
 
-  await AddonManager.installTemporaryAddon(webext);
-  let addon = await promiseAddonByID(ID);
+  let addon = await AddonManager.installTemporaryAddon(webext);
 
   // temporary add-on is installed and started
-  Assert.notEqual(addon, null);
-  Assert.equal(addon.version, "1.0");
-  Assert.equal(addon.name, "Test WebExtension 1 (temporary)");
-  Assert.ok(addon.isCompatible);
-  Assert.ok(!addon.appDisabled);
-  Assert.ok(addon.isActive);
-  Assert.equal(addon.type, "extension");
-  Assert.equal(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_PRIVILEGED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
+  checkAddon(ID, addon, {
+    version: "1.0",
+    name: "Test WebExtension 1 (temporary)",
+    isCompatible: true,
+    appDisabled: false,
+    isActive: true,
+    type: "extension",
+    signedState: AddonManager.SIGNEDSTATE_PRIVILEGED,
+    temporarilyInstalled: true,
+  });
 
   Services.obs.notifyObservers(webext, "flush-cache-entry");
   webext.remove(false);
   webext = createTempWebExtensionFile({
     manifest: {
       version: "2.0",
       name: "Test WebExtension 1 (temporary)",
       applications: {
         gecko: {
           id: ID
         }
       }
     }
   });
 
-  await AddonManager.installTemporaryAddon(webext);
-  addon = await promiseAddonByID(ID);
+  addon = await AddonManager.installTemporaryAddon(webext);
 
   // temporary add-on is installed and started
-  Assert.notEqual(addon, null);
-  Assert.equal(addon.version, "2.0");
-  Assert.equal(addon.name, "Test WebExtension 1 (temporary)");
-  Assert.ok(addon.isCompatible);
-  Assert.ok(!addon.appDisabled);
-  Assert.ok(addon.isActive);
-  Assert.equal(addon.type, "extension");
-  Assert.ok(addon.isWebExtension);
-  Assert.equal(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_PRIVILEGED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
+  checkAddon(ID, addon, {
+    version: "2.0",
+    name: "Test WebExtension 1 (temporary)",
+    isCompatible: true,
+    appDisabled: false,
+    isActive: true,
+    type: "extension",
+    isWebExtension: true,
+    signedState: AddonManager.SIGNEDSTATE_PRIVILEGED,
+    temporarilyInstalled: true,
+  });
 
   await addon.uninstall();
 });
 
+function promiseWriteWebExtensionToDir(dir, manifest) {
+  return AddonTestUtils.promiseWriteFilesToDir(dir.path, {
+    "manifest.json": ExtensionTestCommon.generateManifest(manifest),
+  });
+}
+
 // Install a temporary add-on over the top of an existing add-on.
 // Uninstall it and make sure the existing add-on comes back.
-add_task(async function() {
-  // We can't install unpacked add-ons on release builds. See above.
-  if (!AppConstants.MOZ_ALLOW_LEGACY_EXTENSIONS) {
-    return;
-  }
-
-  await promiseInstallAllFiles([do_get_addon("test_bootstrap1_1")], true);
-
-  BootstrapMonitor.checkAddonInstalled(ID, "1.0");
-  BootstrapMonitor.checkAddonStarted(ID, "1.0");
+add_task(async function test_replace_permanent() {
+  await promiseInstallWebExtension({
+    manifest: {
+      applications: {gecko: {id: ID}},
+      version: "1.0",
+      name: "Test Bootstrap 1",
+    },
+  });
 
-  let tempdir = gTmpD.clone();
-  await promiseWriteInstallRDFToDir({
-    id: ID,
+  Monitor.checkInstalled(ID, "1.0");
+  Monitor.checkStarted(ID, "1.0");
+
+  let unpacked_addon = gTmpD.clone();
+  unpacked_addon.append(ID);
+
+  await promiseWriteWebExtensionToDir(unpacked_addon, {
+    applications: {gecko: {id: ID}},
     version: "2.0",
-    bootstrap: true,
-    unpack: true,
-    targetApplications: [{
-          id: "xpcshell@tests.mozilla.org",
-      minVersion: "1",
-      maxVersion: "1"
-        }],
     name: "Test Bootstrap 1 (temporary)",
-  }, tempdir);
-
-  let unpacked_addon = tempdir.clone();
-  unpacked_addon.append(ID);
+  });
 
   let extInstallCalled = false;
   AddonManager.addInstallListener({
     onExternalInstall: (aInstall) => {
       Assert.equal(aInstall.id, ID);
       Assert.equal(aInstall.version, "2.0");
       extInstallCalled = true;
     },
@@ -415,380 +452,317 @@ add_task(async function() {
         Assert.equal(aInstall.version, "2.0");
       installedCalled = true;
     },
     onInstallStarted: (aInstall) => {
       do_throw("onInstallStarted called unexpectedly");
     }
   });
 
-  await AddonManager.installTemporaryAddon(unpacked_addon);
+  let addon = await AddonManager.installTemporaryAddon(unpacked_addon);
 
   Assert.ok(extInstallCalled);
   Assert.ok(installingCalled);
   Assert.ok(installedCalled);
 
-  let addon = await promiseAddonByID(ID);
-
-  BootstrapMonitor.checkAddonNotInstalled(ID);
-  BootstrapMonitor.checkAddonNotStarted(ID);
+  Monitor.checkInstalled(ID);
+  Monitor.checkStarted(ID);
 
   // temporary add-on is installed and started
-  Assert.notEqual(addon, null);
-  Assert.equal(addon.version, "2.0");
-  Assert.equal(addon.name, "Test Bootstrap 1 (temporary)");
-  Assert.ok(addon.isCompatible);
-  Assert.ok(!addon.appDisabled);
-  Assert.ok(addon.isActive);
-  Assert.equal(addon.type, "extension");
-  Assert.equal(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_UNKNOWN : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
+  checkAddon(ID, addon, {
+    version: "2.0",
+    name: "Test Bootstrap 1 (temporary)",
+    isCompatible: true,
+    appDisabled: false,
+    isActive: true,
+    type: "extension",
+    signedState: AddonManager.SIGNEDSTATE_UNKNOWN,
+    temporarilyInstalled: true,
+  });
 
   await addon.uninstall();
 
-  await new Promise(executeSoon);
-  addon = await promiseAddonByID(ID);
+  Monitor.checkInstalled(ID);
+  Monitor.checkStarted(ID);
 
-  BootstrapMonitor.checkAddonInstalled(ID);
-  BootstrapMonitor.checkAddonStarted(ID);
+  addon = await promiseAddonByID(ID);
 
   // existing add-on is back
-  Assert.notEqual(addon, null);
-  Assert.equal(addon.version, "1.0");
-  Assert.equal(addon.name, "Test Bootstrap 1");
-  Assert.ok(addon.isCompatible);
-  Assert.ok(!addon.appDisabled);
-  Assert.ok(addon.isActive);
-  Assert.equal(addon.type, "extension");
-  Assert.equal(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_PRIVILEGED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
+  checkAddon(ID, addon, {
+    version: "1.0",
+    name: "Test Bootstrap 1",
+    isCompatible: true,
+    appDisabled: false,
+    isActive: true,
+    type: "extension",
+    signedState: AddonManager.SIGNEDSTATE_PRIVILEGED,
+    temporarilyInstalled: false,
+  });
 
   unpacked_addon.remove(true);
   await addon.uninstall();
 
-  BootstrapMonitor.checkAddonNotInstalled(ID);
-  BootstrapMonitor.checkAddonNotStarted(ID);
+  Monitor.checkNotInstalled(ID);
+  Monitor.checkNotStarted(ID);
 
   await promiseRestartManager();
 });
 
 // Install a temporary add-on as a version upgrade over the top of an
 // existing temporary add-on.
-add_task(async function() {
-  // We can't install unpacked add-ons on release builds. See above.
-  if (!AppConstants.MOZ_ALLOW_LEGACY_EXTENSIONS) {
-    return;
-  }
+add_task(async function test_replace_temporary_upgrade_presumably_in_a_different_way_from_the_previous_similar_task() {
+  const unpackedAddon = gTmpD.clone();
+  unpackedAddon.append(ID);
 
-  const tempdir = gTmpD.clone();
-
-  await promiseWriteInstallRDFToDir(sampleRDFManifest, tempdir, "bootstrap1@tests.mozilla.org", "bootstrap.js");
-
-  const unpackedAddon = tempdir.clone();
-  unpackedAddon.append(ID);
-  do_get_file("data/test_temporary/bootstrap.js")
-    .copyTo(unpackedAddon, "bootstrap.js");
+  await promiseWriteWebExtensionToDir(unpackedAddon, {
+    applications: {gecko: {id: ID}},
+    version: "1.0",
+  });
 
   await AddonManager.installTemporaryAddon(unpackedAddon);
 
   // Increment the version number, re-install it, and make sure it
   // gets marked as an upgrade.
-  await promiseWriteInstallRDFToDir(Object.assign({}, sampleRDFManifest, {
-    version: "2.0"
-  }), tempdir, "bootstrap1@tests.mozilla.org");
+  await promiseWriteWebExtensionToDir(unpackedAddon, {
+    applications: {gecko: {id: ID}},
+    version: "2.0",
+  });
 
   const onShutdown = waitForBootstrapEvent("shutdown", ID);
-  const onUninstall = waitForBootstrapEvent("uninstall", ID);
-  const onInstall = waitForBootstrapEvent("install", ID);
+  const onUpdate = waitForBootstrapEvent("update", ID);
   const onStartup = waitForBootstrapEvent("startup", ID);
   await AddonManager.installTemporaryAddon(unpackedAddon);
 
-  const shutdown = await onShutdown;
-  equal(shutdown.data.version, "1.0");
-  equal(shutdown.reason, BOOTSTRAP_REASONS.ADDON_UPGRADE);
-
-  const uninstall = await onUninstall;
-  equal(uninstall.data.version, "1.0");
-  equal(uninstall.reason, BOOTSTRAP_REASONS.ADDON_UPGRADE);
+  await checkEvent(onShutdown, {
+    reason: BOOTSTRAP_REASONS.ADDON_UPGRADE,
+    params: {
+      version: "1.0",
+    },
+  });
 
-  const install = await onInstall;
-  equal(install.data.version, "2.0");
-  equal(install.reason, BOOTSTRAP_REASONS.ADDON_UPGRADE);
-  equal(install.data.oldVersion, "1.0");
+  await checkEvent(onUpdate, {
+    reason: BOOTSTRAP_REASONS.ADDON_UPGRADE,
+    params: {
+      version: "2.0",
+      oldVersion: "1.0",
+    },
+  });
 
-  const startup = await onStartup;
-  equal(startup.data.version, "2.0");
-  equal(startup.reason, BOOTSTRAP_REASONS.ADDON_UPGRADE);
-  equal(startup.data.oldVersion, "1.0");
+  await checkEvent(onStartup, {
+    reason: BOOTSTRAP_REASONS.ADDON_UPGRADE,
+    params: {
+      version: "2.0",
+      oldVersion: "1.0",
+    },
+  });
 
   const addon = await promiseAddonByID(ID);
   await addon.uninstall();
 
   unpackedAddon.remove(true);
   await promiseRestartManager();
 });
 
 // Install a temporary add-on as a version downgrade over the top of an
 // existing temporary add-on.
-add_task(async function() {
-  // We can't install unpacked add-ons on release builds. See above.
-  if (!AppConstants.MOZ_ALLOW_LEGACY_EXTENSIONS) {
-    return;
-  }
+add_task(async function test_replace_temporary_downgrade() {
+  const unpackedAddon = gTmpD.clone();
+  unpackedAddon.append(ID);
 
-  const tempdir = gTmpD.clone();
-
-  await promiseWriteInstallRDFToDir(sampleRDFManifest, tempdir, "bootstrap1@tests.mozilla.org", "bootstrap.js");
-
-  const unpackedAddon = tempdir.clone();
-  unpackedAddon.append(ID);
-  do_get_file("data/test_temporary/bootstrap.js")
-    .copyTo(unpackedAddon, "bootstrap.js");
+  await promiseWriteWebExtensionToDir(unpackedAddon, {
+    applications: {gecko: {id: ID}},
+    version: "1.0",
+  });
 
   await AddonManager.installTemporaryAddon(unpackedAddon);
 
   // Decrement the version number, re-install, and make sure
   // it gets marked as a downgrade.
-  await promiseWriteInstallRDFToDir(Object.assign({}, sampleRDFManifest, {
-    version: "0.8"
-  }), tempdir, "bootstrap1@tests.mozilla.org");
+  await promiseWriteWebExtensionToDir(unpackedAddon, {
+    applications: {gecko: {id: ID}},
+    version: "0.8",
+  });
 
   const onShutdown = waitForBootstrapEvent("shutdown", ID);
-  const onUninstall = waitForBootstrapEvent("uninstall", ID);
-  const onInstall = waitForBootstrapEvent("install", ID);
+  const onUpdate = waitForBootstrapEvent("update", ID);
   const onStartup = waitForBootstrapEvent("startup", ID);
   await AddonManager.installTemporaryAddon(unpackedAddon);
 
-  const shutdown = await onShutdown;
-  equal(shutdown.data.version, "1.0");
-  equal(shutdown.reason, BOOTSTRAP_REASONS.ADDON_DOWNGRADE);
-
-  const uninstall = await onUninstall;
-  equal(uninstall.data.version, "1.0");
-  equal(uninstall.reason, BOOTSTRAP_REASONS.ADDON_DOWNGRADE);
+  await checkEvent(onShutdown, {
+    reason: BOOTSTRAP_REASONS.ADDON_DOWNGRADE,
+    params: {
+      version: "1.0",
+    },
+  });
 
-  const install = await onInstall;
-  equal(install.data.version, "0.8");
-  equal(install.reason, BOOTSTRAP_REASONS.ADDON_DOWNGRADE);
+  await checkEvent(onUpdate, {
+    reason: BOOTSTRAP_REASONS.ADDON_DOWNGRADE,
+    params: {
+      oldVersion: "1.0",
+      version: "0.8",
+    },
+  });
 
-  const startup = await onStartup;
-  equal(startup.data.version, "0.8");
-  equal(startup.reason, BOOTSTRAP_REASONS.ADDON_DOWNGRADE);
+  await checkEvent(onStartup, {
+    reason: BOOTSTRAP_REASONS.ADDON_DOWNGRADE,
+    params: {
+      version: "0.8",
+    },
+  });
 
   const addon = await promiseAddonByID(ID);
   await addon.uninstall();
 
   unpackedAddon.remove(true);
   await promiseRestartManager();
 });
 
 // Installing a temporary add-on over an existing add-on with the same
 // version number should be installed as an upgrade.
-add_task(async function() {
-  // We can't install unpacked add-ons on release builds. See above.
-  if (!AppConstants.MOZ_ALLOW_LEGACY_EXTENSIONS) {
-    return;
-  }
+add_task(async function test_replace_permanent_upgrade_presumably_in_a_different_way_from_the_previous_similar_task() {
+  const unpackedAddon = gTmpD.clone();
+  unpackedAddon.append(ID);
 
-  const tempdir = gTmpD.clone();
-
-  await promiseWriteInstallRDFToDir(sampleRDFManifest, tempdir, "bootstrap1@tests.mozilla.org", "bootstrap.js");
-
-  const unpackedAddon = tempdir.clone();
-  unpackedAddon.append(ID);
-  do_get_file("data/test_temporary/bootstrap.js")
-    .copyTo(unpackedAddon, "bootstrap.js");
+  await promiseWriteWebExtensionToDir(unpackedAddon, {
+    applications: {gecko: {id: ID}},
+    version: "1.0",
+  });
 
   const onInitialInstall = waitForBootstrapEvent("install", ID);
   const onInitialStartup = waitForBootstrapEvent("startup", ID);
   await AddonManager.installTemporaryAddon(unpackedAddon);
 
-  const initialInstall = await onInitialInstall;
-  equal(initialInstall.data.version, "1.0");
-  equal(initialInstall.reason, BOOTSTRAP_REASONS.ADDON_INSTALL);
+  await checkEvent(onInitialInstall, {
+    reason: BOOTSTRAP_REASONS.ADDON_INSTALL,
+    params: {
+      version: "1.0",
+    },
+  });
 
-  const initialStartup = await onInitialStartup;
-  equal(initialStartup.data.version, "1.0");
-  equal(initialStartup.reason, BOOTSTRAP_REASONS.ADDON_INSTALL);
+  await checkEvent(onInitialStartup, {
+    reason: BOOTSTRAP_REASONS.ADDON_INSTALL,
+    params: {
+      version: "1.0",
+    },
+  });
 
-  let info = BootstrapMonitor.started.get(ID);
+  let info = Monitor.started.get(ID);
   Assert.equal(info.reason, BOOTSTRAP_REASONS.ADDON_INSTALL);
 
   // Install it again.
   const onShutdown = waitForBootstrapEvent("shutdown", ID);
-  const onUninstall = waitForBootstrapEvent("uninstall", ID);
-  const onInstall = waitForBootstrapEvent("install", ID);
+  const onUpdate = waitForBootstrapEvent("update", ID);
   const onStartup = waitForBootstrapEvent("startup", ID);
   await AddonManager.installTemporaryAddon(unpackedAddon);
 
-  const shutdown = await onShutdown;
-  equal(shutdown.data.version, "1.0");
-  equal(shutdown.reason, BOOTSTRAP_REASONS.ADDON_UPGRADE);
-
-  const uninstall = await onUninstall;
-  equal(uninstall.data.version, "1.0");
-  equal(uninstall.reason, BOOTSTRAP_REASONS.ADDON_UPGRADE);
+  await checkEvent(onShutdown, {
+    reason: BOOTSTRAP_REASONS.ADDON_UPGRADE,
+    params: {
+      version: "1.0",
+    },
+  });
 
-  const reInstall = await onInstall;
-  equal(reInstall.data.version, "1.0");
-  equal(reInstall.reason, BOOTSTRAP_REASONS.ADDON_UPGRADE);
+  await checkEvent(onUpdate, {
+    reason: BOOTSTRAP_REASONS.ADDON_UPGRADE,
+    params: {
+      oldVersion: "1.0",
+      version: "1.0",
+    },
+  });
 
-  const startup = await onStartup;
-  equal(startup.data.version, "1.0");
-  equal(startup.reason, BOOTSTRAP_REASONS.ADDON_UPGRADE);
+  await checkEvent(onStartup, {
+    reason: BOOTSTRAP_REASONS.ADDON_UPGRADE,
+    params: {
+      version: "1.0",
+    },
+  });
 
   const addon = await promiseAddonByID(ID);
   await addon.uninstall();
 
   unpackedAddon.remove(true);
   await promiseRestartManager();
 });
 
 // Install a temporary add-on over the top of an existing disabled add-on.
 // After restart, the existing add-on should continue to be installed and disabled.
-add_task(async function() {
-  // We can't install unpacked add-ons on release builds. See above.
-  if (!AppConstants.MOZ_ALLOW_LEGACY_EXTENSIONS) {
-    return;
-  }
+add_task(async function test_replace_permanent_disabled() {
+  let {addon} = await AddonTestUtils.promiseInstallXPI(ADDONS.test_bootstrap1_1);
 
-  await promiseInstallAllFiles([do_get_addon("test_bootstrap1_1")], true);
-
-  BootstrapMonitor.checkAddonInstalled(ID, "1.0");
-  BootstrapMonitor.checkAddonStarted(ID, "1.0");
-
-  let addon = await promiseAddonByID(ID);
+  Monitor.checkInstalled(ID, "1.0");
+  Monitor.checkStarted(ID, "1.0");
 
   await addon.disable();
 
-  BootstrapMonitor.checkAddonInstalled(ID, "1.0");
-  BootstrapMonitor.checkAddonNotStarted(ID);
+  Monitor.checkInstalled(ID, "1.0");
+  Monitor.checkNotStarted(ID);
 
-  let tempdir = gTmpD.clone();
-  await promiseWriteInstallRDFToDir({
-    id: ID,
+  let unpacked_addon = gTmpD.clone();
+  unpacked_addon.append(ID);
+
+  await promiseWriteWebExtensionToDir(unpacked_addon, {
+    applications: {gecko: {id: ID}},
+    name: "Test Bootstrap 1 (temporary)",
     version: "2.0",
-    bootstrap: true,
-    unpack: true,
-    targetApplications: [{
-          id: "xpcshell@tests.mozilla.org",
-      minVersion: "1",
-      maxVersion: "1"
-        }],
-    name: "Test Bootstrap 1 (temporary)",
-  }, tempdir, "bootstrap1@tests.mozilla.org", "bootstrap.js");
-
-  let unpacked_addon = tempdir.clone();
-  unpacked_addon.append(ID);
-  do_get_file("data/test_temporary/bootstrap.js")
-    .copyTo(unpacked_addon, "bootstrap.js");
+  });
 
   let extInstallCalled = false;
   AddonManager.addInstallListener({
     onExternalInstall: (aInstall) => {
       Assert.equal(aInstall.id, ID);
       Assert.equal(aInstall.version, "2.0");
       extInstallCalled = true;
     },
   });
 
-  await AddonManager.installTemporaryAddon(unpacked_addon);
+  let tempAddon = await AddonManager.installTemporaryAddon(unpacked_addon);
 
   Assert.ok(extInstallCalled);
 
-  let tempAddon = await promiseAddonByID(ID);
-
-  BootstrapMonitor.checkAddonInstalled(ID, "2.0");
-  BootstrapMonitor.checkAddonStarted(ID);
+  Monitor.checkInstalled(ID, "2.0");
+  Monitor.checkStarted(ID);
 
   // temporary add-on is installed and started
-  Assert.notEqual(tempAddon, null);
-  Assert.equal(tempAddon.version, "2.0");
-  Assert.equal(tempAddon.name, "Test Bootstrap 1 (temporary)");
-  Assert.ok(tempAddon.isCompatible);
-  Assert.ok(!tempAddon.appDisabled);
-  Assert.ok(tempAddon.isActive);
-  Assert.equal(tempAddon.type, "extension");
-  Assert.equal(tempAddon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_UNKNOWN : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
+  checkAddon(ID, tempAddon, {
+    version: "2.0",
+    name: "Test Bootstrap 1 (temporary)",
+    isCompatible: true,
+    appDisabled: false,
+    isActive: true,
+    type: "extension",
+    signedState: AddonManager.SIGNEDSTATE_UNKNOWN,
+    temporarilyInstalled: true,
+  });
 
   await tempAddon.uninstall();
   unpacked_addon.remove(true);
 
   await addon.enable();
   await new Promise(executeSoon);
   addon = await promiseAddonByID(ID);
 
-  BootstrapMonitor.checkAddonInstalled(ID, "1.0");
-  BootstrapMonitor.checkAddonStarted(ID);
+  Monitor.checkInstalled(ID, "1.0");
+  Monitor.checkStarted(ID);
 
   // existing add-on is back
-  Assert.notEqual(addon, null);
-  Assert.equal(addon.version, "1.0");
-  Assert.equal(addon.name, "Test Bootstrap 1");
-  Assert.ok(addon.isCompatible);
-  Assert.ok(!addon.appDisabled);
-  Assert.ok(addon.isActive);
-  Assert.equal(addon.type, "extension");
-  Assert.equal(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_PRIVILEGED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
+  checkAddon(ID, addon, {
+    version: "1.0",
+    name: "Test Bootstrap 1",
+    isCompatible: true,
+    appDisabled: false,
+    isActive: true,
+    type: "extension",
+    signedState: AddonManager.SIGNEDSTATE_PRIVILEGED,
+    temporarilyInstalled: false,
+  });
 
   await addon.uninstall();
 
-  BootstrapMonitor.checkAddonNotInstalled(ID);
-  BootstrapMonitor.checkAddonNotStarted(ID);
-
-  await promiseRestartManager();
-});
-
-// Installing a temporary add-on when there is already a temporary
-// add-on should fail.
-add_task(async function() {
-  await AddonManager.installTemporaryAddon(do_get_addon("test_bootstrap1_1"));
-
-  let addon = await promiseAddonByID(ID);
-
-  BootstrapMonitor.checkAddonInstalled(ID, "1.0");
-  BootstrapMonitor.checkAddonStarted(ID, "1.0");
-
-  Assert.notEqual(addon, null);
-  Assert.equal(addon.version, "1.0");
-  Assert.equal(addon.name, "Test Bootstrap 1");
-  Assert.ok(addon.isCompatible);
-  Assert.ok(!addon.appDisabled);
-  Assert.ok(addon.isActive);
-  Assert.equal(addon.type, "extension");
-  Assert.ok(!addon.isWebExtension);
-  Assert.equal(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_PRIVILEGED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
-
-  await AddonManager.installTemporaryAddon(do_get_addon("test_bootstrap1_1"));
-
-  BootstrapMonitor.checkAddonInstalled(ID, "1.0");
-  BootstrapMonitor.checkAddonStarted(ID, "1.0");
-
-  await promiseRestartManager();
-
-  BootstrapMonitor.checkAddonNotInstalled(ID);
-  BootstrapMonitor.checkAddonNotStarted(ID);
-});
-
-// Check that a temporary add-on is marked as such.
-add_task(async function() {
-  await AddonManager.installTemporaryAddon(do_get_addon("test_bootstrap1_1"));
-  const addon = await promiseAddonByID(ID);
-
-  notEqual(addon, null);
-  equal(addon.temporarilyInstalled, true);
-
-  await promiseRestartManager();
-});
-
-// Check that a permanent add-on is not marked as temporarily installed.
-add_task(async function() {
-  await promiseInstallAllFiles([do_get_addon("test_bootstrap1_1")], true);
-  const addon = await promiseAddonByID(ID);
-
-  notEqual(addon, null);
-  equal(addon.temporarilyInstalled, false);
+  Monitor.checkNotInstalled(ID);
+  Monitor.checkNotStarted(ID);
 
   await promiseRestartManager();
 });
 
 // Tests that XPIs with a .zip extension work when loaded temporarily.
 add_task({ skip_if: () => AppConstants.MOZ_APP_NAME == "thunderbird" },
          async function test_zip_extension() {
   let xpi = createTempWebExtensionFile({
--- a/toolkit/mozapps/extensions/test/xpcshell/test_trash_directory.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_trash_directory.js
@@ -1,12 +1,22 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
+const ADDONS = {
+  test_bootstrap1_1: {
+    "install.rdf": {
+      "id": "bootstrap1@tests.mozilla.org",
+      "name": "Test Bootstrap 1",
+    },
+    "bootstrap.js": BOOTSTRAP_MONITOR_BOOTSTRAP_JS
+  },
+};
+
 add_task(async function setup() {
   createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
   await promiseStartupManager();
 });
 
 add_task(async function() {
   let profileDir = OS.Constants.Path.profileDir;
   let trashDir = OS.Path.join(profileDir, "extensions", "trash");
@@ -35,17 +45,17 @@ add_task(async function() {
         ok(true, "extension installation should not have failed");
         resolve();
       }
     };
 
     AddonManager.addInstallListener(listener);
   });
 
-  await promiseInstallAllFiles([do_get_addon("test_bootstrap1_1")]);
+  await AddonTestUtils.promiseInstallXPI(ADDONS.test_bootstrap1_1);
 
   // The testFile should still exist at this point because we have not
   // yet closed the file handle and as a result, Windows cannot remove it.
   fileExists = await OS.File.exists(testFile);
   ok(fileExists, "test.txt should still exist");
 
   // Wait for the AddonManager to tell us if the installation of the extension
   // succeeded or not.
--- a/toolkit/mozapps/extensions/test/xpcshell/test_undouninstall.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_undouninstall.js
@@ -13,16 +13,45 @@ const ADDON_UPGRADE                   = 
 
 const ID = "undouninstall1@tests.mozilla.org";
 const INCOMPAT_ID = "incompatible@tests.mozilla.org";
 
 
 const profileDir = gProfD.clone();
 profileDir.append("extensions");
 
+const ADDONS = {
+  test_undoincompatible: {
+    "install.rdf": {
+      "id": "incompatible@tests.mozilla.org",
+      "name": "Incompatible Addon",
+      "targetApplications": [
+        {
+          "id": "xpcshell@tests.mozilla.org",
+          "minVersion": "2",
+          "maxVersion": "2"
+        }
+      ]
+    },
+    "bootstrap.js": BOOTSTRAP_MONITOR_BOOTSTRAP_JS
+  },
+  test_undouninstall1: {
+    "install.rdf": {
+      "id": "undouninstall1@tests.mozilla.org",
+      "name": "Test Bootstrap 1",
+    },
+    "bootstrap.js": BOOTSTRAP_MONITOR_BOOTSTRAP_JS
+  },
+};
+
+const XPIS = {};
+for (let [name, files] of Object.entries(ADDONS)) {
+  XPIS[name] = AddonTestUtils.createTempXPIFile(files);
+}
+
 BootstrapMonitor.init();
 
 function getStartupReason(id) {
   let info = BootstrapMonitor.started.get(id);
   return info ? info.reason : undefined;
 }
 
 function getShutdownReason(id) {
@@ -61,17 +90,17 @@ add_task(async function uninstallRestart
       ["onInstalling", false],
       "onInstalled"
     ]
   }, [
     "onNewInstall",
     "onInstallStarted",
     "onInstallEnded"
   ]);
-  await promiseInstallAllFiles([do_get_addon("test_undouninstall1")]);
+  await AddonTestUtils.promiseInstallXPI(ADDONS.test_undouninstall1);
   ensure_test_completed();
 
   let a1 = await promiseAddonByID(ID);
 
   Assert.notEqual(a1, null);
   BootstrapMonitor.checkAddonInstalled(ID, "1.0");
   BootstrapMonitor.checkAddonStarted(ID, "1.0");
   Assert.equal(getInstallReason(ID), ADDON_INSTALL);
@@ -120,17 +149,17 @@ add_task(async function cancelUninstallO
       ["onInstalling", false],
       "onInstalled"
     ]
   }, [
     "onNewInstall",
     "onInstallStarted",
     "onInstallEnded"
   ]);
-  await promiseInstallAllFiles([do_get_addon("test_undouninstall1")]);
+  await AddonTestUtils.promiseInstallXPI(ADDONS.test_undouninstall1);
   ensure_test_completed();
 
   let a1 = await promiseAddonByID(ID);
 
   Assert.notEqual(a1, null);
   BootstrapMonitor.checkAddonInstalled(ID, "1.0");
   BootstrapMonitor.checkAddonStarted(ID, "1.0");
   Assert.equal(getInstallReason(ID), ADDON_INSTALL);
@@ -189,17 +218,17 @@ add_task(async function cancelUninstallO
   Assert.ok(!a1.userDisabled);
 
   await a1.uninstall();
 });
 
 // Tests that reinstalling an enabled restartless add-on waiting to be
 // uninstalled aborts the uninstall and leaves the add-on enabled
 add_task(async function reinstallAddonAwaitingUninstall() {
-  await promiseInstallAllFiles([do_get_addon("test_undouninstall1")]);
+  await AddonTestUtils.promiseInstallXPI(ADDONS.test_undouninstall1);
 
   let a1 = await promiseAddonByID("undouninstall1@tests.mozilla.org");
 
   Assert.notEqual(a1, null);
   BootstrapMonitor.checkAddonInstalled(ID, "1.0");
   BootstrapMonitor.checkAddonStarted(ID, "1.0");
   Assert.equal(getInstallReason(ID), ADDON_INSTALL);
   Assert.equal(getStartupReason(ID), ADDON_INSTALL);
@@ -231,17 +260,17 @@ add_task(async function reinstallAddonAw
       "onInstalled"
     ]
   }, [
     "onNewInstall",
     "onInstallStarted",
     "onInstallEnded"
   ]);
 
-  await promiseInstallAllFiles([do_get_addon("test_undouninstall1")]);
+  await AddonTestUtils.promiseInstallXPI(ADDONS.test_undouninstall1);
 
   a1 = await promiseAddonByID("undouninstall1@tests.mozilla.org");
 
   ensure_test_completed();
 
   BootstrapMonitor.checkAddonInstalled(ID, "1.0");
   BootstrapMonitor.checkAddonStarted(ID, "1.0");
   Assert.equal(getUninstallReason(ID), ADDON_UPGRADE);
@@ -267,17 +296,17 @@ add_task(async function reinstallAddonAw
   Assert.ok(!a1.userDisabled);
 
   await a1.uninstall();
 });
 
 // Tests that a disabled restartless add-on can be uninstalled and goes away
 // when the uninstall is committed
 add_task(async function uninstallDisabledRestartless() {
-  await promiseInstallAllFiles([do_get_addon("test_undouninstall1")]);
+  await AddonTestUtils.promiseInstallXPI(ADDONS.test_undouninstall1);
 
   let a1 = await promiseAddonByID("undouninstall1@tests.mozilla.org");
 
   Assert.notEqual(a1, null);
   BootstrapMonitor.checkAddonInstalled(ID, "1.0");
   BootstrapMonitor.checkAddonStarted(ID, "1.0");
   Assert.equal(getInstallReason(ID), ADDON_INSTALL);
   Assert.equal(getStartupReason(ID), ADDON_INSTALL);
@@ -332,17 +361,17 @@ add_task(async function cancelUninstallD
       ["onInstalling", false],
       "onInstalled"
     ]
   }, [
     "onNewInstall",
     "onInstallStarted",
     "onInstallEnded"
   ]);
-  await promiseInstallAllFiles([do_get_addon("test_undouninstall1")]);
+  await AddonTestUtils.promiseInstallXPI(ADDONS.test_undouninstall1);
   ensure_test_completed();
 
   let a1 = await promiseAddonByID("undouninstall1@tests.mozilla.org");
 
   Assert.notEqual(a1, null);
   BootstrapMonitor.checkAddonInstalled(ID, "1.0");
   BootstrapMonitor.checkAddonStarted(ID, "1.0");
   Assert.equal(getInstallReason(ID), ADDON_INSTALL);
@@ -409,17 +438,17 @@ add_task(async function cancelUninstallD
   Assert.ok(a1.userDisabled);
 
   await a1.uninstall();
 });
 
 // Tests that reinstalling a disabled restartless add-on waiting to be
 // uninstalled aborts the uninstall and leaves the add-on disabled
 add_task(async function reinstallDisabledAddonAwaitingUninstall() {
-  await promiseInstallAllFiles([do_get_addon("test_undouninstall1")]);
+  await AddonTestUtils.promiseInstallXPI(ADDONS.test_undouninstall1);
 
   let a1 = await promiseAddonByID("undouninstall1@tests.mozilla.org");
 
   Assert.notEqual(a1, null);
   BootstrapMonitor.checkAddonInstalled(ID, "1.0");
   BootstrapMonitor.checkAddonStarted(ID, "1.0");
   Assert.equal(getInstallReason(ID), ADDON_INSTALL);
   Assert.equal(getStartupReason(ID), ADDON_INSTALL);
@@ -456,17 +485,17 @@ add_task(async function reinstallDisable
       "onInstalled"
     ]
   }, [
     "onNewInstall",
     "onInstallStarted",
     "onInstallEnded"
   ]);
 
-  await promiseInstallAllFiles([do_get_addon("test_undouninstall1")]);
+  await AddonTestUtils.promiseInstallXPI(ADDONS.test_undouninstall1);
 
   a1 = await promiseAddonByID("undouninstall1@tests.mozilla.org");
 
   ensure_test_completed();
 
   BootstrapMonitor.checkAddonInstalled(ID, "1.0");
   BootstrapMonitor.checkAddonNotStarted(ID, "1.0");
   Assert.equal(getUninstallReason(ID), ADDON_UPGRADE);
@@ -486,17 +515,17 @@ add_task(async function reinstallDisable
   Assert.ok(a1.userDisabled);
 
   await a1.uninstall();
 });
 
 
 // Test that uninstalling a temporary addon can be canceled
 add_task(async function cancelUninstallTemporary() {
-  await AddonManager.installTemporaryAddon(do_get_addon("test_undouninstall1"));
+  await AddonManager.installTemporaryAddon(XPIS.test_undouninstall1);
 
   let a1 = await promiseAddonByID("undouninstall1@tests.mozilla.org");
   Assert.notEqual(a1, null);
   BootstrapMonitor.checkAddonInstalled(ID, "1.0");
   BootstrapMonitor.checkAddonStarted(ID, "1.0");
   Assert.equal(getInstallReason(ID), ADDON_INSTALL);
   Assert.equal(getStartupReason(ID), ADDON_INSTALL);
   Assert.equal(a1.pendingOperations, AddonManager.PENDING_NONE);
@@ -529,17 +558,17 @@ add_task(async function cancelUninstallT
   Assert.equal(a1.pendingOperations, 0);
 
   await promiseRestartManager();
 });
 
 // Tests that cancelling the uninstall of an incompatible restartless addon
 // does not start the addon
 add_task(async function cancelUninstallIncompatibleRestartless() {
-  await promiseInstallAllFiles([do_get_addon("test_undoincompatible")]);
+  await AddonTestUtils.promiseInstallXPI(ADDONS.test_undoincompatible);
 
   let a1 = await promiseAddonByID(INCOMPAT_ID);
   Assert.notEqual(a1, null);
   BootstrapMonitor.checkAddonNotStarted(INCOMPAT_ID);
   Assert.ok(!a1.isActive);
 
   prepare_test({
     "incompatible@tests.mozilla.org": [
--- a/toolkit/mozapps/extensions/test/xpcshell/test_update.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_update.js
@@ -27,80 +27,45 @@ profileDir.append("extensions");
 
 var originalSyncGUID;
 
 const ADDONS = {
   test_update: {
     "install.rdf": {
       id: "addon1@tests.mozilla.org",
       version: "2.0",
-      bootstrap: true,
       name: "Test 1",
-      description: "Test Description",
-
-      targetApplications: [{
-        id: "xpcshell@tests.mozilla.org",
-        minVersion: "1",
-        maxVersion: "1"}],
     },
   },
   test_update8: {
     "install.rdf": {
       id: "addon8@tests.mozilla.org",
       version: "2.0",
-      bootstrap: true,
       name: "Test 8",
-      description: "Test Description",
-
-      targetApplications: [{
-        id: "xpcshell@tests.mozilla.org",
-        minVersion: "1",
-        maxVersion: "1"}],
     },
   },
   test_update12: {
     "install.rdf": {
       id: "addon12@tests.mozilla.org",
       version: "2.0",
-      bootstrap: true,
       name: "Test 12",
-      description: "Test Description",
-
-      targetApplications: [{
-        id: "xpcshell@tests.mozilla.org",
-        minVersion: "1",
-        maxVersion: "1"}],
     },
   },
   test_install2_1: {
     "install.rdf": {
       id: "addon2@tests.mozilla.org",
       version: "2.0",
-      bootstrap: true,
       name: "Real Test 2",
-      description: "Test Description",
-
-      targetApplications: [{
-        id: "xpcshell@tests.mozilla.org",
-        minVersion: "1",
-        maxVersion: "1"}],
     },
   },
   test_install2_2: {
     "install.rdf": {
       id: "addon2@tests.mozilla.org",
       version: "3.0",
-      bootstrap: true,
       name: "Real Test 3",
-      description: "Test Description",
-
-      targetApplications: [{
-        id: "xpcshell@tests.mozilla.org",
-        minVersion: "1",
-        maxVersion: "1"}],
     },
   },
 };
 
 var testserver = AddonTestUtils.createHttpServer({hosts: ["example.com"]});
 testserver.registerDirectory("/data/", do_get_file("data"));
 
 const XPIS = {};
@@ -116,44 +81,33 @@ add_task(async function setup() {
 });
 
 add_task(async function() {
   AddonTestUtils.updateReason = AddonManager.UPDATE_WHEN_USER_REQUESTED;
 
   await promiseStartupManager();
   await promiseInstallXPI({
     id: "addon1@tests.mozilla.org",
-    version: "1.0",
-    bootstrap: true,
     updateURL: "http://example.com/data/" + updateFile,
-    targetApplications: [{
-      id: appId,
-      minVersion: "1",
-      maxVersion: "1"
-    }],
     name: "Test Addon 1",
   });
 
   await promiseInstallXPI({
     id: "addon2@tests.mozilla.org",
-    version: "1.0",
-    bootstrap: true,
     updateURL: "http://example.com/data/" + updateFile,
     targetApplications: [{
       id: appId,
       minVersion: "0",
       maxVersion: "0"
     }],
     name: "Test Addon 2",
   });
 
   await promiseInstallXPI({
     id: "addon3@tests.mozilla.org",
-    version: "1.0",
-    bootstrap: true,
     updateURL: "http://example.com/data/" + updateFile,
     targetApplications: [{
       id: appId,
       minVersion: "5",
       maxVersion: "5"
     }],
     name: "Test Addon 3",
   });
--- a/toolkit/mozapps/extensions/test/xpcshell/test_updateCancel.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_updateCancel.js
@@ -45,17 +45,16 @@ function makeCancelListener() {
 
 // Set up the HTTP server so that we can control when it responds
 var httpReceived = PromiseUtils.defer();
 function dataHandler(aRequest, aResponse) {
   aResponse.processAsync();
   httpReceived.resolve([aRequest, aResponse]);
 }
 var testserver = new HttpServer();
-testserver.registerDirectory("/addons/", do_get_file("addons"));
 testserver.registerPathHandler("/data/test_update.json", dataHandler);
 testserver.start(-1);
 gPort = testserver.identity.primaryPort;
 
 // Set up an add-on for update check
 add_task(async function setup() {
   await promiseWriteInstallRDFForExtension({
     id: "addon1@tests.mozilla.org",
--- a/toolkit/mozapps/extensions/test/xpcshell/test_update_compatmode.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_update_compatmode.js
@@ -6,17 +6,16 @@
 // %COMPATIBILITY_MODE% token in the update URL.
 
 
 // The test extension uses an insecure update url.
 Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
 
 var testserver = AddonTestUtils.createHttpServer({hosts: ["example.com"]});
 testserver.registerDirectory("/data/", do_get_file("data"));
-testserver.registerDirectory("/addons/", do_get_file("addons"));
 
 const profileDir = gProfD.clone();
 profileDir.append("extensions");
 
 async function run_test() {
   do_test_pending();
   createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
 
--- a/toolkit/mozapps/extensions/test/xpcshell/test_updatecheck_errors.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_updatecheck_errors.js
@@ -12,17 +12,16 @@ const profileDir = gProfD.clone();
 profileDir.append("extensions");
 
 add_task(async function setup() {
   createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
 
   // Create and configure the HTTP server.
   testserver = AddonTestUtils.createHttpServer({hosts: ["example.com"]});
   testserver.registerDirectory("/data/", do_get_file("data"));
-  testserver.registerDirectory("/addons/", do_get_file("addons"));
 
   await promiseStartupManager();
 });
 
 // Verify that an update check returns the correct errors.
 add_task(async function() {
   let {addon} = await promiseInstallXPI({
     id: "addon1@tests.mozilla.org",
--- a/toolkit/mozapps/extensions/test/xpcshell/test_updateid.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_updateid.js
@@ -5,16 +5,33 @@
 // This verifies that updating an add-on to a new ID works
 
 // The test extension uses an insecure update url.
 Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
 
 const profileDir = gProfD.clone();
 profileDir.append("extensions");
 
+const ADDONS = {
+  test_updateid1: {
+    "install.rdf": {
+      "id": "addon1@tests.mozilla.org",
+      "updateURL": "http://example.com/data/test_updateid.rdf",
+      "name": "Test Addon 1",
+    },
+  },
+  test_updateid2: {
+    "install.rdf": {
+      "id": "addon1.changed@tests.mozilla.org",
+      "version": "2.0",
+      "name": "Test Addon 1",
+    },
+  },
+};
+
 function promiseInstallUpdate(install) {
   return new Promise((resolve, reject) => {
     install.addListener({
       onDownloadFailed: () => {
         let err = new Error("download error");
         err.code = install.error;
         reject(err);
       },
@@ -28,26 +45,31 @@ function promiseInstallUpdate(install) {
 
     install.install();
   });
 }
 
 // Create and configure the HTTP server.
 let testserver = AddonTestUtils.createHttpServer({hosts: ["example.com"]});
 testserver.registerDirectory("/data/", do_get_file("data"));
-testserver.registerDirectory("/addons/", do_get_file("addons"));
+
+const XPIS = {};
+for (let [name, files] of Object.entries(ADDONS)) {
+  XPIS[name] = AddonTestUtils.createTempXPIFile(files);
+  testserver.registerFile(`/addons/${name}.xpi`, XPIS[name]);
+}
 
 add_task(async function setup() {
   createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
   await promiseStartupManager();
 });
 
 // Verify that an update to an add-on with a new ID fails
 add_task(async function test_update_new_id() {
-  await promiseInstallFile(do_get_addon("test_updateid1"));
+  await AddonTestUtils.promiseInstallXPI(ADDONS.test_updateid1);
 
   let addon = await promiseAddonByID("addon1@tests.mozilla.org");
   Assert.notEqual(addon, null);
   Assert.equal(addon.version, "1.0");
 
   let update = await promiseFindAddonUpdates(addon, AddonManager.UPDATE_WHEN_USER_REQUESTED);
   let install = update.updateAvailable;
   Assert.equal(install.name, addon.name);
--- a/toolkit/mozapps/extensions/test/xpcshell/test_webextension.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_webextension.js
@@ -4,28 +4,83 @@
 
 ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
 
 const ID = "webextension1@tests.mozilla.org";
 
 const profileDir = gProfD.clone();
 profileDir.append("extensions");
 
+const ADDONS = {
+  webextension_1: {
+    "manifest.json": {
+      "name": "Web Extension Name",
+      "version": "1.0",
+      "manifest_version": 2,
+      "applications": {
+        "gecko": {
+          "id": "webextension1@tests.mozilla.org"
+        }
+      },
+      "icons": {
+        "48": "icon48.png",
+        "64": "icon64.png"
+      }
+    },
+    "chrome.manifest": "content webex ./\n"
+  },
+  webextension_3: {
+    "manifest.json": {
+      "name": "Web Extensiøn __MSG_name__",
+      "description": "Descriptïon __MSG_desc__ of add-on",
+      "version": "1.0",
+      "manifest_version": 2,
+      "default_locale": "en",
+      "applications": {
+        "gecko": {
+          "id": "webextension3@tests.mozilla.org"
+        }
+      }
+    },
+    "_locales/en/messages.json": {
+      "name": {
+        "message": "foo ☹",
+        "description": "foo"
+      },
+      "desc": {
+        "message": "bar ☹",
+        "description": "bar"
+      }
+    },
+    "_locales/fr/messages.json": {
+      "name": {
+        "message": "le foo ☺",
+        "description": "foo"
+      },
+      "desc": {
+        "message": "le bar ☺",
+        "description": "bar"
+      }
+    }
+  },
+};
+
+
 let chromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIChromeRegistry);
 
 createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42");
 
 const { GlobalManager } = ChromeUtils.import("resource://gre/modules/Extension.jsm", {});
 
 add_task(async function test_1() {
   await promiseStartupManager();
 
   equal(GlobalManager.extensionMap.size, 0);
 
-  let {addon} = await promiseInstallFile(do_get_addon("webextension_1"), true);
+  let {addon} = await AddonTestUtils.promiseInstallXPI(ADDONS.webextension_1);
 
   equal(GlobalManager.extensionMap.size, 1);
   ok(GlobalManager.extensionMap.has(ID));
 
   Assert.throws(() => chromeReg.convertChromeURL(Services.io.newURI("chrome://webex/content/webex.xul")),
                 error => error.result == Cr.NS_ERROR_FILE_NOT_FOUND,
                 "Chrome manifest should not have been registered");
 
@@ -35,17 +90,17 @@ add_task(async function test_1() {
     version: "1.0",
     name: "Web Extension Name",
     isCompatible: true,
     appDisabled: false,
     isActive: true,
     isSystem: false,
     type: "extension",
     isWebExtension: true,
-    signedState: mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_PRIVILEGED : AddonManager.SIGNEDSTATE_NOT_REQUIRED,
+    signedState: AddonManager.SIGNEDSTATE_PRIVILEGED,
     iconURL: `${uri}icon48.png`,
     icon64URL: `${uri}icon64.png`,
   });
 
   // Should persist through a restart
   await promiseShutdownManager();
 
   equal(GlobalManager.extensionMap.size, 0);
@@ -62,17 +117,17 @@ add_task(async function test_1() {
   checkAddon(ID, addon, {
     version: "1.0",
     name: "Web Extension Name",
     isCompatible: true,
     appDisabled: false,
     isActive: true,
     isSystem: false,
     type: "extension",
-    signedState: mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_PRIVILEGED : AddonManager.SIGNEDSTATE_NOT_REQUIRED,
+    signedState: AddonManager.SIGNEDSTATE_PRIVILEGED,
     iconURL: `${uri}icon48.png`,
     icon64URL: `${uri}icon64.png`,
   });
 
   await addon.disable();
 
   equal(GlobalManager.extensionMap.size, 0);
 
@@ -108,28 +163,28 @@ add_task(async function test_2() {
   checkAddon(ID, addon, {
     version: "1.0",
     name: "Web Extension Name",
     isCompatible: true,
     appDisabled: false,
     isActive: true,
     isSystem: false,
     type: "extension",
-    signedState: mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_PRIVILEGED : AddonManager.SIGNEDSTATE_NOT_REQUIRED,
+    signedState: AddonManager.SIGNEDSTATE_PRIVILEGED,
   });
 
   await addon.uninstall();
 
   await promiseRestartManager();
 });
 
 add_task(async function test_manifest_localization() {
   const extensionId = "webextension3@tests.mozilla.org";
 
-  let {addon} = await promiseInstallFile(do_get_addon("webextension_3"), true);
+  let {addon} = await AddonTestUtils.promiseInstallXPI(ADDONS.webextension_3);
 
   await addon.disable();
 
   checkAddon(ID, addon, {
     name: "Web Extensiøn foo ☹",
     description: "Descriptïon bar ☹ of add-on",
   });
 
@@ -350,17 +405,17 @@ add_task(async function testThemeExtensi
     name: "Web Extension Name",
     isCompatible: true,
     appDisabled: false,
     isActive: false,
     userDisabled: true,
     isSystem: false,
     type: "theme",
     isWebExtension: true,
-    signedState: mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_PRIVILEGED : AddonManager.SIGNEDSTATE_NOT_REQUIRED,
+    signedState: AddonManager.SIGNEDSTATE_PRIVILEGED,
   });
 
   await addon.uninstall();
 
   // Also test one without a proper 'theme' section.
   addon = await promiseInstallWebExtension({
     manifest: {
       "author": "Some author",
@@ -396,17 +451,17 @@ add_task(async function test_theme_upgra
 
   checkAddon(ID, addon, {
     version: "1.0",
     name: "Test WebExtension 1 (temporary)",
     isCompatible: true,
     appDisabled: false,
     isActive: true,
     type: "extension",
-    signedState: mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_PRIVILEGED : AddonManager.SIGNEDSTATE_NOT_REQUIRED,
+    signedState: AddonManager.SIGNEDSTATE_PRIVILEGED,
   });
 
   // Create a webextension theme with the same ID
   addon = await promiseInstallWebExtension({
     manifest: {
       version: "2.0",
       name: "Test WebExtension 1 (temporary)",
       applications: {
--- a/toolkit/mozapps/extensions/test/xpcshell/test_webextension_embedded.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_webextension_embedded.js
@@ -25,20 +25,16 @@ function promiseWebExtensionShutdown() {
       Management.off("shutdown", listener);
       resolve(extension);
     };
 
     Management.on("shutdown", listener);
   });
 }
 
-const BOOTSTRAP = String.raw`
-  Components.utils.import("resource://xpcshell-data/BootstrapMonitor.jsm").monitor(this);
-`;
-
 const BOOTSTRAP_WITHOUT_SHUTDOWN = String.raw`
   Components.utils.import("resource://xpcshell-data/BootstrapMonitor.jsm").monitor(this, [
     "install", "startup", "uninstall",
   ]);
 `;
 
 const EMBEDDED_WEBEXT_MANIFEST = JSON.stringify({
   name: "embedded webextension addon",
@@ -61,17 +57,17 @@ add_task(async function has_embedded_web
     bootstrap: true,
     hasEmbeddedWebExtension: true,
     targetApplications: [{
       id: "xpcshell@tests.mozilla.org",
       minVersion: "1",
       maxVersion: "1.9.2"
     }]
   }, {
-    "bootstrap.js": BOOTSTRAP,
+    "bootstrap.js": BOOTSTRAP_MONITOR_BOOTSTRAP_JS,
     "webextension/manifest.json": EMBEDDED_WEBEXT_MANIFEST,
   });
 
   await promiseInstallFile(xpiFile);
 
   let addon = await promiseAddonByID(ID);
 
   notEqual(addon, null, "Got an addon object as expected");
@@ -134,17 +130,17 @@ add_task(async function run_embedded_web
     bootstrap: true,
     hasEmbeddedWebExtension: true,
     targetApplications: [{
       id: "xpcshell@tests.mozilla.org",
       minVersion: "1",
       maxVersion: "1.9.2"
     }]
   }, {
-    "bootstrap.js": BOOTSTRAP,
+    "bootstrap.js": BOOTSTRAP_MONITOR_BOOTSTRAP_JS,
     "webextension/manifest.json": EMBEDDED_WEBEXT_MANIFEST,
   });
 
   await AddonManager.installTemporaryAddon(xpiFile);
 
   let addon = await promiseAddonByID(ID);
 
   notEqual(addon, null, "Got an addon object as expected");
@@ -219,17 +215,17 @@ add_task(async function reload_embedded_
     bootstrap: true,
     hasEmbeddedWebExtension: true,
     targetApplications: [{
       id: "xpcshell@tests.mozilla.org",
       minVersion: "1",
       maxVersion: "1.9.2"
     }]
   }, {
-    "bootstrap.js": BOOTSTRAP,
+    "bootstrap.js": BOOTSTRAP_MONITOR_BOOTSTRAP_JS,
     "webextension/manifest.json": EMBEDDED_WEBEXT_MANIFEST,
   });
 
   await AddonManager.installTemporaryAddon(xpiFile);
 
   let addon = await promiseAddonByID(ID);
 
   notEqual(addon, null, "Got an addon object as expected");
--- a/toolkit/mozapps/extensions/test/xpcshell/test_webextension_install.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_webextension_install.js
@@ -15,39 +15,31 @@ add_task(async function setup() {
 
 const IMPLICIT_ID_XPI = "data/webext-implicit-id.xpi";
 const IMPLICIT_ID_ID = "webext_implicit_id@tests.mozilla.org";
 
 // webext-implicit-id.xpi has a minimal manifest with no
 // applications or browser_specific_settings, so its id comes
 // from its signature, which should be the ID constant defined below.
 add_task(async function test_implicit_id() {
-  // This test needs to read the xpi certificate which only works
-  // if signing is enabled.
-  ok(AddonSettings.ADDON_SIGNING, "Add-on signing is enabled");
-
   let addon = await promiseAddonByID(IMPLICIT_ID_ID);
   equal(addon, null, "Add-on is not installed");
 
   await promiseInstallFile(do_get_file(IMPLICIT_ID_XPI));
 
   addon = await promiseAddonByID(IMPLICIT_ID_ID);
   notEqual(addon, null, "Add-on is installed");
 
   await addon.uninstall();
 });
 
 // We should also be able to install webext-implicit-id.xpi temporarily
 // and it should look just like the regular install (ie, the ID should
 // come from the signature)
 add_task(async function test_implicit_id_temp() {
-  // This test needs to read the xpi certificate which only works
-  // if signing is enabled.
-  ok(AddonSettings.ADDON_SIGNING, "Add-on signing is enabled");
-
   let addon = await promiseAddonByID(IMPLICIT_ID_ID);
   equal(addon, null, "Add-on is not installed");
 
   let xpifile = do_get_file(IMPLICIT_ID_XPI);
   await AddonManager.installTemporaryAddon(xpifile);
 
   addon = await promiseAddonByID(IMPLICIT_ID_ID);
   notEqual(addon, null, "Add-on is installed");
--- a/toolkit/mozapps/extensions/test/xpcshell/test_webextension_langpack.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_webextension_langpack.js
@@ -9,16 +9,52 @@ const { L10nRegistry } = ChromeUtils.imp
 
 const ID = "langpack-und@test.mozilla.org";
 
 const profileDir = gProfD.clone();
 profileDir.append("extensions");
 
 createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "58");
 
+const ADDONS = {
+  langpack_1: {
+    "browser/localization/und/browser.ftl": "message-browser = Value from Browser\n",
+    "localization/und/toolkit_test.ftl": "message-id1 = Value 1\n",
+    "chrome/und/locale/und/global/test.properties": "message = Value from .properties\n",
+    "manifest.json": {
+      "name": "und Language Pack",
+      "version": "1",
+      "manifest_version": 2,
+      "applications": {
+        "gecko": {
+          "id": "langpack-und@test.mozilla.org",
+          "strict_min_version": "58.0",
+          "strict_max_version": "58.*"
+        }
+      },
+      "sources": {
+        "browser": {
+          "base_path": "browser/"
+        }
+      },
+      "langpack_id": "und",
+      "languages": {
+        "und": {
+          "chrome_resources": {
+            "global": "chrome/und/locale/und/global/"
+          },
+          "version": "20171001190118"
+        }
+      },
+      "author": "Mozilla Localization Task Force",
+      "description": "Language pack for Testy for und"
+    }
+  },
+};
+
 function promiseLangpackStartup() {
   return new Promise(resolve => {
     const EVENT = "webextension-langpack-startup";
     Services.obs.addObserver(function observer() {
       Services.obs.removeObserver(observer, EVENT);
       resolve();
     }, EVENT);
   });
@@ -33,17 +69,17 @@ add_task(async function() {
   await promiseStartupManager();
 
   // Make sure that `und` locale is not installed.
   equal(L10nRegistry.getAvailableLocales().includes("und"), false);
   equal(Services.locale.getAvailableLocales().includes("und"), false);
 
   let [, {addon}] = await Promise.all([
     promiseLangpackStartup(),
-    promiseInstallFile(do_get_addon("langpack_1"), true),
+    AddonTestUtils.promiseInstallXPI(ADDONS.langpack_1),
   ]);
 
   // Now make sure that `und` locale is available.
   equal(L10nRegistry.getAvailableLocales().includes("und"), true);
   equal(Services.locale.getAvailableLocales().includes("und"), true);
 
   await addon.disable();
 
@@ -71,17 +107,17 @@ add_task(async function() {
 
 /**
  * This test verifies that registries are able to load and return
  * correct strings available in the language pack.
  */
 add_task(async function() {
   let [, {addon}] = await Promise.all([
     promiseLangpackStartup(),
-    promiseInstallFile(do_get_addon("langpack_1"), true),
+    AddonTestUtils.promiseInstallXPI(ADDONS.langpack_1),
   ]);
 
   {
     // Toolkit string
     let ctxs = L10nRegistry.generateContexts(["und"], ["toolkit_test.ftl"]);
     let ctx0 = (await ctxs.next()).value;
     equal(ctx0.hasMessage("message-id1"), true);
   }
@@ -112,17 +148,17 @@ add_task(async function() {
 
 /**
  * This test verifies that language pack will get disabled after app
  * gets upgraded.
  */
 add_task(async function() {
   let [, {addon}] = await Promise.all([
     promiseLangpackStartup(),
-    promiseInstallFile(do_get_addon("langpack_1"), true),
+    AddonTestUtils.promiseInstallXPI(ADDONS.langpack_1),
   ]);
   Assert.ok(addon.isActive);
 
   await promiseShutdownManager();
 
   gAppInfo.version = "59";
   gAppInfo.platformVersion = "59";
 
--- a/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
+++ b/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
@@ -71,17 +71,16 @@ tags = blocklist
 # Times out during parallel runs on desktop
 requesttimeoutfactor = 2
 tags = blocklist
 [test_bootstrap.js]
 [test_bootstrap_const.js]
 [test_bootstrap_globals.js]
 [test_bootstrapped_chrome_manifest.js]
 [test_cache_certdb.js]
-run-if = addon_signing
 [test_cacheflush.js]
 [test_childprocess.js]
 [test_compatoverrides.js]
 [test_corrupt.js]
 [test_corruptfile.js]
 [test_crash_annotation_quoting.js]
 [test_db_path.js]
 head =
@@ -156,17 +155,16 @@ skip-if = !allow_legacy_extensions || ap
 skip-if = os == "android"
 [test_moved_extension_metadata.js]
 [test_no_addons.js]
 [test_nodisable_hidden.js]
 [test_onPropertyChanged_appDisabled.js]
 [test_overrideblocklist.js]
 run-sequentially = Uses global XCurProcD dir.
 tags = blocklist
-[test_pass_symbol.js]
 [test_permissions.js]
 [test_permissions_prefs.js]
 [test_pluginBlocklistCtp.js]
 # Bug 676992: test consistently fails on Android
 fail-if = os == "android"
 tags = blocklist
 [test_pluginInfoURL.js]
 tags = blocklist
@@ -191,31 +189,25 @@ skip-if = os != 'win'
 skip-if = os == "android" || os == "win"
 tags = webextensions
 [test_safemode.js]
 [test_schema_change.js]
 [test_seen.js]
 [test_shutdown.js]
 [test_sideloads.js]
 [test_signed_inject.js]
-run-if = addon_signing
 # Bug 1394122
 skip-if = true
 [test_signed_install.js]
-run-if = addon_signing
 run-sequentially = Uses hardcoded ports in xpi files.
 [test_signed_langpack.js]
-run-if = addon_signing
 [test_signed_long.js]
-run-if = addon_signing
 [test_signed_updatepref.js]
-run-if = addon_signing
 skip-if = require_signing || !allow_legacy_extensions
 [test_signed_verify.js]
-run-if = addon_signing
 [test_softblocked.js]
 tags = blocklist
 [test_sourceURI.js]
 [test_startup.js]
 # Bug 676992: test consistently fails on Android
 fail-if = os == "android"
 [test_strictcompatibility.js]
 [test_switch_os.js]