Merge inbound to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Thu, 01 Jun 2017 11:54:38 -0400
changeset 361804 62005e6aecdf95c9cffe5fb825d93123ec49c4b3
parent 361786 cac2fd43de81d44b36396704220996b0c4a39f1a (current diff)
parent 361803 9255719d469c99b4c11cacf6541c66e353518f24 (diff)
child 361816 0e9853e31da9848ab638bdd0df4eb734a5ebc232
child 361836 5a12c5e3eac314f257c595f0605f0302f12a6057
child 361868 d92e242ec5f184c0e0eeafa58ef8a3301456a981
push id31943
push userryanvm@gmail.com
push dateThu, 01 Jun 2017 15:54:45 +0000
treeherdermozilla-central@62005e6aecdf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone55.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 inbound to m-c. a=merge
browser/base/content/test/social/social_crash_content_helper.js
dom/browser-element/mochitest/priority/test_Activity.html
toolkit/components/thumbnails/test/thumbnails_crash_content_helper.js
--- a/browser/base/content/test/general/browser_restore_isAppTab.js
+++ b/browser/base/content/test/general/browser_restore_isAppTab.js
@@ -2,71 +2,20 @@
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /* eslint-env mozilla/frame-script */
 
 const {TabStateFlusher} = Cu.import("resource:///modules/sessionstore/TabStateFlusher.jsm", {});
 
 const DUMMY = "http://example.com/browser/browser/base/content/test/general/dummy_page.html";
 
-function getMinidumpDirectory() {
-  let dir = Services.dirsvc.get("ProfD", Ci.nsIFile);
-  dir.append("minidumps");
-  return dir;
-}
-
-// This observer is needed so we can clean up all evidence of the crash so
-// the testrunner thinks things are peachy.
-var CrashObserver = {
-  observe(subject, topic, data) {
-    is(topic, "ipc:content-shutdown", "Received correct observer topic.");
-    ok(subject instanceof Ci.nsIPropertyBag2,
-       "Subject implements nsIPropertyBag2.");
-    // we might see this called as the process terminates due to previous tests.
-    // We are only looking for "abnormal" exits...
-    if (!subject.hasKey("abnormal")) {
-      info("This is a normal termination and isn't the one we are looking for...");
-      return;
-    }
-
-    let dumpID;
-    if ("nsICrashReporter" in Ci) {
-      dumpID = subject.getPropertyAsAString("dumpID");
-      ok(dumpID, "dumpID is present and not an empty string");
-    }
-
-    if (dumpID) {
-      let minidumpDirectory = getMinidumpDirectory();
-      let file = minidumpDirectory.clone();
-      file.append(dumpID + ".dmp");
-      file.remove(true);
-      file = minidumpDirectory.clone();
-      file.append(dumpID + ".extra");
-      file.remove(true);
-    }
-  }
-}
-Services.obs.addObserver(CrashObserver, "ipc:content-shutdown");
-
-registerCleanupFunction(() => {
-  Services.obs.removeObserver(CrashObserver, "ipc:content-shutdown");
-});
-
 function frameScript() {
   addMessageListener("Test:GetIsAppTab", function() {
     sendAsyncMessage("Test:IsAppTab", { isAppTab: docShell.isAppTab });
   });
-
-  addMessageListener("Test:Crash", function() {
-    privateNoteIntentionalCrash();
-    Components.utils.import("resource://gre/modules/ctypes.jsm");
-    let zero = new ctypes.intptr_t(8);
-    let badptr = ctypes.cast(zero, ctypes.PointerType(ctypes.int32_t));
-    badptr.contents
-  });
 }
 
 function loadFrameScript(browser) {
   browser.messageManager.loadFrameScript("data:,(" + frameScript.toString() + ")();", true);
 }
 
 function isBrowserAppTab(browser) {
   return new Promise(resolve => {
@@ -87,18 +36,17 @@ function isBrowserAppTab(browser) {
 var restart = async function(browser) {
   // If the tab isn't remote this would crash the main process so skip it
   if (!browser.isRemoteBrowser)
     return;
 
   // Make sure the main process has all of the current tab state before crashing
   await TabStateFlusher.flush(browser);
 
-  browser.messageManager.sendAsyncMessage("Test:Crash");
-  await promiseWaitForEvent(browser, "AboutTabCrashedLoad", false, true);
+  await BrowserTestUtils.crashBrowser(browser);
 
   let tab = gBrowser.getTabForBrowser(browser);
   SessionStore.reviveCrashedTab(tab);
 
   await promiseTabLoaded(tab);
 };
 
 add_task(async function navigate() {
--- a/browser/base/content/test/plugins/browser_pluginCrashReportNonDeterminism.js
+++ b/browser/base/content/test/plugins/browser_pluginCrashReportNonDeterminism.js
@@ -88,30 +88,32 @@ add_task(async function setup() {
   let crashObserver = (subject, topic, data) => {
     if (topic != "plugin-crashed") {
       return;
     }
 
     let propBag = subject.QueryInterface(Ci.nsIPropertyBag2);
     let minidumpID = propBag.getPropertyAsAString("pluginDumpID");
 
-    let minidumpDir = Services.dirsvc.get("ProfD", Ci.nsIFile);
-    minidumpDir.append("minidumps");
+    Services.crashmanager.ensureCrashIsPresent(minidumpID).then(() => {
+      let minidumpDir = Services.dirsvc.get("ProfD", Ci.nsIFile);
+      minidumpDir.append("minidumps");
 
-    let pluginDumpFile = minidumpDir.clone();
-    pluginDumpFile.append(minidumpID + ".dmp");
+      let pluginDumpFile = minidumpDir.clone();
+      pluginDumpFile.append(minidumpID + ".dmp");
 
-    let extraFile = minidumpDir.clone();
-    extraFile.append(minidumpID + ".extra");
+      let extraFile = minidumpDir.clone();
+      extraFile.append(minidumpID + ".extra");
 
-    ok(pluginDumpFile.exists(), "Found minidump");
-    ok(extraFile.exists(), "Found extra file");
+      ok(pluginDumpFile.exists(), "Found minidump");
+      ok(extraFile.exists(), "Found extra file");
 
-    pluginDumpFile.remove(false);
-    extraFile.remove(false);
+      pluginDumpFile.remove(false);
+      extraFile.remove(false);
+    });
   };
 
   Services.obs.addObserver(crashObserver, "plugin-crashed");
   // plugins.testmode will make BrowserPlugins:Test:ClearCrashData work.
   Services.prefs.setBoolPref("plugins.testmode", true);
   registerCleanupFunction(() => {
     Services.prefs.clearUserPref("plugins.testmode");
     Services.obs.removeObserver(crashObserver, "plugin-crashed");
deleted file mode 100644
--- a/browser/base/content/test/social/social_crash_content_helper.js
+++ /dev/null
@@ -1,33 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-* http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/* eslint-env mozilla/frame-script */
-
-var Cu = Components.utils;
-
-// Ideally we would use CrashTestUtils.jsm, but that's only available for
-// xpcshell tests - so we just copy a ctypes crasher from it.
-Cu.import("resource://gre/modules/ctypes.jsm");
-var crash = function() { // this will crash when called.
-  let zero = new ctypes.intptr_t(8);
-  let badptr = ctypes.cast(zero, ctypes.PointerType(ctypes.int32_t));
-  badptr.contents
-};
-
-
-var TestHelper = {
-  init() {
-    addMessageListener("social-test:crash", this);
-  },
-
-  receiveMessage(msg) {
-    switch (msg.name) {
-      case "social-test:crash":
-        privateNoteIntentionalCrash();
-        crash();
-      break;
-    }
-  },
-}
-
-TestHelper.init();
--- a/dom/base/TabGroup.cpp
+++ b/dom/base/TabGroup.cpp
@@ -95,18 +95,17 @@ TabGroup::GetFromWindow(mozIDOMWindowPro
   return nullptr;
 }
 
 /* static */ TabGroup*
 TabGroup::GetFromActor(TabChild* aTabChild)
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
 
-  ContentChild* cc = ContentChild::GetSingleton();
-  nsCOMPtr<nsIEventTarget> target = cc->GetActorEventTarget(aTabChild);
+  nsCOMPtr<nsIEventTarget> target = aTabChild->Manager()->GetEventTargetFor(aTabChild);
   if (!target) {
     return nullptr;
   }
 
   // We have an event target. We assume the IPC code created it via
   // TabGroup::CreateEventTarget.
   RefPtr<SchedulerGroup> group =
     SchedulerGroup::FromEventTarget(target);
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -10346,18 +10346,36 @@ HtmlObjectContentSupportsDocument(const 
     do_GetService("@mozilla.org/streamConverters;1");
   bool canConvert = false;
   if (convServ) {
     rv = convServ->CanConvert(aMimeType.get(), "*/*", &canConvert);
   }
   return NS_SUCCEEDED(rv) && canConvert;
 }
 
+/* static */
+already_AddRefed<nsIPluginTag>
+nsContentUtils::PluginTagForType(const nsCString& aMIMEType, bool aNoFakePlugin)
+{
+  RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
+  nsCOMPtr<nsIPluginTag> tag;
+  NS_ENSURE_TRUE(pluginHost, nullptr);
+
+  // ShouldPlay will handle the case where the plugin is disabled
+  pluginHost->GetPluginTagForType(aMIMEType,
+                                  aNoFakePlugin ? nsPluginHost::eExcludeFake
+                                                : nsPluginHost::eExcludeNone,
+                                  getter_AddRefs(tag));
+
+  return tag.forget();
+}
+
 /* static */ uint32_t
 nsContentUtils::HtmlObjectContentTypeForMIMEType(const nsCString& aMIMEType,
+                                                 bool aNoFakePlugin,
                                                  nsIContent* aContent)
 {
   if (aMIMEType.IsEmpty()) {
     return nsIObjectLoadingContent::TYPE_NULL;
   }
 
   if (imgLoader::SupportImageWithMimeType(aMIMEType.get())) {
     return nsIObjectLoadingContent::TYPE_IMAGE;
@@ -10370,20 +10388,27 @@ nsContentUtils::HtmlObjectContentTypeFor
     return nsIObjectLoadingContent::TYPE_DOCUMENT;
   }
 
   if (HtmlObjectContentSupportsDocument(aMIMEType, aContent)) {
     return nsIObjectLoadingContent::TYPE_DOCUMENT;
   }
 
   RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
-  if (pluginHost &&
-      pluginHost->HavePluginForType(aMIMEType, nsPluginHost::eExcludeNone)) {
-    // ShouldPlay will handle checking for disabled plugins
-    return nsIObjectLoadingContent::TYPE_PLUGIN;
+  if (pluginHost) {
+    nsCOMPtr<nsIPluginTag> tag = PluginTagForType(aMIMEType, aNoFakePlugin);
+    if (tag) {
+      if (!aNoFakePlugin &&
+          nsCOMPtr<nsIFakePluginTag>(do_QueryInterface(tag))) {
+        return nsIObjectLoadingContent::TYPE_FAKE_PLUGIN;
+      }
+
+      // ShouldPlay will handle checking for disabled plugins
+      return nsIObjectLoadingContent::TYPE_PLUGIN;
+    }
   }
 
   return nsIObjectLoadingContent::TYPE_NULL;
 }
 
 /* static */ already_AddRefed<nsIEventTarget>
 nsContentUtils::GetEventTargetByLoadInfo(nsILoadInfo* aLoadInfo, TaskCategory aCategory)
 {
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -78,16 +78,17 @@ class nsIIOService;
 class nsILineBreaker;
 class nsILoadInfo;
 class nsILoadGroup;
 class nsIMessageBroadcaster;
 class nsNameSpaceManager;
 class nsIObserver;
 class nsIParser;
 class nsIParserService;
+class nsIPluginTag;
 class nsIPresShell;
 class nsIPrincipal;
 class nsIRequest;
 class nsIRunnable;
 class nsIScriptContext;
 class nsIScriptSecurityManager;
 class nsIStringBundle;
 class nsIStringBundleService;
@@ -2900,30 +2901,42 @@ public:
 
   /**
    * Walks up the tree from aElement until it finds an element that is
    * not native anonymous content.  aElement must be NAC itself.
    */
   static Element* GetClosestNonNativeAnonymousAncestor(Element* aElement);
 
   /**
+   * Returns the nsIPluginTag for the plugin we should try to use for a given
+   * MIME type.
+   *
+   * @param aMIMEType  The MIME type of the document being loaded.
+   * @param aNoFakePlugin  If false then this method should consider JS plugins.
+   */
+  static already_AddRefed<nsIPluginTag>
+    PluginTagForType(const nsCString& aMIMEType, bool aNoFakePlugin);
+
+  /**
    * Returns one of the nsIObjectLoadingContent::TYPE_ values describing the
    * content type which will be used for the given MIME type when loaded within
    * an nsObjectLoadingContent.
    *
    * NOTE: This method doesn't take capabilities into account. The caller must
    * take that into account.
    *
    * @param aMIMEType  The MIME type of the document being loaded.
+   * @param aNoFakePlugin  If false then this method should consider JS plugins.
    * @param aContent The nsIContent object which is performing the load. May be
    *                 nullptr in which case the docshell's plugin permissions
    *                 will not be checked.
    */
   static uint32_t
   HtmlObjectContentTypeForMIMEType(const nsCString& aMIMEType,
+                                   bool aNoFakePlugin,
                                    nsIContent* aContent);
 
   static already_AddRefed<nsIEventTarget>
   GetEventTargetByLoadInfo(nsILoadInfo* aLoadInfo, mozilla::TaskCategory aCategory);
 
   /**
    * Detect whether a string is a local-url.
    * https://drafts.csswg.org/css-values/#local-urls
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -151,22 +151,24 @@ NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFrameL
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFrameLoader)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFrameLoader)
   NS_INTERFACE_MAP_ENTRY(nsIFrameLoader)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIFrameLoader)
   NS_INTERFACE_MAP_ENTRY(nsIWebBrowserPersistable)
 NS_INTERFACE_MAP_END
 
-nsFrameLoader::nsFrameLoader(Element* aOwner, nsPIDOMWindowOuter* aOpener, bool aNetworkCreated)
+nsFrameLoader::nsFrameLoader(Element* aOwner, nsPIDOMWindowOuter* aOpener,
+                             bool aNetworkCreated, int32_t aJSPluginID)
   : mOwnerContent(aOwner)
   , mDetachedSubdocFrame(nullptr)
   , mOpener(aOpener)
   , mRemoteBrowser(nullptr)
   , mChildID(0)
+  , mJSPluginID(aJSPluginID)
   , mEventMode(EVENT_MODE_NORMAL_DISPATCH)
   , mBrowserChangingProcessBlockers(nullptr)
   , mIsPrerendered(false)
   , mDepthTooGreat(false)
   , mIsTopLevelContent(false)
   , mDestroyCalled(false)
   , mNeedsAsyncDestroy(false)
   , mInSwap(false)
@@ -188,17 +190,18 @@ nsFrameLoader::~nsFrameLoader()
 {
   if (mMessageManager) {
     mMessageManager->Disconnect();
   }
   MOZ_RELEASE_ASSERT(mDestroyCalled);
 }
 
 nsFrameLoader*
-nsFrameLoader::Create(Element* aOwner, nsPIDOMWindowOuter* aOpener, bool aNetworkCreated)
+nsFrameLoader::Create(Element* aOwner, nsPIDOMWindowOuter* aOpener, bool aNetworkCreated,
+                      int32_t aJSPluginId)
 {
   NS_ENSURE_TRUE(aOwner, nullptr);
   nsIDocument* doc = aOwner->OwnerDoc();
 
   // We never create nsFrameLoaders for elements in resource documents.
   //
   // We never create nsFrameLoaders for elements in data documents, unless the
   // document is a static document.
@@ -218,17 +221,17 @@ nsFrameLoader::Create(Element* aOwner, n
   // since for a static document we know aOwner will end up in a document and
   // the nsFrameLoader will be used for its docShell.)
   //
   NS_ENSURE_TRUE(!doc->IsResourceDoc() &&
                  ((!doc->IsLoadedAsData() && aOwner->IsInComposedDoc()) ||
                   doc->IsStaticDocument()),
                  nullptr);
 
-  return new nsFrameLoader(aOwner, aOpener, aNetworkCreated);
+  return new nsFrameLoader(aOwner, aOpener, aNetworkCreated, aJSPluginId);
 }
 
 NS_IMETHODIMP
 nsFrameLoader::LoadFrame()
 {
   NS_ENSURE_TRUE(mOwnerContent, NS_ERROR_NOT_INITIALIZED);
 
   nsAutoString src;
@@ -309,18 +312,25 @@ NS_IMETHODIMP
 nsFrameLoader::LoadURI(nsIURI* aURI)
 {
   if (!aURI)
     return NS_ERROR_INVALID_POINTER;
   NS_ENSURE_STATE(!mDestroyCalled && mOwnerContent);
 
   nsCOMPtr<nsIDocument> doc = mOwnerContent->OwnerDoc();
 
-  nsresult rv = CheckURILoad(aURI);
-  NS_ENSURE_SUCCESS(rv, rv);
+  nsresult rv;
+  // If IsForJSPlugin() returns true then we want to allow the load. We're just
+  // loading the source for the implementation of the JS plugin from a URI
+  // that's under our control. We will already have done the security checks for
+  // loading the plugin content itself in the object/embed loading code.
+  if (!IsForJSPlugin()) {
+    rv = CheckURILoad(aURI);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
 
   mURIToLoad = aURI;
   rv = doc->InitializeFrameLoader(this);
   if (NS_FAILED(rv)) {
     mURIToLoad = nullptr;
   }
   return rv;
 }
@@ -2241,16 +2251,20 @@ nsFrameLoader::OwnerIsIsolatedMozBrowser
   }
 
   return false;
 }
 
 bool
 nsFrameLoader::ShouldUseRemoteProcess()
 {
+  if (IsForJSPlugin()) {
+    return true;
+  }
+
   if (PR_GetEnv("MOZ_DISABLE_OOP_TABS") ||
       Preferences::GetBool("dom.ipc.tabs.disabled", false)) {
     return false;
   }
 
   // Don't try to launch nested children if we don't have OMTC.
   // They won't render!
   if (XRE_IsContentProcess() &&
@@ -2905,17 +2919,19 @@ nsFrameLoader::TryRemoteBrowser()
 
   if (openingTab &&
       openingTab->Manager() &&
       openingTab->Manager()->IsContentParent()) {
     openerContentParent = openingTab->Manager()->AsContentParent();
   }
 
   // <iframe mozbrowser> gets to skip these checks.
-  if (!OwnerIsMozBrowserFrame()) {
+  // iframes for JS plugins also get to skip these checks. We control the URL that gets
+  // loaded, but the load is triggered from the document containing the plugin.
+  if (!OwnerIsMozBrowserFrame() && !IsForJSPlugin()) {
     if (parentDocShell->ItemType() != nsIDocShellTreeItem::typeChrome) {
       // Allow about:addon an exception to this rule so it can load remote
       // extension options pages.
       //
       // Note that the new frame's message manager will not be a child of the
       // chrome window message manager, and, the values of window.top and
       // window.parent will be different than they would be for a non-remote
       // frame.
@@ -3619,16 +3635,21 @@ nsFrameLoader::MaybeUpdatePrimaryTabPare
     }
   }
 }
 
 nsresult
 nsFrameLoader::GetNewTabContext(MutableTabContext* aTabContext,
                                 nsIURI* aURI)
 {
+  if (IsForJSPlugin()) {
+    return aTabContext->SetTabContextForJSPluginFrame(mJSPluginID) ? NS_OK :
+           NS_ERROR_FAILURE;
+  }
+
   OriginAttributes attrs;
   attrs.mInIsolatedMozBrowser = OwnerIsIsolatedMozBrowserFrame();
   nsresult rv;
 
   attrs.mAppId = nsIScriptSecurityManager::NO_APP_ID;
 
   // set the userContextId on the attrs before we pass them into
   // the tab context
--- a/dom/base/nsFrameLoader.h
+++ b/dom/base/nsFrameLoader.h
@@ -21,16 +21,17 @@
 #include "nsFrameMessageManager.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/Attributes.h"
 #include "nsStubMutationObserver.h"
 #include "Units.h"
 #include "nsIWebBrowserPersistable.h"
 #include "nsIFrame.h"
 #include "nsIGroupedSHistory.h"
+#include "nsPluginTags.h"
 
 class nsIURI;
 class nsSubDocumentFrame;
 class nsView;
 class nsIInProcessContentFrameMessageManager;
 class AutoResetInShow;
 class AutoResetInFrameSwap;
 class nsITabParent;
@@ -74,17 +75,18 @@ class nsFrameLoader final : public nsIFr
   friend class RequestGroupedHistoryNavigationHelper;
   typedef mozilla::dom::PBrowserParent PBrowserParent;
   typedef mozilla::dom::TabParent TabParent;
   typedef mozilla::layout::RenderFrameParent RenderFrameParent;
 
 public:
   static nsFrameLoader* Create(mozilla::dom::Element* aOwner,
                                nsPIDOMWindowOuter* aOpener,
-                               bool aNetworkCreated);
+                               bool aNetworkCreated,
+                               int32_t aJSPluginID = nsFakePluginTag::NOT_JSPLUGIN);
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsFrameLoader, nsIFrameLoader)
   NS_DECL_NSIFRAMELOADER
   NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
   NS_DECL_NSIWEBBROWSERPERSISTABLE
   nsresult CheckForRecursiveLoad(nsIURI* aURI);
   nsresult ReallyStartLoading();
@@ -225,28 +227,34 @@ public:
 
   // public because a callback needs these.
   RefPtr<nsFrameMessageManager> mMessageManager;
   nsCOMPtr<nsIInProcessContentFrameMessageManager> mChildMessageManager;
 
 private:
   nsFrameLoader(mozilla::dom::Element* aOwner,
                 nsPIDOMWindowOuter* aOpener,
-                bool aNetworkCreated);
+                bool aNetworkCreated,
+                int32_t aJSPluginID);
   ~nsFrameLoader();
 
   void SetOwnerContent(mozilla::dom::Element* aContent);
 
   bool ShouldUseRemoteProcess();
 
   /**
    * Return true if the frame is a remote frame. Return false otherwise
    */
   bool IsRemoteFrame();
 
+  bool IsForJSPlugin()
+  {
+    return mJSPluginID != nsFakePluginTag::NOT_JSPLUGIN;
+  }
+
   /**
    * Is this a frame loader for a bona fide <iframe mozbrowser>?
    * <xul:browser> is not a mozbrowser, so this is false for that case.
    */
   bool OwnerIsMozBrowserFrame();
 
   /**
    * Is this a frame loader for an isolated <iframe mozbrowser>?
@@ -338,16 +346,18 @@ private:
   nsCOMPtr<nsIDocument> mContainerDocWhileDetached;
 
   // An opener window which should be used when the docshell is created.
   nsCOMPtr<nsPIDOMWindowOuter> mOpener;
 
   TabParent* mRemoteBrowser;
   uint64_t mChildID;
 
+  int32_t mJSPluginID;
+
   // See nsIFrameLoader.idl. EVENT_MODE_NORMAL_DISPATCH automatically
   // forwards some input events to out-of-process content.
   uint32_t mEventMode;
 
   // Holds the last known size of the frame.
   mozilla::ScreenIntSize mLazySize;
 
   nsCOMPtr<nsIPartialSHistory> mPartialSHistory;
--- a/dom/base/nsIObjectLoadingContent.idl
+++ b/dom/base/nsIObjectLoadingContent.idl
@@ -26,21 +26,22 @@ class nsNPAPIPluginInstance;
  */
 
 [scriptable, uuid(2eb3195e-3eea-4083-bb1d-d2d70fa35ccb)]
 interface nsIObjectLoadingContent : nsISupports
 {
   /**
    * See notes in nsObjectLoadingContent.h
    */
-  const unsigned long TYPE_LOADING  = 0;
-  const unsigned long TYPE_IMAGE    = 1;
-  const unsigned long TYPE_PLUGIN   = 2;
-  const unsigned long TYPE_DOCUMENT = 3;
-  const unsigned long TYPE_NULL     = 4;
+  const unsigned long TYPE_LOADING     = 0;
+  const unsigned long TYPE_IMAGE       = 1;
+  const unsigned long TYPE_PLUGIN      = 2;
+  const unsigned long TYPE_FAKE_PLUGIN = 3;
+  const unsigned long TYPE_DOCUMENT    = 4;
+  const unsigned long TYPE_NULL        = 5;
 
   const unsigned long PLUGIN_ACTIVE               = 0xFF;
 
   // The content type is not supported (e.g. plugin not installed)
   const unsigned long PLUGIN_UNSUPPORTED          = 0;
   // Showing alternate content
   const unsigned long PLUGIN_ALTERNATE            = 1;
   // The plugin exists, but is disabled
@@ -158,9 +159,14 @@ interface nsIObjectLoadingContent : nsIS
    */
   [noscript] void initializeFromChannel(in nsIRequest request);
 
   /**
    * The URL of the data/src loaded in the object. This may be null (i.e.
    * an <embed> with no src).
    */
   readonly attribute nsIURI srcURI;
+
+  /**
+   * Disable the use of fake plugins and reload the tag if necessary.
+   */
+  void skipFakePlugins();
 };
--- a/dom/base/nsObjectLoadingContent.cpp
+++ b/dom/base/nsObjectLoadingContent.cpp
@@ -11,16 +11,17 @@
 
 // Interface headers
 #include "imgLoader.h"
 #include "nsIClassOfService.h"
 #include "nsIConsoleService.h"
 #include "nsIContent.h"
 #include "nsIContentInlines.h"
 #include "nsIDocShell.h"
+#include "nsIDocShellLoadInfo.h"
 #include "nsIDocument.h"
 #include "nsIDOMCustomEvent.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMHTMLObjectElement.h"
 #include "nsIDOMHTMLAppletElement.h"
 #include "nsIExternalProtocolHandler.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIObjectFrame.h"
@@ -141,16 +142,23 @@ InActiveDocument(nsIContent *aContent)
 {
   if (!aContent->IsInComposedDoc()) {
     return false;
   }
   nsIDocument *doc = aContent->OwnerDoc();
   return (doc && doc->IsActive());
 }
 
+static bool
+IsPluginType(nsObjectLoadingContent::ObjectType type)
+{
+  return type == nsObjectLoadingContent::eType_Plugin ||
+         type == nsObjectLoadingContent::eType_FakePlugin;
+}
+
 ///
 /// Runnables and helper classes
 ///
 
 class nsAsyncInstantiateEvent : public Runnable {
 public:
   explicit nsAsyncInstantiateEvent(nsObjectLoadingContent* aContent)
     : Runnable("nsAsyncInstantiateEvent"), mContent(aContent) {}
@@ -543,16 +551,63 @@ nsObjectLoadingContent::MakePluginListen
   NS_ENSURE_SUCCESS(rv, false);
   rv = pluginHost->NewPluginStreamListener(mURI, inst,
                                            getter_AddRefs(finalListener));
   NS_ENSURE_SUCCESS(rv, false);
   mFinalListener = finalListener;
   return true;
 }
 
+// Helper to spawn the frameloader.
+void
+nsObjectLoadingContent::SetupFrameLoader(int32_t aJSPluginId)
+{
+  nsCOMPtr<nsIContent> thisContent =
+    do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
+  NS_ASSERTION(thisContent, "must be a content");
+
+  mFrameLoader = nsFrameLoader::Create(thisContent->AsElement(),
+                                       /* aOpener = */ nullptr,
+                                       mNetworkCreated, aJSPluginId);
+  if (!mFrameLoader) {
+    NS_NOTREACHED("nsFrameLoader::Create failed");
+  }
+}
+
+// Helper to spawn the frameloader and return a pointer to its docshell.
+already_AddRefed<nsIDocShell>
+nsObjectLoadingContent::SetupDocShell(nsIURI* aRecursionCheckURI)
+{
+  SetupFrameLoader(nsFakePluginTag::NOT_JSPLUGIN);
+  if (!mFrameLoader) {
+    return nullptr;
+  }
+
+  nsCOMPtr<nsIDocShell> docShell;
+
+  if (aRecursionCheckURI) {
+    nsresult rv = mFrameLoader->CheckForRecursiveLoad(aRecursionCheckURI);
+    if (NS_SUCCEEDED(rv)) {
+      rv = mFrameLoader->GetDocShell(getter_AddRefs(docShell));
+      if (NS_FAILED(rv)) {
+        NS_NOTREACHED("Could not get DocShell from mFrameLoader?");
+      }
+    } else {
+      LOG(("OBJLC [%p]: Aborting recursive load", this));
+    }
+  }
+
+  if (!docShell) {
+    mFrameLoader->Destroy();
+    mFrameLoader = nullptr;
+    return nullptr;
+  }
+
+  return docShell.forget();
+}
 
 nsresult
 nsObjectLoadingContent::BindToTree(nsIDocument* aDocument,
                                    nsIContent* aParent,
                                    nsIContent* aBindingParent,
                                    bool aCompileEventHandlers)
 {
   nsImageLoadingContent::BindToTree(aDocument, aParent, aBindingParent,
@@ -570,16 +625,18 @@ nsObjectLoadingContent::UnbindFromTree(b
   nsImageLoadingContent::UnbindFromTree(aDeep, aNullParent);
 
   nsCOMPtr<nsIContent> thisContent =
     do_QueryInterface(static_cast<nsIObjectLoadingContent*>(this));
   MOZ_ASSERT(thisContent);
   nsIDocument* ownerDoc = thisContent->OwnerDoc();
   ownerDoc->RemovePlugin(this);
 
+  /// XXX(johns): Do we want to somehow propogate the reparenting behavior to
+  ///             FakePlugin types as well?
   if (mType == eType_Plugin && (mInstanceOwner || mInstantiating)) {
     // we'll let the plugin continue to run at least until we get back to
     // the event loop. If we get back to the event loop and the node
     // has still not been added back to the document then we tear down the
     // plugin
     QueueCheckPluginStopEvent();
   } else if (mType != eType_Image) {
     // nsImageLoadingContent handles the image case.
@@ -603,16 +660,17 @@ nsObjectLoadingContent::nsObjectLoadingC
   , mFallbackType(eFallbackAlternate)
   , mRunID(0)
   , mHasRunID(false)
   , mChannelLoaded(false)
   , mInstantiating(false)
   , mNetworkCreated(true)
   , mActivated(false)
   , mContentBlockingEnabled(false)
+  , mSkipFakePlugins(false)
   , mIsStopping(false)
   , mIsLoading(false)
   , mScriptRequested(false)
   , mRewrittenYoutubeEmbed(false)
   , mPreferFallback(false)
   , mPreferFallbackKnown(false) {}
 
 nsObjectLoadingContent::~nsObjectLoadingContent()
@@ -1194,17 +1252,17 @@ nsObjectLoadingContent::GetPluginInstanc
 
   return mInstanceOwner->GetInstance(aInstance);
 }
 
 NS_IMETHODIMP
 nsObjectLoadingContent::GetContentTypeForMIMEType(const nsACString& aMIMEType,
                                                   uint32_t* aType)
 {
-  *aType = GetTypeOfContent(PromiseFlatCString(aMIMEType));
+  *aType = GetTypeOfContent(PromiseFlatCString(aMIMEType), false);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsObjectLoadingContent::GetBaseURI(nsIURI **aResult)
 {
   NS_IF_ADDREF(*aResult = mBaseURI);
   return NS_OK;
@@ -1289,16 +1347,17 @@ EventStates
 nsObjectLoadingContent::ObjectState() const
 {
   switch (mType) {
     case eType_Loading:
       return NS_EVENT_STATE_LOADING;
     case eType_Image:
       return ImageState();
     case eType_Plugin:
+    case eType_FakePlugin:
     case eType_Document:
       // These are OK. If documents start to load successfully, they display
       // something, and are thus not broken in this sense. The same goes for
       // plugins.
       return EventStates();
     case eType_Null:
       switch (mFallbackType) {
         case eFallbackSuppressed:
@@ -1532,16 +1591,19 @@ nsObjectLoadingContent::CheckProcessPoli
   int32_t objectType;
   switch (mType) {
     case eType_Image:
       objectType = nsIContentPolicy::TYPE_INTERNAL_IMAGE;
       break;
     case eType_Document:
       objectType = nsIContentPolicy::TYPE_DOCUMENT;
       break;
+    // FIXME Fake plugins look just like real plugins to CSP, should they use
+    // the fake plugin's handler URI and look like documents instead?
+    case eType_FakePlugin:
     case eType_Plugin:
       objectType = GetContentPolicyType();
       break;
     default:
       NS_NOTREACHED("Calling checkProcessPolicy with a unloadable type");
       return false;
   }
 
@@ -1745,17 +1807,17 @@ nsObjectLoadingContent::UpdateObjectPara
       NS_TryToSetImmutable(newURI);
     } else {
       stateInvalid = true;
     }
   }
 
   // For eAllowPluginSkipChannel tags, if we have a non-plugin type, but can get
   // a plugin type from the extension, prefer that to falling back to a channel.
-  if (GetTypeOfContent(newMime) != eType_Plugin && newURI &&
+  if (!IsPluginType(GetTypeOfContent(newMime, mSkipFakePlugins)) && newURI &&
       (caps & eAllowPluginSkipChannel) &&
       IsPluginEnabledByExtension(newURI, newMime)) {
     LOG(("OBJLC [%p]: Using extension as type hint (%s)", this, newMime.get()));
     if (!isJava && IsJavaMIME(newMime)) {
       return UpdateObjectParameters(true);
     }
   }
 
@@ -1811,18 +1873,18 @@ nsObjectLoadingContent::UpdateObjectPara
 
     // Channel can change our URI through redirection
     rv = NS_GetFinalChannelURI(mChannel, getter_AddRefs(newURI));
     if (NS_FAILED(rv)) {
       NS_NOTREACHED("NS_GetFinalChannelURI failure");
       stateInvalid = true;
     }
 
-    ObjectType typeHint = newMime.IsEmpty() ?
-                          eType_Null : GetTypeOfContent(newMime);
+    ObjectType typeHint = newMime.IsEmpty() ? eType_Null
+                          : GetTypeOfContent(newMime, mSkipFakePlugins);
 
     //
     // In order of preference:
     //
     // 1) Perform typemustmatch check.
     //    If check is sucessful use type without further checks.
     //    If check is unsuccessful set stateInvalid to true
     // 2) Use our type hint if it matches a plugin
@@ -1833,17 +1895,17 @@ nsObjectLoadingContent::UpdateObjectPara
     //    4b) If the uri file extension matches a plugin type, use that
     // 5) Use the channel type
 
     bool overrideChannelType = false;
     if (thisContent->HasAttr(kNameSpaceID_None, nsGkAtoms::typemustmatch)) {
       if (!typeAttr.LowerCaseEqualsASCII(channelType.get())) {
         stateInvalid = true;
       }
-    } else if (typeHint == eType_Plugin) {
+    } else if (IsPluginType(typeHint)) {
       LOG(("OBJLC [%p]: Using plugin type hint in favor of any channel type",
            this));
       overrideChannelType = true;
     } else if ((caps & eAllowPluginSkipChannel) &&
                IsPluginEnabledByExtension(newURI, newMime)) {
       LOG(("OBJLC [%p]: Using extension as type hint for "
            "eAllowPluginSkipChannel tag (%s)", this, newMime.get()));
       overrideChannelType = true;
@@ -1895,26 +1957,28 @@ nsObjectLoadingContent::UpdateObjectPara
   //     type.
   //  3) If we have a plugin type and no URI, use that type.
   //  4) If we have a plugin type and eAllowPluginSkipChannel, use that type.
   //  5) if we have a URI, set type to loading to indicate we'd need a channel
   //     to proceed.
   //  6) Otherwise, type null to indicate unloadable content (fallback)
   //
 
+  ObjectType newMime_Type = GetTypeOfContent(newMime, mSkipFakePlugins);
+
   if (stateInvalid) {
     newType = eType_Null;
     newMime.Truncate();
   } else if (newChannel) {
     // If newChannel is set above, we considered it in setting newMime
-    newType = GetTypeOfContent(newMime);
+    newType = newMime_Type;
     LOG(("OBJLC [%p]: Using channel type", this));
   } else if (((caps & eAllowPluginSkipChannel) || !newURI) &&
-             GetTypeOfContent(newMime) == eType_Plugin) {
-    newType = eType_Plugin;
+             IsPluginType(newMime_Type)) {
+    newType = newMime_Type;
     LOG(("OBJLC [%p]: Plugin type with no URI, skipping channel load", this));
   } else if (newURI) {
     // We could potentially load this if we opened a channel on mURI, indicate
     // This by leaving type as loading
     newType = eType_Loading;
   } else {
     // Unloadable - no URI, and no plugin type. Non-plugin types (images,
     // documents) always load with a channel.
@@ -2076,33 +2140,36 @@ nsObjectLoadingContent::LoadObject(bool 
   LOG(("OBJLC [%p]: LoadObject - plugin state changed (%u)",
        this, stateChange));
 
   // Setup fallback info. We may also change type to fallback below in case of
   // sanity/OOM/etc. errors. We default to showing alternate content
   // NOTE LoadFallback can override this in some cases
   FallbackType fallbackType = eFallbackAlternate;
 
-  // mType can differ with GetTypeOfContent(mContentType) if we support this
-  // type, but the parameters are invalid e.g. a embed tag with type "image/png"
-  // but no URI -- don't show a plugin error or unknown type error in that case.
-  if (mType == eType_Null && GetTypeOfContent(mContentType) == eType_Null) {
+  // If GetTypeOfContent(mContentType) is null we truly have no handler for the
+  // type -- otherwise, we have a handler but UpdateObjectParameters rejected
+  // the configuration for another reason (e.g. an embed tag with type
+  // "image/png" but no URI). Don't show a plugin error or unknown type error in
+  // the latter case.
+  if (mType == eType_Null &&
+      GetTypeOfContent(mContentType, mSkipFakePlugins) == eType_Null) {
     fallbackType = eFallbackUnsupported;
   }
 
   // Explicit user activation should reset if the object changes content types
   if (mActivated && (stateChange & eParamContentTypeChanged)) {
     LOG(("OBJLC [%p]: Content type changed, clearing activation state", this));
     mActivated = false;
   }
 
   // We synchronously start/stop plugin instances below, which may spin the
   // event loop. Re-entering into the load is fine, but at that point the
   // original load call needs to abort when unwinding
-  // NOTE this is located *after* the state change check, a subseqent load
+  // NOTE this is located *after* the state change check, a subsequent load
   //      with no subsequently changed state will be a no-op.
   if (mIsLoading) {
     LOG(("OBJLC [%p]: Re-entering into LoadObject", this));
   }
   mIsLoading = true;
   AutoSetLoadingToFalse reentryCheck(this);
 
   // Unload existing content, keeping in mind stopping plugins might spin the
@@ -2207,34 +2274,33 @@ nsObjectLoadingContent::LoadObject(bool 
 
       nestedURI->GetInnerURI(getter_AddRefs(tempURI));
       nestedURI = do_QueryInterface(tempURI);
     }
   }
 
   // Items resolved as Image/Document are no candidates for content blocking,
   // as well as invalid plugins (they will not have the mContentType set).
-  if ((mType == eType_Null || mType == eType_Plugin) && ShouldBlockContent()) {
+  if ((mType == eType_Null || IsPluginType(mType)) && ShouldBlockContent()) {
     LOG(("OBJLC [%p]: Enable content blocking", this));
     mType = eType_Loading;
   }
 
   // If we're a plugin but shouldn't start yet, load fallback with
   // reason click-to-play instead. Items resolved as Image/Document
   // will not be checked for previews, as well as invalid plugins
   // (they will not have the mContentType set).
   FallbackType clickToPlayReason;
-  if (!mActivated && (mType == eType_Null || mType == eType_Plugin) &&
-      !ShouldPlay(clickToPlayReason, false)) {
+  if (!mActivated && IsPluginType(mType) && !ShouldPlay(clickToPlayReason)) {
     LOG(("OBJLC [%p]: Marking plugin as click-to-play", this));
     mType = eType_Null;
     fallbackType = clickToPlayReason;
   }
 
-  if (!mActivated && mType == eType_Plugin) {
+  if (!mActivated && IsPluginType(mType)) {
     // Object passed ShouldPlay, so it should be considered
     // activated until it changes content type
     LOG(("OBJLC [%p]: Object implicitly activated", this));
     mActivated = true;
   }
 
   // Sanity check: We shouldn't have any loaded resources, pending events, or
   // a final listener at this point
@@ -2301,69 +2367,142 @@ nsObjectLoadingContent::LoadObject(bool 
 
         // We'll handle this below
         doSpawnPlugin = true;
       } else {
         rv = AsyncStartPluginInstance();
       }
     }
     break;
+    case eType_FakePlugin:
+    {
+      if (mChannel) {
+        /// XXX(johns): Ideally we'd have some way to pass the channel to the
+        ///             fake plugin handler, but for now handlers will need to
+        ///             request element.srcURI themselves if they want it
+        LOG(("OBJLC [%p]: Closing unused channel for fake plugin type", this));
+        CloseChannel();
+      }
+
+      /// XXX(johns) Bug FIXME - We need to cleanup the various plugintag
+      ///            classes to be more sane and avoid this dance
+      nsCOMPtr<nsIPluginTag> basetag =
+        nsContentUtils::PluginTagForType(mContentType, false);
+      nsCOMPtr<nsIFakePluginTag> tag = do_QueryInterface(basetag);
+
+      uint32_t id;
+      if (NS_FAILED(tag->GetId(&id))) {
+        rv = NS_ERROR_FAILURE;
+        break;
+      }
+
+      MOZ_ASSERT(id <= PR_INT32_MAX,
+                 "Something went wrong, nsPluginHost::RegisterFakePlugin shouldn't have "
+                 "given out this id.");
+
+      SetupFrameLoader(int32_t(id));
+      if (!mFrameLoader) {
+        rv = NS_ERROR_FAILURE;
+        break;
+      }
+
+
+      nsString sandboxScript;
+      tag->GetSandboxScript(sandboxScript);
+      if (!sandboxScript.IsEmpty()) {
+        // Create a sandbox.
+        AutoJSAPI jsapi;
+        jsapi.Init();
+        JS::Rooted<JSObject*> sandbox(jsapi.cx());
+        rv = nsContentUtils::XPConnect()->
+          CreateSandbox(jsapi.cx(), nsContentUtils::GetSystemPrincipal(),
+                        sandbox.address());
+        if (NS_FAILED(rv)) {
+          break;
+        }
+
+        AutoEntryScript aes(sandbox, "JS plugin sandbox code");
+
+        JS::Rooted<JS::Value> element(aes.cx());
+        if (!ToJSValue(aes.cx(), thisContent, &element)) {
+          rv = NS_ERROR_FAILURE;
+          break;
+        }
+
+        if (!JS_DefineProperty(aes.cx(), sandbox, "pluginElement", element, JSPROP_ENUMERATE)) {
+          rv = NS_ERROR_FAILURE;
+          break;
+        }
+
+        JS::Rooted<JS::Value> rval(aes.cx());
+        // If the eval'ed code throws we won't load and do fallback instead.
+        rv = nsContentUtils::XPConnect()->EvalInSandboxObject(sandboxScript, nullptr, aes.cx(), sandbox, &rval);
+        if (NS_FAILED(rv)) {
+          break;
+        }
+      }
+
+      nsCOMPtr<nsIURI> handlerURI;
+      if (tag) {
+        tag->GetHandlerURI(getter_AddRefs(handlerURI));
+      }
+
+      if (!handlerURI) {
+        NS_NOTREACHED("Selected type is not a proper fake plugin handler");
+        rv = NS_ERROR_FAILURE;
+        break;
+      }
+
+      nsCString spec;
+      handlerURI->GetSpec(spec);
+      LOG(("OBJLC [%p]: Loading fake plugin handler (%s)", this, spec.get()));
+
+      rv = mFrameLoader->LoadURI(handlerURI);
+      if (NS_FAILED(rv)) {
+        LOG(("OBJLC [%p]: LoadURI() failed for fake handler", this));
+        mFrameLoader->Destroy();
+        mFrameLoader = nullptr;
+      }
+    }
+    break;
     case eType_Document:
     {
       if (!mChannel) {
         // We could mFrameLoader->LoadURI(mURI), but UpdateObjectParameters
         // requires documents have a channel, so this is not a valid state.
         NS_NOTREACHED("Attempting to load a document without a channel");
-        mType = eType_Null;
+        rv = NS_ERROR_FAILURE;
         break;
       }
 
-      mFrameLoader = nsFrameLoader::Create(thisContent->AsElement(),
-                                           /* aOpener = */ nullptr,
-                                           mNetworkCreated);
-      if (!mFrameLoader) {
-        NS_NOTREACHED("nsFrameLoader::Create failed");
-        mType = eType_Null;
-        break;
-      }
-
-      rv = mFrameLoader->CheckForRecursiveLoad(mURI);
-      if (NS_FAILED(rv)) {
-        LOG(("OBJLC [%p]: Aborting recursive load", this));
-        mFrameLoader->Destroy();
-        mFrameLoader = nullptr;
-        mType = eType_Null;
+      nsCOMPtr<nsIDocShell> docShell = SetupDocShell(mURI);
+      if (!docShell) {
+        rv = NS_ERROR_FAILURE;
         break;
       }
 
       // We're loading a document, so we have to set LOAD_DOCUMENT_URI
       // (especially important for firing onload)
       nsLoadFlags flags = 0;
       mChannel->GetLoadFlags(&flags);
       flags |= nsIChannel::LOAD_DOCUMENT_URI;
       mChannel->SetLoadFlags(flags);
 
-      nsCOMPtr<nsIDocShell> docShell;
-      rv = mFrameLoader->GetDocShell(getter_AddRefs(docShell));
-      if (NS_FAILED(rv)) {
-        NS_NOTREACHED("Could not get DocShell from mFrameLoader?");
-        mType = eType_Null;
-        break;
-      }
-
       nsCOMPtr<nsIInterfaceRequestor> req(do_QueryInterface(docShell));
       NS_ASSERTION(req, "Docshell must be an ifreq");
 
       nsCOMPtr<nsIURILoader>
         uriLoader(do_GetService(NS_URI_LOADER_CONTRACTID, &rv));
       if (NS_FAILED(rv)) {
         NS_NOTREACHED("Failed to get uriLoader service");
-        mType = eType_Null;
+        mFrameLoader->Destroy();
+        mFrameLoader = nullptr;
         break;
       }
+
       rv = uriLoader->OpenChannel(mChannel, nsIURILoader::DONT_RETARGET, req,
                                   getter_AddRefs(finalListener));
       // finalListener will receive OnStartRequest below
     }
     break;
     case eType_Loading:
       // If our type remains Loading, we need a channel to proceed
       rv = OpenChannel();
@@ -2707,34 +2846,37 @@ nsObjectLoadingContent::NotifyStateChang
   if (aSync) {
     NS_ASSERTION(InActiveDocument(thisContent), "Something is confused");
     // Make sure that frames are actually constructed immediately.
     doc->FlushPendingNotifications(FlushType::Frames);
   }
 }
 
 nsObjectLoadingContent::ObjectType
-nsObjectLoadingContent::GetTypeOfContent(const nsCString& aMIMEType)
+nsObjectLoadingContent::GetTypeOfContent(const nsCString& aMIMEType,
+                                         bool aNoFakePlugin)
 {
   nsCOMPtr<nsIContent> thisContent =
     do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
   NS_ASSERTION(thisContent, "must be a content");
 
   ObjectType type = static_cast<ObjectType>(
-    nsContentUtils::HtmlObjectContentTypeForMIMEType(aMIMEType, thisContent));
+    nsContentUtils::HtmlObjectContentTypeForMIMEType(aMIMEType, aNoFakePlugin,
+                                                     thisContent));
 
   // Switch the result type to eType_Null ic the capability is not present.
   uint32_t caps = GetCapabilities();
   if (!(caps & eSupportImages) && type == eType_Image) {
     type = eType_Null;
   }
   if (!(caps & eSupportDocuments) && type == eType_Document) {
     type = eType_Null;
   }
-  if (!(caps & eSupportPlugins) && type == eType_Plugin) {
+  if (!(caps & eSupportPlugins) &&
+      (type == eType_Plugin || type == eType_FakePlugin)) {
     type = eType_Null;
   }
 
   return type;
 }
 
 nsPluginFrame*
 nsObjectLoadingContent::GetExistingFrame()
@@ -3013,26 +3155,33 @@ nsObjectLoadingContent::DoStopPlugin(nsP
   // instance we are about to destroy. We prevent that with the mIsStopping
   // flag.
   if (mIsStopping) {
     return;
   }
   mIsStopping = true;
 
   RefPtr<nsPluginInstanceOwner> kungFuDeathGrip(aInstanceOwner);
-  RefPtr<nsNPAPIPluginInstance> inst;
-  aInstanceOwner->GetInstance(getter_AddRefs(inst));
-  if (inst) {
+  if (mType == eType_FakePlugin) {
+    if (mFrameLoader) {
+      mFrameLoader->Destroy();
+      mFrameLoader = nullptr;
+    }
+  } else {
+    RefPtr<nsNPAPIPluginInstance> inst;
+    aInstanceOwner->GetInstance(getter_AddRefs(inst));
+    if (inst) {
 #if defined(XP_MACOSX)
-    aInstanceOwner->HidePluginWindow();
+      aInstanceOwner->HidePluginWindow();
 #endif
 
-    RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
-    NS_ASSERTION(pluginHost, "No plugin host?");
-    pluginHost->StopPluginInstance(inst);
+      RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
+      NS_ASSERTION(pluginHost, "No plugin host?");
+      pluginHost->StopPluginInstance(inst);
+    }
   }
 
   aInstanceOwner->Destroy();
 
   // If we re-enter in plugin teardown UnloadObject will tear down the
   // protochain -- the current protochain could be from a new, unrelated, load.
   if (!mIsStopping) {
     LOG(("OBJLC [%p]: Re-entered in plugin teardown", this));
@@ -3119,39 +3268,55 @@ nsObjectLoadingContent::PlayPlugin(Syste
   }
 }
 
 NS_IMETHODIMP
 nsObjectLoadingContent::Reload(bool aClearActivation)
 {
   if (aClearActivation) {
     mActivated = false;
+    mSkipFakePlugins = false;
   }
 
   return LoadObject(true, true);
 }
 
 NS_IMETHODIMP
 nsObjectLoadingContent::GetActivated(bool *aActivated)
 {
   *aActivated = Activated();
   return NS_OK;
 }
 
 uint32_t
 nsObjectLoadingContent::DefaultFallbackType()
 {
   FallbackType reason;
-  bool go = ShouldPlay(reason, true);
-  if (go) {
+  if (ShouldPlay(reason)) {
     return PLUGIN_ACTIVE;
   }
   return reason;
 }
 
+NS_IMETHODIMP
+nsObjectLoadingContent::SkipFakePlugins()
+{
+  if (!nsContentUtils::IsCallerChrome())
+    return NS_ERROR_NOT_AVAILABLE;
+
+  mSkipFakePlugins = true;
+
+  // If we're showing a fake plugin now, reload
+  if (mType == eType_FakePlugin) {
+    return LoadObject(true, true);
+  }
+
+  return NS_OK;
+}
+
 uint32_t
 nsObjectLoadingContent::GetRunID(SystemCallerGuarantee, ErrorResult& aRv)
 {
   if (!mHasRunID) {
     // The plugin instance must not have a run ID, so we must
     // be running the plugin in-process.
     aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
     return 0;
@@ -3188,17 +3353,17 @@ nsObjectLoadingContent::ShouldBlockConte
   if (mContentBlockingEnabled && mURI && IsFlashMIME(mContentType) && sBlockURIs ) {
     return true;
   }
 
   return false;
 }
 
 bool
-nsObjectLoadingContent::ShouldPlay(FallbackType &aReason, bool aIgnoreCurrentType)
+nsObjectLoadingContent::ShouldPlay(FallbackType &aReason)
 {
   nsresult rv;
 
   if (!sPrefsInitialized) {
     initializeObjectLoadingContentPrefs();
   }
 
   if (BrowserTabsRemoteAutostart()) {
@@ -3211,21 +3376,16 @@ nsObjectLoadingContent::ShouldPlay(Fallb
       // the other. Otherwise we'll get hangs.
       aReason = eFallbackDisabled;
       return false;
     }
   }
 
   RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
 
-  // at this point if it's not a plugin, we let it play/fallback
-  if (!aIgnoreCurrentType && mType != eType_Plugin) {
-    return true;
-  }
-
   // Order of checks:
   // * Assume a default of click-to-play
   // * If globally disabled, per-site permissions cannot override.
   // * If blocklisted, override the reason with the blocklist reason
   // * Check if the flash blocking status for this page denies flash from loading.
   // * Check per-site permissions and follow those if specified.
   // * Honor per-plugin disabled permission
   // * Blocklisted plugins are forced to CtP
@@ -3251,18 +3411,17 @@ nsObjectLoadingContent::ShouldPlay(Fallb
   if (blocklistState == nsIBlocklistService::STATE_BLOCKED) {
     // no override possible
     aReason = eFallbackBlocklisted;
     return false;
   }
 
   if (blocklistState == nsIBlocklistService::STATE_VULNERABLE_UPDATE_AVAILABLE) {
     aReason = eFallbackVulnerableUpdatable;
-  }
-  else if (blocklistState == nsIBlocklistService::STATE_VULNERABLE_NO_UPDATE) {
+  } else if (blocklistState == nsIBlocklistService::STATE_VULNERABLE_NO_UPDATE) {
     aReason = eFallbackVulnerableNoUpdate;
   }
 
   // Document and window lookup
   nsCOMPtr<nsIContent> thisContent = do_QueryInterface(static_cast<nsIObjectLoadingContent*>(this));
   MOZ_ASSERT(thisContent);
   nsIDocument* ownerDoc = thisContent->OwnerDoc();
 
@@ -3286,20 +3445,20 @@ nsObjectLoadingContent::ShouldPlay(Fallb
   }
 
   // Check the permission manager for permission based on the principal of
   // the toplevel content.
   nsCOMPtr<nsIPermissionManager> permissionManager = services::GetPermissionManager();
   NS_ENSURE_TRUE(permissionManager, false);
 
   // For now we always say that the system principal uses click-to-play since
-  // that maintains current behavior and we have tests that expect this.
-  // What we really should do is disable plugins entirely in pages that use
-  // the system principal, i.e. in chrome pages. That way the click-to-play
-  // code here wouldn't matter at all. Bug 775301 is tracking this.
+  // that maintains current behavior and we have tests that expect this.  What
+  // we really should do is disable plugins entirely in pages that use the
+  // system principal, i.e. in chrome pages. That way the click-to-play code
+  // here wouldn't matter at all. Bug 775301 is tracking this.
   if (!nsContentUtils::IsSystemPrincipal(topDoc->NodePrincipal())) {
     nsAutoCString permissionString;
     rv = pluginHost->GetPermissionStringForType(mContentType,
                                                 nsPluginHost::eExcludeNone,
                                                 permissionString);
     NS_ENSURE_SUCCESS(rv, false);
     uint32_t permission;
     rv = permissionManager->TestPermissionFromPrincipal(topDoc->NodePrincipal(),
--- a/dom/base/nsObjectLoadingContent.h
+++ b/dom/base/nsObjectLoadingContent.h
@@ -59,22 +59,26 @@ class nsObjectLoadingContent : public ns
     // nsIObjectLoadingContent
     enum ObjectType {
       // Loading, type not yet known. We may be waiting for a channel to open.
       eType_Loading        = TYPE_LOADING,
       // Content is a *non-svg* image
       eType_Image          = TYPE_IMAGE,
       // Content is a plugin
       eType_Plugin         = TYPE_PLUGIN,
+      // Content is a fake plugin, which loads as a document but behaves as a
+      // plugin (see nsPluginHost::CreateFakePlugin)
+      eType_FakePlugin     = TYPE_FAKE_PLUGIN,
       // Content is a subdocument, possibly SVG
       eType_Document       = TYPE_DOCUMENT,
       // No content loaded (fallback). May be showing alternate content or
       // a custom error handler - *including* click-to-play dialogs
       eType_Null           = TYPE_NULL
     };
+
     enum FallbackType {
       // The content type is not supported (e.g. plugin not installed)
       eFallbackUnsupported = nsIObjectLoadingContent::PLUGIN_UNSUPPORTED,
       // Showing alternate content
       eFallbackAlternate = nsIObjectLoadingContent::PLUGIN_ALTERNATE,
       // The plugin exists, but is disabled
       eFallbackDisabled = nsIObjectLoadingContent::PLUGIN_DISABLED,
       // The plugin is blocklisted and disabled
@@ -187,17 +191,17 @@ class nsObjectLoadingContent : public ns
       CopyUTF8toUTF16(mContentType, aType);
     }
     uint32_t DisplayedType() const
     {
       return mType;
     }
     uint32_t GetContentTypeForMIMEType(const nsAString& aMIMEType)
     {
-      return GetTypeOfContent(NS_ConvertUTF16toUTF8(aMIMEType));
+      return GetTypeOfContent(NS_ConvertUTF16toUTF8(aMIMEType), false);
     }
     void PlayPlugin(mozilla::dom::SystemCallerGuarantee,
                     mozilla::ErrorResult& aRv);
     void Reload(bool aClearActivation, mozilla::ErrorResult& aRv)
     {
       aRv = Reload(aClearActivation);
     }
     bool Activated() const
@@ -218,16 +222,21 @@ class nsObjectLoadingContent : public ns
     uint32_t PluginFallbackType() const
     {
       return mFallbackType;
     }
     bool HasRunningPlugin() const
     {
       return !!mInstanceOwner;
     }
+    // FIXME rename this
+    void SkipFakePlugins(mozilla::ErrorResult& aRv)
+    {
+      aRv = SkipFakePlugins();
+    }
     void SwapFrameLoaders(mozilla::dom::HTMLIFrameElement& aOtherLoaderOwner,
                           mozilla::ErrorResult& aRv)
     {
       aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
     }
     void SwapFrameLoaders(nsXULElement& aOtherLoaderOwner,
                           mozilla::ErrorResult& aRv)
     {
@@ -456,19 +465,20 @@ class nsObjectLoadingContent : public ns
      * If this object should be tested against blocking list.
      */
     bool ShouldBlockContent();
 
     /**
      * If this object is allowed to play plugin content, or if it would display
      * click-to-play instead.
      * NOTE that this does not actually check if the object is a loadable plugin
-     * NOTE This ignores the current activated state. The caller should check this if appropriate.
+     * NOTE This ignores the current activated state. The caller should check
+     *      this if appropriate.
      */
-    bool ShouldPlay(FallbackType &aReason, bool aIgnoreCurrentType);
+    bool ShouldPlay(FallbackType &aReason);
 
     /**
      * This method tells if the fallback content should be attempted to be used
      * over the original object content.
      * It will look at prefs and this plugin's CTP state to make a decision.
      *
      * NOTE that this doesn't say whether the fallback _will_ be used, only whether
      * we should look into it to possibly use it. The final answer will be
@@ -516,16 +526,26 @@ class nsObjectLoadingContent : public ns
     bool CheckProcessPolicy(int16_t *aContentPolicy);
 
     /**
      * Gets the plugin instance and creates a plugin stream listener, assigning
      * it to mFinalListener
      */
     bool MakePluginListener();
 
+    void SetupFrameLoader(int32_t aJSPluginId);
+
+    /**
+     * Helper to spawn mFrameLoader and return a pointer to its docshell
+     *
+     * @param aURI URI we intend to load for the recursive load check (does not
+     *             actually load anything)
+     */
+    already_AddRefed<nsIDocShell> SetupDocShell(nsIURI* aRecursionCheckURI);
+
     /**
      * Unloads all content and resets the object to a completely unloaded state
      *
      * NOTE Calls StopPluginInstance() and may spin the event loop
      *
      * @param aResetState Reset the object type to 'loading' and destroy channel
      *                    as well
      */
@@ -545,20 +565,24 @@ class nsObjectLoadingContent : public ns
                             mozilla::EventStates aOldState,
                             bool aSync, bool aNotify);
 
     /**
      * Returns a ObjectType value corresponding to the type of content we would
      * support the given MIME type as, taking capabilities and plugin state
      * into account
      *
+     * @param aNoFakePlugin Don't select a fake plugin handler as a valid type,
+     *                      as when SkipFakePlugins() is called.
+     * @return The ObjectType enum value that we would attempt to load
+     *
      * NOTE this does not consider whether the content would be suppressed by
      *      click-to-play or other content policy checks
      */
-    ObjectType GetTypeOfContent(const nsCString& aMIMEType);
+    ObjectType GetTypeOfContent(const nsCString& aMIMEType, bool aNoFakePlugin);
 
     /**
      * Gets the frame that's associated with this content node.
      * Does not flush.
      */
     nsPluginFrame* GetExistingFrame();
 
     /**
@@ -682,24 +706,27 @@ class nsObjectLoadingContent : public ns
 
     // Used to keep track of whether or not a plugin has been explicitly
     // activated by PlayPlugin(). (see ShouldPlay())
     bool                        mActivated : 1;
 
     // Whether content blocking is enabled or not for this object.
     bool                        mContentBlockingEnabled : 1;
 
+    // If we should not use fake plugins until the next type change
+    bool                        mSkipFakePlugins : 1;
+
     // Protects DoStopPlugin from reentry (bug 724781).
     bool                        mIsStopping : 1;
 
     // Protects LoadObject from re-entry
     bool                        mIsLoading : 1;
 
-    // For plugin stand-in types (click-to-play) tracks
-    // whether content js has tried to access the plugin script object.
+    // For plugin stand-in types (click-to-play) tracks whether content js has
+    // tried to access the plugin script object.
     bool                        mScriptRequested : 1;
 
     // True if object represents an object/embed tag pointing to a flash embed
     // for a youtube video. When possible (see IsRewritableYoutubeEmbed function
     // comments for details), we change these to try to load HTML5 versions of
     // videos.
     bool                        mRewrittenYoutubeEmbed : 1;
 
deleted file mode 100644
--- a/dom/browser-element/mochitest/priority/test_Activity.html
+++ /dev/null
@@ -1,62 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-Test that calling setVisible("false") on an iframe that has an open activity
-causes its priority to change.
--->
-<head>
-  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-  <script type="application/javascript" src="../browserElementTestHelpers.js"></script>
-  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
-</head>
-<body>
-
-<script type="application/javascript">
-"use strict";
-
-SimpleTest.waitForExplicitFinish();
-browserElementTestHelpers.setEnabledPref(true);
-browserElementTestHelpers.enableProcessPriorityManager();
-
-function runTest() {
-  var os = SpecialPowers.Cc["@mozilla.org/observer-service;1"]
-                        .getService(SpecialPowers.Ci.nsIObserverService);
-  var iframe = document.createElement("iframe");
-  iframe.setAttribute("mozbrowser", true);
-
-  iframe.src = browserElementTestHelpers.emptyPage1;
-
-  var childID = null;
-  Promise.all([
-    expectOnlyOneProcessCreated("FOREGROUND").then(function(chid) {
-      childID = chid;
-    }),
-    expectMozbrowserEvent(iframe, "loadend")
-  ]).then(function() {
-    var p = expectPriorityChange(childID, "BACKGROUND_PERCEIVABLE");
-
-    // We simulate opening an activity
-    os.notifyObservers(null, "activity-opened", childID);
-
-    // We wait until mozbrowserloadend before calling setVisible, because
-    // setVisible isn't available until mozbrowser has loaded.  In practice, that
-    // means we can call setVisible once we've gotten /any/ mozbrowser event.
-    iframe.setVisible(false);
-    return p;
-  }).then(function() {
-    var p = expectPriorityChange(childID, "BACKGROUND");
-
-    // Now we simulate closing an activity
-    os.notifyObservers(null, "activity-closed", childID);
-
-    return p;
-  }).then(SimpleTest.finish);
-
-  document.body.appendChild(iframe);
-}
-
-addEventListener("testready", runTest);
-
-</script>
-</body>
-</html>
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -90,16 +90,17 @@
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/HTMLLabelElement.h"
 
 #include "mozilla/Preferences.h"
 #include "mozilla/LookAndFeel.h"
 #include "GeckoProfiler.h"
 #include "Units.h"
 #include "mozilla/layers/APZCTreeManager.h"
+#include "nsIObjectLoadingContent.h"
 
 #ifdef XP_MACOSX
 #import <ApplicationServices/ApplicationServices.h>
 #endif
 
 namespace mozilla {
 
 using namespace dom;
@@ -1277,35 +1278,19 @@ EventStateManager::DispatchCrossProcessE
   }
   default: {
     MOZ_CRASH("Attempt to send non-whitelisted event?");
   }
   }
 }
 
 bool
-EventStateManager::IsRemoteTarget(nsIContent* target) {
-  if (!target) {
-    return false;
-  }
-
-  // <browser/iframe remote=true> from XUL
-  if (target->IsAnyOfXULElements(nsGkAtoms::browser, nsGkAtoms::iframe) &&
-      target->AttrValueIs(kNameSpaceID_None, nsGkAtoms::Remote,
-                          nsGkAtoms::_true, eIgnoreCase)) {
-    return true;
-  }
-
-  // <frame/iframe mozbrowser>
-  nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(target);
-  if (browserFrame && browserFrame->GetReallyIsBrowser()) {
-    return !!TabParent::GetFrom(target);
-  }
-
-  return false;
+EventStateManager::IsRemoteTarget(nsIContent* target)
+{
+  return !!TabParent::GetFrom(target);
 }
 
 bool
 EventStateManager::HandleCrossProcessEvent(WidgetEvent* aEvent,
                                            nsEventStatus *aStatus) {
   if (*aStatus == nsEventStatus_eConsumeNoDefault ||
       !aEvent->CanBeSentToRemoteProcess()) {
     return false;
--- a/dom/html/HTMLObjectElement.cpp
+++ b/dom/html/HTMLObjectElement.cpp
@@ -340,17 +340,17 @@ bool
 HTMLObjectElement::IsFocusableForTabIndex()
 {
   nsIDocument* doc = GetComposedDoc();
   if (!doc || doc->HasFlag(NODE_IS_EDITABLE)) {
     return false;
   }
 
   return IsEditableRoot() ||
-         (Type() == eType_Document &&
+         ((Type() == eType_Document || Type() == eType_FakePlugin) &&
           nsContentUtils::IsSubDocumentTabbable(this));
 }
 
 bool
 HTMLObjectElement::IsHTMLFocusable(bool aWithMouse,
                                    bool *aIsFocusable, int32_t *aTabIndex)
 {
   // TODO: this should probably be managed directly by IsHTMLFocusable.
@@ -364,17 +364,18 @@ HTMLObjectElement::IsHTMLFocusable(bool 
     *aIsFocusable = false;
 
     return false;
   }
 
   // This method doesn't call nsGenericHTMLFormElement intentionally.
   // TODO: It should probably be changed when bug 597242 will be fixed.
   if (Type() == eType_Plugin || IsEditableRoot() ||
-      (Type() == eType_Document && nsContentUtils::IsSubDocumentTabbable(this))) {
+      ((Type() == eType_Document || Type() == eType_FakePlugin) &&
+       nsContentUtils::IsSubDocumentTabbable(this))) {
     // Has plugin content: let the plugin decide what to do in terms of
     // internal focus from mouse clicks
     if (aTabIndex) {
       GetTabIndex(aTabIndex);
     }
 
     *aIsFocusable = true;
 
@@ -395,17 +396,17 @@ HTMLObjectElement::IsHTMLFocusable(bool 
 }
 
 nsIContent::IMEState
 HTMLObjectElement::GetDesiredIMEState()
 {
   if (Type() == eType_Plugin) {
     return IMEState(IMEState::PLUGIN);
   }
-   
+
   return nsGenericHTMLFormElement::GetDesiredIMEState();
 }
 
 NS_IMETHODIMP
 HTMLObjectElement::Reset()
 {
   return NS_OK;
 }
--- a/dom/ipc/ContentBridgeChild.cpp
+++ b/dom/ipc/ContentBridgeChild.cpp
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/ContentBridgeChild.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/TabChild.h"
+#include "mozilla/dom/TabGroup.h"
 #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
 #include "mozilla/ipc/InputStreamUtils.h"
 #include "base/task.h"
 
 using namespace mozilla::ipc;
 using namespace mozilla::jsipc;
 
 namespace mozilla {
@@ -219,10 +220,27 @@ ContentBridgeChild::RecvDeactivate(PBrow
 
 mozilla::ipc::IPCResult
 ContentBridgeChild::RecvParentActivated(PBrowserChild* aTab, const bool& aActivated)
 {
   TabChild* tab = static_cast<TabChild*>(aTab);
   return tab->RecvParentActivated(aActivated);
 }
 
+already_AddRefed<nsIEventTarget>
+ContentBridgeChild::GetConstructedEventTarget(const Message& aMsg)
+{
+  // Currently we only set targets for PBrowser.
+  if (aMsg.type() != PContentBridge::Msg_PBrowserConstructor__ID) {
+    return nullptr;
+  }
+
+  return nsIContentChild::GetConstructedEventTarget(aMsg);
+}
+
+already_AddRefed<nsIEventTarget>
+ContentBridgeChild::GetEventTargetFor(TabChild* aTabChild)
+{
+  return IToplevelProtocol::GetActorEventTarget(aTabChild);
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/ipc/ContentBridgeChild.h
+++ b/dom/ipc/ContentBridgeChild.h
@@ -50,16 +50,18 @@ public:
 
   virtual mozilla::ipc::IPCResult RecvActivate(PBrowserChild* aTab) override;
 
   virtual mozilla::ipc::IPCResult RecvDeactivate(PBrowserChild* aTab) override;
 
   virtual mozilla::ipc::IPCResult RecvParentActivated(PBrowserChild* aTab,
                                                       const bool& aActivated) override;
 
+  virtual already_AddRefed<nsIEventTarget> GetEventTargetFor(TabChild* aTabChild) override;
+
   FORWARD_SHMEM_ALLOCATOR_TO(PContentBridgeChild)
 
 protected:
   virtual ~ContentBridgeChild();
 
   virtual PBrowserChild* AllocPBrowserChild(const TabId& aTabId,
                                             const TabId& aSameTabGroupAs,
                                             const IPCTabContext& aContext,
@@ -99,16 +101,20 @@ protected:
   virtual PFileDescriptorSetChild*
   AllocPFileDescriptorSetChild(const mozilla::ipc::FileDescriptor& aFD) override;
 
   virtual bool
   DeallocPFileDescriptorSetChild(mozilla::ipc::PFileDescriptorSetChild* aActor) override;
 
   DISALLOW_EVIL_CONSTRUCTORS(ContentBridgeChild);
 
+private:
+  virtual already_AddRefed<nsIEventTarget>
+  GetConstructedEventTarget(const Message& aMsg) override;
+
 protected: // members
   RefPtr<ContentBridgeChild> mSelfRef;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_ContentBridgeChild_h
--- a/dom/ipc/ContentBridgeParent.cpp
+++ b/dom/ipc/ContentBridgeParent.cpp
@@ -17,16 +17,17 @@ using namespace mozilla::jsipc;
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_ISUPPORTS(ContentBridgeParent,
                   nsIContentParent,
                   nsIObserver)
 
 ContentBridgeParent::ContentBridgeParent()
+  : mIsForJSPlugin(false)
 {}
 
 ContentBridgeParent::~ContentBridgeParent()
 {
 }
 
 void
 ContentBridgeParent::ActorDestroy(ActorDestroyReason aWhy)
--- a/dom/ipc/ContentBridgeParent.h
+++ b/dom/ipc/ContentBridgeParent.h
@@ -57,16 +57,21 @@ public:
   {
     return mIsForBrowser;
   }
   virtual int32_t Pid() const override
   {
     // XXX: do we need this for ContentBridgeParent?
     return -1;
   }
+  virtual bool IsForJSPlugin() const override
+  {
+    return mIsForJSPlugin;
+  }
+
 
   virtual mozilla::ipc::PParentToChildStreamParent*
   SendPParentToChildStreamConstructor(mozilla::ipc::PParentToChildStreamParent*) override;
 
   virtual bool SendActivate(PBrowserParent* aTab) override
   {
     return PContentBridgeParent::SendActivate(aTab);
   }
@@ -89,16 +94,20 @@ protected:
   {
     mChildID = aId;
   }
 
   void SetIsForBrowser(bool aIsForBrowser)
   {
     mIsForBrowser = aIsForBrowser;
   }
+  void SetIsForJSPlugin(bool aIsForJSPlugin)
+  {
+    mIsForJSPlugin = aIsForJSPlugin;
+  }
 
   void Close()
   {
     // Trick NewRunnableMethod
     PContentBridgeParent::Close();
   }
 
 protected:
@@ -159,16 +168,17 @@ protected:
   DeallocPFileDescriptorSetParent(PFileDescriptorSetParent*) override;
 
   DISALLOW_EVIL_CONSTRUCTORS(ContentBridgeParent);
 
 protected: // members
   RefPtr<ContentBridgeParent> mSelfRef;
   ContentParentId mChildID;
   bool mIsForBrowser;
+  bool mIsForJSPlugin;
 
 private:
   friend class ContentParent;
 };
 
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -3235,47 +3235,17 @@ ContentChild::DeallocPURLClassifierLocal
 already_AddRefed<nsIEventTarget>
 ContentChild::GetConstructedEventTarget(const Message& aMsg)
 {
   // Currently we only set targets for PBrowser.
   if (aMsg.type() != PContent::Msg_PBrowserConstructor__ID) {
     return nullptr;
   }
 
-  ActorHandle handle;
-  TabId tabId, sameTabGroupAs;
-  PickleIterator iter(aMsg);
-  if (!IPC::ReadParam(&aMsg, &iter, &handle)) {
-    return nullptr;
-  }
-  aMsg.IgnoreSentinel(&iter);
-  if (!IPC::ReadParam(&aMsg, &iter, &tabId)) {
-    return nullptr;
-  }
-  aMsg.IgnoreSentinel(&iter);
-  if (!IPC::ReadParam(&aMsg, &iter, &sameTabGroupAs)) {
-    return nullptr;
-  }
-
-  // If sameTabGroupAs is non-zero, then the new tab will be in the same
-  // TabGroup as a previously created tab. Rather than try to find the
-  // previously created tab (whose constructor message may not even have been
-  // processed yet, in theory) and look up its event target, we just use the
-  // default event target. This means that runnables for this tab will not be
-  // labeled. However, this path is only taken for print preview and view
-  // source, which are not performance-sensitive.
-  if (sameTabGroupAs) {
-    return nullptr;
-  }
-
-  // If the request for a new TabChild is coming from the parent process, then
-  // there is no opener. Therefore, we create a fresh TabGroup.
-  RefPtr<TabGroup> tabGroup = new TabGroup();
-  nsCOMPtr<nsIEventTarget> target = tabGroup->EventTargetFor(TaskCategory::Other);
-  return target.forget();
+  return nsIContentChild::GetConstructedEventTarget(aMsg);
 }
 
 void
 ContentChild::FileCreationRequest(nsID& aUUID, FileCreatorHelper* aHelper,
                                   const nsAString& aFullPath,
                                   const nsAString& aType,
                                   const nsAString& aName,
                                   const Optional<int64_t>& aLastModified,
@@ -3393,10 +3363,16 @@ ContentChild::RecvSetPermissionsWithKey(
 mozilla::ipc::IPCResult
 ContentChild::RecvRefreshScreens(nsTArray<ScreenDetails>&& aScreens)
 {
   ScreenManager& screenManager = ScreenManager::GetSingleton();
   screenManager.Refresh(Move(aScreens));
   return IPC_OK();
 }
 
+already_AddRefed<nsIEventTarget>
+ContentChild::GetEventTargetFor(TabChild* aTabChild)
+{
+  return IToplevelProtocol::GetActorEventTarget(aTabChild);
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -654,16 +654,18 @@ public:
                       const nsAString& aFullPath, const nsAString& aType,
                       const nsAString& aName,
                       const Optional<int64_t>& aLastModified,
                       bool aExistenceCheck, bool aIsFromNsIFile);
 
   typedef std::function<void(PRFileDesc*)> AnonymousTemporaryFileCallback;
   nsresult AsyncOpenAnonymousTemporaryFile(const AnonymousTemporaryFileCallback& aCallback);
 
+  virtual already_AddRefed<nsIEventTarget> GetEventTargetFor(TabChild* aTabChild) override;
+
 private:
   static void ForceKillTimerCallback(nsITimer* aTimer, void* aClosure);
   void StartForceKillTimer();
 
   virtual void ActorDestroy(ActorDestroyReason why) override;
 
   virtual void ProcessingError(Result aCode, const char* aReason) override;
 
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -545,16 +545,17 @@ GetTelemetryProcessID(const nsAString& r
   // For Telemetry though we want to break out collected data from the WebExtensions process into
   // a separate bucket, to make sure we can analyze it separately and avoid skewing normal content
   // process metrics.
   return remoteType.EqualsLiteral(EXTENSION_REMOTE_TYPE) ? ProcessID::Extension : ProcessID::Content;
 }
 
 } // anonymous namespace
 
+nsDataHashtable<nsUint32HashKey, ContentParent*>* ContentParent::sJSPluginContentParents;
 nsTArray<ContentParent*>* ContentParent::sPrivateContent;
 StaticAutoPtr<LinkedList<ContentParent> > ContentParent::sContentParents;
 #if defined(XP_LINUX) && defined(MOZ_CONTENT_SANDBOX)
 UniquePtr<SandboxBrokerPolicyFactory> ContentParent::sSandboxBrokerPolicyFactory;
 #endif
 uint64_t ContentParent::sNextTabParentId = 0;
 nsDataHashtable<nsUint64HashKey, TabParent*> ContentParent::sNextTabParents;
 
@@ -875,16 +876,45 @@ ContentParent::GetNewOrUsedBrowserProces
   }
 
   p->Init();
 
   contentParents.AppendElement(p);
   return p.forget();
 }
 
+/*static*/ already_AddRefed<ContentParent>
+ContentParent::GetNewOrUsedJSPluginProcess(uint32_t aPluginID,
+                                           const hal::ProcessPriority& aPriority)
+{
+  RefPtr<ContentParent> p;
+  if (sJSPluginContentParents) {
+    p = sJSPluginContentParents->Get(aPluginID);
+  } else {
+    sJSPluginContentParents =
+      new nsDataHashtable<nsUint32HashKey, ContentParent*>();
+  }
+
+  if (p) {
+    return p.forget();
+  }
+
+  p = new ContentParent(aPluginID);
+
+  if (!p->LaunchSubprocess(aPriority)) {
+    return nullptr;
+  }
+
+  p->Init();
+
+  sJSPluginContentParents->Put(aPluginID, p);
+
+  return p.forget();
+}
+
 /*static*/ ProcessPriority
 ContentParent::GetInitialProcessPriority(Element* aFrameElement)
 {
   // Frames with mozapptype == critical which are expecting a system message
   // get FOREGROUND_HIGH priority.
 
   if (!aFrameElement) {
     return PROCESS_PRIORITY_FOREGROUND;
@@ -935,67 +965,78 @@ ContentParent::RecvCreateChildProcess(co
   MaybeInvalidTabContext tc(aContext);
   if (!tc.IsValid()) {
     NS_ERROR(nsPrintfCString("Received an invalid TabContext from "
                              "the child process. (%s)",
                              tc.GetInvalidReason()).get());
     return IPC_FAIL_NO_REASON(this);
   }
 
-  cp = GetNewOrUsedBrowserProcess(NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE),
-                                  aPriority, this);
+  if (tc.GetTabContext().IsJSPlugin()) {
+    cp = GetNewOrUsedJSPluginProcess(tc.GetTabContext().JSPluginId(),
+                                     aPriority);
+  }
+  else {
+    cp = GetNewOrUsedBrowserProcess(NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE),
+                                    aPriority, this);
+  }
 
   if (!cp) {
     *aCpId = 0;
     *aIsForBrowser = false;
     return IPC_OK();
   }
 
   *aCpId = cp->ChildID();
   *aIsForBrowser = cp->IsForBrowser();
 
   ContentProcessManager *cpm = ContentProcessManager::GetSingleton();
+  if (cp->IsForJSPlugin()) {
+    // We group all the iframes for a specific JS plugin into one process, regardless of
+    // origin. As a consequence that process can't be a child of the content process that
+    // contains the document with the element loading the plugin. All content processes
+    // need to be able to communicate with the process for the JS plugin.
+    cpm->RegisterRemoteFrame(aTabId, ChildID(), aOpenerTabId, aContext, cp->ChildID());
+    return IPC_OK();
+  }
+
+  // cp was already added to the ContentProcessManager, this just sets the parent ID.
   cpm->AddContentProcess(cp, this->ChildID());
 
   if (cpm->AddGrandchildProcess(this->ChildID(), cp->ChildID()) &&
-      cpm->RegisterRemoteFrame(aTabId, aOpenerTabId, aContext, cp->ChildID())) {
+      cpm->RegisterRemoteFrame(aTabId, ChildID(), aOpenerTabId, aContext, cp->ChildID())) {
     return IPC_OK();
   }
 
   return IPC_FAIL_NO_REASON(this);
 }
 
 mozilla::ipc::IPCResult
 ContentParent::RecvBridgeToChildProcess(const ContentParentId& aCpId,
                                         Endpoint<PContentBridgeParent>* aEndpoint)
 {
   ContentProcessManager *cpm = ContentProcessManager::GetSingleton();
   ContentParent* cp = cpm->GetContentProcessById(aCpId);
 
-  if (cp) {
-    ContentParentId parentId;
-    if (cpm->GetParentProcessId(cp->ChildID(), &parentId) &&
-      parentId == this->ChildID()) {
-
-      Endpoint<PContentBridgeParent> parent;
-      Endpoint<PContentBridgeChild> child;
-
-      if (NS_FAILED(PContentBridge::CreateEndpoints(OtherPid(), cp->OtherPid(),
-                                                    &parent, &child))) {
-        return IPC_FAIL(this, "CreateEndpoints failed");
-      }
-
-      *aEndpoint = Move(parent);
-
-      if (!cp->SendInitContentBridgeChild(Move(child))) {
-        return IPC_FAIL(this, "SendInitContentBridgeChild failed");
-      }
-
-      return IPC_OK();
+  if (cp && cp->CanCommunicateWith(ChildID())) {
+    Endpoint<PContentBridgeParent> parent;
+    Endpoint<PContentBridgeChild> child;
+
+    if (NS_FAILED(PContentBridge::CreateEndpoints(OtherPid(), cp->OtherPid(),
+                                                  &parent, &child))) {
+      return IPC_FAIL(this, "CreateEndpoints failed");
     }
+
+    *aEndpoint = Move(parent);
+
+    if (!cp->SendInitContentBridgeChild(Move(child))) {
+      return IPC_FAIL(this, "SendInitContentBridgeChild failed");
+    }
+
+    return IPC_OK();
   }
 
   // You can't bridge to a process you didn't open!
   KillHard("BridgeToChildProcess");
   return IPC_FAIL_NO_REASON(this);
 }
 
 static nsIDocShell* GetOpenerDocShellHelper(Element* aFrameElement)
@@ -1120,19 +1161,20 @@ ContentParent::RecvGetBlocklistState(con
   }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 ContentParent::RecvFindPlugins(const uint32_t& aPluginEpoch,
                                nsresult* aRv,
                                nsTArray<PluginTag>* aPlugins,
+                               nsTArray<FakePluginTag>* aFakePlugins,
                                uint32_t* aNewPluginEpoch)
 {
-  *aRv = mozilla::plugins::FindPluginsForContent(aPluginEpoch, aPlugins, aNewPluginEpoch);
+  *aRv = mozilla::plugins::FindPluginsForContent(aPluginEpoch, aPlugins, aFakePlugins, aNewPluginEpoch);
   return IPC_OK();
 }
 
 /*static*/ TabParent*
 ContentParent::CreateBrowser(const TabContext& aContext,
                              Element* aFrameElement,
                              ContentParent* aOpenerContentParent,
                              TabParent* aSameTabGroupAs,
@@ -1167,31 +1209,38 @@ ContentParent::CreateBrowser(const TabCo
   nsAutoString remoteType;
   if (!aFrameElement->GetAttr(kNameSpaceID_None, nsGkAtoms::RemoteType,
                               remoteType)) {
     remoteType.AssignLiteral(DEFAULT_REMOTE_TYPE);
   }
 
   RefPtr<nsIContentParent> constructorSender;
   if (isInContentProcess) {
-    MOZ_ASSERT(aContext.IsMozBrowserElement());
+    MOZ_ASSERT(aContext.IsMozBrowserElement() || aContext.IsJSPlugin());
     constructorSender = CreateContentBridgeParent(aContext, initialPriority,
                                                   openerTabId, tabId);
   } else {
     if (aOpenerContentParent) {
       constructorSender = aOpenerContentParent;
     } else {
-      constructorSender =
-        GetNewOrUsedBrowserProcess(remoteType, initialPriority, nullptr);
+      if (aContext.IsJSPlugin()) {
+        constructorSender =
+          GetNewOrUsedJSPluginProcess(aContext.JSPluginId(),
+                                      initialPriority);
+      } else {
+        constructorSender =
+          GetNewOrUsedBrowserProcess(remoteType, initialPriority, nullptr);
+      }
       if (!constructorSender) {
         return nullptr;
       }
     }
     ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
     cpm->RegisterRemoteFrame(tabId,
+                             ContentParentId(0),
                              openerTabId,
                              aContext.AsIPCTabContext(),
                              constructorSender->ChildID());
   }
   if (constructorSender) {
     nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
     docShell->GetTreeOwner(getter_AddRefs(treeOwner));
     if (!treeOwner) {
@@ -1269,16 +1318,17 @@ ContentParent::CreateContentBridgeParent
   }
   Endpoint<PContentBridgeParent> endpoint;
   if (!child->SendBridgeToChildProcess(cpId, &endpoint)) {
     return nullptr;
   }
   ContentBridgeParent* parent = ContentBridgeParent::Create(Move(endpoint));
   parent->SetChildID(cpId);
   parent->SetIsForBrowser(isForBrowser);
+  parent->SetIsForJSPlugin(aContext.IsJSPlugin());
   return parent;
 }
 
 void
 ContentParent::GetAll(nsTArray<ContentParent*>& aArray)
 {
   aArray.Clear();
 
@@ -1471,17 +1521,25 @@ ContentParent::ShutDownMessageManager()
 
   mMessageManager->Disconnect();
   mMessageManager = nullptr;
 }
 
 void
 ContentParent::MarkAsTroubled()
 {
-  if (sBrowserContentParents) {
+  if (IsForJSPlugin()) {
+    if (sJSPluginContentParents) {
+      sJSPluginContentParents->Remove(mJSPluginID);
+      if (!sJSPluginContentParents->Count()) {
+        delete sJSPluginContentParents;
+        sJSPluginContentParents = nullptr;
+      }
+    }
+  } else if (sBrowserContentParents) {
     nsTArray<ContentParent*>* contentParents =
       sBrowserContentParents->Get(mRemoteType);
     if (contentParents) {
       contentParents->RemoveElement(this);
       if (contentParents->IsEmpty()) {
         sBrowserContentParents->Remove(mRemoteType);
         if (sBrowserContentParents->IsEmpty()) {
           delete sBrowserContentParents;
@@ -1585,49 +1643,51 @@ ContentParent::AllocateLayerTreeId(Conte
 mozilla::ipc::IPCResult
 ContentParent::RecvAllocateLayerTreeId(const ContentParentId& aCpId,
                                        const TabId& aTabId, uint64_t* aId)
 {
   // Protect against spoofing by a compromised child. aCpId must either
   // correspond to the process that this ContentParent represents or be a
   // child of it.
   ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
-  if (ChildID() != aCpId) {
-    ContentParentId parent;
-    if (!cpm->GetParentProcessId(aCpId, &parent) ||
-        ChildID() != parent) {
-      return IPC_FAIL_NO_REASON(this);
-    }
+  RefPtr<ContentParent> contentParent = cpm->GetContentProcessById(aCpId);
+  if (ChildID() != aCpId && !contentParent->CanCommunicateWith(ChildID())) {
+    return IPC_FAIL_NO_REASON(this);
   }
 
   // GetTopLevelTabParentByProcessAndTabId will make sure that aTabId
   // lives in the process for aCpId.
-  RefPtr<ContentParent> contentParent = cpm->GetContentProcessById(aCpId);
   RefPtr<TabParent> browserParent =
     cpm->GetTopLevelTabParentByProcessAndTabId(aCpId, aTabId);
   MOZ_ASSERT(contentParent && browserParent);
 
   if (!AllocateLayerTreeId(contentParent, browserParent, aTabId, aId)) {
     return IPC_FAIL_NO_REASON(this);
   }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
-ContentParent::RecvDeallocateLayerTreeId(const uint64_t& aId)
+ContentParent::RecvDeallocateLayerTreeId(const ContentParentId& aCpId,
+                                         const uint64_t& aId)
 {
   GPUProcessManager* gpu = GPUProcessManager::Get();
 
-  if (!gpu->IsLayerTreeIdMapped(aId, OtherPid()))
-  {
+  ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+  RefPtr<ContentParent> contentParent = cpm->GetContentProcessById(aCpId);
+  if (!contentParent->CanCommunicateWith(ChildID())) {
+    return IPC_FAIL(this, "Spoofed DeallocateLayerTreeId call");
+  }
+
+  if (!gpu->IsLayerTreeIdMapped(aId, contentParent->OtherPid())) {
     // You can't deallocate layer tree ids that you didn't allocate
     KillHard("DeallocateLayerTreeId");
   }
 
-  gpu->UnmapLayerTreeId(aId, OtherPid());
+  gpu->UnmapLayerTreeId(aId, contentParent->OtherPid());
 
   return IPC_OK();
 }
 
 namespace {
 
 void
 DelayedDeleteSubprocess(GeckoChildProcessHost* aSubprocess)
@@ -1794,16 +1854,20 @@ ContentParent::ActorDestroy(ActorDestroy
 #if defined(XP_WIN32) && defined(ACCESSIBILITY)
   a11y::AccessibleWrap::ReleaseContentProcessIdFor(ChildID());
 #endif
 }
 
 bool
 ContentParent::ShouldKeepProcessAlive() const
 {
+  if (IsForJSPlugin()) {
+    return true;
+  }
+
   if (!sBrowserContentParents) {
     return false;
   }
 
   // If we have already been marked as troubled/dead, don't prevent shutdown.
   if (!IsAvailable()) {
     return false;
   }
@@ -2026,24 +2090,26 @@ ContentParent::LaunchSubprocess(ProcessP
     cpId.AppendInt(static_cast<uint64_t>(this->ChildID()));
     obs->NotifyObservers(static_cast<nsIObserver*>(this), "ipc:content-initializing", cpId.get());
   }
 
   return true;
 }
 
 ContentParent::ContentParent(ContentParent* aOpener,
-                             const nsAString& aRemoteType)
+                             const nsAString& aRemoteType,
+                             int32_t aJSPluginID)
   : nsIContentParent()
   , mSubprocess(nullptr)
   , mLaunchTS(TimeStamp::Now())
   , mOpener(aOpener)
   , mRemoteType(aRemoteType)
   , mChildID(gContentChildID++)
   , mGeolocationWatchID(-1)
+  , mJSPluginID(aJSPluginID)
   , mNumDestroyingTabs(0)
   , mIsAvailable(true)
   , mIsAlive(true)
   , mIsForBrowser(!mRemoteType.IsEmpty())
   , mCalledClose(false)
   , mCalledKillHard(false)
   , mCreatedPairedMinidumps(false)
   , mShutdownPending(false)
@@ -2079,19 +2145,24 @@ ContentParent::~ContentParent()
   if (mForceKillTimer) {
     mForceKillTimer->Cancel();
   }
 
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   // We should be removed from all these lists in ActorDestroy.
   MOZ_ASSERT(!sPrivateContent || !sPrivateContent->Contains(this));
-  MOZ_ASSERT(!sBrowserContentParents ||
-             !sBrowserContentParents->Contains(mRemoteType) ||
-             !sBrowserContentParents->Get(mRemoteType)->Contains(this));
+  if (IsForJSPlugin()) {
+    MOZ_ASSERT(!sJSPluginContentParents ||
+               !sJSPluginContentParents->Get(mJSPluginID));
+  } else {
+    MOZ_ASSERT(!sBrowserContentParents ||
+               !sBrowserContentParents->Contains(mRemoteType) ||
+               !sBrowserContentParents->Get(mRemoteType)->Contains(this));
+  }
 }
 
 void
 ContentParent::InitInternal(ProcessPriority aInitialPriority,
                             bool aSetupOffMainThreadCompositing,
                             bool aSendRegisteredChrome)
 {
   Telemetry::Accumulate(Telemetry::CONTENT_PROCESS_LAUNCH_TIME_MS,
@@ -5205,8 +5276,24 @@ ContentParent::RecvFileCreationRequest(c
   }
 
   if (!SendFileCreationResponse(aID, FileCreationSuccessResult(ipcBlob))) {
     return IPC_FAIL_NO_REASON(this);
   }
 
   return IPC_OK();
 }
+
+bool
+ContentParent::CanCommunicateWith(ContentParentId aOtherProcess)
+{
+  // Normally a process can only communicate with its parent, but a JS plugin process can
+  // communicate with any process.
+  ContentProcessManager *cpm = ContentProcessManager::GetSingleton();
+  ContentParentId parentId;
+  if (!cpm->GetParentProcessId(ChildID(), &parentId)) {
+    return false;
+  }
+  if (IsForJSPlugin()) {
+    return parentId == ContentParentId(0);
+  }
+  return parentId == aOtherProcess;
+}
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -17,16 +17,17 @@
 #include "mozilla/HalTypes.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/MemoryReportingProcess.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/UniquePtr.h"
 
 #include "nsDataHashtable.h"
+#include "nsPluginTags.h"
 #include "nsFrameMessageManager.h"
 #include "nsHashKeys.h"
 #include "nsIObserver.h"
 #include "nsIThreadInternal.h"
 #include "nsIDOMGeoPositionCallback.h"
 #include "nsIDOMGeoPositionErrorCallback.h"
 #include "nsRefPtrHashtable.h"
 #include "PermissionMessageUtils.h"
@@ -173,16 +174,24 @@ public:
    */
   static already_AddRefed<ContentParent>
   GetNewOrUsedBrowserProcess(const nsAString& aRemoteType = NS_LITERAL_STRING(NO_REMOTE_TYPE),
                              hal::ProcessPriority aPriority =
                              hal::ProcessPriority::PROCESS_PRIORITY_FOREGROUND,
                              ContentParent* aOpener = nullptr);
 
   /**
+   * Get or create a content process for a JS plugin. aPluginID is the id of the JS plugin
+   * (@see nsFakePlugin::mId). There is a maximum of one process per JS plugin.
+   */
+  static already_AddRefed<ContentParent>
+  GetNewOrUsedJSPluginProcess(uint32_t aPluginID,
+                              const hal::ProcessPriority& aPriority);
+
+  /**
    * Get or create a content process for the given TabContext.  aFrameElement
    * should be the frame/iframe element with which this process will
    * associated.
    */
   static TabParent*
   CreateBrowser(const TabContext& aContext,
                 Element* aFrameElement,
                 ContentParent* aOpenerContentParent,
@@ -294,16 +303,17 @@ public:
                                                           Endpoint<PPluginModuleParent>* aEndpoint) override;
 
   virtual mozilla::ipc::IPCResult RecvGetBlocklistState(const uint32_t& aPluginId,
                                                         uint32_t* aIsBlocklisted) override;
 
   virtual mozilla::ipc::IPCResult RecvFindPlugins(const uint32_t& aPluginEpoch,
                                                   nsresult* aRv,
                                                   nsTArray<PluginTag>* aPlugins,
+                                                  nsTArray<FakePluginTag>* aFakePlugins,
                                                   uint32_t* aNewPluginEpoch) override;
 
   virtual mozilla::ipc::IPCResult RecvUngrabPointer(const uint32_t& aTime) override;
 
   virtual mozilla::ipc::IPCResult RecvRemovePermission(const IPC::Principal& aPrincipal,
                                                        const nsCString& aPermissionType,
                                                        nsresult* aRv) override;
 
@@ -356,16 +366,20 @@ public:
     return mIsAvailable;
   }
   bool IsAlive() const override;
 
   virtual bool IsForBrowser() const override
   {
     return mIsForBrowser;
   }
+  virtual bool IsForJSPlugin() const override
+  {
+    return mJSPluginID != nsFakePluginTag::NOT_JSPLUGIN;
+  }
 
   GeckoChildProcessHost* Process() const
   {
     return mSubprocess;
   }
 
   ContentParent* Opener() const
   {
@@ -662,16 +676,17 @@ private:
    * currently available to host *new* tabs/frames of that type.
    *
    * If a content process is identified as troubled or dead, it will be
    * removed from this list, but will still be in the sContentParents list for
    * the GetAll/GetAllEvenIfDead APIs.
    */
   static nsClassHashtable<nsStringHashKey, nsTArray<ContentParent*>>* sBrowserContentParents;
   static nsTArray<ContentParent*>* sPrivateContent;
+  static nsDataHashtable<nsUint32HashKey, ContentParent*> *sJSPluginContentParents;
   static StaticAutoPtr<LinkedList<ContentParent> > sContentParents;
 
   static void JoinProcessesIOThread(const nsTArray<ContentParent*>* aProcesses,
                                     Monitor* aMonitor, bool* aDone);
 
   static hal::ProcessPriority GetInitialProcessPriority(Element* aFrameElement);
 
   static ContentBridgeParent* CreateContentBridgeParent(const TabContext& aContext,
@@ -705,18 +720,27 @@ private:
                      const float& aFullZoom,
                      uint64_t aNextTabParentId,
                      nsresult& aResult,
                      nsCOMPtr<nsITabParent>& aNewTabParent,
                      bool* aWindowIsNew);
 
   FORWARD_SHMEM_ALLOCATOR_TO(PContentParent)
 
+  explicit ContentParent(int32_t aPluginID)
+    : ContentParent(nullptr, EmptyString(), aPluginID)
+  {}
   ContentParent(ContentParent* aOpener,
-                const nsAString& aRemoteType);
+                const nsAString& aRemoteType)
+    : ContentParent(aOpener, aRemoteType, nsFakePluginTag::NOT_JSPLUGIN)
+  {}
+
+  ContentParent(ContentParent* aOpener,
+                const nsAString& aRemoteType,
+                int32_t aPluginID);
 
   // Launch the subprocess and associated initialization.
   // Returns false if the process fails to start.
   bool LaunchSubprocess(hal::ProcessPriority aInitialPriority = hal::PROCESS_PRIORITY_FOREGROUND);
 
   // Common initialization after sub process launch or adoption.
   void InitInternal(ProcessPriority aPriority,
                     bool aSetupOffMainThreadCompositing,
@@ -1044,17 +1068,18 @@ private:
                                                   const bool& aInPrivateBrowsing) override;
 
   virtual void ProcessingError(Result aCode, const char* aMsgName) override;
 
   virtual mozilla::ipc::IPCResult RecvAllocateLayerTreeId(const ContentParentId& aCpId,
                                                           const TabId& aTabId,
                                                           uint64_t* aId) override;
 
-  virtual mozilla::ipc::IPCResult RecvDeallocateLayerTreeId(const uint64_t& aId) override;
+  virtual mozilla::ipc::IPCResult RecvDeallocateLayerTreeId(const ContentParentId& aCpId,
+                                                            const uint64_t& aId) override;
 
   virtual mozilla::ipc::IPCResult RecvGraphicsError(const nsCString& aError) override;
 
   virtual mozilla::ipc::IPCResult
   RecvBeginDriverCrashGuard(const uint32_t& aGuardType,
                             bool* aOutCrashed) override;
 
   virtual mozilla::ipc::IPCResult RecvEndDriverCrashGuard(const uint32_t& aGuardType) override;
@@ -1147,31 +1172,39 @@ public:
   void SendGetFilesResponseAndForget(const nsID& aID,
                                      const GetFilesResponseResult& aResult);
 
   bool SendRequestMemoryReport(const uint32_t& aGeneration,
                                const bool& aAnonymize,
                                const bool& aMinimizeMemoryUsage,
                                const MaybeFileDesc& aDMDFile) override;
 
+  bool CanCommunicateWith(ContentParentId aOtherProcess);
+
 private:
 
   // If you add strong pointers to cycle collected objects here, be sure to
   // release these objects in ShutDownProcess.  See the comment there for more
   // details.
 
   GeckoChildProcessHost* mSubprocess;
   const TimeStamp mLaunchTS; // used to calculate time to start content process
   ContentParent* mOpener;
 
   nsString mRemoteType;
 
   ContentParentId mChildID;
   int32_t mGeolocationWatchID;
 
+  // This contains the id for the JS plugin (@see nsFakePluginTag) if this is the
+  // ContentParent for a process containing iframes for that JS plugin.
+  // If this is not a ContentParent for a JS plugin then it contains the value
+  // nsFakePluginTag::NOT_JSPLUGIN.
+  int32_t mJSPluginID;
+
   nsCString mKillHardAnnotation;
 
   // After we initiate shutdown, we also start a timer to ensure
   // that even content processes that are 100% blocked (say from
   // SIGSTOP), are still killed eventually.  This task enforces that
   // timer.
   nsCOMPtr<nsITimer> mForceKillTimer;
   // How many tabs we're waiting to finish their destruction
--- a/dom/ipc/ContentProcessManager.cpp
+++ b/dom/ipc/ContentProcessManager.cpp
@@ -42,20 +42,24 @@ ContentProcessManager::GetSingleton()
 
 void
 ContentProcessManager::AddContentProcess(ContentParent* aChildCp,
                                          const ContentParentId& aParentCpId)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aChildCp);
 
-  ContentProcessInfo info;
-  info.mCp = aChildCp;
+  ContentProcessInfo& info = mContentParentMap[aChildCp->ChildID()];
+  if (!info.mCp) {
+    info.mCp = aChildCp;
+  } else {
+    MOZ_ASSERT(info.mCp == aChildCp);
+    MOZ_ASSERT_IF(!!info.mParentCpId, info.mParentCpId == aParentCpId);
+  }
   info.mParentCpId = aParentCpId;
-  mContentParentMap[aChildCp->ChildID()] = info;
 }
 
 void
 ContentProcessManager::RemoveContentProcess(const ContentParentId& aChildCpId)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mContentParentMap.find(aChildCpId) != mContentParentMap.end());
 
@@ -130,16 +134,17 @@ ContentProcessManager::GetAllChildProces
     cpIdArray.AppendElement(*cpIter);
   }
 
   return Move(cpIdArray);
 }
 
 bool
 ContentProcessManager::RegisterRemoteFrame(const TabId& aTabId,
+                                           const ContentParentId& aOpenerCpId,
                                            const TabId& aOpenerTabId,
                                            const IPCTabContext& aContext,
                                            const ContentParentId& aChildCpId)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   auto iter = mContentParentMap.find(aChildCpId);
   if (NS_WARN_IF(iter == mContentParentMap.end())) {
@@ -153,27 +158,29 @@ ContentProcessManager::RegisterRemoteFra
   // open a new tab. aOpenerTabId has to be it's parent frame's opener id.
   if (aContext.type() == IPCTabContext::TPopupIPCTabContext) {
     auto remoteFrameIter = iter->second.mRemoteFrames.find(aOpenerTabId);
     if (remoteFrameIter == iter->second.mRemoteFrames.end()) {
       ASSERT_UNLESS_FUZZING("Failed to find parent frame's opener id.");
       return false;
     }
 
+    info.mOpenerCpId = remoteFrameIter->second.mOpenerCpId;
     info.mOpenerTabId = remoteFrameIter->second.mOpenerTabId;
     info.mContext = remoteFrameIter->second.mContext;
   }
   else {
     MaybeInvalidTabContext tc(aContext);
     if (!tc.IsValid()) {
       NS_ERROR(nsPrintfCString("Received an invalid TabContext from "
                                "the child process. (%s)",
                                tc.GetInvalidReason()).get());
       return false;
     }
+    info.mOpenerCpId = aOpenerCpId;
     info.mOpenerTabId = aOpenerTabId;
     info.mContext = tc.GetTabContext();
   }
 
   iter->second.mRemoteFrames[aTabId] = info;
   return true;
 }
 
@@ -239,31 +246,33 @@ ContentProcessManager::GetTabContextByCo
   }
 
   return Move(tabContextArray);
 }
 
 bool
 ContentProcessManager::GetRemoteFrameOpenerTabId(const ContentParentId& aChildCpId,
                                                  const TabId& aChildTabId,
+                                                 /*out*/ContentParentId* aOpenerCpId,
                                                  /*out*/TabId* aOpenerTabId)
 {
   MOZ_ASSERT(NS_IsMainThread());
   auto iter = mContentParentMap.find(aChildCpId);
   if (NS_WARN_IF(iter == mContentParentMap.end())) {
     ASSERT_UNLESS_FUZZING();
     return false;
   }
 
   auto remoteFrameIter = iter->second.mRemoteFrames.find(aChildTabId);
   if (NS_WARN_IF(remoteFrameIter == iter->second.mRemoteFrames.end())) {
     ASSERT_UNLESS_FUZZING();
     return false;
   }
 
+  *aOpenerCpId = remoteFrameIter->second.mOpenerCpId;
   *aOpenerTabId = remoteFrameIter->second.mOpenerTabId;
 
   return true;
 }
 
 already_AddRefed<TabParent>
 ContentProcessManager::GetTabParentByProcessAndTabId(const ContentParentId& aChildCpId,
                                                      const TabId& aChildTabId)
@@ -305,18 +314,17 @@ ContentProcessManager::GetTopLevelTabPar
   // Stop this loop when the upper ContentParentId of
   // the current ContentParentId is chrome(ContentParentId = 0).
   do {
     // Update the current ContentParentId and TabId in iteration
     currentCpId = parentCpId;
     currentTabId = openerTabId;
 
     // Get the ContentParentId and TabId on upper level
-    if (!GetParentProcessId(currentCpId, &parentCpId) ||
-        !GetRemoteFrameOpenerTabId(currentCpId, currentTabId, &openerTabId)) {
+    if (!GetRemoteFrameOpenerTabId(currentCpId, currentTabId, &parentCpId, &openerTabId)) {
       return nullptr;
     }
   } while (parentCpId);
 
   // Get the top level TabParent by the current ContentParentId and TabId
   return GetTabParentByProcessAndTabId(currentCpId, currentTabId);
 }
 
--- a/dom/ipc/ContentProcessManager.h
+++ b/dom/ipc/ContentProcessManager.h
@@ -15,16 +15,17 @@
 #include "nsTArray.h"
 
 namespace mozilla {
 namespace dom {
 class ContentParent;
 
 struct RemoteFrameInfo
 {
+  ContentParentId mOpenerCpId;
   TabId mOpenerTabId;
   TabContext mContext;
 };
 
 struct ContentProcessInfo
 {
   ContentParent* mCp;
   ContentParentId mParentCpId;
@@ -74,16 +75,17 @@ public:
   /**
    * Register RemoteFrameInfo with given tab id.
    * Used when a content process wants to create a new tab. aOpenerTabId and
    * aContext are saved in RemoteFrameInfo, which is a part of
    * ContentProcessInfo.  We can use the tab id and process id to locate the
    * TabContext for future use.
    */
   bool RegisterRemoteFrame(const TabId& aTabId,
+                           const ContentParentId& aOpenerCpId,
                            const TabId& aOpenerTabId,
                            const IPCTabContext& aContext,
                            const ContentParentId& aChildCpId);
 
 
   /**
    * Remove the RemoteFrameInfo by the given process and tab id.
    */
@@ -105,16 +107,17 @@ public:
   GetTabContextByContentProcess(const ContentParentId& aChildCpId);
 
   /**
    * Query a tab's opener id by the given process and tab id.
    * XXX Currently not used. Plan to be used for bug 1020179.
    */
   bool GetRemoteFrameOpenerTabId(const ContentParentId& aChildCpId,
                                  const TabId& aChildTabId,
+                                 /*out*/ContentParentId* aOpenerCpId,
                                  /*out*/ TabId* aOpenerTabId);
 
   /**
    * Get all TabParents' Ids managed by the givent content process.
    * Return empty array when TabParent couldn't be found via aChildCpId
    */
   nsTArray<TabId>
   GetTabParentsByProcessId(const ContentParentId& aChildCpId);
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -653,17 +653,17 @@ parent:
      * epoch number every time the set of plugins changes. The content process
      * sends up the last epoch it observed. If the epochs are the same, the
      * chrome process returns no plugins. Otherwise it returns a complete list.
      *
      * |pluginEpoch| is the epoch last observed by the content
      * process. |newPluginEpoch| is the current epoch in the chrome process. If
      * |pluginEpoch == newPluginEpoch|, then |plugins| will be left empty.
      */
-    sync FindPlugins(uint32_t pluginEpoch) returns (nsresult aResult, PluginTag[] plugins, uint32_t newPluginEpoch);
+    sync FindPlugins(uint32_t pluginEpoch) returns (nsresult aResult, PluginTag[] plugins, FakePluginTag[] fakePlugins, uint32_t newPluginEpoch);
 
     async PJavaScript();
 
     async PRemoteSpellcheckEngine();
 
     async InitCrashReporter(Shmem shmem, NativeThreadId tid);
 
     /**
@@ -841,17 +841,17 @@ parent:
 
     sync NotifyKeywordSearchLoading(nsString providerName, nsString keyword);
 
     async CopyFavicon(URIParams oldURI, URIParams newURI, Principal aLoadingPrincipal, bool isPrivate);
 
     // Tell the compositor to allocate a layer tree id for nested remote mozbrowsers.
     sync AllocateLayerTreeId(ContentParentId cpId, TabId tabId)
         returns (uint64_t id);
-    async DeallocateLayerTreeId(uint64_t id);
+    async DeallocateLayerTreeId(ContentParentId cpId, uint64_t id);
 
     /**
      * Notifies the parent about a recording device is starting or shutdown.
      * @param recordingStatus starting or shutdown
      * @param pageURL URL that request that changing the recording status
      * @param isAudio recording start with microphone
      * @param isVideo recording start with camera
      */
--- a/dom/ipc/PTabContext.ipdlh
+++ b/dom/ipc/PTabContext.ipdlh
@@ -51,16 +51,21 @@ struct FrameIPCTabContext
   // presented content.
   nsString presentationURL;
 
   // Keyboard indicator state inherited from the parent.
   UIStateChangeType showAccelerators;
   UIStateChangeType showFocusRings;
 };
 
+struct JSPluginFrameIPCTabContext
+{
+  uint32_t jsPluginId;
+};
+
 // XXXcatalinb: This is only used by ServiceWorkerClients::OpenWindow.
 // Because service workers don't have an associated TabChild
 // we can't satisfy the security constraints on b2g. As such, the parent
 // process will accept this tab context only on desktop.
 struct UnsafeIPCTabContext
 { };
 
 // IPCTabContext is an analog to mozilla::dom::TabContext.  Both specify an
@@ -69,13 +74,14 @@ struct UnsafeIPCTabContext
 // travel over IPC.
 //
 // We need IPCTabContext (specifically, PopupIPCTabContext) to prevent a
 // privilege escalation attack by a compromised child process.
 union IPCTabContext
 {
   PopupIPCTabContext;
   FrameIPCTabContext;
+  JSPluginFrameIPCTabContext;
   UnsafeIPCTabContext;
 };
 
 }
 }
--- a/dom/ipc/ProcessPriorityManager.cpp
+++ b/dom/ipc/ProcessPriorityManager.cpp
@@ -248,18 +248,16 @@ public:
    * The reference returned here is guaranteed to be live until the next call
    * to NameWithComma() or until the ParticularProcessPriorityManager is
    * destroyed, whichever comes first.
    */
   const nsAutoCString& NameWithComma();
 
   void OnRemoteBrowserFrameShown(nsISupports* aSubject);
   void OnTabParentDestroyed(nsISupports* aSubject);
-  void OnActivityOpened(const char16_t* aData);
-  void OnActivityClosed(const char16_t* aData);
 
   ProcessPriority CurrentPriority();
   ProcessPriority ComputePriority();
 
   enum TimeoutPref {
     BACKGROUND_PERCEIVABLE_GRACE_PERIOD,
     BACKGROUND_GRACE_PERIOD,
   };
@@ -283,17 +281,16 @@ private:
     const char* aTopic,
     const char* aData = nullptr);
 
   ContentParent* mContentParent;
   uint64_t mChildID;
   ProcessPriority mPriority;
   bool mHoldsCPUWakeLock;
   bool mHoldsHighPriorityWakeLock;
-  bool mIsActivityOpener;
 
   /**
    * Used to implement NameWithComma().
    */
   nsAutoCString mNameWithComma;
 
   nsCOMPtr<nsITimer> mResetPriorityTimer;
 };
@@ -548,17 +545,16 @@ NS_IMPL_ISUPPORTS(ParticularProcessPrior
 
 ParticularProcessPriorityManager::ParticularProcessPriorityManager(
   ContentParent* aContentParent)
   : mContentParent(aContentParent)
   , mChildID(aContentParent->ChildID())
   , mPriority(PROCESS_PRIORITY_UNKNOWN)
   , mHoldsCPUWakeLock(false)
   , mHoldsHighPriorityWakeLock(false)
-  , mIsActivityOpener(false)
 {
   MOZ_ASSERT(XRE_IsParentProcess());
   LOGP("Creating ParticularProcessPriorityManager.");
 }
 
 void
 ParticularProcessPriorityManager::StaticInit()
 {
@@ -572,18 +568,16 @@ void
 ParticularProcessPriorityManager::Init()
 {
   RegisterWakeLockObserver(this);
 
   nsCOMPtr<nsIObserverService> os = services::GetObserverService();
   if (os) {
     os->AddObserver(this, "remote-browser-shown", /* ownsWeak */ true);
     os->AddObserver(this, "ipc:browser-destroyed", /* ownsWeak */ true);
-    os->AddObserver(this, "activity-opened", /* ownsWeak */ true);
-    os->AddObserver(this, "activity-closed", /* ownsWeak */ true);
   }
 
   // This process may already hold the CPU lock; for example, our parent may
   // have acquired it on our behalf.
   WakeLockInformation info1, info2;
   GetWakeLockInfo(NS_LITERAL_STRING("cpu"), &info1);
   mHoldsCPUWakeLock = info1.lockingProcesses().Contains(ChildID());
 
@@ -645,20 +639,16 @@ ParticularProcessPriorityManager::Observ
   }
 
   nsDependentCString topic(aTopic);
 
   if (topic.EqualsLiteral("remote-browser-shown")) {
     OnRemoteBrowserFrameShown(aSubject);
   } else if (topic.EqualsLiteral("ipc:browser-destroyed")) {
     OnTabParentDestroyed(aSubject);
-  } else if (topic.EqualsLiteral("activity-opened")) {
-    OnActivityOpened(aData);
-  } else if (topic.EqualsLiteral("activity-closed")) {
-    OnActivityClosed(aData);
   } else {
     MOZ_ASSERT(false);
   }
 
   return NS_OK;
 }
 
 uint64_t
@@ -733,40 +723,16 @@ ParticularProcessPriorityManager::OnTabP
   if (TabParent::GetFrom(tp)->Manager() != mContentParent) {
     return;
   }
 
   ResetPriority();
 }
 
 void
-ParticularProcessPriorityManager::OnActivityOpened(const char16_t* aData)
-{
-  uint64_t childID = nsCRT::atoll(NS_ConvertUTF16toUTF8(aData).get());
-
-  if (ChildID() == childID) {
-    LOGP("Marking as activity opener");
-    mIsActivityOpener = true;
-    ResetPriority();
-  }
-}
-
-void
-ParticularProcessPriorityManager::OnActivityClosed(const char16_t* aData)
-{
-  uint64_t childID = nsCRT::atoll(NS_ConvertUTF16toUTF8(aData).get());
-
-  if (ChildID() == childID) {
-    LOGP("Unmarking as activity opener");
-    mIsActivityOpener = false;
-    ResetPriority();
-  }
-}
-
-void
 ParticularProcessPriorityManager::ResetPriority()
 {
   ProcessPriority processPriority = ComputePriority();
   if (mPriority == PROCESS_PRIORITY_UNKNOWN ||
       mPriority > processPriority) {
     // Apps set at a perceivable background priority are often playing media.
     // Most media will have short gaps while changing tracks between songs,
     // switching videos, etc.  Give these apps a longer grace period so they
@@ -835,18 +801,17 @@ ParticularProcessPriorityManager::Comput
 {
   // TODO...
   return PROCESS_PRIORITY_FOREGROUND;
 
   if (mHoldsCPUWakeLock || mHoldsHighPriorityWakeLock) {
     return PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE;
   }
 
-  return mIsActivityOpener ? PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE
-                           : PROCESS_PRIORITY_BACKGROUND;
+  return PROCESS_PRIORITY_BACKGROUND;
 }
 
 void
 ParticularProcessPriorityManager::SetPriorityNow(ProcessPriority aPriority)
 {
   if (aPriority == PROCESS_PRIORITY_UNKNOWN) {
     MOZ_ASSERT(false);
     return;
--- a/dom/ipc/TabContext.cpp
+++ b/dom/ipc/TabContext.cpp
@@ -18,17 +18,17 @@ using namespace mozilla::layout;
 
 namespace mozilla {
 namespace dom {
 
 TabContext::TabContext()
   : mIsPrerendered(false)
   , mInitialized(false)
   , mIsMozBrowserElement(false)
-  , mOriginAttributes()
+  , mJSPluginID(-1)
   , mShowAccelerators(UIStateChangeType_NoChange)
   , mShowFocusRings(UIStateChangeType_NoChange)
 {
 }
 
 bool
 TabContext::IsMozBrowserElement() const
 {
@@ -43,16 +43,28 @@ TabContext::IsIsolatedMozBrowserElement(
 
 bool
 TabContext::IsMozBrowser() const
 {
   return IsMozBrowserElement();
 }
 
 bool
+TabContext::IsJSPlugin() const
+{
+  return mJSPluginID >= 0;
+}
+
+int32_t
+TabContext::JSPluginId() const
+{
+  return mJSPluginID;
+}
+
+bool
 TabContext::SetTabContext(const TabContext& aContext)
 {
   NS_ENSURE_FALSE(mInitialized, false);
 
   *this = aContext;
   mInitialized = true;
 
   return true;
@@ -122,32 +134,47 @@ TabContext::SetTabContext(bool aIsMozBro
   mIsPrerendered = aIsPrerendered;
   mOriginAttributes = aOriginAttributes;
   mPresentationURL = aPresentationURL;
   mShowAccelerators = aShowAccelerators;
   mShowFocusRings = aShowFocusRings;
   return true;
 }
 
+bool
+TabContext::SetTabContextForJSPluginFrame(int32_t aJSPluginID)
+{
+  NS_ENSURE_FALSE(mInitialized, false);
+
+  mInitialized = true;
+  mJSPluginID = aJSPluginID;
+  return true;
+}
+
 IPCTabContext
 TabContext::AsIPCTabContext() const
 {
+  if (IsJSPlugin()) {
+    return IPCTabContext(JSPluginFrameIPCTabContext(mJSPluginID));
+  }
+
   return IPCTabContext(FrameIPCTabContext(mOriginAttributes,
                                           mIsMozBrowserElement,
                                           mIsPrerendered,
                                           mPresentationURL,
                                           mShowAccelerators,
                                           mShowFocusRings));
 }
 
 MaybeInvalidTabContext::MaybeInvalidTabContext(const IPCTabContext& aParams)
   : mInvalidReason(nullptr)
 {
   bool isMozBrowserElement = false;
   bool isPrerendered = false;
+  int32_t jsPluginId = -1;
   OriginAttributes originAttributes;
   nsAutoString presentationURL;
   UIStateChangeType showAccelerators = UIStateChangeType_NoChange;
   UIStateChangeType showFocusRings = UIStateChangeType_NoChange;
 
   switch(aParams.type()) {
     case IPCTabContext::TPopupIPCTabContext: {
       const PopupIPCTabContext &ipcContext = aParams.get_PopupIPCTabContext();
@@ -190,16 +217,23 @@ MaybeInvalidTabContext::MaybeInvalidTabC
       // (if any).
       //
       // Otherwise, we're a new app window and we inherit from our
       // opener app.
       isMozBrowserElement = ipcContext.isMozBrowserElement();
       originAttributes = context->mOriginAttributes;
       break;
     }
+    case IPCTabContext::TJSPluginFrameIPCTabContext: {
+      const JSPluginFrameIPCTabContext &ipcContext =
+        aParams.get_JSPluginFrameIPCTabContext();
+
+      jsPluginId = ipcContext.jsPluginId();
+      break;
+    }
     case IPCTabContext::TFrameIPCTabContext: {
       const FrameIPCTabContext &ipcContext =
         aParams.get_FrameIPCTabContext();
 
       isMozBrowserElement = ipcContext.isMozBrowserElement();
       isPrerendered = ipcContext.isPrerendered();
       presentationURL = ipcContext.presentationURL();
       showAccelerators = ipcContext.showAccelerators();
@@ -224,22 +258,26 @@ MaybeInvalidTabContext::MaybeInvalidTabC
       break;
     }
     default: {
       MOZ_CRASH();
     }
   }
 
   bool rv;
-  rv = mTabContext.SetTabContext(isMozBrowserElement,
-                                 isPrerendered,
-                                 showAccelerators,
-                                 showFocusRings,
-                                 originAttributes,
-                                 presentationURL);
+  if (jsPluginId >= 0) {
+    rv = mTabContext.SetTabContextForJSPluginFrame(jsPluginId);
+  } else {
+    rv = mTabContext.SetTabContext(isMozBrowserElement,
+                                   isPrerendered,
+                                   showAccelerators,
+                                   showFocusRings,
+                                   originAttributes,
+                                   presentationURL);
+  }
   if (!rv) {
     mInvalidReason = "Couldn't initialize TabContext.";
   }
 }
 
 bool
 MaybeInvalidTabContext::IsValid()
 {
--- a/dom/ipc/TabContext.h
+++ b/dom/ipc/TabContext.h
@@ -58,16 +58,19 @@ public:
 
   /**
    * Does this TabContext correspond to a mozbrowser?  This is equivalent to
    * IsMozBrowserElement().  Returns false for <xul:browser>, which isn't a
    * mozbrowser.
    */
   bool IsMozBrowser() const;
 
+  bool IsJSPlugin() const;
+  int32_t JSPluginId() const;
+
   /**
    * OriginAttributesRef() returns the OriginAttributes of this frame to
    * the caller. This is used to store any attribute associated with the frame's
    * docshell.
    */
   const OriginAttributes& OriginAttributesRef() const;
 
   /**
@@ -119,30 +122,40 @@ protected:
    */
   bool UpdateTabContextAfterSwap(const TabContext& aContext);
 
   /**
    * Whether this TabContext is in prerender mode.
    */
   bool mIsPrerendered;
 
+  /**
+   * Set this TabContext to be for a JS plugin. aPluginID is the id of the JS plugin
+   * (@see nsFakePlugin::mId).
+   * As with the other protected mutator methods, this lets you modify a TabContext once.
+   * (@see TabContext::SetTabContext above for more details).
+   */
+  bool SetTabContextForJSPluginFrame(int32_t aJSPluginID);
+
 private:
   /**
    * Has this TabContext been initialized?  If so, mutator methods will fail.
    */
   bool mInitialized;
 
   /**
    * Whether this TabContext corresponds to a mozbrowser.
    *
    * <iframe mozbrowser> and <xul:browser> are not considered to be
    * mozbrowser elements.
    */
   bool mIsMozBrowserElement;
 
+  int32_t mJSPluginID;
+
   /**
    * OriginAttributes of the top level tab docShell
    */
   OriginAttributes mOriginAttributes;
 
   /**
    * The requested presentation URL.
    */
@@ -178,16 +191,22 @@ public:
   {
     return TabContext::SetTabContext(aIsMozBrowserElement,
                                      aIsPrerendered,
                                      aShowAccelerators,
                                      aShowFocusRings,
                                      aOriginAttributes,
                                      aPresentationURL);
   }
+
+  bool SetTabContextForJSPluginFrame(uint32_t aJSPluginID)
+  {
+    return TabContext::SetTabContextForJSPluginFrame(aJSPluginID);
+  }
+
 };
 
 /**
  * MaybeInvalidTabContext is a simple class that lets you transform an
  * IPCTabContext into a TabContext.
  *
  * The issue is that an IPCTabContext is not necessarily valid.  So to convert
  * an IPCTabContext into a TabContext, you construct a MaybeInvalidTabContext,
--- a/dom/ipc/nsIContentChild.cpp
+++ b/dom/ipc/nsIContentChild.cpp
@@ -6,16 +6,17 @@
 
 #include "nsIContentChild.h"
 
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/DOMTypes.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
 #include "mozilla/dom/TabChild.h"
+#include "mozilla/dom/TabGroup.h"
 #include "mozilla/dom/ipc/StructuredCloneData.h"
 #include "mozilla/ipc/FileDescriptorSetChild.h"
 #include "mozilla/ipc/InputStreamUtils.h"
 #include "mozilla/ipc/IPCStreamAlloc.h"
 #include "mozilla/ipc/IPCStreamDestination.h"
 #include "mozilla/ipc/IPCStreamSource.h"
 #include "mozilla/ipc/PChildToParentStreamChild.h"
 #include "mozilla/ipc/PParentToChildStreamChild.h"
@@ -182,10 +183,48 @@ nsIContentChild::RecvAsyncMessage(const 
     ipc::UnpackClonedMessageDataForChild(aData, data);
 
     cpm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(cpm.get()), nullptr,
                         aMsg, false, &data, &cpows, aPrincipal, nullptr);
   }
   return IPC_OK();
 }
 
+/* static */
+already_AddRefed<nsIEventTarget>
+nsIContentChild::GetConstructedEventTarget(const IPC::Message& aMsg)
+{
+  ActorHandle handle;
+  TabId tabId, sameTabGroupAs;
+  PickleIterator iter(aMsg);
+  if (!IPC::ReadParam(&aMsg, &iter, &handle)) {
+    return nullptr;
+  }
+  aMsg.IgnoreSentinel(&iter);
+  if (!IPC::ReadParam(&aMsg, &iter, &tabId)) {
+    return nullptr;
+  }
+  aMsg.IgnoreSentinel(&iter);
+  if (!IPC::ReadParam(&aMsg, &iter, &sameTabGroupAs)) {
+    return nullptr;
+  }
+
+  // If sameTabGroupAs is non-zero, then the new tab will be in the same
+  // TabGroup as a previously created tab. Rather than try to find the
+  // previously created tab (whose constructor message may not even have been
+  // processed yet, in theory) and look up its event target, we just use the
+  // default event target. This means that runnables for this tab will not be
+  // labeled. However, this path is only taken for print preview and view
+  // source, which are not performance-sensitive.
+  if (sameTabGroupAs) {
+    return nullptr;
+  }
+
+  // If the request for a new TabChild is coming from the parent process, then
+  // there is no opener. Therefore, we create a fresh TabGroup.
+  RefPtr<TabGroup> tabGroup = new TabGroup();
+  nsCOMPtr<nsIEventTarget> target = tabGroup->EventTargetFor(TaskCategory::Other);
+  return target.forget();
+}
+
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/ipc/nsIContentChild.h
+++ b/dom/ipc/nsIContentChild.h
@@ -65,16 +65,19 @@ public:
                           const bool& aIsForBrowser) = 0;
 
   virtual mozilla::ipc::PFileDescriptorSetChild*
   SendPFileDescriptorSetConstructor(const mozilla::ipc::FileDescriptor&) = 0;
 
   virtual mozilla::ipc::PChildToParentStreamChild*
   SendPChildToParentStreamConstructor(mozilla::ipc::PChildToParentStreamChild*) = 0;
 
+  virtual already_AddRefed<nsIEventTarget>
+  GetEventTargetFor(TabChild* aTabChild) = 0;
+
 protected:
   virtual jsipc::PJavaScriptChild* AllocPJavaScriptChild();
   virtual bool DeallocPJavaScriptChild(jsipc::PJavaScriptChild*);
 
   virtual PBrowserChild* AllocPBrowserChild(const TabId& aTabId,
                                             const TabId& aSameTabGroupAs,
                                             const IPCTabContext& aContext,
                                             const uint32_t& aChromeFlags,
@@ -111,16 +114,18 @@ protected:
 
   virtual bool
   DeallocPFileDescriptorSetChild(mozilla::ipc::PFileDescriptorSetChild* aActor);
 
   virtual mozilla::ipc::IPCResult RecvAsyncMessage(const nsString& aMsg,
                                                    InfallibleTArray<jsipc::CpowEntry>&& aCpows,
                                                    const IPC::Principal& aPrincipal,
                                                    const ClonedMessageData& aData);
+
+  static already_AddRefed<nsIEventTarget> GetConstructedEventTarget(const IPC::Message& aMsg);
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIContentChild, NS_ICONTENTCHILD_IID)
 
 } // namespace dom
 } // namespace mozilla
 
 #endif /* mozilla_dom_nsIContentChild_h */
--- a/dom/ipc/nsIContentParent.cpp
+++ b/dom/ipc/nsIContentParent.cpp
@@ -133,23 +133,25 @@ nsIContentParent::AllocPBrowserParent(co
   Unused << aIsForBrowser;
 
   if (!CanOpenBrowser(aContext)) {
     return nullptr;
   }
 
   uint32_t chromeFlags = aChromeFlags;
   TabId openerTabId(0);
+  ContentParentId openerCpId(0);
   if (aContext.type() == IPCTabContext::TPopupIPCTabContext) {
     // CanOpenBrowser has ensured that the IPCTabContext is of
     // type PopupIPCTabContext, and that the opener TabParent is
     // reachable.
     const PopupIPCTabContext& popupContext = aContext.get_PopupIPCTabContext();
     auto opener = TabParent::GetFrom(popupContext.opener().get_PBrowserParent());
     openerTabId = opener->GetTabId();
+    openerCpId = opener->Manager()->ChildID();
 
     // We must ensure that the private browsing and remoteness flags
     // match those of the opener.
     nsCOMPtr<nsILoadContext> loadContext = opener->GetLoadContext();
     if (!loadContext) {
       return nullptr;
     }
 
@@ -173,17 +175,17 @@ nsIContentParent::AllocPBrowserParent(co
     if (!XRE_IsParentProcess()) {
       return nullptr;
     }
 
     // The creation of PBrowser was triggered from content process through
     // either window.open() or service worker's openWindow().
     // We need to register remote frame with the child generated tab id.
     ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
-    if (!cpm->RegisterRemoteFrame(aTabId, openerTabId, aContext, aCpId)) {
+    if (!cpm->RegisterRemoteFrame(aTabId, openerCpId, openerTabId, aContext, aCpId)) {
       return nullptr;
     }
   }
 
   // And because we're allocating a remote browser, of course the
   // window is remote.
   chromeFlags |= nsIWebBrowserChrome::CHROME_REMOTE_WINDOW;
 
--- a/dom/ipc/nsIContentParent.h
+++ b/dom/ipc/nsIContentParent.h
@@ -56,16 +56,17 @@ class nsIContentParent : public nsISuppo
 {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_ICONTENTPARENT_IID)
 
   nsIContentParent();
 
   virtual ContentParentId ChildID() const = 0;
   virtual bool IsForBrowser() const = 0;
+  virtual bool IsForJSPlugin() const = 0;
 
   virtual mozilla::ipc::PIPCBlobInputStreamParent*
   SendPIPCBlobInputStreamConstructor(mozilla::ipc::PIPCBlobInputStreamParent* aActor,
                                      const nsID& aID,
                                      const uint64_t& aSize) = 0;
 
   MOZ_MUST_USE virtual PBrowserParent*
   SendPBrowserConstructor(PBrowserParent* actor,
--- a/dom/ipc/tests/process_error.xul
+++ b/dom/ipc/tests/process_error.xul
@@ -8,54 +8,33 @@
   <script type="application/javascript"><![CDATA[
     Components.utils.import("resource://gre/modules/Services.jsm");
 
     const ok = window.opener.wrappedJSObject.ok;
     const is = window.opener.wrappedJSObject.is;
     const done = window.opener.wrappedJSObject.done;
     const SimpleTest = window.opener.wrappedJSObject.SimpleTest;
 
-    function getMinidumpDirectory() {
-      var dir = Services.dirsvc.get('ProfD', Components.interfaces.nsIFile);
-      dir.append("minidumps");
-      return dir;
-    }
-
-    function removeFile(directory, filename) {
-      var file = directory.clone();
-      file.append(filename);
-      if (file.exists()) {
-        file.remove(false);
-      }
-    }
-
     function crashObserver(subject, topic, data) {
       is(topic, 'ipc:content-shutdown', 'Received correct observer topic.');
       ok(subject instanceof Components.interfaces.nsIPropertyBag2,
          'Subject implements nsIPropertyBag2.');
 
+      var waitCrash = Promise.resolve();
       var dumpID;
       if ('nsICrashReporter' in Components.interfaces) {
         dumpID = subject.getPropertyAsAString('dumpID');
         ok(dumpID, "dumpID is present and not an empty string");
-      }
-
-      let p = Promise.resolve();
-
-      if (dumpID) {
-        var minidumpDirectory = getMinidumpDirectory();
-        p = Services.crashmanager.ensureCrashIsPresent(dumpID).then(() => {
-          removeFile(minidumpDirectory, dumpID + '.dmp');
-          removeFile(minidumpDirectory, dumpID + '.extra');
-        });
+        waitCrash = Services.crashmanager.ensureCrashIsPresent(dumpID);
       }
 
       Services.obs.removeObserver(crashObserver, 'ipc:content-shutdown');
-      p.then(done);
+      waitCrash.then(done);
     }
+
     Services.obs.addObserver(crashObserver, 'ipc:content-shutdown');
 
     document.getElementById('thebrowser')
             .QueryInterface(Components.interfaces.nsIFrameLoaderOwner)
             .frameLoader.messageManager
             .loadFrameScript('chrome://mochitests/content/chrome/dom/ipc/tests/process_error_contentscript.js', true);
   ]]></script>
 
--- a/dom/ipc/tests/test_process_error.xul
+++ b/dom/ipc/tests/test_process_error.xul
@@ -3,16 +3,17 @@
 <?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
 
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
   <script type="application/javascript"
 	  src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
 
   <script>
   SimpleTest.waitForExplicitFinish();
+  SimpleTest.expectChildProcessCrash();
 
   var w = window.open('process_error.xul', '_blank', 'chrome,resizable=yes,width=400,height=600');
 
   function done()
   {
     w.close();
     SimpleTest.finish();
   }
--- a/dom/plugins/base/nsIPluginTag.idl
+++ b/dom/plugins/base/nsIPluginTag.idl
@@ -64,13 +64,30 @@ interface nsIPluginTag : nsISupports
 /**
  * An interface representing a "fake" plugin: one implemented in JavaScript, not
  * as a NPAPI plug-in.  See nsIPluginHost.registerFakePlugin and the
  * documentation for the FakePluginTagInit dictionary.
  */
 [scriptable, uuid(6d22c968-226d-4156-b230-da6ad6bbf6e8)]
 interface nsIFakePluginTag : nsIPluginTag
 {
-  // The URI that should be loaded into the tag (as a frame) to handle the
-  // plugin. Note that the original data/src value for the plugin is not loaded
-  // and will need to be requested by the handler via XHR or similar if desired.
+  /**
+   * The URI that should be loaded into the tag (as a frame) to handle the
+   * plugin. Note that the original data/src value for the plugin is not loaded
+   * and will need to be requested by the handler via XHR or similar if desired.
+   */
   readonly attribute nsIURI handlerURI;
+
+  /**
+   * Optional script to run in a sandbox when instantiating a plugin. If this
+   * value is an empty string then no such script will be run.
+   * The script runs in a sandbox with system principal in the process that
+   * contains the element that instantiates the plugin (ie the EMBED or OBJECT
+   * element). The sandbox global has a 'pluginElement' property that the script
+   * can use to access the element that instantiates the plugin.
+   */
+  readonly attribute AString sandboxScript;
+
+  /**
+   * A unique id for this JS-implemented plugin. 0 is a valid id.
+   */
+  readonly attribute unsigned long id;
 };
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -52,16 +52,17 @@
 #include "nsPluginStreamListenerPeer.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/FakePluginTagInitBinding.h"
 #include "mozilla/LoadInfo.h"
 #include "mozilla/plugins/PluginAsyncSurrogate.h"
 #include "mozilla/plugins/PluginBridge.h"
 #include "mozilla/plugins/PluginTypes.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/ipc/URIUtils.h"
 
 #include "nsEnumeratorUtils.h"
 #include "nsXPCOM.h"
 #include "nsXPCOMCID.h"
 #include "nsISupportsPrimitives.h"
 
 #include "nsXULAppAPI.h"
 #include "nsIXULRuntime.h"
@@ -111,19 +112,21 @@
 #if MOZ_CRASHREPORTER
 #include "nsExceptionHandler.h"
 #endif
 
 #include "npapi.h"
 
 using namespace mozilla;
 using mozilla::TimeStamp;
+using mozilla::plugins::FakePluginTag;
 using mozilla::plugins::PluginTag;
 using mozilla::plugins::PluginAsyncSurrogate;
 using mozilla::dom::FakePluginTagInit;
+using mozilla::dom::FakePluginMimeEntry;
 
 // Null out a strong ref to a linked list iteratively to avoid
 // exhausting the stack (bug 486349).
 #define NS_ITERATIVE_UNREF_LIST(type_, list_, mNext_)                \
   {                                                                  \
     while (list_) {                                                  \
       type_ temp = list_->mNext_;                                    \
       list_->mNext_ = nullptr;                                       \
@@ -1464,39 +1467,78 @@ nsPluginHost::EnumerateSiteData(const ns
     if (firstMatchOnly) {
       break;
     }
   }
 
   return NS_OK;
 }
 
+static bool
+MimeTypeIsAllowedForFakePlugin(const nsString& aMimeType)
+{
+  static const char* const allowedFakePlugins[] = {
+    // Flash
+    "application/x-shockwave-flash",
+    // PDF
+    "application/pdf",
+    "application/vnd.adobe.pdf",
+    "application/vnd.adobe.pdfxml",
+    "application/vnd.adobe.x-mars",
+    "application/vnd.adobe.xdp+xml",
+    "application/vnd.adobe.xfdf",
+    "application/vnd.adobe.xfd+xml",
+    "application/vnd.fdf",
+  };
+
+  for (const auto allowed : allowedFakePlugins) {
+    if (aMimeType.EqualsASCII(allowed)) {
+      return true;
+    }
+  }
+  return false;
+}
+
 NS_IMETHODIMP
 nsPluginHost::RegisterFakePlugin(JS::Handle<JS::Value> aInitDictionary,
                                  JSContext* aCx,
                                  nsIFakePluginTag **aResult)
 {
   FakePluginTagInit initDictionary;
   if (!initDictionary.Init(aCx, aInitDictionary)) {
     return NS_ERROR_FAILURE;
   }
 
+  for (const FakePluginMimeEntry& mimeEntry : initDictionary.mMimeEntries) {
+    if (!MimeTypeIsAllowedForFakePlugin(mimeEntry.mType)) {
+      return NS_ERROR_FAILURE;
+    }
+  }
+
   RefPtr<nsFakePluginTag> newTag;
   nsresult rv = nsFakePluginTag::Create(initDictionary, getter_AddRefs(newTag));
   NS_ENSURE_SUCCESS(rv, rv);
 
   for (const auto& existingTag : mFakePlugins) {
     if (newTag->HandlerURIMatches(existingTag->HandlerURI())) {
       return NS_ERROR_UNEXPECTED;
     }
   }
 
   mFakePlugins.AppendElement(newTag);
-  // FIXME-jsplugins do we need to register with the category manager here?  For
-  // shumway, for now, probably not.
+
+  nsAdoptingCString disableFullPage =
+    Preferences::GetCString(kPrefDisableFullPage);
+  for (uint32_t i = 0; i < newTag->MimeTypes().Length(); i++) {
+    if (!IsTypeInList(newTag->MimeTypes()[i], disableFullPage)) {
+      RegisterWithCategoryManager(newTag->MimeTypes()[i],
+                                  ePluginRegister);
+    }
+  }
+
   newTag.forget(aResult);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsPluginHost::UnregisterFakePlugin(const nsACString& aHandlerURI)
 {
   nsCOMPtr<nsIURI> handlerURI;
@@ -2275,18 +2317,19 @@ nsresult nsPluginHost::LoadPlugins()
 nsresult
 nsPluginHost::FindPluginsInContent(bool aCreatePluginList, bool* aPluginsChanged)
 {
   MOZ_ASSERT(XRE_IsContentProcess());
 
   dom::ContentChild* cp = dom::ContentChild::GetSingleton();
   nsresult rv;
   nsTArray<PluginTag> plugins;
+  nsTArray<FakePluginTag> fakePlugins;
   uint32_t parentEpoch;
-  if (!cp->SendFindPlugins(ChromeEpochForContent(), &rv, &plugins, &parentEpoch) ||
+  if (!cp->SendFindPlugins(ChromeEpochForContent(), &rv, &plugins, &fakePlugins, &parentEpoch) ||
       NS_FAILED(rv)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   if (parentEpoch != ChromeEpochForContent()) {
     *aPluginsChanged = true;
     if (!aCreatePluginList) {
       return NS_OK;
@@ -2318,16 +2361,44 @@ nsPluginHost::FindPluginsInContent(bool 
                                                tag.isFlashPlugin(),
                                                tag.supportsAsyncInit(),
                                                tag.supportsAsyncRender(),
                                                tag.lastModifiedTime(),
                                                tag.isFromExtension(),
                                                tag.sandboxLevel());
       AddPluginTag(pluginTag);
     }
+
+    for (const auto& tag : fakePlugins) {
+      // Don't add the same plugin again.
+      for (const auto& existingTag : mFakePlugins) {
+        if (existingTag->Id() == tag.id()) {
+          continue;
+        }
+      }
+
+      RefPtr<nsFakePluginTag> pluginTag =
+      *mFakePlugins.AppendElement(new nsFakePluginTag(tag.id(),
+                                                      mozilla::ipc::DeserializeURI(tag.handlerURI()),
+                                                      tag.name().get(),
+                                                      tag.description().get(),
+                                                      tag.mimeTypes(),
+                                                      tag.mimeDescriptions(),
+                                                      tag.extensions(),
+                                                      tag.niceName(),
+                                                      tag.sandboxScript()));
+      nsAdoptingCString disableFullPage =
+        Preferences::GetCString(kPrefDisableFullPage);
+      for (uint32_t i = 0; i < pluginTag->MimeTypes().Length(); i++) {
+        if (!IsTypeInList(pluginTag->MimeTypes()[i], disableFullPage)) {
+          RegisterWithCategoryManager(pluginTag->MimeTypes()[i],
+                                      ePluginRegister);
+        }
+      }
+    }
   }
 
   mPluginsLoaded = true;
   return NS_OK;
 }
 
 // if aCreatePluginList is false we will just scan for plugins
 // and see if any changes have been made to the plugins.
@@ -2467,27 +2538,29 @@ nsresult nsPluginHost::FindPlugins(bool 
   NS_ITERATIVE_UNREF_LIST(RefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
 
   return NS_OK;
 }
 
 nsresult
 mozilla::plugins::FindPluginsForContent(uint32_t aPluginEpoch,
                                         nsTArray<PluginTag>* aPlugins,
+                                        nsTArray<FakePluginTag>* aFakePlugins,
                                         uint32_t* aNewPluginEpoch)
 {
   MOZ_ASSERT(XRE_IsParentProcess());
 
   RefPtr<nsPluginHost> host = nsPluginHost::GetInst();
-  return host->FindPluginsForContent(aPluginEpoch, aPlugins, aNewPluginEpoch);
+  return host->FindPluginsForContent(aPluginEpoch, aPlugins, aFakePlugins, aNewPluginEpoch);
 }
 
 nsresult
 nsPluginHost::FindPluginsForContent(uint32_t aPluginEpoch,
                                     nsTArray<PluginTag>* aPlugins,
+                                    nsTArray<FakePluginTag>* aFakePlugins,
                                     uint32_t* aNewPluginEpoch)
 {
   MOZ_ASSERT(XRE_IsParentProcess());
 
   // Load plugins so that the epoch is correct.
   nsresult rv = LoadPlugins();
   if (NS_FAILED(rv)) {
     return rv;
@@ -2501,19 +2574,30 @@ nsPluginHost::FindPluginsForContent(uint
   nsTArray<nsCOMPtr<nsIInternalPluginTag>> plugins;
   GetPlugins(plugins, true);
 
   for (size_t i = 0; i < plugins.Length(); i++) {
     nsCOMPtr<nsIInternalPluginTag> basetag = plugins[i];
 
     nsCOMPtr<nsIFakePluginTag> faketag = do_QueryInterface(basetag);
     if (faketag) {
-      /// FIXME-jsplugins - We need to make content processes properly
-      /// aware of jsplugins (and add a nsIInternalPluginTag->AsNative() to
-      /// avoid this hacky static cast)
+      /// FIXME-jsplugins - We need to add a nsIInternalPluginTag->AsNative() to
+      /// avoid this hacky static cast
+      nsFakePluginTag* tag = static_cast<nsFakePluginTag*>(basetag.get());
+      mozilla::ipc::URIParams handlerURI;
+      SerializeURI(tag->HandlerURI(), handlerURI);
+      aFakePlugins->AppendElement(FakePluginTag(tag->Id(),
+                                                handlerURI,
+                                                tag->Name(),
+                                                tag->Description(),
+                                                tag->MimeTypes(),
+                                                tag->MimeDescriptions(),
+                                                tag->Extensions(),
+                                                tag->GetNiceFileName(),
+                                                tag->SandboxScript()));
       continue;
     }
 
     /// FIXME-jsplugins - We need to cleanup the various plugintag classes
     /// to be more sane and avoid this dance
     nsPluginTag *tag = static_cast<nsPluginTag *>(basetag.get());
 
     aPlugins->AppendElement(PluginTag(tag->mId,
@@ -3831,16 +3915,17 @@ nsPluginHost::DestroyRunningInstances(ns
 bool
 nsPluginHost::CanUsePluginForMIMEType(const nsACString& aMIMEType)
 {
   // We only support flash as a plugin, so if the mime types don't match for
   // those, exit before we start loading plugins.
   //
   // XXX: Remove test/java cases when bug 1351885 lands.
   if (nsPluginHost::GetSpecialType(aMIMEType) == nsPluginHost::eSpecialType_Flash ||
+      MimeTypeIsAllowedForFakePlugin(NS_ConvertUTF8toUTF16(aMIMEType)) ||
       aMIMEType.LowerCaseEqualsLiteral("application/x-test") ||
       aMIMEType.LowerCaseEqualsLiteral("application/x-second-test") ||
       aMIMEType.LowerCaseEqualsLiteral("application/x-third-test") ||
       aMIMEType.LowerCaseEqualsLiteral("application/x-java-test")) {
     return true;
   }
 
   return false;
--- a/dom/plugins/base/nsPluginHost.h
+++ b/dom/plugins/base/nsPluginHost.h
@@ -29,16 +29,17 @@
 
 #ifdef XP_WIN
 #include <minwindef.h>
 #include "nsIWindowsRegKey.h"
 #endif
 
 namespace mozilla {
 namespace plugins {
+class FakePluginTag;
 class PluginAsyncSurrogate;
 class PluginTag;
 } // namespace plugins
 } // namespace mozilla
 
 class nsNPAPIPlugin;
 class nsIFile;
 class nsIChannel;
@@ -113,16 +114,17 @@ public:
                               /* out */ nsACString & aMimeType,
                               PluginFilter aFilter = eExcludeDisabled);
 
   void GetPlugins(nsTArray<nsCOMPtr<nsIInternalPluginTag>>& aPluginArray,
                   bool aIncludeDisabled = false);
 
   nsresult FindPluginsForContent(uint32_t aPluginEpoch,
                                  nsTArray<mozilla::plugins::PluginTag>* aPlugins,
+                                 nsTArray<mozilla::plugins::FakePluginTag>* aFakePlugins,
                                  uint32_t* aNewPluginEpoch);
 
   nsresult GetURL(nsISupports* pluginInst,
                   const char* url,
                   const char* target,
                   nsNPAPIPluginStreamListener* streamListener,
                   const char* altHost,
                   const char* referrer,
--- a/dom/plugins/base/nsPluginTags.cpp
+++ b/dom/plugins/base/nsPluginTags.cpp
@@ -813,21 +813,42 @@ nsPluginTag::GetLastModifiedTime(PRTime*
 
 bool nsPluginTag::IsFromExtension() const
 {
   return mIsFromExtension;
 }
 
 /* nsFakePluginTag */
 
+uint32_t nsFakePluginTag::sNextId;
+
 nsFakePluginTag::nsFakePluginTag()
-  : mState(nsPluginTag::ePluginState_Disabled)
+  : mId(sNextId++),
+    mState(nsPluginTag::ePluginState_Disabled)
 {
 }
 
+nsFakePluginTag::nsFakePluginTag(uint32_t aId,
+                                 already_AddRefed<nsIURI>&& aHandlerURI,
+                                 const char* aName,
+                                 const char* aDescription,
+                                 const nsTArray<nsCString>& aMimeTypes,
+                                 const nsTArray<nsCString>& aMimeDescriptions,
+                                 const nsTArray<nsCString>& aExtensions,
+                                 const nsCString& aNiceName,
+                                 const nsString& aSandboxScript)
+  : nsIInternalPluginTag(aName, aDescription, nullptr, nullptr,
+                         aMimeTypes, aMimeDescriptions, aExtensions),
+    mId(aId),
+    mHandlerURI(aHandlerURI),
+    mNiceName(aNiceName),
+    mSandboxScript(aSandboxScript),
+    mState(nsPluginTag::ePluginState_Enabled)
+{}
+
 nsFakePluginTag::~nsFakePluginTag()
 {
 }
 
 NS_IMPL_ADDREF(nsFakePluginTag)
 NS_IMPL_RELEASE(nsFakePluginTag)
 NS_INTERFACE_TABLE_HEAD(nsFakePluginTag)
   NS_INTERFACE_TABLE_BEGIN
@@ -840,29 +861,31 @@ NS_INTERFACE_TABLE_HEAD(nsFakePluginTag)
   NS_INTERFACE_TABLE_END
 NS_INTERFACE_TABLE_TAIL
 
 /* static */
 nsresult
 nsFakePluginTag::Create(const FakePluginTagInit& aInitDictionary,
                         nsFakePluginTag** aPluginTag)
 {
+  NS_ENSURE_TRUE(sNextId <= PR_INT32_MAX, NS_ERROR_OUT_OF_MEMORY);
   NS_ENSURE_TRUE(!aInitDictionary.mMimeEntries.IsEmpty(), NS_ERROR_INVALID_ARG);
 
   RefPtr<nsFakePluginTag> tag = new nsFakePluginTag();
   nsresult rv = NS_NewURI(getter_AddRefs(tag->mHandlerURI),
                           aInitDictionary.mHandlerURI);
   NS_ENSURE_SUCCESS(rv, rv);
 
   CopyUTF16toUTF8(aInitDictionary.mNiceName, tag->mNiceName);
   CopyUTF16toUTF8(aInitDictionary.mFullPath, tag->mFullPath);
   CopyUTF16toUTF8(aInitDictionary.mName, tag->mName);
   CopyUTF16toUTF8(aInitDictionary.mDescription, tag->mDescription);
   CopyUTF16toUTF8(aInitDictionary.mFileName, tag->mFileName);
   CopyUTF16toUTF8(aInitDictionary.mVersion, tag->mVersion);
+  tag->mSandboxScript = aInitDictionary.mSandboxScript;
 
   for (const FakePluginMimeEntry& mimeEntry : aInitDictionary.mMimeEntries) {
     CopyUTF16toUTF8(mimeEntry.mType, *tag->mMimeTypes.AppendElement());
     CopyUTF16toUTF8(mimeEntry.mDescription,
                     *tag->mMimeDescriptions.AppendElement());
     CopyUTF16toUTF8(mimeEntry.mExtension, *tag->mExtensions.AppendElement());
   }
 
@@ -880,16 +903,23 @@ nsFakePluginTag::HandlerURIMatches(nsIUR
 NS_IMETHODIMP
 nsFakePluginTag::GetHandlerURI(nsIURI **aResult)
 {
   NS_IF_ADDREF(*aResult = mHandlerURI);
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsFakePluginTag::GetSandboxScript(nsAString& aSandboxScript)
+{
+  aSandboxScript = mSandboxScript;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsFakePluginTag::GetDescription(/* utf-8 */ nsACString& aResult)
 {
   aResult = mDescription;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsFakePluginTag::GetFilename(/* utf-8 */ nsACString& aResult)
@@ -1035,8 +1065,15 @@ nsFakePluginTag::GetLastModifiedTime(PRT
 
 // We don't load fake plugins out of a library, so they should always be there.
 NS_IMETHODIMP
 nsFakePluginTag::GetLoaded(bool* ret)
 {
   *ret = true;
   return NS_OK;
 }
+
+NS_IMETHODIMP
+nsFakePluginTag::GetId(uint32_t* aId)
+{
+  *aId = mId;
+  return NS_OK;
+}
--- a/dom/plugins/base/nsPluginTags.h
+++ b/dom/plugins/base/nsPluginTags.h
@@ -206,31 +206,59 @@ class nsFakePluginTag : public nsIIntern
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIPLUGINTAG
   NS_DECL_NSIFAKEPLUGINTAG
 
   static nsresult Create(const mozilla::dom::FakePluginTagInit& aInitDictionary,
                          nsFakePluginTag** aPluginTag);
+  nsFakePluginTag(uint32_t aId,
+                  already_AddRefed<nsIURI>&& aHandlerURI,
+                  const char* aName,
+                  const char* aDescription,
+                  const nsTArray<nsCString>& aMimeTypes,
+                  const nsTArray<nsCString>& aMimeDescriptions,
+                  const nsTArray<nsCString>& aExtensions,
+                  const nsCString& aNiceName,
+                  const nsString& aSandboxScript);
 
   bool IsEnabled() override;
   const nsCString& GetNiceFileName() override;
 
   bool HandlerURIMatches(nsIURI* aURI);
 
   nsIURI* HandlerURI() const { return mHandlerURI; }
 
+  uint32_t Id() const { return mId; }
+
+  const nsString& SandboxScript() const { return mSandboxScript; }
+
+  static const int32_t NOT_JSPLUGIN = -1;
+
 private:
   nsFakePluginTag();
   virtual ~nsFakePluginTag();
 
+  // A unique id for this JS-implemented plugin. Registering a plugin through
+  // nsPluginHost::RegisterFakePlugin assigns a new id. The id is transferred
+  // through IPC when getting the list of JS-implemented plugins from child
+  // processes, so it should be consistent across processes.
+  // 0 is a valid id.
+  uint32_t      mId;
+
   // The URI of the handler for our fake plugin.
   // FIXME-jsplugins do we need to sanity check these?
   nsCOMPtr<nsIURI>    mHandlerURI;
 
   nsCString     mFullPath;
   nsCString     mNiceName;
 
+  nsString      mSandboxScript;
+
   nsPluginTag::PluginState mState;
+
+  // Stores the id to use for the JS-implemented plugin that gets registered
+  // next through nsPluginHost::RegisterFakePlugin.
+  static uint32_t sNextId;
 };
 
 #endif // nsPluginTags_h_
--- a/dom/plugins/ipc/PluginBridge.h
+++ b/dom/plugins/ipc/PluginBridge.h
@@ -27,16 +27,17 @@ class PPluginModuleParent;
 bool
 SetupBridge(uint32_t aPluginId, dom::ContentParent* aContentParent,
             bool aForceBridgeNow, nsresult* aResult, uint32_t* aRunID,
             ipc::Endpoint<PPluginModuleParent>* aEndpoint);
 
 nsresult
 FindPluginsForContent(uint32_t aPluginEpoch,
                       nsTArray<PluginTag>* aPlugins,
+                      nsTArray<FakePluginTag>* aFakePlugins,
                       uint32_t* aNewPluginEpoch);
 
 void
 TakeFullMinidump(uint32_t aPluginId,
                  base::ProcessId aContentProcessId,
                  const nsAString& aBrowserDumpId,
                  nsString& aDumpId);
 
--- a/dom/plugins/ipc/PluginTypes.ipdlh
+++ b/dom/plugins/ipc/PluginTypes.ipdlh
@@ -1,13 +1,15 @@
 /* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+include URIParams;
+
 namespace mozilla {
 namespace plugins {
 
 struct PluginTag
 {
   uint32_t id;
   nsCString name;
   nsCString description;
@@ -20,16 +22,29 @@ struct PluginTag
   bool supportsAsyncRender; // flash specific
   nsCString filename;
   nsCString version;
   int64_t lastModifiedTime;
   bool isFromExtension;
   int32_t sandboxLevel;
 };
 
+struct FakePluginTag
+{
+  uint32_t id;
+  URIParams handlerURI;
+  nsCString name;
+  nsCString description;
+  nsCString[] mimeTypes;
+  nsCString[] mimeDescriptions;
+  nsCString[] extensions;
+  nsCString niceName;
+  nsString sandboxScript;
+};
+
 union PluginIdentifier
 {
   nsCString;
   int32_t;
 };
 
 } // namespace plugins
 } // namespace mozilla
--- a/dom/plugins/test/mochitest/hang_test.js
+++ b/dom/plugins/test/mochitest/hang_test.js
@@ -60,23 +60,16 @@ var testObserver = {
       ok(cpuUsage == 0, "plugin cpu usage is 0%");
     } else {
       ok(cpuUsage > 0, "plugin cpu usage is >0%");
     }
 
     // check processor count field
     ok("NumberOfProcessors" in extraData, "got extra field for processor count");
     ok(parseInt(extraData["NumberOfProcessors"]) > 0, "number of processors is >0");
-
-    // cleanup, to be nice
-    pluginDumpFile.remove(false);
-    pluginExtraFile.remove(false);
-    for (let file of additionalDumpFiles) {
-      file.remove(false);
-    }
   },
 
   QueryInterface: function(iid) {
     if (iid.equals(Ci.nsIObserver) ||
         iid.equals(Ci.nsISupportsWeakReference) ||
         iid.equals(Ci.nsISupports))
       return this;
     throw Components.results.NS_NOINTERFACE;
@@ -105,10 +98,12 @@ function onPluginCrashed(aEvent) {
   // allow either true or false here.
   ok("submittedCrashReport" in aEvent, "submittedCrashReport is a property of event");
   is(typeof aEvent.submittedCrashReport, "boolean", "submittedCrashReport is correct type");
 
   var os = Cc["@mozilla.org/observer-service;1"].
            getService(Ci.nsIObserverService);
   os.removeObserver(testObserver, "plugin-crashed");
 
-  SimpleTest.finish();
+  Services.crashmanager.ensureCrashIsPresent(aEvent.pluginDumpID).then(() => {
+    SimpleTest.finish();
+  });
 }
--- a/dom/plugins/test/mochitest/test_busy_hang.xul
+++ b/dom/plugins/test/mochitest/test_busy_hang.xul
@@ -14,17 +14,20 @@
   <script type="application/javascript">
     setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
   </script>
   <body xmlns="http://www.w3.org/1999/xhtml" onload="runTests()">
     <embed id="plugin1" type="application/x-test" width="200" height="200"></embed>
   </body>
   <script class="testbody" type="application/javascript">
     <![CDATA[
+Components.utils.import("resource://gre/modules/Services.jsm");
+
 SimpleTest.waitForExplicitFinish();
+SimpleTest.expectChildProcessCrash();
 
 function runTests() {
   // Default plugin hang timeout is too high for mochitests
   var prefs = Cc["@mozilla.org/preferences-service;1"]
                     .getService(Ci.nsIPrefBranch);
   var timeoutPref = "dom.ipc.plugins.timeoutSecs";
   prefs.setIntPref(timeoutPref, 5);
 
--- a/dom/plugins/test/mochitest/test_crash_notify.xul
+++ b/dom/plugins/test/mochitest/test_crash_notify.xul
@@ -10,17 +10,20 @@
   <script type="application/javascript">
     setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
   </script>
 <body xmlns="http://www.w3.org/1999/xhtml" onload="runTests()">
 <embed id="plugin1" type="application/x-test" width="200" height="200"></embed>
 </body>
 <script class="testbody" type="application/javascript">
 <![CDATA[
+Components.utils.import("resource://gre/modules/Services.jsm");
+
 SimpleTest.waitForExplicitFinish();
+SimpleTest.expectChildProcessCrash();
 
 var success = false;
 
 var observerFired = false;
 
 var testObserver = {
   observe: function(subject, topic, data) {
     observerFired = true;
@@ -38,19 +41,16 @@ var testObserver = {
     let profD = directoryService.get("ProfD", Components.interfaces.nsIFile);
     profD.append("minidumps");
     let dumpFile = profD.clone();
     dumpFile.append(id + ".dmp");
     ok(dumpFile.exists(), "minidump exists");
     let extraFile = profD.clone();
     extraFile.append(id + ".extra");
     ok(extraFile.exists(), "extra file exists");
-    // cleanup, to be nice
-    dumpFile.remove(false);
-    extraFile.remove(false);
   },
 
   QueryInterface: function(iid) {
     if (iid.equals(Components.interfaces.nsIObserver) ||
         iid.equals(Components.interfaces.nsISupportsWeakReference) ||
         iid.equals(Components.interfaces.nsISupports))
       return this;
     throw Components.results.NS_NOINTERFACE;
@@ -79,17 +79,19 @@ function onPluginCrashed(aEvent) {
   // allow either true or false here.
   ok("submittedCrashReport" in aEvent, "submittedCrashReport is a property of event");
   is(typeof aEvent.submittedCrashReport, "boolean", "submittedCrashReport is correct type");
 
   var os = Components.classes["@mozilla.org/observer-service;1"].
            getService(Components.interfaces.nsIObserverService);
   os.removeObserver(testObserver, "plugin-crashed");
 
-  SimpleTest.finish();
+  Services.crashmanager.ensureCrashIsPresent(aEvent.pluginDumpID).then(() => {
+    SimpleTest.finish();
+  });
 }
 
 function runTests() {
   var os = Components.classes["@mozilla.org/observer-service;1"].
            getService(Components.interfaces.nsIObserverService);
   os.addObserver(testObserver, "plugin-crashed", true);
 
   document.addEventListener("PluginCrashed", onPluginCrashed, false);
--- a/dom/plugins/test/mochitest/test_crash_notify_no_report.xul
+++ b/dom/plugins/test/mochitest/test_crash_notify_no_report.xul
@@ -10,16 +10,18 @@
   <script type="application/javascript">
     setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
   </script>
 <body xmlns="http://www.w3.org/1999/xhtml" onload="runTests()">
 <embed id="plugin1" type="application/x-test" width="200" height="200"></embed>
 </body>
 <script class="testbody" type="application/javascript">
 <![CDATA[
+Components.utils.import("resource://gre/modules/Services.jsm");
+
 SimpleTest.waitForExplicitFinish();
 
 var success = false;
 
 var observerFired = false;
 
 var testObserver = {
   observe: function(subject, topic, data) {
@@ -35,25 +37,27 @@ var testObserver = {
     isnot(id, "", "got a non-empty crash id");
     let directoryService =
       Components.classes["@mozilla.org/file/directory_service;1"].
       getService(Components.interfaces.nsIProperties);
     let pendingD = directoryService.get("UAppData",
                                         Components.interfaces.nsIFile);
     pendingD.append("Crash Reports");
     pendingD.append("pending");
-    let dumpFile = pendingD.clone();    
+    let dumpFile = pendingD.clone();
     dumpFile.append(id + ".dmp");
     ok(dumpFile.exists(), "minidump exists");
     let extraFile = pendingD.clone();
     extraFile.append(id + ".extra");
     ok(extraFile.exists(), "extra file exists");
-    // cleanup, to be nice
-    dumpFile.remove(false);
-    extraFile.remove(false);
+    Services.crashmanager.ensureCrashIsPresent(id).then(() => {
+      // cleanup, to be nice, this will execute before SimpleTest.finish()
+      dumpFile.remove(false);
+      extraFile.remove(false);
+    });
   },
 
   QueryInterface: function(iid) {
     if (iid.equals(Components.interfaces.nsIObserver) ||
         iid.equals(Components.interfaces.nsISupportsWeakReference) ||
         iid.equals(Components.interfaces.nsISupports))
       return this;
     throw Components.results.NS_NOINTERFACE;
@@ -82,17 +86,19 @@ function onPluginCrashed(aEvent) {
   var os = Components.classes["@mozilla.org/observer-service;1"].
            getService(Components.interfaces.nsIObserverService);
   os.removeObserver(testObserver, "plugin-crashed");
 
   // re-set MOZ_CRASHREPORTER_NO_REPORT
   let env = Components.classes["@mozilla.org/process/environment;1"]
                       .getService(Components.interfaces.nsIEnvironment);
   env.set("MOZ_CRASHREPORTER_NO_REPORT", "1");
-  SimpleTest.finish();
+  Services.crashmanager.ensureCrashIsPresent(aEvent.pluginDumpID).then(() => {
+    SimpleTest.finish();
+  });
 }
 
 function runTests() {
   // the test harness will have set MOZ_CRASHREPORTER_NO_REPORT,
   // ensure that we can change the setting and have our minidumps
   // wind up in Crash Reports/pending
   let env = Components.classes["@mozilla.org/process/environment;1"]
                       .getService(Components.interfaces.nsIEnvironment);
--- a/dom/plugins/test/mochitest/test_hangui.xul
+++ b/dom/plugins/test/mochitest/test_hangui.xul
@@ -16,16 +16,17 @@
           src="http://mochi.test:8888/chrome/dom/plugins/test/mochitest/hangui_common.js" />
 
 <body xmlns="http://www.w3.org/1999/xhtml">
   <iframe id="iframe1" src="hangui_subpage.html" width="400" height="400"></iframe>
 </body>
 <script class="testbody" type="application/javascript">
 <![CDATA[
 SimpleTest.waitForExplicitFinish();
+SimpleTest.expectChildProcessCrash();
 setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
 
 Components.utils.import("resource://gre/modules/Services.jsm");
 
 const hangUITimeoutPref = "dom.ipc.plugins.hangUITimeoutSecs";
 const hangUIMinDisplayPref = "dom.ipc.plugins.hangUIMinDisplaySecs";
 const timeoutPref = "dom.ipc.plugins.timeoutSecs";
 
--- a/dom/plugins/test/mochitest/test_idle_hang.xul
+++ b/dom/plugins/test/mochitest/test_idle_hang.xul
@@ -14,17 +14,20 @@
   <script type="application/javascript">
     getTestPlugin().enabledState = Ci.nsIPluginTag.STATE_ENABLED;
   </script>
 <body xmlns="http://www.w3.org/1999/xhtml" onload="runTests()">
 <embed id="plugin1" type="application/x-test" width="200" height="200"></embed>
 </body>
 <script class="testbody" type="application/javascript">
 <![CDATA[
+Components.utils.import("resource://gre/modules/Services.jsm");
+
 SimpleTest.waitForExplicitFinish();
+SimpleTest.expectChildProcessCrash();
 
 function runTests() {
   // Default plugin hang timeout is too high for mochitests
   var prefs = Cc["@mozilla.org/preferences-service;1"]
                     .getService(Ci.nsIPrefBranch);
   var timeoutPref = "dom.ipc.plugins.timeoutSecs";
   prefs.setIntPref(timeoutPref, 5);
 
--- a/dom/webidl/FakePluginTagInit.webidl
+++ b/dom/webidl/FakePluginTagInit.webidl
@@ -21,16 +21,25 @@ dictionary FakePluginTagInit {
   DOMString niceName = "";
 
   // Other things can be provided but don't really matter that much.
   DOMString fullPath = "";
   DOMString name = "";
   DOMString description = "";
   DOMString fileName = "";
   DOMString version = "";
+
+  /**
+   * Optional script to run in a sandbox when instantiating a plugin. The script
+   * runs in a sandbox with system principal in the process that contains the
+   * element that instantiates the plugin (ie the EMBED or OBJECT element). The
+   * sandbox global has a 'pluginElement' property that the script can use to
+   * access the element that instantiates the plugin.
+   */
+  DOMString sandboxScript = "";
 };
 
 /**
  * A single MIME entry for the fake plugin.
  */
 dictionary FakePluginMimeEntry {
   required DOMString type;
   DOMString description = "";
--- a/dom/webidl/HTMLObjectElement.webidl
+++ b/dom/webidl/HTMLObjectElement.webidl
@@ -78,25 +78,27 @@ partial interface HTMLObjectElement {
 };
 
 [NoInterfaceObject]
 interface MozObjectLoadingContent {
   // Mirrored chrome-only scriptable nsIObjectLoadingContent methods.  Please
   // make sure to update this list if nsIObjectLoadingContent changes.  Also,
   // make sure everything on here is [ChromeOnly].
   [ChromeOnly]
-  const unsigned long TYPE_LOADING  = 0;
+  const unsigned long TYPE_LOADING     = 0;
   [ChromeOnly]
-  const unsigned long TYPE_IMAGE    = 1;
+  const unsigned long TYPE_IMAGE       = 1;
+  [ChromeOnly]
+  const unsigned long TYPE_PLUGIN      = 2;
   [ChromeOnly]
-  const unsigned long TYPE_PLUGIN   = 2;
+  const unsigned long TYPE_FAKE_PLUGIN = 3;
   [ChromeOnly]
-  const unsigned long TYPE_DOCUMENT = 3;
+  const unsigned long TYPE_DOCUMENT    = 4;
   [ChromeOnly]
-  const unsigned long TYPE_NULL     = 4;
+  const unsigned long TYPE_NULL        = 5;
 
   // The content type is not supported (e.g. plugin not installed)
   [ChromeOnly]
   const unsigned long PLUGIN_UNSUPPORTED          = 0;
   // Showing alternate content
   [ChromeOnly]
   const unsigned long PLUGIN_ALTERNATE            = 1;
   // The plugin exists, but is disabled
@@ -199,16 +201,22 @@ interface MozObjectLoadingContent {
 
   /**
    * If this object currently owns a running plugin, regardless of whether or
    * not one is pending spawn/despawn.
    */
   [ChromeOnly]
   readonly attribute boolean hasRunningPlugin;
 
+  /**
+   * Disable the use of fake plugins and reload the tag if necessary
+   */
+  [ChromeOnly, Throws]
+  void skipFakePlugins();
+
   [ChromeOnly, Throws, NeedsCallerType]
   readonly attribute unsigned long runID;
 };
 
 /**
  * Name:Value pair type used for passing parameters to NPAPI or javascript
  * plugins.
  */
--- a/image/FrameAnimator.cpp
+++ b/image/FrameAnimator.cpp
@@ -25,31 +25,33 @@ namespace image {
 
 ///////////////////////////////////////////////////////////////////////////////
 // AnimationState implementation.
 ///////////////////////////////////////////////////////////////////////////////
 
 const gfx::IntRect
 AnimationState::UpdateState(bool aAnimationFinished,
                             RasterImage *aImage,
-                            const gfx::IntSize& aSize)
+                            const gfx::IntSize& aSize,
+                            bool aAllowInvalidation /* = true */)
 {
   LookupResult result =
     SurfaceCache::Lookup(ImageKey(aImage),
                          RasterSurfaceKey(aSize,
                                           DefaultSurfaceFlags(),
                                           PlaybackType::eAnimated));
 
   return UpdateStateInternal(result, aAnimationFinished, aSize);
 }
 
 const gfx::IntRect
 AnimationState::UpdateStateInternal(LookupResult& aResult,
                                     bool aAnimationFinished,
-                                    const gfx::IntSize& aSize)
+                                    const gfx::IntSize& aSize,
+                                    bool aAllowInvalidation /* = true */)
 {
   // Update mDiscarded and mIsCurrentlyDecoded.
   if (aResult.Type() == MatchType::NOT_FOUND) {
     // no frames, we've either been discarded, or never been decoded before.
     mDiscarded = mHasBeenDecoded;
     mIsCurrentlyDecoded = false;
   } else if (aResult.Type() == MatchType::PENDING) {
     // no frames yet, but a decoder is or will be working on it.
@@ -73,41 +75,43 @@ AnimationState::UpdateStateInternal(Look
       } else {
         mIsCurrentlyDecoded = false;
       }
     }
   }
 
   gfx::IntRect ret;
 
-  // Update the value of mCompositedFrameInvalid.
-  if (mIsCurrentlyDecoded || aAnimationFinished) {
-    // Animated images that have finished their animation (ie because it is a
-    // finite length animation) don't have RequestRefresh called on them, and so
-    // mCompositedFrameInvalid would never get cleared. We clear it here (and
-    // also in RasterImage::Decode when we create a decoder for an image that
-    // has finished animated so it can display sooner than waiting until the
-    // decode completes). We also do it if we are fully decoded. This is safe
-    // to do for images that aren't finished animating because before we paint
-    // the refresh driver will call into us to advance to the correct frame,
-    // and that will succeed because we have all the frames.
-    if (mCompositedFrameInvalid) {
-      // Invalidate if we are marking the composited frame valid.
-      ret.SizeTo(aSize);
+  if (aAllowInvalidation) {
+    // Update the value of mCompositedFrameInvalid.
+    if (mIsCurrentlyDecoded || aAnimationFinished) {
+      // Animated images that have finished their animation (ie because it is a
+      // finite length animation) don't have RequestRefresh called on them, and so
+      // mCompositedFrameInvalid would never get cleared. We clear it here (and
+      // also in RasterImage::Decode when we create a decoder for an image that
+      // has finished animated so it can display sooner than waiting until the
+      // decode completes). We also do it if we are fully decoded. This is safe
+      // to do for images that aren't finished animating because before we paint
+      // the refresh driver will call into us to advance to the correct frame,
+      // and that will succeed because we have all the frames.
+      if (mCompositedFrameInvalid) {
+        // Invalidate if we are marking the composited frame valid.
+        ret.SizeTo(aSize);
+      }
+      mCompositedFrameInvalid = false;
+    } else if (aResult.Type() == MatchType::NOT_FOUND ||
+               aResult.Type() == MatchType::PENDING) {
+      if (mHasBeenDecoded) {
+        MOZ_ASSERT(gfxPrefs::ImageMemAnimatedDiscardable());
+        mCompositedFrameInvalid = true;
+      }
     }
-    mCompositedFrameInvalid = false;
-  } else if (aResult.Type() == MatchType::NOT_FOUND ||
-             aResult.Type() == MatchType::PENDING) {
-    if (mHasBeenDecoded) {
-      MOZ_ASSERT(gfxPrefs::ImageMemAnimatedDiscardable());
-      mCompositedFrameInvalid = true;
-    }
+    // Otherwise don't change the value of mCompositedFrameInvalid, it will be
+    // updated by RequestRefresh.
   }
-  // Otherwise don't change the value of mCompositedFrameInvalid, it will be
-  // updated by RequestRefresh.
 
   return ret;
 }
 
 void
 AnimationState::NotifyDecodeComplete()
 {
   mHasBeenDecoded = true;
--- a/image/FrameAnimator.h
+++ b/image/FrameAnimator.h
@@ -37,26 +37,28 @@ public:
     , mIsCurrentlyDecoded(false)
     , mCompositedFrameInvalid(false)
     , mDiscarded(false)
   { }
 
   /**
    * Call this whenever a decode completes, a decode starts, or the image is
    * discarded. It will update the internal state. Specifically mDiscarded,
-   * mCompositedFrameInvalid, and mIsCurrentlyDecoded. Returns a rect to
-   * invalidate.
+   * mCompositedFrameInvalid, and mIsCurrentlyDecoded. If aAllowInvalidation
+   * is true then returns a rect to invalidate.
    */
   const gfx::IntRect UpdateState(bool aAnimationFinished,
                             RasterImage *aImage,
-                            const gfx::IntSize& aSize);
+                            const gfx::IntSize& aSize,
+                            bool aAllowInvalidation = true);
 private:
   const gfx::IntRect UpdateStateInternal(LookupResult& aResult,
                                     bool aAnimationFinished,
-                                    const gfx::IntSize& aSize);
+                                    const gfx::IntSize& aSize,
+                                    bool aAllowInvalidation = true);
 
 public:
   /**
    * Call when a decode of this image has been completed.
    */
   void NotifyDecodeComplete();
 
   /**
--- a/image/RasterImage.cpp
+++ b/image/RasterImage.cpp
@@ -1256,30 +1256,23 @@ RasterImage::Decode(const IntSize& aSize
   }
 
   // Create a decoder.
   RefPtr<IDecodingTask> task;
   if (mAnimationState && aPlaybackType == PlaybackType::eAnimated) {
     task = DecoderFactory::CreateAnimationDecoder(mDecoderType, WrapNotNull(this),
                                                   mSourceBuffer, mSize,
                                                   decoderFlags, surfaceFlags);
-    // We may not be able to send an invalidation right here because of async
-    // notifications but that shouldn't be a problem because we shouldn't be
-    // getting a non-empty rect back from UpdateState. This is because UpdateState
-    // will only return a non-empty rect if we are currently decoded, or the
-    // animation is finished. We can't be decoded because we are creating a decoder
-    // here. If the animation is finished then the composited frame would have
-    // been valid when the animation finished, and it's not possible to mark
-    // the composited frame as invalid when the animation is finished. So
-    // the composited frame can't change from invalid to valid in this UpdateState
-    // call, and hence no rect can be returned.
+    // We pass false for aAllowInvalidation because we may be asked to use
+    // async notifications. Any potential invalidation here will be sent when
+    // RequestRefresh is called, or NotifyDecodeComplete.
 #ifdef DEBUG
     gfx::IntRect rect =
 #endif
-      mAnimationState->UpdateState(mAnimationFinished, this, mSize);
+      mAnimationState->UpdateState(mAnimationFinished, this, mSize, false);
     MOZ_ASSERT(rect.IsEmpty());
   } else {
     task = DecoderFactory::CreateDecoder(mDecoderType, WrapNotNull(this),
                                          mSourceBuffer, mSize, aSize,
                                          decoderFlags, surfaceFlags);
   }
 
   // Make sure DecoderFactory was able to create a decoder successfully.
--- a/intl/lwbrk/nsIWordBreaker.h
+++ b/intl/lwbrk/nsIWordBreaker.h
@@ -16,26 +16,38 @@
 { 0xe86b3379, 0xbf89, 0x11d2, \
    { 0xb3, 0xaf, 0x0, 0x80, 0x5f, 0x8a, 0x66, 0x70 } }
 
 typedef struct {
   uint32_t mBegin;
   uint32_t mEnd;
 } nsWordRange;
 
+enum nsWordBreakClass : uint8_t {
+  kWbClassSpace = 0,
+  kWbClassAlphaLetter,
+  kWbClassPunct,
+  kWbClassHanLetter,
+  kWbClassKatakanaLetter,
+  kWbClassHiraganaLetter,
+  kWbClassHWKatakanaLetter,
+  kWbClassThaiLetter
+};
+
 class nsIWordBreaker : public nsISupports
 {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_IWORDBREAKER_IID)
 
   virtual bool BreakInBetween(const char16_t* aText1 , uint32_t aTextLen1,
                                 const char16_t* aText2 ,
                                 uint32_t aTextLen2) = 0;
   virtual nsWordRange FindWord(const char16_t* aText1 , uint32_t aTextLen1,
                                uint32_t aOffset) = 0;
   virtual int32_t NextWord(const char16_t* aText, uint32_t aLen, 
                            uint32_t aPos) = 0;
                            
+  static nsWordBreakClass GetClass(char16_t aChar);
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIWordBreaker, NS_IWORDBREAKER_IID)
 
 #endif  /* nsIWordBreaker_h__ */
--- a/intl/lwbrk/nsSampleWordBreaker.cpp
+++ b/intl/lwbrk/nsSampleWordBreaker.cpp
@@ -20,34 +20,35 @@ bool nsSampleWordBreaker::BreakInBetween
   const char16_t* aText2 , uint32_t aTextLen2)
 {
   NS_PRECONDITION( nullptr != aText1, "null ptr");
   NS_PRECONDITION( nullptr != aText2, "null ptr");
 
   if(!aText1 || !aText2 || (0 == aTextLen1) || (0 == aTextLen2))
     return false;
 
-  return (this->GetClass(aText1[aTextLen1-1]) != this->GetClass(aText2[0]));
+  return GetClass(aText1[aTextLen1-1]) != GetClass(aText2[0]);
 }
 
 
 #define IS_ASCII(c)            (0 == ( 0xFF80 & (c)))
 #define ASCII_IS_ALPHA(c)         ((( 'a' <= (c)) && ((c) <= 'z')) || (( 'A' <= (c)) && ((c) <= 'Z')))
 #define ASCII_IS_DIGIT(c)         (( '0' <= (c)) && ((c) <= '9'))
 #define ASCII_IS_SPACE(c)         (( ' ' == (c)) || ( '\t' == (c)) || ( '\r' == (c)) || ( '\n' == (c)))
 #define IS_ALPHABETICAL_SCRIPT(c) ((c) < 0x2E80) 
 
 // we change the beginning of IS_HAN from 0x4e00 to 0x3400 to relfect Unicode 3.0 
 #define IS_HAN(c)              (( 0x3400 <= (c)) && ((c) <= 0x9fff))||(( 0xf900 <= (c)) && ((c) <= 0xfaff))
 #define IS_KATAKANA(c)         (( 0x30A0 <= (c)) && ((c) <= 0x30FF))
 #define IS_HIRAGANA(c)         (( 0x3040 <= (c)) && ((c) <= 0x309F))
 #define IS_HALFWIDTHKATAKANA(c)         (( 0xFF60 <= (c)) && ((c) <= 0xFF9F))
 #define IS_THAI(c)         (0x0E00 == (0xFF80 & (c) )) // Look at the higest 9 bits
 
-uint8_t nsSampleWordBreaker::GetClass(char16_t c)
+/* static */ nsWordBreakClass
+nsIWordBreaker::GetClass(char16_t c)
 {
   // begin of the hack
 
   if (IS_ALPHABETICAL_SCRIPT(c))  {
 	  if(IS_ASCII(c))  {
 		  if(ASCII_IS_SPACE(c)) {
 			  return kWbClassSpace;
 		  } else if(ASCII_IS_ALPHA(c) || ASCII_IS_DIGIT(c)) {
@@ -70,17 +71,17 @@ uint8_t nsSampleWordBreaker::GetClass(ch
 	  } else if(IS_HIRAGANA(c))   {
 		  return kWbClassHiraganaLetter;
 	  } else if(IS_HALFWIDTHKATAKANA(c))  {
 		  return kWbClassHWKatakanaLetter;
 	  } else  {
 		  return kWbClassAlphaLetter;
 	  }
   }
-  return 0;
+  return static_cast<nsWordBreakClass>(0);
 }
 
 nsWordRange nsSampleWordBreaker::FindWord(
   const char16_t* aText , uint32_t aTextLen,
   uint32_t aOffset)
 {
   nsWordRange range;
   NS_PRECONDITION( nullptr != aText, "null ptr");
@@ -88,59 +89,59 @@ nsWordRange nsSampleWordBreaker::FindWor
   NS_PRECONDITION( aOffset <= aTextLen, "aOffset > aTextLen");
 
   range.mBegin = aTextLen + 1;
   range.mEnd = aTextLen + 1;
 
   if(!aText || aOffset > aTextLen)
     return range;
 
-  uint8_t c = this->GetClass(aText[aOffset]);
+  nsWordBreakClass c = GetClass(aText[aOffset]);
   uint32_t i;
   // Scan forward
   range.mEnd--;
   for(i = aOffset +1;i <= aTextLen; i++)
   {
-     if( c != this->GetClass(aText[i]))
+     if( c != GetClass(aText[i]))
      {
        range.mEnd = i;
        break;
      }
   }
 
   // Scan backward
   range.mBegin = 0;
   for(i = aOffset ;i > 0; i--)
   {
-     if( c != this->GetClass(aText[i-1]))
+     if( c != GetClass(aText[i-1]))
      {
        range.mBegin = i;
        break;
      }
   }
   if(kWbClassThaiLetter == c)
   {
 	// need to call Thai word breaker from here
 	// we should pass the whole Thai segment to the thai word breaker to find a shorter answer
   }
   return range;
 }
 
 int32_t nsSampleWordBreaker::NextWord( 
   const char16_t* aText, uint32_t aLen, uint32_t aPos) 
 {
-  int8_t c1, c2;
+  nsWordBreakClass c1, c2;
   uint32_t cur = aPos;
   if (cur == aLen)
     return NS_WORDBREAKER_NEED_MORE_TEXT;
-  c1 = this->GetClass(aText[cur]);
+  c1 = GetClass(aText[cur]);
  
   for(cur++; cur <aLen; cur++)
   {
-     c2 = this->GetClass(aText[cur]);
+     c2 = GetClass(aText[cur]);
      if(c2 != c1) 
        break;
   }
   if(kWbClassThaiLetter == c1)
   {
 	// need to call Thai word breaker from here
 	// we should pass the whole Thai segment to the thai word breaker to find a shorter answer
   }
--- a/intl/lwbrk/nsSampleWordBreaker.h
+++ b/intl/lwbrk/nsSampleWordBreaker.h
@@ -3,40 +3,27 @@
  * 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/. */
 #ifndef nsSampleWordBreaker_h__
 #define nsSampleWordBreaker_h__
 
 
 #include "nsIWordBreaker.h"
 
-typedef enum {
-  kWbClassSpace = 0,
-  kWbClassAlphaLetter,
-  kWbClassPunct,
-  kWbClassHanLetter,
-  kWbClassKatakanaLetter,
-  kWbClassHiraganaLetter,
-  kWbClassHWKatakanaLetter,
-  kWbClassThaiLetter
-} wb_class;
-
 class nsSampleWordBreaker : public nsIWordBreaker
 {
   NS_DECL_ISUPPORTS
 public:
 
   nsSampleWordBreaker() ;
 
   bool BreakInBetween(const char16_t* aText1 , uint32_t aTextLen1,
                         const char16_t* aText2 , uint32_t aTextLen2) override;
   nsWordRange FindWord(const char16_t* aText1 , uint32_t aTextLen1,
                        uint32_t aOffset) override;
 
   int32_t NextWord(const char16_t* aText, uint32_t aLen, uint32_t aPos) override;
 
 protected:
-  uint8_t  GetClass(char16_t aChar);
-
   virtual ~nsSampleWordBreaker();
 };
 
 #endif  /* nsSampleWordBreaker_h__ */
--- a/ipc/glue/CrashReporterHost.cpp
+++ b/ipc/glue/CrashReporterHost.cpp
@@ -1,22 +1,20 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "CrashReporterHost.h"
 #include "CrashReporterMetadataShmem.h"
-#include "mozilla/LazyIdleThread.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/SyncRunnable.h"
 #include "mozilla/Telemetry.h"
 #ifdef MOZ_CRASHREPORTER
-# include "nsIAsyncShutdown.h"
 # include "nsICrashService.h"
 #endif
 
 namespace mozilla {
 namespace ipc {
 
 CrashReporterHost::CrashReporterHost(GeckoProcessType aProcessType,
                                      const Shmem& aShmem,
@@ -100,177 +98,38 @@ CrashReporterHost::FinalizeCrashReport()
   // Use mExtraNotes, since NotifyCrashService looks for "PluginHang" which is
   // set in the parent process.
   NotifyCrashService(mProcessType, mDumpID, &mExtraNotes);
 
   mFinalized = true;
   return true;
 }
 
-/**
- * Runnable used to execute the minidump analyzer program asynchronously after
- * a crash. This should run on a background thread not to block the main thread
- * over the potentially long minidump analysis. Once analysis is over, the
- * crash information will be relayed to the crash manager via another runnable
- * sent to the main thread. Shutdown will be blocked for the duration of the
- * entire process to ensure this information is sent.
- */
-class AsyncMinidumpAnalyzer final : public nsIRunnable
-                                  , public nsIAsyncShutdownBlocker
-{
-public:
-  /**
-   * Create a new minidump analyzer runnable, this will also block shutdown
-   * until the associated crash has been added to the crash manager.
-   */
-  AsyncMinidumpAnalyzer(int32_t aProcessType,
-                        int32_t aCrashType,
-                        const nsString& aChildDumpID)
-    : mProcessType(aProcessType)
-    , mCrashType(aCrashType)
-    , mChildDumpID(aChildDumpID)
-    , mName(NS_LITERAL_STRING("Crash reporter: blocking on minidump analysis"))
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-
-    nsresult rv = GetShutdownBarrier()->AddBlocker(
-      this, NS_LITERAL_STRING(__FILE__), __LINE__,
-      NS_LITERAL_STRING("Minidump analysis"));
-    Unused << NS_WARN_IF(NS_FAILED(rv));
-  }
-
-  NS_IMETHOD Run() override
-  {
-    MOZ_ASSERT(!NS_IsMainThread());
-
-    if (mProcessType == nsICrashService::PROCESS_TYPE_CONTENT ||
-        mProcessType == nsICrashService::PROCESS_TYPE_GPU) {
-      CrashReporter::RunMinidumpAnalyzer(mChildDumpID);
-    }
-
-    // Make a copy of these so we can copy them into the runnable lambda
-    int32_t processType = mProcessType;
-    int32_t crashType = mCrashType;
-    nsString childDumpID(mChildDumpID);
-    nsCOMPtr<nsIAsyncShutdownBlocker> self(this);
-
-    NS_DispatchToMainThread(NS_NewRunnableFunction([
-      self, processType, crashType, childDumpID
-    ] {
-      nsCOMPtr<nsICrashService> crashService =
-        do_GetService("@mozilla.org/crashservice;1");
-      if (crashService) {
-        crashService->AddCrash(processType, crashType, childDumpID);
-      }
-
-      nsCOMPtr<nsIAsyncShutdownClient> barrier = GetShutdownBarrier();
-
-      if (barrier) {
-        barrier->RemoveBlocker(self);
-      }
-    }));
-
-    return NS_OK;
-  }
-
-  NS_IMETHOD BlockShutdown(nsIAsyncShutdownClient* aBarrierClient) override
-  {
-    return NS_OK;
-  }
-
-  NS_IMETHOD GetName(nsAString& aName) override
-  {
-    aName = mName;
-    return NS_OK;
-  }
-
-  NS_IMETHOD GetState(nsIPropertyBag**) override
-  {
-    return NS_OK;
-  }
-
-  NS_DECL_THREADSAFE_ISUPPORTS
-
-private:
-  ~AsyncMinidumpAnalyzer() {}
-
-  // Returns the profile-before-change shutdown barrier
-  static nsCOMPtr<nsIAsyncShutdownClient> GetShutdownBarrier()
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-
-    nsCOMPtr<nsIAsyncShutdownService> svc = services::GetAsyncShutdown();
-    nsCOMPtr<nsIAsyncShutdownClient> barrier;
-    nsresult rv = svc->GetProfileBeforeChange(getter_AddRefs(barrier));
-
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return nullptr;
-    }
-
-    return barrier.forget();
-  }
-
-  int32_t mProcessType;
-  int32_t mCrashType;
-  const nsString mChildDumpID;
-  const nsString mName;
-};
-
-NS_IMPL_ISUPPORTS(AsyncMinidumpAnalyzer, nsIRunnable, nsIAsyncShutdownBlocker)
-
-/**
- * Add information about a crash to the crash manager. This method runs the
- * various activities required to gather additional information and notify the
- * crash manager asynchronously, since many of them involve I/O or potentially
- * long processing.
- *
- * @param aProcessType The type of process that crashed
- * @param aCrashType The type of crash (crash or hang)
- * @param aChildDumpID A string holding the ID of the dump associated with this
- *        crash
- */
-/* static */ void
-CrashReporterHost::AsyncAddCrash(int32_t aProcessType,
-                                 int32_t aCrashType,
-                                 const nsString& aChildDumpID)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  static StaticRefPtr<LazyIdleThread> sBackgroundThread;
-
-  if (!sBackgroundThread) {
-    // Background thread used for running minidump analysis. It will be
-    // destroyed immediately after it's done with the task.
-    sBackgroundThread =
-      new LazyIdleThread(0, NS_LITERAL_CSTRING("CrashReporterHost"));
-    ClearOnShutdown(&sBackgroundThread);
-  }
-
-  RefPtr<AsyncMinidumpAnalyzer> task =
-    new AsyncMinidumpAnalyzer(aProcessType, aCrashType, aChildDumpID);
-
-  Unused << sBackgroundThread->Dispatch(task, NS_DISPATCH_NORMAL);
-}
-
 /* static */ void
 CrashReporterHost::NotifyCrashService(GeckoProcessType aProcessType,
                                       const nsString& aChildDumpID,
                                       const AnnotationTable* aNotes)
 {
   if (!NS_IsMainThread()) {
     RefPtr<Runnable> runnable = NS_NewRunnableFunction([=] () -> void {
       CrashReporterHost::NotifyCrashService(aProcessType, aChildDumpID, aNotes);
     });
     RefPtr<nsIThread> mainThread = do_GetMainThread();
     SyncRunnable::DispatchToThread(mainThread, runnable);
     return;
   }
 
   MOZ_ASSERT(!aChildDumpID.IsEmpty());
 
+  nsCOMPtr<nsICrashService> crashService =
+    do_GetService("@mozilla.org/crashservice;1");
+  if (!crashService) {
+    return;
+  }
+
   int32_t processType;
   int32_t crashType = nsICrashService::CRASH_TYPE_CRASH;
 
   nsCString telemetryKey;
 
   switch (aProcessType) {
     case GeckoProcessType_Content:
       processType = nsICrashService::PROCESS_TYPE_CONTENT;
@@ -295,17 +154,18 @@ CrashReporterHost::NotifyCrashService(Ge
       processType = nsICrashService::PROCESS_TYPE_GPU;
       telemetryKey.AssignLiteral("gpu");
       break;
     default:
       NS_ERROR("unknown process type");
       return;
   }
 
-  AsyncAddCrash(processType, crashType, aChildDumpID);
+  nsCOMPtr<nsISupports> promise;
+  crashService->AddCrash(processType, crashType, aChildDumpID, getter_AddRefs(promise));
   Telemetry::Accumulate(Telemetry::SUBPROCESS_CRASHES_WITH_DUMP, telemetryKey, 1);
 }
 
 void
 CrashReporterHost::AddNote(const nsCString& aKey, const nsCString& aValue)
 {
   mExtraNotes.Put(aKey, aValue);
 }
--- a/ipc/ipdl/ipdl/cxx/ast.py
+++ b/ipc/ipdl/ipdl/cxx/ast.py
@@ -31,16 +31,19 @@ class Visitor:
 
     def visitTypeArray(self, ta):
         ta.basetype.accept(self)
         ta.nmemb.accept(self)
 
     def visitTypeEnum(self, enum):
         pass
 
+    def visitTypeFunction(self, fn):
+        pass
+
     def visitTypeUnion(self, union):
         for t, name in union.components:
             t.accept(self)
 
     def visitTypedef(self, tdef):
         tdef.fromtype.accept(self)
 
     def visitUsing(self, us):
@@ -370,16 +373,22 @@ class TypeUnion(Node):
     def __init__(self, name=None):
         Node.__init__(self)
         self.name = name
         self.components = [ ]           # [ Decl ]
 
     def addComponent(self, type, name):
         self.components.append(Decl(type, name))
 
+class TypeFunction(Node):
+    def __init__(self, params=[ ], ret=Type('void')):
+        '''Anonymous function type std::function<>'''
+        self.params = params
+        self.ret = ret
+
 class Typedef(Node):
     def __init__(self, fromtype, totypename, templateargs=[]):
         assert isinstance(totypename, str)
         
         Node.__init__(self)
         self.fromtype = fromtype
         self.totypename = totypename
         self.templateargs = templateargs
--- a/ipc/ipdl/ipdl/cxx/cgen.py
+++ b/ipc/ipdl/ipdl/cxx/cgen.py
@@ -89,16 +89,22 @@ class CxxCodeGen(CodePrinter, Visitor):
         for decl in u.components:
             self.printdent()
             decl.accept(self)
             self.println(';')
         self.dedent()
 
         self.printdent('}')
 
+    def visitTypeFunction(self, fn):
+        self.write('std::function<')
+        fn.ret.accept(self)
+        self.write('(')
+        self.writeDeclList(fn.params)
+        self.write(')>')
 
     def visitTypedef(self, td):
         if td.templateargs:
             formals = ', '.join([ 'class ' + T for T in td.templateargs ])
             args = ', '.join(td.templateargs)
             self.printdent('template<' + formals + '> using ' + td.totypename + ' = ')
             td.fromtype.accept(self)
             self.println('<' + args + '>;')
--- a/ipc/ipdl/ipdl/lower.py
+++ b/ipc/ipdl/ipdl/lower.py
@@ -337,16 +337,23 @@ def _makePromise(returns, side, resolver
     if len(returns) > 1:
         resolvetype = _tuple([d.bareType(side) for d in returns])
     else:
         resolvetype = returns[0].bareType(side)
     return _promise(resolvetype,
                     _PromiseRejectReason.Type(),
                     ExprLiteral.FALSE, resolver=resolver)
 
+def _makeResolver(returns, side):
+    if len(returns) > 1:
+        resolvetype = _tuple([d.bareType(side) for d in returns])
+    else:
+        resolvetype = returns[0].bareType(side)
+    return TypeFunction([Decl(resolvetype, '')])
+
 def _cxxArrayType(basetype, const=0, ref=0):
     return Type('nsTArray', T=basetype, const=const, ref=ref, hasimplicitcopyctor=False)
 
 def _cxxManagedContainerType(basetype, const=0, ref=0):
     return Type('ManagedContainer', T=basetype,
                 const=const, ref=ref, hasimplicitcopyctor=False)
 
 def _callCxxArrayLength(arr):
@@ -973,16 +980,19 @@ class MessageDecl(ipdl.ast.MessageDecl):
 
     def promiseName(self):
         name = self.baseName()
         if self.decl.type.isCtor():
             name += 'Constructor'
         name += 'Promise'
         return name
 
+    def resolverName(self):
+        return self.baseName() + 'Resolver'
+
     def actorDecl(self):
         return self.params[0]
 
     def makeCxxParams(self, paramsems='in', returnsems='out',
                       side=None, implicit=1):
         """Return a list of C++ decls per the spec'd configuration.
 |params| and |returns| is the C++ semantics of those: 'in', 'out', or None."""
 
@@ -991,18 +1001,17 @@ class MessageDecl(ipdl.ast.MessageDecl):
                 return Decl(d.inType(side), d.name)
             elif sems is 'move':
                 return Decl(d.moveType(side), d.name)
             elif sems is 'out':
                 return Decl(d.outType(side), d.name)
             else: assert 0
 
         def makeResolverDecl(returns):
-            return Decl(_refptr(Type(self.promiseName()), ref=2),
-                        'aPromise')
+            return Decl(Type(self.resolverName(), ref=2), 'aResolve')
 
         cxxparams = [ ]
         if paramsems is not None:
             cxxparams.extend([ makeDecl(d, paramsems) for d in self.params ])
 
         if returnsems is 'promise' and self.returns:
             pass
         elif returnsems is 'resolver' and self.returns:
@@ -1038,17 +1047,17 @@ class MessageDecl(ipdl.ast.MessageDecl):
                 if retcallsems is 'in':
                     cxxargs.append(ExprDeref(ret.var()))
                 elif retcallsems is 'out':
                     cxxargs.append(ret.var())
                 else: assert 0
             elif retsems is 'resolver':
                 pass
         if retsems is 'resolver':
-            cxxargs.append(ExprMove(ExprVar('promise')))
+            cxxargs.append(ExprMove(ExprVar('resolver')))
 
         if not implicit:
             assert self.decl.type.hasImplicitActorParam()
             cxxargs = cxxargs[1:]
 
         return cxxargs
 
 
@@ -2570,71 +2579,95 @@ class _GenerateProtocolActorCode(ipdl.as
         # FIXME: all actors impl Iface for now
         if ptype.isManager() or 1:
             self.hdrfile.addthing(CppDirective('include', '"base/id_map.h"'))
 
         self.hdrfile.addthings([
             CppDirective('include', '"'+ p.channelHeaderFile() +'"'),
             Whitespace.NL ])
 
+        hasAsyncReturns = False
+        for md in p.messageDecls:
+            if md.hasAsyncReturns():
+                hasAsyncReturns = True
+                break
+
         inherits = []
         if ptype.isToplevel():
             inherits.append(Inherit(p.openedProtocolInterfaceType(),
                                     viz='public'))
         else:
             inherits.append(Inherit(p.managerInterfaceType(), viz='public'))
 
+        if hasAsyncReturns:
+            inherits.append(Inherit(Type('SupportsWeakPtr', T=ExprVar(self.clsname)),
+                                    viz='public'))
+            self.hdrfile.addthing(CppDirective('include', '"mozilla/WeakPtr.h"'))
+
         if ptype.isToplevel() and self.side is 'parent':
             self.hdrfile.addthings([
                     _makeForwardDeclForQClass('nsIFile', []),
                     Whitespace.NL
                     ])
 
         self.cls = Class(
             self.clsname,
             inherits=inherits,
             abstract=True)
 
+        if hasAsyncReturns:
+            self.cls.addstmts([
+                Label.PUBLIC,
+                Whitespace('', indent=1),
+                ExprCall(ExprVar('MOZ_DECLARE_WEAKREFERENCE_TYPENAME'),
+                         [ ExprVar(self.clsname) ]),
+                Whitespace.NL
+            ])
+
+        self.cls.addstmt(Label.PRIVATE)
         friends = _FindFriends().findFriends(ptype)
         if ptype.isManaged():
             friends.update(ptype.managers)
 
         # |friend| managed actors so that they can call our Dealloc*()
         friends.update(ptype.manages)
 
         # don't friend ourself if we're a self-managed protocol
         friends.discard(ptype)
 
         for friend in friends:
             self.actorForwardDecls.extend([
                 _makeForwardDeclForActor(friend, self.prettyside),
                 Whitespace.NL
             ])
-            self.cls.addstmts([
-                FriendClassDecl(_actorName(friend.fullname(),
-                                           self.prettyside)),
-                Whitespace.NL ])
+            self.cls.addstmt(FriendClassDecl(_actorName(friend.fullname(),
+                                                        self.prettyside)))
 
         self.cls.addstmt(Label.PROTECTED)
         for typedef in p.cxxTypedefs():
             self.cls.addstmt(typedef)
         for typedef in self.includedActorTypedefs:
             self.cls.addstmt(typedef)
 
         self.cls.addstmt(Whitespace.NL)
 
         self.cls.addstmts([ Typedef(p.fqStateType(), 'State'), Whitespace.NL ])
 
-        self.cls.addstmt(Label.PUBLIC)
-        for md in p.messageDecls:
-            if self.receivesMessage(md) and md.hasAsyncReturns():
-                self.cls.addstmt(
-                    Typedef(_makePromise(md.returns, self.side, resolver=True),
-                            md.promiseName()))
-        self.cls.addstmt(Whitespace.NL)
+        if hasAsyncReturns:
+            self.cls.addstmt(Label.PUBLIC)
+            for md in p.messageDecls:
+                if self.sendsMessage(md) and md.hasAsyncReturns():
+                    self.cls.addstmt(
+                        Typedef(_makePromise(md.returns, self.side),
+                                md.promiseName()))
+                if self.receivesMessage(md) and md.hasAsyncReturns():
+                    self.cls.addstmt(
+                        Typedef(_makeResolver(md.returns, self.side),
+                                md.resolverName()))
+            self.cls.addstmt(Whitespace.NL)
 
         self.cls.addstmt(Label.PROTECTED)
         # interface methods that the concrete subclass has to impl
         for md in p.messageDecls:
             isctor, isdtor = md.decl.type.isCtor(), md.decl.type.isDtor()
 
             if self.receivesMessage(md):
                 # generate Recv/Answer* interface
@@ -4172,17 +4205,17 @@ class _GenerateProtocolActorCode(ipdl.as
 
         stmts = self.deserializeMessage(md, self.side, errfn=errfnRecv,
                                         errfnSent=errfnSentinel(_Result.ValuError))
 
         idvar, saveIdStmts = self.saveActorId(md)
         declstmts = [ StmtDecl(Decl(r.bareType(self.side), r.var().name))
                       for r in md.returns ]
         if md.decl.type.isAsync() and md.returns:
-            declstmts = self.makePromise(md, errfnRecv, routingId=idvar)
+            declstmts = self.makeResolver(md, errfnRecv, routingId=idvar)
         case.addstmts(
             stmts
             + self.transition(md)
             + saveIdStmts
             + declstmts
             + self.invokeRecvHandler(md)
             + [ Whitespace.NL ]
             + self.makeReply(md, errfnRecv, routingId=idvar)
@@ -4218,27 +4251,27 @@ class _GenerateProtocolActorCode(ipdl.as
                  + [ Whitespace.NL ]
                  + [ self.checkedWrite(p.ipdltype, p.var(), msgvar, sentinelKey=p.name, this=this)
                      for p in md.params ]
                  + [ Whitespace.NL ]
                  + self.setMessageFlags(md, msgvar, reply=0))
         return msgvar, stmts
 
 
-    def makePromise(self, md, errfn, routingId):
+    def makeResolver(self, md, errfn, routingId):
         if routingId is None:
             routingId = self.protocol.routingId()
         if not md.decl.type.isAsync() or not md.hasReply():
             return [ ]
 
         sendok = ExprVar('sendok__')
         seqno = ExprVar('seqno__')
         resolve = ExprVar('resolve__')
         reason = ExprVar('reason__')
-        promise = Type(md.promiseName())
+        resolvertype = Type(md.resolverName())
         failifsendok = StmtIf(ExprNot(sendok))
         failifsendok.addifstmt(_printWarningMessage('Error sending reply'))
         sendmsg = (self.setMessageFlags(md, self.replyvar, reply=1, seqno=seqno)
                    + [ self.logMessage(md, self.replyvar, 'Sending reply '),
                        StmtDecl(Decl(Type.BOOL, sendok.name),
                                 init=ExprCall(
                                     ExprSelect(self.protocol.callGetChannel(),
                                                '->', 'Send'),
@@ -4248,68 +4281,52 @@ class _GenerateProtocolActorCode(ipdl.as
             resolvedecl = Decl(_tuple([p.bareType(self.side) for p in md.returns],
                                       const=1, ref=1),
                                'aParam')
             destructexpr = ExprCall(ExprVar('Tie'),
                                     args=[ p.var() for p in md.returns ])
         else:
             resolvedecl = Decl(md.returns[0].bareType(self.side), 'aParam')
             destructexpr = md.returns[0].var()
-        promisethen = ExprLambda([ExprVar.THIS, routingId, seqno],
+        selfvar = ExprVar('self__')
+        ifactorisdead = StmtIf(ExprNot(selfvar))
+        ifactorisdead.addifstmts([_printWarningMessage("Not resolving promise because actor is dead."),
+                                  StmtReturn()])
+        ifactorisdestroyed = StmtIf(ExprBinary(self.protocol.stateVar(), '==',
+                                               self.protocol.deadState()))
+        ifactorisdestroyed.addifstmts([_printWarningMessage("Not resolving promise because actor is destroyed."),
+                                  StmtReturn()])
+        returnifactorisdead = [ ifactorisdead,
+                                ifactorisdestroyed ]
+        resolverfn = ExprLambda([ExprVar.THIS, selfvar, routingId, seqno],
                                  [resolvedecl])
-        promisethen.addstmts([ StmtDecl(Decl(Type.BOOL, resolve.name),
-                                        init=ExprLiteral.TRUE) ]
-                             + [ StmtDecl(Decl(p.bareType(self.side), p.var().name))
+        resolverfn.addstmts(returnifactorisdead
+                            + [ StmtDecl(Decl(Type.BOOL, resolve.name),
+                                         init=ExprLiteral.TRUE) ]
+                            + [ StmtDecl(Decl(p.bareType(self.side), p.var().name))
                                for p in md.returns ]
-                             + [ StmtExpr(ExprAssn(destructexpr, ExprVar('aParam'))),
-                                 StmtDecl(Decl(Type('IPC::Message', ptr=1), self.replyvar.name),
-                                          init=ExprCall(ExprVar(md.pqReplyCtorFunc()),
-                                                        args=[ routingId ])) ]
-                             + [ self.checkedWrite(None, resolve, self.replyvar,
-                                                   sentinelKey=resolve.name) ]
-                             + [ self.checkedWrite(r.ipdltype, r.var(), self.replyvar,
-                                                   sentinelKey=r.name)
+                            + [ StmtExpr(ExprAssn(destructexpr, ExprVar('aParam'))),
+                                StmtDecl(Decl(Type('IPC::Message', ptr=1), self.replyvar.name),
+                                         init=ExprCall(ExprVar(md.pqReplyCtorFunc()),
+                                                       args=[ routingId ])) ]
+                            + [ self.checkedWrite(None, resolve, self.replyvar,
+                                                  sentinelKey=resolve.name) ]
+                            + [ self.checkedWrite(r.ipdltype, r.var(), self.replyvar,
+                                                  sentinelKey=r.name)
                                  for r in md.returns ])
-        promisethen.addstmts(sendmsg)
-        promiserej = ExprLambda([ExprVar.THIS, routingId, seqno],
-                                [Decl(_PromiseRejectReason.Type(), reason.name)])
-
-        # If-statement for silently cancelling the promise due to ActorDestroyed.
-        returnifactordestroyed = StmtIf(ExprBinary(reason, '==',
-                                                   _PromiseRejectReason.ActorDestroyed))
-        returnifactordestroyed.addifstmts([_printWarningMessage("Reject due to ActorDestroyed"),
-                                           StmtReturn()])
-
-        promiserej.addstmts([ returnifactordestroyed,
-                              StmtExpr(ExprCall(ExprVar('MOZ_ASSERT'),
-                                                args=[ ExprBinary(reason, '==',
-                                                                  _PromiseRejectReason.HandlerRejected) ])),
-                              StmtExpr(ExprAssn(reason, _PromiseRejectReason.HandlerRejected)),
-                              StmtDecl(Decl(Type.BOOL, resolve.name),
-                                       init=ExprLiteral.FALSE),
-                              StmtDecl(Decl(Type('IPC::Message', ptr=1), self.replyvar.name),
-                                          init=ExprCall(ExprVar(md.pqReplyCtorFunc()),
-                                                        args=[ routingId ])),
-                              self.checkedWrite(None, resolve, self.replyvar,
-                                                  sentinelKey=resolve.name),
-                              self.checkedWrite(None, reason, self.replyvar,
-                                                sentinelKey=reason.name) ])
-        promiserej.addstmts(sendmsg)
+        resolverfn.addstmts(sendmsg)
 
         makepromise = [ Whitespace.NL,
                         StmtDecl(Decl(Type.INT32, seqno.name),
                                  init=ExprCall(ExprSelect(self.msgvar, '.', 'seqno'))),
-                        StmtDecl(Decl(_refptr(promise), 'promise'),
-                                 init=ExprNew(promise, args=[ExprVar('__func__')])),
-                        StmtExpr(ExprCall(
-                            ExprSelect(ExprVar('promise'), '->', 'Then'),
-                            args=[ ExprCall(ExprVar('AbstractThread::GetCurrent')),
-                                   ExprVar('__func__'),
-                                   promisethen,
-                                   promiserej ])) ]
+                        StmtDecl(Decl(Type('WeakPtr', T=ExprVar(self.clsname)),
+                                      selfvar.name),
+                                 init=ExprVar.THIS),
+                        StmtDecl(Decl(resolvertype, 'resolver'),
+                                 init=resolverfn) ]
         return makepromise
 
     def makeReply(self, md, errfn, routingId):
         if routingId is None:
             routingId = self.protocol.routingId()
         # TODO special cases for async ctor/dtor replies
         if not md.decl.type.hasReply():
             return [ ]
@@ -4628,17 +4645,17 @@ class _GenerateProtocolActorCode(ipdl.as
         decl = self.makeSendMethodDecl(md)
         decl.static = 1
         return decl
 
     def makeSendMethodDecl(self, md):
         implicit = md.decl.type.hasImplicitActorParam()
         if md.decl.type.isAsync() and md.returns:
             returnsems = 'promise'
-            rettype = _refptr(_makePromise(md.returns, self.side))
+            rettype = _refptr(Type(md.promiseName()))
         else:
             returnsems = 'out'
             rettype = Type.BOOL
         decl = MethodDecl(
             md.sendMethod().name,
             params=md.makeCxxParams(paramsems='in', returnsems=returnsems,
                                     side=self.side, implicit=implicit),
             warn_unused=(self.side == 'parent'),
--- a/ipc/ipdl/test/cxx/TestAsyncReturns.cpp
+++ b/ipc/ipdl/test/cxx/TestAsyncReturns.cpp
@@ -52,19 +52,19 @@ TestAsyncReturnsParent::Main()
                    },
                    [](PromiseRejectReason aReason) {
                      fail("sending Ping");
                    });
 }
 
 
 mozilla::ipc::IPCResult
-TestAsyncReturnsParent::RecvPong(RefPtr<PongPromise>&& aPromise)
+TestAsyncReturnsParent::RecvPong(PongResolver&& aResolve)
 {
-  aPromise->Resolve(MakeTuple(sMagic1, sMagic2), __func__);
+  aResolve(MakeTuple(sMagic1, sMagic2));
   return IPC_OK();
 }
 
 
 //-----------------------------------------------------------------------------
 // child
 
 TestAsyncReturnsChild::TestAsyncReturnsChild()
@@ -73,37 +73,36 @@ TestAsyncReturnsChild::TestAsyncReturnsC
 }
 
 TestAsyncReturnsChild::~TestAsyncReturnsChild()
 {
   MOZ_COUNT_DTOR(TestAsyncReturnsChild);
 }
 
 mozilla::ipc::IPCResult
-TestAsyncReturnsChild::RecvNoReturn(RefPtr<NoReturnPromise>&& aPromise)
+TestAsyncReturnsChild::RecvNoReturn(NoReturnResolver&& aResolve)
 {
-  // Leak the promise intentionally
-  Unused << do_AddRef(aPromise);
+  // Not resolving the promise intentionally
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
-TestAsyncReturnsChild::RecvPing(RefPtr<PingPromise>&& aPromise)
+TestAsyncReturnsChild::RecvPing(PingResolver&& aResolve)
 {
   if (!AbstractThread::MainThread()) {
     fail("AbstractThread not initalized");
   }
   SendPong()->Then(AbstractThread::MainThread(), __func__,
-                   [aPromise](const Tuple<uint32_t, uint32_t>& aParam) {
+                   [aResolve](const Tuple<uint32_t, uint32_t>& aParam) {
                      if (Get<0>(aParam) == sMagic1 && Get<1>(aParam) == sMagic2) {
                        passed("take two arguments");
                      } else {
                        fail("get two argument but has wrong value");
                      }
-                     aPromise->Resolve(true, __func__);
+                     aResolve(true);
                    },
                    [](PromiseRejectReason aReason) {
                      fail("sending Pong");
                    });
   return IPC_OK();
 }
 
 
--- a/ipc/ipdl/test/cxx/TestAsyncReturns.h
+++ b/ipc/ipdl/test/cxx/TestAsyncReturns.h
@@ -18,17 +18,17 @@ public:
   virtual ~TestAsyncReturnsParent();
 
   static bool RunTestInProcesses() { return true; }
   static bool RunTestInThreads() { return true; }
 
   void Main();
 
 protected:
-  mozilla::ipc::IPCResult RecvPong(RefPtr<PongPromise>&& aPromise) override;
+  mozilla::ipc::IPCResult RecvPong(PongResolver&& aResolve) override;
 
   virtual void ActorDestroy(ActorDestroyReason why) override
   {
     if (NormalShutdown != why)
       fail("unexpected destruction!");
     passed("ok");
     QuitParent();
   }
@@ -38,18 +38,18 @@ protected:
 class TestAsyncReturnsChild :
     public PTestAsyncReturnsChild
 {
 public:
   TestAsyncReturnsChild();
   virtual ~TestAsyncReturnsChild();
 
 protected:
-  mozilla::ipc::IPCResult RecvPing(RefPtr<PingPromise>&& aPromise) override;
-  mozilla::ipc::IPCResult RecvNoReturn(RefPtr<NoReturnPromise>&& aPromise) override;
+  mozilla::ipc::IPCResult RecvPing(PingResolver&& aResolve) override;
+  mozilla::ipc::IPCResult RecvNoReturn(NoReturnResolver&& aResolve) override;
 
   virtual void ActorDestroy(ActorDestroyReason why) override
   {
     if (NormalShutdown != why)
       fail("unexpected destruction!");
     QuitChild();
   }
 };
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -3804,16 +3804,19 @@ nsCSSFrameConstructor::FindObjectData(El
   static const FrameConstructionDataByInt sObjectData[] = {
     SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_LOADING,
                       NS_NewEmptyFrame),
     SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_PLUGIN,
                       NS_NewObjectFrame),
     SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_IMAGE,
                       NS_NewImageFrame),
     SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_DOCUMENT,
+                      NS_NewSubDocumentFrame),
+    // Fake plugin handlers load as documents
+    SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_FAKE_PLUGIN,
                       NS_NewSubDocumentFrame)
     // Nothing for TYPE_NULL so we'll construct frames by display there
   };
 
   return FindDataByInt((int32_t)type, aElement, aStyleContext,
                        sObjectData, ArrayLength(sObjectData));
 }
 
--- a/layout/ipc/RenderFrameParent.cpp
+++ b/layout/ipc/RenderFrameParent.cpp
@@ -240,17 +240,18 @@ RenderFrameParent::OwnerContentChanged(n
 
 void
 RenderFrameParent::ActorDestroy(ActorDestroyReason why)
 {
   if (mLayersId != 0) {
     if (XRE_IsParentProcess()) {
       GPUProcessManager::Get()->UnmapLayerTreeId(mLayersId, OtherPid());
     } else if (XRE_IsContentProcess()) {
-      ContentChild::GetSingleton()->SendDeallocateLayerTreeId(mLayersId);
+      TabParent* browser = TabParent::GetFrom(mFrameLoader);
+      ContentChild::GetSingleton()->SendDeallocateLayerTreeId(browser->Manager()->ChildID(), mLayersId);
     }
   }
 
   mFrameLoader = nullptr;
   mLayerManager = nullptr;
 }
 
 mozilla::ipc::IPCResult
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1246,18 +1246,22 @@ pref("dom.experimental_forms", false);
 
 // Enable <input type=number>:
 pref("dom.forms.number", true);
 
 // Enable <input type=color> by default. It will be turned off for remaining
 // platforms which don't have a color picker implemented yet.
 pref("dom.forms.color", true);
 
-// Support for input type=date and type=time. By default, disabled.
+// Support for input type=date and type=time. Enabled by default on Nightly.
+#ifdef NIGHTLY_BUILD
+pref("dom.forms.datetime", true);
+#else
 pref("dom.forms.datetime", false);
+#endif
 
 // Support for input type=month, type=week and type=datetime-local. By default,
 // disabled.
 pref("dom.forms.datetime.others", false);
 
 // Enable time picker UI. By default, disabled.
 pref("dom.forms.datetime.timepicker", false);
 
--- a/netwerk/base/nsNetUtil.cpp
+++ b/netwerk/base/nsNetUtil.cpp
@@ -379,17 +379,17 @@ NS_GetIsDocumentChannel(nsIChannel * aCh
       *aIsDocument = false;
       return NS_OK;
   }
   nsAutoCString mimeType;
   rv = aChannel->GetContentType(mimeType);
   if (NS_FAILED(rv)) {
       return rv;
   }
-  if (nsContentUtils::HtmlObjectContentTypeForMIMEType(mimeType, nullptr) ==
+  if (nsContentUtils::HtmlObjectContentTypeForMIMEType(mimeType, false, nullptr) ==
       nsIObjectLoadingContent::TYPE_DOCUMENT) {
       *aIsDocument = true;
       return NS_OK;
   }
   *aIsDocument = false;
   return NS_OK;
 }
 
--- a/netwerk/test/browser/browser_child_resource.js
+++ b/netwerk/test/browser/browser_child_resource.js
@@ -7,83 +7,31 @@ http://creativecommons.org/publicdomain/
 const TEST_URL = "http://example.com/browser/netwerk/test/browser/dummy.html";
 
 const expectedRemote = gMultiProcessBrowser ? "true" : "";
 
 Components.utils.import("resource://gre/modules/Services.jsm");
 const resProtocol = Cc["@mozilla.org/network/protocol;1?name=resource"]
                         .getService(Ci.nsIResProtocolHandler);
 
-function getMinidumpDirectory() {
-  var dir = Services.dirsvc.get('ProfD', Ci.nsIFile);
-  dir.append("minidumps");
-  return dir;
-}
-
-// This observer is needed so we can clean up all evidence of the crash so
-// the testrunner thinks things are peachy.
-var CrashObserver = {
-  observe: function(subject, topic, data) {
-    is(topic, 'ipc:content-shutdown', 'Received correct observer topic.');
-    ok(subject instanceof Ci.nsIPropertyBag2,
-       'Subject implements nsIPropertyBag2.');
-    // we might see this called as the process terminates due to previous tests.
-    // We are only looking for "abnormal" exits...
-    if (!subject.hasKey("abnormal")) {
-      info("This is a normal termination and isn't the one we are looking for...");
-      return;
-    }
-
-    var dumpID;
-    if ('nsICrashReporter' in Ci) {
-      dumpID = subject.getPropertyAsAString('dumpID');
-      ok(dumpID, "dumpID is present and not an empty string");
-    }
-
-    if (dumpID) {
-      var minidumpDirectory = getMinidumpDirectory();
-      let file = minidumpDirectory.clone();
-      file.append(dumpID + '.dmp');
-      file.remove(true);
-      file = minidumpDirectory.clone();
-      file.append(dumpID + '.extra');
-      file.remove(true);
-    }
-  }
-}
-Services.obs.addObserver(CrashObserver, 'ipc:content-shutdown');
-
-registerCleanupFunction(() => {
-  Services.obs.removeObserver(CrashObserver, 'ipc:content-shutdown');
-});
-
 function frameScript() {
   Components.utils.import("resource://gre/modules/Services.jsm");
   let resProtocol = Components.classes["@mozilla.org/network/protocol;1?name=resource"]
                               .getService(Components.interfaces.nsIResProtocolHandler);
 
   addMessageListener("Test:ResolveURI", function({ data: uri }) {
     uri = Services.io.newURI(uri);
     try {
       let resolved = resProtocol.resolveURI(uri);
       sendAsyncMessage("Test:ResolvedURI", resolved);
     }
     catch (e) {
       sendAsyncMessage("Test:ResolvedURI", null);
     }
   });
-
-  addMessageListener("Test:Crash", function() {
-    dump("Crashing\n");
-    privateNoteIntentionalCrash();
-    Components.utils.import("resource://gre/modules/ctypes.jsm");
-    let zero = new ctypes.intptr_t(8);
-    let badptr = ctypes.cast(zero, ctypes.PointerType(ctypes.int32_t));
-    badptr.contents
-  });
 }
 
 function waitForEvent(obj, name, capturing, chromeEvent) {
   info("Waiting for " + name);
   return new Promise((resolve) => {
     function listener(event) {
       info("Saw " + name);
       obj.removeEventListener(name, listener, capturing, chromeEvent);
@@ -128,18 +76,17 @@ var loadTestTab = Task.async(function*()
 
 // Restarts the child process by crashing it then reloading the tab
 var restart = Task.async(function*() {
   let browser = gBrowser.selectedBrowser;
   // If the tab isn't remote this would crash the main process so skip it
   if (browser.getAttribute("remote") != "true")
     return browser;
 
-  browser.messageManager.sendAsyncMessage("Test:Crash");
-  yield waitForEvent(browser, "AboutTabCrashedLoad", false, true);
+  yield BrowserTestUtils.crashBrowser(browser);
 
   browser.reload();
 
   yield BrowserTestUtils.browserLoaded(browser);
   is(browser.getAttribute("remote"), expectedRemote, "Browser should be in the right process");
   browser.messageManager.loadFrameScript("data:,(" + frameScript.toString() + ")();", true);
   return browser;
 });
--- a/netwerk/test/browser/dummy.html
+++ b/netwerk/test/browser/dummy.html
@@ -1,7 +1,11 @@
 <!DOCTYPE html>
 
+<head>
+<meta http-equiv="content-type" content="text/html; charset=utf-8">
+</head>
+
 <html>
 <body>
   <p>Dummy Page</p>
 </body>
 </html>
--- a/testing/marionette/interaction.js
+++ b/testing/marionette/interaction.js
@@ -71,16 +71,40 @@ const SELECTED_PROPERTY_SUPPORTED_XUL = 
   "MENU",
   "MENUITEM",
   "MENUSEPARATOR",
   "RADIO",
   "RICHLISTITEM",
   "TAB",
 ]);
 
+/**
+ * Common form controls that user can change the value property interactively.
+ */
+const COMMON_FORM_CONTROLS = new Set([
+  "input",
+  "textarea",
+  "select",
+]);
+
+/**
+ * Input elements that do not fire "input" and "change" events when value
+ * property changes.
+ */
+const INPUT_TYPES_NO_EVENT = new Set([
+  "checkbox",
+  "radio",
+  "file",
+  "hidden",
+  "image",
+  "reset",
+  "button",
+  "submit",
+]);
+
 this.interaction = {};
 
 /**
  * Interact with an element by clicking it.
  *
  * The element is scrolled into view before visibility- or interactability
  * checks are performed.
  *
@@ -340,16 +364,42 @@ interaction.uploadFile = function* (el, 
   event.click(el);
 
   el.mozSetFileArray(fs);
 
   event.change(el);
 };
 
 /**
+ * Sets a form element's value.
+ *
+ * @param {DOMElement} el
+ *     An form element, e.g. input, textarea, etc.
+ * @param {string} value
+ *     The value to be set.
+ *
+ * @throws TypeError
+ *     If |el| is not an supported form element.
+ */
+interaction.setFormControlValue = function* (el, value) {
+  if (!COMMON_FORM_CONTROLS.has(el.localName)) {
+    throw new TypeError("This function is for form elements only");
+  }
+
+  el.value = value;
+
+  if (INPUT_TYPES_NO_EVENT.has(el.type)) {
+    return;
+  }
+
+  event.input(el);
+  event.change(el);
+};
+
+/**
  * Send keys to element.
  *
  * @param {DOMElement|XULElement} el
  *     Element to send key events to.
  * @param {Array.<string>} value
  *     Sequence of keystrokes to send to the element.
  * @param {boolean} ignoreVisibility
  *     Flag to enable or disable element visibility tests.
--- a/testing/marionette/listener.js
+++ b/testing/marionette/listener.js
@@ -25,21 +25,24 @@ Cu.import("chrome://marionette/content/i
 Cu.import("chrome://marionette/content/legacyaction.js");
 Cu.import("chrome://marionette/content/logging.js");
 Cu.import("chrome://marionette/content/navigate.js");
 Cu.import("chrome://marionette/content/proxy.js");
 Cu.import("chrome://marionette/content/session.js");
 Cu.import("chrome://marionette/content/simpletest.js");
 
 Cu.import("resource://gre/modules/FileUtils.jsm");
+Cu.import("resource://gre/modules/Preferences.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 Cu.importGlobalProperties(["URL"]);
 
+const INPUT_DATETIME_PREF = "dom.forms.datetime";
+
 var contentLog = new logging.ContentLogger();
 
 var marionetteTestName;
 var winUtil = content.QueryInterface(Ci.nsIInterfaceRequestor)
     .getInterface(Ci.nsIDOMWindowUtils);
 var listenerId = null; // unique ID of this listener
 var curContainer = { frame: content, shadowRoot: null };
 var isRemoteBrowser = () => curContainer.frame.contentWindow !== null;
@@ -1432,16 +1435,19 @@ function isElementSelected(id) {
   return interaction.isElementSelected(
       el, capabilities.get("moz:accessibilityChecks"));
 }
 
 function* sendKeysToElement(id, val) {
   let el = seenEls.get(id, curContainer);
   if (el.type == "file") {
     yield interaction.uploadFile(el, val);
+  } else if ((el.type == "date" || el.type == "time") &&
+      Preferences.get(INPUT_DATETIME_PREF)) {
+    yield interaction.setFormControlValue(el, val);
   } else {
     yield interaction.sendKeysToElement(
         el, val, false, capabilities.get("moz:accessibilityChecks"));
   }
 }
 
 /**
  * Clear the text of an element.
--- a/toolkit/components/crashes/CrashManagerTest.jsm
+++ b/toolkit/components/crashes/CrashManagerTest.jsm
@@ -173,13 +173,14 @@ this.getManager = function() {
     // paths are triggered.
     let storeD = await makeDir(false);
 
     let m = new TestingCrashManager({
       pendingDumpsDir: pendingD,
       submittedDumpsDir: submittedD,
       eventsDirs: [eventsD1, eventsD2],
       storeDir: storeD,
+      telemetryStoreSizeKey: "CRASH_STORE_COMPRESSED_BYTES",
     });
 
     return m;
   })();
 };
--- a/toolkit/components/crashes/CrashService.js
+++ b/toolkit/components/crashes/CrashService.js
@@ -1,85 +1,109 @@
 /* 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/. */
 
 "use strict";
 
 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
+Cu.import("resource://gre/modules/AppConstants.jsm", this);
 Cu.import("resource://gre/modules/AsyncShutdown.jsm", this);
 Cu.import("resource://gre/modules/KeyValueParser.jsm");
 Cu.import("resource://gre/modules/osfile.jsm", this);
 Cu.import("resource://gre/modules/Promise.jsm", this);
 Cu.import("resource://gre/modules/Services.jsm", this);
 Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
 
 /**
- * Computes the SHA256 hash of the minidump file associated with a crash
+ * Run the minidump analyzer tool to gather stack traces from the minidump. The
+ * stack traces will be stored in the .extra file under the StackTraces= entry.
+ *
+ * @param minidumpPath {string} The path to the minidump file
  *
- * @param crashID {string} Crash ID. Likely a UUID.
+ * @returns {Promise} A promise that gets resolved once minidump analysis has
+ *          finished.
+ */
+function runMinidumpAnalyzer(minidumpPath) {
+  return new Promise((resolve, reject) => {
+    const binSuffix = AppConstants.platform === "win" ? ".exe" : "";
+    const exeName = "minidump-analyzer" + binSuffix;
+
+    let exe = Services.dirsvc.get("GreBinD", Ci.nsIFile);
+
+    if (AppConstants.platform === "macosx") {
+        exe.append("crashreporter.app");
+        exe.append("Contents");
+        exe.append("MacOS");
+    }
+
+    exe.append(exeName);
+
+    let args = [ minidumpPath ];
+    let process = Cc["@mozilla.org/process/util;1"]
+                    .createInstance(Ci.nsIProcess);
+    process.init(exe);
+    process.startHidden = true;
+    process.runAsync(args, args.length, (subject, topic, data) => {
+      switch (topic) {
+        case "process-finished":
+          resolve();
+          break;
+        default:
+          reject(topic);
+          break;
+      }
+    });
+  });
+}
+
+/**
+ * Computes the SHA256 hash of a minidump file
+ *
+ * @param minidumpPath {string} The path to the minidump file
  *
  * @returns {Promise} A promise that resolves to the hash value of the
- *          minidump. If the hash could not be computed then null is returned
- *          instead.
+ *          minidump.
  */
-function computeMinidumpHash(id) {
-  let cr = Cc["@mozilla.org/toolkit/crash-reporter;1"]
-             .getService(Components.interfaces.nsICrashReporter);
-
+function computeMinidumpHash(minidumpPath) {
   return (async function() {
-    try {
-      let minidumpFile = cr.getMinidumpForID(id);
-      let minidumpData = await OS.File.read(minidumpFile.path);
-      let hasher = Cc["@mozilla.org/security/hash;1"]
-                     .createInstance(Ci.nsICryptoHash);
-      hasher.init(hasher.SHA256);
-      hasher.update(minidumpData, minidumpData.length);
-
-      let hashBin = hasher.finish(false);
-      let hash = "";
+    let minidumpData = await OS.File.read(minidumpPath);
+    let hasher = Cc["@mozilla.org/security/hash;1"]
+                   .createInstance(Ci.nsICryptoHash);
+    hasher.init(hasher.SHA256);
+    hasher.update(minidumpData, minidumpData.length);
 
-      for (let i = 0; i < hashBin.length; i++) {
-        // Every character in the hash string contains a byte of the hash data
-        hash += ("0" + hashBin.charCodeAt(i).toString(16)).slice(-2);
-      }
+    let hashBin = hasher.finish(false);
+    let hash = "";
 
-      return hash;
-    } catch (e) {
-      Cu.reportError(e);
-      return null;
+    for (let i = 0; i < hashBin.length; i++) {
+      // Every character in the hash string contains a byte of the hash data
+      hash += ("0" + hashBin.charCodeAt(i).toString(16)).slice(-2);
     }
+
+    return hash;
   })();
 }
 
 /**
- * Process the .extra file associated with the crash id and return the
- * annotations it contains in an object.
+ * Process the given .extra file and return the annotations it contains in an
+ * object.
  *
- * @param crashID {string} Crash ID. Likely a UUID.
+ * @param extraPath {string} The path to the .extra file
  *
  * @return {Promise} A promise that resolves to an object holding the crash
- *         annotations, this object may be empty if no annotations were found.
+ *         annotations.
  */
-function processExtraFile(id) {
-  let cr = Cc["@mozilla.org/toolkit/crash-reporter;1"]
-             .getService(Components.interfaces.nsICrashReporter);
-
+function processExtraFile(extraPath) {
   return (async function() {
-    try {
-      let extraFile = cr.getExtraFileForID(id);
-      let decoder = new TextDecoder();
-      let extraData = await OS.File.read(extraFile.path);
+    let decoder = new TextDecoder();
+    let extraData = await OS.File.read(extraPath);
 
-      return parseKeyValuePairs(decoder.decode(extraData));
-    } catch (e) {
-      Cu.reportError(e);
-      return {};
-    }
+    return parseKeyValuePairs(decoder.decode(extraData));
   })();
 }
 
 /**
  * This component makes crash data available throughout the application.
  *
  * It is a service because some background activity will eventually occur.
  */
@@ -120,32 +144,47 @@ CrashService.prototype = Object.freeze({
     case Ci.nsICrashService.CRASH_TYPE_HANG:
       crashType = Services.crashmanager.CRASH_TYPE_HANG;
       break;
     default:
       throw new Error("Unrecognized CRASH_TYPE: " + crashType);
     }
 
     let blocker = (async function() {
-      let metadata = await processExtraFile(id);
-      let hash = await computeMinidumpHash(id);
+      let metadata = {};
+      let hash = null;
+
+      try {
+        let cr = Cc["@mozilla.org/toolkit/crash-reporter;1"]
+                   .getService(Components.interfaces.nsICrashReporter);
+        let minidumpPath = cr.getMinidumpForID(id).path;
+        let extraPath = cr.getExtraFileForID(id).path;
+
+        await runMinidumpAnalyzer(minidumpPath);
+        metadata = await processExtraFile(extraPath);
+        hash = await computeMinidumpHash(minidumpPath);
+      } catch (e) {
+        Cu.reportError(e);
+      }
 
       if (hash) {
         metadata.MinidumpSha256Hash = hash;
       }
 
       await Services.crashmanager.addCrash(processType, crashType, id,
                                            new Date(), metadata);
     })();
 
     AsyncShutdown.profileBeforeChange.addBlocker(
       "CrashService waiting for content crash ping to be sent", blocker
     );
 
     blocker.then(AsyncShutdown.profileBeforeChange.removeBlocker(blocker));
+
+    return blocker;
   },
 
   observe(subject, topic, data) {
     switch (topic) {
       case "profile-after-change":
         // Side-effect is the singleton is instantiated.
         Services.crashmanager;
         break;
--- a/toolkit/components/crashes/nsICrashService.idl
+++ b/toolkit/components/crashes/nsICrashService.idl
@@ -14,17 +14,17 @@ interface nsICrashService : nsISupports
    *        One of the PROCESS_TYPE constants defined below.
    * @param crashType
    *        One of the CRASH_TYPE constants defined below.
    * @param id
    *        Crash ID. Likely a UUID.
    *
    * @return {Promise} A promise that resolves after the crash has been stored
    */
-  void addCrash(in long processType, in long crashType, in AString id);
+  nsISupports addCrash(in long processType, in long crashType, in AString id);
 
   const long PROCESS_TYPE_MAIN = 0;
   const long PROCESS_TYPE_CONTENT = 1;
   const long PROCESS_TYPE_PLUGIN = 2;
   const long PROCESS_TYPE_GMPLUGIN = 3;
   const long PROCESS_TYPE_GPU = 4;
 
   const long CRASH_TYPE_CRASH = 0;
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..1a6feee7cc1f6a988c3810c325dc43df70562799
GIT binary patch
literal 11317
zc%1E83v`s#o&V3|!6ZBq!a!4bG~iGo%OixS3AiwMA+`yL5|0c<!y{9Yl7wjz)3E)H
znUIi7jNpt?lA}?h#V}T-S)mdv;vnhD*`?=XD|-lC+|8k=AS_}<z`SPvzk9!LW)d})
zv^}S1*E{FVef{6}fA9Cd_xm!_GP9rB8+&gU5yg;9WqI$Vt5pi(oX@#dM-<GZJ%s2y
z*SVks&LNYDLb-HvKErto=S0qrbB^E~&AF4ajyByrj>|8MG(rj`!+4!8M8Ffph7;+z
zEEpp@?zWaZQdnZWr=;9k)Xhx2eHVV^<rJ;TnHw=w_N!xM$@DydT%JoYi&C$)b!w?0
zF8B4^T&mK0MPkW@BtOKgA@C<yfa9u;nykKbWuOYhw9leM^Bfy5X^O<zHJ%lkI1Ii=
z!k@p^B%0AYq0?`b@arOp#*1FFgzb`=7qP7#8#5|ZDVFoRNh3ix6<Lm@>wgNT1G^)W
z_c5=xf<GCA#dSO@wT9Smtz52R1@lioj1sJtGa@g$<YkVa%+d&|efpKO%fon8kCnVo
zj>Ky==ZX<|t@^1bx+c-)Gn444BOjzB7>4p{3zxi5ev@9$jL7SK$*Vn#&W#VJy==pB
z!{}@Xz1|v;*GH09WEgGh45NL_>&;Q}Vz;BSBl7xG^0Ea}S647y%-WyYKa7`NoiF`2
z%ol7JGUA?cQu3lO%FPZVi|+d=cZCh*wM?C_W#2Gg&}+_!ypBp<yh*;NgwTCWk;#t+
z@WSpWZ5Ybsk(q(KhVMAw^@9<4B~^%`OGl<wy_hdg*yz_+@rb<IETV`ECzB(bo@QPY
zK000|&QFZU%cI(msB*fF4l=K_zkt{N5qZ5WZP?E1@I|73WnPX^@`4RdjlgRf=hbAT
zQmUXr+Q_vS%BJ;P+sw6i9-YB?BQ2p^D(BHM{?Fra8~+w?yOJ~UC{e0_d4TSrRg^|q
zTwBfUGTJI|tW->knO-iHa4VmR__shU<^KQY;%^qxmhgX(=<!pz87a0fhIJVJ%}BAH
zu_}Ze3Wrg->n5m_GX6&XFXxzLbb4%JtiMEpN4O1pj-Em>w<}aFMyEp|*NTTpu<R!A
z^JxRyp+I<qZIm=BgcZtImM!dCqtnC2ZRrty{E=cd#ARAc=3lKyvK@Vp+dA?8S4&Gt
z?%~K<9DDQ?yRznR3hf$<60xR(OT?m6xxPZLqgrm?D&!56(Vy(!(<Ihp4ygY>OzB}R
zWfkYdH)io(uP4)iS;EfFWS2s=fvS*5r*h(TX`7b)vncwqQ?W6;=UkJrWtmIaz}dhE
zXBhtg=NVtYW5EH5kFyMD&%i0Ef1qmN#;u$3OA9Nk3pXxI%SydJGyTy;3o|p*SLI}J
zB_k^}Ib$_fa;(KRjg&4<(n{4jW-uL1hqb4P4Ev=Vf6u;*)WNZ}{Z5C{?P*d<4j}e6
zQ!SrUc3pHT(wF%x`PsL=d^71=Uq-4|N4(zJKAd}v6hEeNj8WA1jLiFE>p&Fi-p>Dt
zlU>U0$qC*lLgw?!Pv%C<<uZp(FmA!UW?GY(!|k~OQ{y?rmmf5ed##bWIKy}4QP@rW
ze1;D6<xAZ5`?4mZKf#y5(=W42!5xNQa-v)w^ULp4<})q77B1yts7vwd!e{m&8p-6>
ztDX}2qsXsa6y4xFZ=oD}nESMGr1n9{+rv3;rdf;uZ)wHc2p6~G7R4`yUWrLdmfrP`
z%a)syQ^dT&9!vjy#;xX1BXz8aobhl_k97e*f8+T%yojI2`TSq0Gtzwe5%(l=Y&Bo-
zOA@sW+;cnU`3x0D&8v*uvT|*)_`ic&czPETe9Ea9NP2=J-lZJjJq2{9)77z+j2Fx<
zl*YEo=Ioha5<K5JWLH{wOtHTs&I<+}L;oe#?F#1)c^vdGjxogFCN3|`ty9o<V68*J
z9)@`!LF4F8?I4sL#OEA&7Y2H{JReh5)+%9BUCMo{(^h{F-@LXo_Gog=&eILX3kSXM
z?u|=w%dKVQdFySq1-ASR1h`-4-FruB*SU1pwbI@Nv6UXMDJjn{D)K?(R-Qk6;DPj%
zw!((ZKmP8a96)TgmX=tH7RUR5UbikSxVoY~dH3%x7iZjmaUy`W6j=ix@`@fx&y4;}
z`mcZcS;V%cdyRltpI@=wRu}+Ox^~Zz7pDIq`4?~2?SC<7;%Weu+BTM!C-^{){ODs(
z8=d;$zU$AWEpgSg0Ay2sAd$?U{KMs6)I6Kv&{xcyv#{p-fOw?LS{eXy?6b<Y{~5nI
zrN$Y5Xm|6YiLg#_Sp_rlfwb)T&b+C!cV-+q``V;ar$3$oDuu<x0Yu7vet*)`vwvE#
zGq&*7BhUS~0z~pRY$~u8`OVwu43Dl2TDD@_`<)NYd+cBds8m=>%L=!Y_+Y+x;n+*h
z7j&n-HtVsl+Y%dWfU%XaVFUy&3~`Bui#ODb7u5?ZW|uVi%NK9MFV?&@otGMdP4A|e
z;GWcwYcg;>5+FI};gatd$cc1+<E5HQ*$ru?arrOcrn1q#hY?g%fOBb3OBEd#sExs<
z#t$Ak=G?Id$5`5NkEGUkG9p1#j%fq=CONVr$7J~3f@93CrTP+X_!_pK8Sr`=c69T9
zW4fU+Gq_>pxE<T)q0`X;nVa|E--)~1hW1wcceH?H=C}q2E^Qk#sljnTRQekn9<Ds@
zIE?@1W?>7@1*h_Fv+YVpz$GF!Fl;zjTt=cH+dtHs)8kYYB=JKnd!S0Y<b)X#*xrSA
zzw0>3rTgg`<;HAzsn^Wyc$J6tpg6~U-sxu2`rN&e--KyqLLJGwb#6rd7c9$EGa*2M
zOk~!RjmHh<6aixg9_LkYb&7`POFUP47Wj{!NWa*6T6V9z`t_T943D9|HP0wE`CjfT
zzb5HwoBS1hv(F}pSw>$!HXFEsISaJfEX{ecEkt2j&cVH84s8|>jA(`dj?h6sv{@Y2
zo+sz$QS?%wOS!>voX)Lx*twm`@~;fuE-vgb?qE9ZnI@l_fH485Z=eeO;Q6^GU*>k+
z1e=9TF-OpA_esgOT`dhPQ*@TpKT<7a8+K<^+2-GLt%0<V<fSrjD|Oq&<{Rsi(kKE<
z_)que@&S5<^~c=94j+Ekr8qv^OL4rAb)3CZAzL%qxzEn>!ERbRFrjVWc?Rw&%aaOp
zHK_9r_#4a<hqp7=zRc6W@PL86)`+@PSGz1*yFo7;zF;s&^#1o?kUFkopB=0cH4h#<
z+Xd!)k9?37{NPs>&@kQNQtTXHYqI_>_er^C1bv<0o1QJ#k1uf>aG2|pEb~>a+c`kg
zUZ`L^`g#;;f#Y`;R4d;3)hdrR!B^<c>k>HUnFi)J|At(X1b8IIIc_r1`#bkqG+)kO
zx_1~}7BsXzl<T<v+?bawMc1Ql$|s6LT#7SK`cv9?dEK^-eFHC%;2TH?0J=HgF^ATR
z09sowq;dkBJE&f)DFo2k82X#JAMnVqMHkb#al1pTLztlTqkR_M_9>2ohWDOvDQR5a
zpxP<>!ahs)$NMax4IPcaetjY74TkZkW1#nQ1N^bgf*=CpVXAyDGVK=5kPmgpkgxLx
zm}m>1=MsAb_@V8AD{;V9-Y0R32EXi|t<RcC3szO}uvaekFs&2dn`svPy}WihRK6Gs
zO_2R~RH2Q154!X&5_2c|kTrd=uWq}|?CaBDR&xL2gL3aXYci8rZDS1j0I!a!l^ot%
zy1$TcQ<!Vc))e1fJLUn{O;3AxT+4d$zqspE@c?gRTVyaTl(d%dxZcWj$Y|yEo{(y#
z{AiQ%QHb>St89bjK8fF|mQGRH#WNx>X>w^g4^NAEe#QC_FQV+qdCv2g#`n%Tm5IIe
zVorLtNWMkAG8n}n41G`h3okds8tZsX^~!Z-OR5t%SNK`_hdXM;vnlME8l_#8t9_V!
zEi88j=abz3_MJ`z?H;e}vvF?cdN=o@4~LY-uYdtxok|P+>nfl6N*L^i=*Jw0=Tf}%
zIRx&D^Ib}TuwlJ;4g%)PB9{`yW7V7igM%r8cpO#}878MsJ~KP}qI~p-Cb9EJ5g!D^
zC0Xysa_vG6P1nGey0}Est8;^kwZG>Jly5Hs|2FCW>IDjX!-h+N6pnZ2)Qj~b5HiGm
z8>8**S_p1tIG?;~IgasvmYS@-f+VWtI`JNiI2`K$(IE016&{xep!?6e%w%FdxGX??
zh6@DXtr2^Do2C7b+oF5OOp|{pc^+WmIIs%^Y3N4{hsVf^L-hG#?G`gxIPZGhOj(c2
zjqtFXmsXnvZd_75Wpnm&Kcs7oF^TvE>mK7FKFTJlWxQ8=<zCgb#Y}t0{u8E%uz#Ks
z`|eQu<J=D!#My!OG;MFzQfiLW$4-&<nBFFO{*>H*R$gynz7d2A5iteazVndXhr1|9
z#_>q&&p4G&JLDMn*seGE=;v@fZky4!O9j46RCp|gX-**ZxxEg5ZePq}V7w>)!>Ker
zV^><(o?SPZ#2D&{JZ7&lQp|t3Es?#lznS|TeNEI;C1H>PX$2<qzZpbdn>4(@<QWDJ
zns|v58UR21B@Qu6K_D$|pRAIy+gP9Vm)({g@kx=JrmbRs<-WIu_3h!lyieQ~`RS4T
z2&t3%0JCneiH>u9?b}1g@!Zw;;EY|w=U0b%QKZ3PERJngb<AyP?Q~n(*><2;z;RYK
zFUAaAuy|3diQYpKW{%a32^Rs~6501Df;4f4P+u^ku7}U+MAp_UUOc0Lm(Muk#NH=u
zlAz8VO4kJ5m)PGB4<G0ASnjIrLKcC(k^~N5dw3q=?)&n5cA|fq)c5NW{AMLDj7q)y
zcnk7g;-cej-zP%>$>_7w(4^D7%>S^}8L!M(Z<VY=4(HdNbt#W?`A+DTstGE_G+qE0
zhh7C-Zr^8DD*Kzr#JT1_-4@h=V`tc%+%~Eoo?+eWW?4IUJn#Y)ypcNxny9<CiL`V2
zOz1LBp3`BYb{@l=UuAqe2M~v#4cJ#gC9M-|V{P7h*b3(u$Gy>|l=rDP28n~%@4<Q1
zO8q=FuIvj~=K~TJ`$hhMlocOpfj;QM?qZFwonH0fN$+qDs`pLYpS_xWuhA&tFE9J-
zwei)8TV2aqJMip-w1#o+f_>CAK4n6+0=qybjKw@*q%}-)MTASqR@;yzPKAeU(MB2Q
zgP^x=(61xrAx_l;vS8dDu+M<kntrK6q>2MsD%p=Ak0zgqC-urWxm42nCH;3fe*S2t
zT%Wjq11#>}ntA+H&c8qFwhSNZRCU(;-~_i<{m~Dn`8!~Hcnm)9)aze8uGtGO2`ah%
z(tfu^ql@_qyc0wccgX`yK0M(MH~C@T@C2H#!WY>v%}n><gKi7pkst%?h*Zn#?IiR3
z*`c8_Cf;KdW7@OEz;(!^iYzX=k?|58A8lg&Blf}lA3o^Bu85C6@eybTmvJON0M$MO
zEl}qgrB=sBq4+rTw#o7?wSS7dGsXBi-jDFnAwGOu6fQp^#7Chx*WW%*;$trGF(>u~
z=;u&%)KUiX?N5^TEU=I7euK7)_?qiF*b|qTFn>0wt12>p$gm?q0^6nXO}7R1(s%>J
zq3*Y^TfSG$Dg0Z!R2eJJLP#Bb{xjLW80pi%h5Vp}cw=IW*{7RtjReqr?QOdfGuy6Q
zQs)cv7I;q`7&~bn)D|(!;efl*qFc<;=X_^GGvO`(^47dzSHwMNK%e+<G_<A8!*sgq
Vme*-9k@F4vkeDd$78Rb;{{lqd;6nfa
new file mode 100644
--- /dev/null
+++ b/toolkit/components/crashes/tests/xpcshell/crash.extra
@@ -0,0 +1,32 @@
+E10SCohort=unsupportedChannel
+ContentSandboxLevel=2
+TelemetryEnvironment={}
+EMCheckCompatibility=true
+ProductName=Firefox
+ContentSandboxCapabilities=119
+TelemetryClientId=
+Vendor=Mozilla
+InstallTime=1000000000
+Theme=classic/1.0
+ReleaseChannel=default
+AddonsShouldHaveBlockedE10s=1
+ServerURL=https://crash-reports.mozilla.com
+SafeMode=0
+ContentSandboxCapable=1
+useragent_locale=en-US
+Version=55.0a1
+BuildID=20170512114708
+ProductID={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
+TelemetryServerURL=
+DOMIPCEnabled=1
+Add-ons=
+CrashTime=1494582646
+UptimeTS=14.9179586
+ThreadIdNameMapping=
+ContentSandboxLevel=2
+ContentSandboxEnabled=1
+ProcessType=content
+DOMIPCEnabled=1
+StartupTime=1000000000
+URL=about:home
+ContentSandboxCapabilities=119
--- a/toolkit/components/crashes/tests/xpcshell/test_crash_service.js
+++ b/toolkit/components/crashes/tests/xpcshell/test_crash_service.js
@@ -1,17 +1,19 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 var {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
+Cu.import("resource://gre/modules/osfile.jsm", this);
 Cu.import("resource://gre/modules/Services.jsm", this);
 Cu.import("resource://testing-common/AppData.jsm", this);
+Cu.import("resource://testing-common/CrashManagerTest.jsm", this);
 var bsp = Cu.import("resource://gre/modules/CrashManager.jsm", {});
 
 function run_test() {
   run_next_test();
 }
 
 add_task(async function test_instantiation() {
   Assert.ok(!bsp.gCrashManager, "CrashManager global instance not initially defined.");
@@ -24,8 +26,56 @@ add_task(async function test_instantiati
     .getService(Ci.nsIObserver)
     .observe(null, "profile-after-change", null);
 
   Assert.ok(bsp.gCrashManager, "Profile creation makes it available.");
   Assert.ok(Services.crashmanager, "CrashManager available via Services.");
   Assert.strictEqual(bsp.gCrashManager, Services.crashmanager,
                      "The objects are the same.");
 });
+
+add_task(async function test_addCrash() {
+  const crashId = "56cd87bc-bb26-339b-3a8e-f00c0f11380e";
+
+  let cwd = await OS.File.getCurrentDirectory();
+  let minidump = OS.Path.join(cwd, "crash.dmp");
+  let extra = OS.Path.join(cwd, "crash.extra");
+  let dir = do_get_tempdir();
+
+  // Make a copy of the files because the .extra file will be modified
+  await OS.File.copy(minidump, OS.Path.join(dir.path, crashId + ".dmp"));
+  await OS.File.copy(extra, OS.Path.join(dir.path, crashId + ".extra"));
+
+  // Ensure that the nsICrashReporter methods can find the dump
+  let crashReporter =
+      Components.classes["@mozilla.org/toolkit/crash-reporter;1"]
+                .getService(Components.interfaces.nsICrashReporter);
+  crashReporter.minidumpPath = dir;
+
+  let cs = Cc["@mozilla.org/crashservice;1"].getService(Ci.nsICrashService);
+  await cs.addCrash(Ci.nsICrashService.PROCESS_TYPE_CONTENT,
+                    Ci.nsICrashService.CRASH_TYPE_CRASH, crashId);
+  let crashes = await Services.crashmanager.getCrashes();
+  let crash = crashes.find(c => { return c.id === crashId; });
+  Assert.ok(crash, "Crash " + crashId + " has been stored successfully.");
+  Assert.equal(crash.metadata.ProcessType, "content");
+  Assert.equal(crash.metadata.MinidumpSha256Hash,
+    "24b0ea7794b2d2523c46c9aea72c03ccbb0ab88ad76d8258d3752c7b71d233ff");
+  Assert.ok(crash.metadata.StackTraces, "The StackTraces field is present.\n")
+
+  try {
+    let stackTraces = JSON.parse(crash.metadata.StackTraces);
+    Assert.equal(stackTraces.status, "OK");
+    Assert.ok(stackTraces.crash_info, "The crash_info field is populated.");
+    Assert.ok(stackTraces.modules && (stackTraces.modules.length > 0),
+              "The module list is populated.");
+    Assert.ok(stackTraces.threads && (stackTraces.modules.length > 0),
+              "The thread list is populated.");
+
+    let frames = stackTraces.threads[0].frames;
+    Assert.ok(frames && (frames.length > 0), "The stack trace is present.\n");
+  } catch (e) {
+    Assert.ok(false, "StackTraces does not contain valid JSON.");
+  }
+
+  // Remove the minidumps to prevent the test harness from thinking we crashed
+  await OS.File.removeDir(dir.path);
+});
--- a/toolkit/components/crashes/tests/xpcshell/xpcshell.ini
+++ b/toolkit/components/crashes/tests/xpcshell/xpcshell.ini
@@ -1,7 +1,10 @@
 [DEFAULT]
 head =
 skip-if = toolkit == 'android'
+support-files =
+  crash.dmp
+  crash.extra
 
 [test_crash_manager.js]
 [test_crash_service.js]
 [test_crash_store.js]
--- a/toolkit/components/thumbnails/test/browser.ini
+++ b/toolkit/components/thumbnails/test/browser.ini
@@ -2,17 +2,16 @@
 support-files =
   authenticate.sjs
   background_red.html
   background_red_redirect.sjs
   background_red_scroll.html
   head.js
   privacy_cache_control.sjs
   thumbnails_background.sjs
-  thumbnails_crash_content_helper.js
   thumbnails_update.sjs
 
 [browser_thumbnails_bg_bad_url.js]
 [browser_thumbnails_bg_crash_during_capture.js]
 skip-if = !crashreporter
 [browser_thumbnails_bg_crash_while_idle.js]
 skip-if = !crashreporter
 [browser_thumbnails_bg_basic.js]
--- a/toolkit/components/thumbnails/test/browser_thumbnails_bg_crash_during_capture.js
+++ b/toolkit/components/thumbnails/test/browser_thumbnails_bg_crash_during_capture.js
@@ -1,49 +1,45 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 function* runTests() {
-  let crashObserver = bgAddCrashObserver();
-
   // make a good capture first - this ensures we have the <browser>
   let goodUrl = bgTestPageURL();
   yield bgCapture(goodUrl);
   ok(thumbnailExists(goodUrl), "Thumbnail should be cached after capture");
   removeThumbnail(goodUrl);
 
-  // inject our content script.
-  let mm = bgInjectCrashContentScript();
-
   // queue up 2 captures - the first has a wait, so this is the one that
   // will die.  The second one should immediately capture after the crash.
   let waitUrl = bgTestPageURL({ wait: 30000 });
   let sawWaitUrlCapture = false;
   bgCapture(waitUrl, { onDone: () => {
     sawWaitUrlCapture = true;
     ok(!thumbnailExists(waitUrl), "Thumbnail should not have been saved due to the crash");
   }});
   bgCapture(goodUrl, { onDone: () => {
     ok(sawWaitUrlCapture, "waitUrl capture should have finished first");
     ok(thumbnailExists(goodUrl), "We should have recovered and completed the 2nd capture after the crash");
     removeThumbnail(goodUrl);
     // Test done.
-    ok(crashObserver.crashed, "Saw a crash from this test");
     next();
   }});
   let crashPromise = new Promise(resolve => {
     bgAddPageThumbObserver(waitUrl).catch(function(err) {
       ok(true, `page-thumbnail error thrown for ${waitUrl}`);
       resolve();
     });
   });
   let capturePromise = new Promise(resolve => {
     bgAddPageThumbObserver(goodUrl).then(() => {
       ok(true, `page-thumbnail created for ${goodUrl}`);
       resolve();
     });
   });
 
   info("Crashing the thumbnail content process.");
-  mm.sendAsyncMessage("thumbnails-test:crash");
+  let crash = yield BrowserTestUtils.crashBrowser(BackgroundPageThumbs._thumbBrowser, false);
+  ok(crash.CrashTime, "Saw a crash from this test");
+
   yield crashPromise;
   yield capturePromise;
 }
--- a/toolkit/components/thumbnails/test/browser_thumbnails_bg_crash_while_idle.js
+++ b/toolkit/components/thumbnails/test/browser_thumbnails_bg_crash_while_idle.js
@@ -1,38 +1,25 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 function* runTests() {
-  let crashObserver = bgAddCrashObserver();
-
   // make a good capture first - this ensures we have the <browser>
   let goodUrl = bgTestPageURL();
   yield bgCapture(goodUrl);
   ok(thumbnailExists(goodUrl), "Thumbnail should be cached after capture");
   removeThumbnail(goodUrl);
 
-  // inject our content script.
-  let mm = bgInjectCrashContentScript();
-
-  // the observer for the crashing process is basically async, so it's hard
-  // to know when the <browser> has actually seen it.  Easist is to just add
-  // our own observer.
-  Services.obs.addObserver(function onCrash() {
-    Services.obs.removeObserver(onCrash, "oop-frameloader-crashed");
-    // spin the event loop to ensure the BPT observer was called.
-    executeSoon(function() {
-      // Now queue another capture and ensure it recovers.
-      bgCapture(goodUrl, { onDone: () => {
-        ok(thumbnailExists(goodUrl), "We should have recovered and handled new capture requests");
-        removeThumbnail(goodUrl);
-        // Test done.
-        ok(crashObserver.crashed, "Saw a crash from this test");
-        next();
-      }});
-    });
-  }, "oop-frameloader-crashed");
-
   // Nothing is pending - crash the process.
   info("Crashing the thumbnail content process.");
-  mm.sendAsyncMessage("thumbnails-test:crash");
+  let crash = yield BrowserTestUtils.crashBrowser(BackgroundPageThumbs._thumbBrowser, false);
+  ok(crash.CrashTime, "Saw a crash from this test");
+
+  // Now queue another capture and ensure it recovers.
+  bgCapture(goodUrl, { onDone: () => {
+    ok(thumbnailExists(goodUrl), "We should have recovered and handled new capture requests");
+    removeThumbnail(goodUrl);
+    // Test done.
+    next();
+  }});
+
   yield true;
 }
--- a/toolkit/components/thumbnails/test/head.js
+++ b/toolkit/components/thumbnails/test/head.js
@@ -296,64 +296,8 @@ function bgAddPageThumbObserver(url) {
         Services.obs.removeObserver(observe, "page-thumbnail:create");
         Services.obs.removeObserver(observe, "page-thumbnail:error");
       }
     }
     Services.obs.addObserver(observe, "page-thumbnail:create");
     Services.obs.addObserver(observe, "page-thumbnail:error");
   });
 }
-
-function bgAddCrashObserver() {
-  let crashed = false;
-  Services.obs.addObserver(function crashObserver(subject, topic, data) {
-    is(topic, "ipc:content-shutdown", "Received correct observer topic.");
-    ok(subject instanceof Components.interfaces.nsIPropertyBag2,
-       "Subject implements nsIPropertyBag2.");
-    // we might see this called as the process terminates due to previous tests.
-    // We are only looking for "abnormal" exits...
-    if (!subject.hasKey("abnormal")) {
-      info("This is a normal termination and isn't the one we are looking for...");
-      return;
-    }
-    Services.obs.removeObserver(crashObserver, "ipc:content-shutdown");
-    crashed = true;
-
-    var dumpID;
-    if ("nsICrashReporter" in Components.interfaces) {
-      dumpID = subject.getPropertyAsAString("dumpID");
-      ok(dumpID, "dumpID is present and not an empty string");
-    }
-
-    if (dumpID) {
-      var minidumpDirectory = getMinidumpDirectory();
-      removeFile(minidumpDirectory, dumpID + ".dmp");
-      removeFile(minidumpDirectory, dumpID + ".extra");
-    }
-  }, "ipc:content-shutdown");
-  return {
-    get crashed() {
-      return crashed;
-    }
-  };
-}
-
-function bgInjectCrashContentScript() {
-  const TEST_CONTENT_HELPER = "chrome://mochitests/content/browser/toolkit/components/thumbnails/test/thumbnails_crash_content_helper.js";
-  let thumbnailBrowser = BackgroundPageThumbs._thumbBrowser;
-  let mm = thumbnailBrowser.messageManager;
-  mm.loadFrameScript(TEST_CONTENT_HELPER, false);
-  return mm;
-}
-
-function getMinidumpDirectory() {
-  var dir = Services.dirsvc.get("ProfD", Components.interfaces.nsIFile);
-  dir.append("minidumps");
-  return dir;
-}
-
-function removeFile(directory, filename) {
-  var file = directory.clone();
-  file.append(filename);
-  if (file.exists()) {
-    file.remove(false);
-  }
-}
deleted file mode 100644
--- a/toolkit/components/thumbnails/test/thumbnails_crash_content_helper.js
+++ /dev/null
@@ -1,33 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-* http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/* eslint-env mozilla/frame-script */
-
-var Cu = Components.utils;
-
-// Ideally we would use CrashTestUtils.jsm, but that's only available for
-// xpcshell tests - so we just copy a ctypes crasher from it.
-Cu.import("resource://gre/modules/ctypes.jsm");
-var crash = function() { // this will crash when called.
-  let zero = new ctypes.intptr_t(8);
-  let badptr = ctypes.cast(zero, ctypes.PointerType(ctypes.int32_t));
-  badptr.contents
-};
-
-
-var TestHelper = {
-  init() {
-    addMessageListener("thumbnails-test:crash", this);
-  },
-
-  receiveMessage(msg) {
-    switch (msg.name) {
-      case "thumbnails-test:crash":
-        privateNoteIntentionalCrash();
-        crash();
-      break;
-    }
-  },
-}
-
-TestHelper.init();
--- a/toolkit/content/tests/browser/browser_crash_previous_frameloader.js
+++ b/toolkit/content/tests/browser/browser_crash_previous_frameloader.js
@@ -17,28 +17,26 @@ function getCrashDumpId(subject) {
 }
 
 /**
  * Cleans up the .dmp and .extra file from a crash.
  *
  * @param id {String} The crash dump id.
  */
 function cleanUpMinidump(id) {
-  if (id) {
-    let dir = Services.dirsvc.get("ProfD", Ci.nsIFile);
-    dir.append("minidumps");
+  let dir = Services.dirsvc.get("ProfD", Ci.nsIFile);
+  dir.append("minidumps");
 
-    let file = dir.clone();
-    file.append(id + ".dmp");
-    file.remove(true);
+  let file = dir.clone();
+  file.append(id + ".dmp");
+  file.remove(true);
 
-    file = dir.clone();
-    file.append(id + ".extra");
-    file.remove(true);
-  }
+  file = dir.clone();
+  file.append(id + ".extra");
+  file.remove(true);
 }
 
 /**
  * This test ensures that if a remote frameloader crashes after
  * the frameloader owner swaps it out for a new frameloader,
  * that a oop-browser-crashed event is not sent to the new
  * frameloader's browser element.
  */
--- a/toolkit/crashreporter/nsExceptionHandler.cpp
+++ b/toolkit/crashreporter/nsExceptionHandler.cpp
@@ -192,22 +192,19 @@ static xpstring *defaultMemoryReportPath
 
 static const char kCrashMainID[] = "crash.main.2\n";
 
 static google_breakpad::ExceptionHandler* gExceptionHandler = nullptr;
 
 static XP_CHAR* pendingDirectory;
 static XP_CHAR* crashReporterPath;
 static XP_CHAR* memoryReportPath;
-#if !defined(MOZ_WIDGET_ANDROID)
-static XP_CHAR* minidumpAnalyzerPath;
 #ifdef XP_MACOSX
 static XP_CHAR* libraryPath; // Path where the NSS library is
 #endif // XP_MACOSX
-#endif // !defined(MOZ_WIDGET_ANDROID)
 
 // Where crash events should go.
 static XP_CHAR* eventsDirectory;
 static char* eventsEnv = nullptr;
 
 // The current telemetry session ID to write to the event file
 static char* currentSessionId = nullptr;
 
@@ -814,21 +811,19 @@ WriteGlobalMemoryStatus(PlatformWriter* 
 
 /**
  * Launches the program specified in aProgramPath with aMinidumpPath as its
  * sole argument.
  *
  * @param aProgramPath The path of the program to be launched
  * @param aMinidumpPath The path of the minidump file, passed as an argument
  *        to the launched program
- * @param aWait If true wait for the program termination
  */
 static bool
-LaunchProgram(const XP_CHAR* aProgramPath, const XP_CHAR* aMinidumpPath,
-              bool aWait = false)
+LaunchProgram(const XP_CHAR* aProgramPath, const XP_CHAR* aMinidumpPath)
 {
 #ifdef XP_WIN
   XP_CHAR cmdLine[CMDLINE_SIZE];
   XP_CHAR* p;
 
   size_t size = CMDLINE_SIZE;
   p = Concat(cmdLine, L"\"", &size);
   p = Concat(p, aProgramPath, &size);
@@ -839,20 +834,16 @@ LaunchProgram(const XP_CHAR* aProgramPat
   PROCESS_INFORMATION pi = {};
   STARTUPINFO si = {};
   si.cb = sizeof(si);
 
   // If CreateProcess() fails don't do anything
   if (CreateProcess(nullptr, (LPWSTR)cmdLine, nullptr, nullptr, FALSE,
                     NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW,
                     nullptr, nullptr, &si, &pi)) {
-    if (aWait) {
-      WaitForSingleObject(pi.hProcess, INFINITE);
-    }
-
     CloseHandle(pi.hProcess);
     CloseHandle(pi.hThread);
   }
 #elif defined(XP_UNIX)
   pid_t pid = sys_fork();
 
   if (pid == -1) {
     return false;
@@ -863,20 +854,16 @@ LaunchProgram(const XP_CHAR* aProgramPat
     unsetenv("LD_LIBRARY_PATH");
 #else // XP_MACOSX
     // Needed to locate NSS and its dependencies
     setenv("DYLD_LIBRARY_PATH", libraryPath, /* overwrite */ 1);
 #endif
     Unused << execl(aProgramPath,
                     aProgramPath, aMinidumpPath, (char*)0);
     _exit(1);
-  } else {
-    if (aWait) {
-      waitpid(pid, nullptr, 0);
-    }
   }
 #endif // XP_UNIX
 
   return true;
 }
 
 #else
 
@@ -1692,32 +1679,21 @@ nsresult SetExceptionHandler(nsIFile* aX
 
     nsAutoString libraryPath_temp;
     rv = libPath->GetPath(libraryPath_temp);
     if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
     }
 #endif // XP_MACOSX
 
-    nsAutoString minidumpAnalyzerPath_temp;
-    rv = LocateExecutable(aXREDirectory,
-                          NS_LITERAL_CSTRING(MINIDUMP_ANALYZER_FILENAME),
-                          minidumpAnalyzerPath_temp);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
 #ifdef XP_WIN32
   crashReporterPath =
     reinterpret_cast<wchar_t*>(ToNewUnicode(crashReporterPath_temp));
-  minidumpAnalyzerPath =
-    reinterpret_cast<wchar_t*>(ToNewUnicode(minidumpAnalyzerPath_temp));
 #else
   crashReporterPath = ToNewCString(crashReporterPath_temp);
-  minidumpAnalyzerPath = ToNewCString(minidumpAnalyzerPath_temp);
 #ifdef XP_MACOSX
   libraryPath = ToNewCString(libraryPath_temp);
 #endif
 #endif // XP_WIN32
 #else
     // On Android, we launch using the application package name instead of a
     // filename, so use the dynamically set MOZ_ANDROID_PACKAGE_NAME, or fall
     // back to the static ANDROID_PACKAGE_NAME.
@@ -2202,28 +2178,22 @@ nsresult UnsetExceptionHandler()
     pendingDirectory = nullptr;
   }
 
   if (crashReporterPath) {
     free(crashReporterPath);
     crashReporterPath = nullptr;
   }
 
-#if !defined(MOZ_WIDGET_ANDROID)
-  if (minidumpAnalyzerPath) {
-    free(minidumpAnalyzerPath);
-    minidumpAnalyzerPath = nullptr;
-  }
 #ifdef XP_MACOSX
   if (libraryPath) {
     free(libraryPath);
     libraryPath = nullptr;
   }
 #endif // XP_MACOSX
-#endif // !defined(MOZ_WIDGET_ANDROID)
 
   if (eventsDirectory) {
     free(eventsDirectory);
     eventsDirectory = nullptr;
   }
 
   if (currentSessionId) {
     free(currentSessionId);
@@ -3126,44 +3096,16 @@ bool
 AppendExtraData(const nsAString& id, const AnnotationTable& data)
 {
   nsCOMPtr<nsIFile> extraFile;
   if (!GetExtraFileForID(id, getter_AddRefs(extraFile)))
     return false;
   return AppendExtraData(extraFile, data);
 }
 
-/**
- * Runs the minidump analyzer program on the specified crash dump. The analyzer
- * will extract the stack traces from the dump and store them in JSON format as
- * an annotation in the extra file associated with the crash.
- *
- * This method waits synchronously for the program to have finished executing,
- * do not call it from the main thread!
- */
-void
-RunMinidumpAnalyzer(const nsAString& id)
-{
-#if !defined(MOZ_WIDGET_ANDROID)
-  nsCOMPtr<nsIFile> file;
-
-  if (CrashReporter::GetMinidumpForID(id, getter_AddRefs(file))) {
-#ifdef XP_WIN
-    nsAutoString path;
-    file->GetPath(path);
-#else
-    nsAutoCString path;
-    file->GetNativePath(path);
-#endif
-
-    LaunchProgram(minidumpAnalyzerPath, path.get(), /* aWait */ true);
-  }
-#endif // !defined(MOZ_WIDGET_ANDROID)
-}
-
 //-----------------------------------------------------------------------------
 // Helpers for AppendExtraData()
 //
 struct Blacklist {
   Blacklist() : mItems(nullptr), mLen(0) { }
   Blacklist(const char** items, int len) : mItems(items), mLen(len) { }
 
   bool Contains(const nsACString& key) const {
--- a/toolkit/crashreporter/nsExceptionHandler.h
+++ b/toolkit/crashreporter/nsExceptionHandler.h
@@ -98,17 +98,16 @@ typedef nsDataHashtable<nsCStringHashKey
 
 void DeleteMinidumpFilesForID(const nsAString& id);
 bool GetMinidumpForID(const nsAString& id, nsIFile** minidump);
 bool GetIDFromMinidump(nsIFile* minidump, nsAString& id);
 bool GetExtraFileForID(const nsAString& id, nsIFile** extraFile);
 bool GetExtraFileForMinidump(nsIFile* minidump, nsIFile** extraFile);
 bool AppendExtraData(const nsAString& id, const AnnotationTable& data);
 bool AppendExtraData(nsIFile* extraFile, const AnnotationTable& data);
-void RunMinidumpAnalyzer(const nsAString& id);
 
 /*
  * Renames the stand alone dump file aDumpFile to:
  *  |aOwnerDumpFile-aDumpFileProcessType.dmp|
  * and moves it into the same directory as aOwnerDumpFile. Does not
  * modify aOwnerDumpFile in any way.
  *
  * @param aDumpFile - the dump file to associate with aOwnerDumpFile.
--- a/toolkit/crashreporter/test/unit/head_crashreporter.js
+++ b/toolkit/crashreporter/test/unit/head_crashreporter.js
@@ -164,34 +164,34 @@ function do_content_crash(setup, callbac
   if (setup) {
     if (typeof(setup) == "function") {
       // funky, but convenient
       setup = "(" + setup.toSource() + ")();";
     }
   }
 
   let handleCrash = function() {
-    do_get_profile();
-    makeFakeAppDir().then(() => {
-      let id = getMinidump().leafName.slice(0, -4);
-      return Services.crashmanager.ensureCrashIsPresent(id);
-    }).then(() => {
+    let id = getMinidump().leafName.slice(0, -4);
+    Services.crashmanager.ensureCrashIsPresent(id).then(() => {
       try {
         handleMinidump(callback);
       } catch (x) {
         do_report_unexpected_exception(x);
       }
       do_test_finished();
     });
   };
 
-  sendCommand("load(\"" + headfile.path.replace(/\\/g, "/") + "\");", () =>
-    sendCommand(setup, () =>
-      sendCommand("load(\"" + tailfile.path.replace(/\\/g, "/") + "\");", () =>
-        do_execute_soon(handleCrash)
+  do_get_profile();
+  makeFakeAppDir().then(() => {
+    sendCommand("load(\"" + headfile.path.replace(/\\/g, "/") + "\");", () =>
+      sendCommand(setup, () =>
+        sendCommand("load(\"" + tailfile.path.replace(/\\/g, "/") + "\");", () =>
+          do_execute_soon(handleCrash)
+        )
       )
-    )
-  );
+    );
+  });
 }
 
 // Import binary APIs via js-ctypes.
 Components.utils.import("resource://test/CrashTestUtils.jsm");
 Components.utils.import("resource://gre/modules/KeyValueParser.jsm");
--- a/tools/profiler/gecko/ProfilerChild.cpp
+++ b/tools/profiler/gecko/ProfilerChild.cpp
@@ -68,19 +68,19 @@ CollectProfileOrEmptyString()
     profileCString = nsCString(profile.get(), strlen(profile.get()));
   } else {
     profileCString = EmptyCString();
   }
   return profileCString;
 }
 
 mozilla::ipc::IPCResult
-ProfilerChild::RecvGatherProfile(RefPtr<GatherProfilePromise>&& aPromise)
+ProfilerChild::RecvGatherProfile(GatherProfileResolver&& aResolve)
 {
-  aPromise->Resolve(CollectProfileOrEmptyString(), __func__);
+  aResolve(CollectProfileOrEmptyString());
   return IPC_OK();
 }
 
 void
 ProfilerChild::ActorDestroy(ActorDestroyReason aActorDestroyReason)
 {
   mDestroyed = true;
 }
--- a/tools/profiler/public/ProfilerChild.h
+++ b/tools/profiler/public/ProfilerChild.h
@@ -34,17 +34,17 @@ class ProfilerChild final : public PProf
 
 private:
   virtual ~ProfilerChild();
 
   mozilla::ipc::IPCResult RecvStart(const ProfilerInitParams& params) override;
   mozilla::ipc::IPCResult RecvStop() override;
   mozilla::ipc::IPCResult RecvPause() override;
   mozilla::ipc::IPCResult RecvResume() override;
-  mozilla::ipc::IPCResult RecvGatherProfile(RefPtr<GatherProfilePromise>&& aPromise) override;
+  mozilla::ipc::IPCResult RecvGatherProfile(GatherProfileResolver&& aResolve) override;
 
   void ActorDestroy(ActorDestroyReason aActorDestroyReason) override;
 
   nsCOMPtr<nsIThread> mThread;
   bool mDestroyed;
 };
 
 } // namespace mozilla