Merge inbound to mozilla-central. a=merge
authorCsoregi Natalia <ncsoregi@mozilla.com>
Tue, 15 May 2018 12:53:24 +0300
changeset 418327 cf3ee14023483cbbb57129479537c713e22c1980
parent 418274 2fa8aedae0ffbed270af75dd62489bc33f589b0f (current diff)
parent 418326 c81dfed2fa9fbde91af9d33a929461105102b92e (diff)
child 418328 fa83b2d9e6b9e26cde42fedcc599288975ae31ab
child 418390 48d6f36d141287c064af3a24e9968b6cc4ac92a0
push id63989
push userncsoregi@mozilla.com
push dateTue, 15 May 2018 09:55:49 +0000
treeherderautoland@fa83b2d9e6b9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone62.0a1
first release with
nightly linux32
cf3ee1402348 / 62.0a1 / 20180515100038 / files
nightly linux64
cf3ee1402348 / 62.0a1 / 20180515100038 / files
nightly mac
cf3ee1402348 / 62.0a1 / 20180515100038 / files
nightly win32
cf3ee1402348 / 62.0a1 / 20180515100038 / files
nightly win64
cf3ee1402348 / 62.0a1 / 20180515100038 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
dom/webidl/XMLStylesheetProcessingInstruction.webidl
layout/generic/nsFrame.cpp
toolkit/components/telemetry/Telemetry.cpp
toolkit/components/telemetry/nsITelemetry.idl
--- a/browser/app/LauncherProcessWin.cpp
+++ b/browser/app/LauncherProcessWin.cpp
@@ -75,17 +75,41 @@ ShowError(DWORD aError = ::GetLastError(
   if (!result) {
     return;
   }
 
   ::MessageBoxW(nullptr, rawMsgBuf, L"Firefox", MB_OK | MB_ICONERROR);
   ::LocalFree(rawMsgBuf);
 }
 
-static wchar_t gAbsPath[MAX_PATH];
+static bool
+SetArgv0ToFullBinaryPath(wchar_t* aArgv[])
+{
+  DWORD bufLen = MAX_PATH;
+  mozilla::UniquePtr<wchar_t[]> buf;
+
+  while (true) {
+    buf = mozilla::MakeUnique<wchar_t[]>(bufLen);
+    DWORD retLen = ::GetModuleFileNameW(nullptr, buf.get(), bufLen);
+    if (!retLen) {
+      return false;
+    }
+
+    if (retLen == bufLen && ::GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
+      bufLen *= 2;
+      continue;
+    }
+
+    break;
+  }
+
+  // We intentionally leak buf into argv[0]
+  aArgv[0] = buf.release();
+  return true;
+}
 
 namespace mozilla {
 
 // Eventually we want to be able to set a build config flag such that, when set,
 // this function will always return true.
 bool
 RunAsLauncherProcess(int& argc, wchar_t** argv)
 {
@@ -107,28 +131,21 @@ LauncherMain(int argc, wchar_t* argv[])
 
       DebugOnly<BOOL> setOk = pSetProcessMitigationPolicy(ProcessImageLoadPolicy,
                                                           &imgLoadPol,
                                                           sizeof(imgLoadPol));
       MOZ_ASSERT(setOk);
     }
   }
 
-  // Convert argv[0] to an absolute path if necessary
-  DWORD absPathLen = ::SearchPathW(nullptr, argv[0], L".exe",
-                                   ArrayLength(gAbsPath), gAbsPath, nullptr);
-  if (!absPathLen) {
+  if (!SetArgv0ToFullBinaryPath(argv)) {
     ShowError();
     return 1;
   }
 
-  if (absPathLen < ArrayLength(gAbsPath)) {
-    argv[0] = gAbsPath;
-  }
-
   // If we're elevated, we should relaunch ourselves as a normal user
   Maybe<bool> isElevated = IsElevated();
   if (!isElevated) {
     return 1;
   }
 
   if (isElevated.value()) {
     return !LaunchUnelevated(argc, argv);
--- a/browser/base/content/test/tabs/browser.ini
+++ b/browser/base/content/test/tabs/browser.ini
@@ -20,16 +20,17 @@ skip-if = os == 'mac'
 [browser_navigatePinnedTab.js]
 [browser_new_file_whitelisted_http_tab.js]
 skip-if = !e10s # Test only relevant for e10s.
 [browser_new_web_tab_in_file_process_pref.js]
 skip-if = !e10s # Pref and test only relevant for e10s.
 [browser_newwindow_tabstrip_overflow.js]
 [browser_opened_file_tab_navigated_to_web.js]
 [browser_new_tab_insert_position.js]
+skip-if = (debug && os == 'linux' && bits == 32) #Bug 1455882, disabled on Linux32 for almost permafailing
 support-files = file_new_tab_page.html
 [browser_overflowScroll.js]
 [browser_pinnedTabs.js]
 [browser_pinnedTabs_clickOpen.js]
 [browser_pinnedTabs_closeByKeyboard.js]
 [browser_positional_attributes.js]
 [browser_preloadedBrowser_zoom.js]
 [browser_reload_deleted_file.js]
--- a/dom/base/DocumentOrShadowRoot.cpp
+++ b/dom/base/DocumentOrShadowRoot.cpp
@@ -278,10 +278,53 @@ DocumentOrShadowRoot::ElementsFromPointH
       // element we find.
       if (aFlags & nsIDocument::IS_ELEMENT_FROM_POINT) {
         return;
       }
     }
   }
 }
 
+Element*
+DocumentOrShadowRoot::AddIDTargetObserver(nsAtom* aID,
+                                          IDTargetObserver aObserver,
+                                          void* aData, bool aForImage)
+{
+  nsDependentAtomString id(aID);
+
+  if (!CheckGetElementByIdArg(id)) {
+    return nullptr;
+  }
+
+  nsIdentifierMapEntry* entry = mIdentifierMap.PutEntry(aID);
+  NS_ENSURE_TRUE(entry, nullptr);
+
+  entry->AddContentChangeCallback(aObserver, aData, aForImage);
+  return aForImage ? entry->GetImageIdElement() : entry->GetIdElement();
+}
+
+void
+DocumentOrShadowRoot::RemoveIDTargetObserver(nsAtom* aID,
+                                             IDTargetObserver aObserver,
+                                             void* aData, bool aForImage)
+{
+  nsDependentAtomString id(aID);
+
+  if (!CheckGetElementByIdArg(id)) {
+    return;
+  }
+
+  nsIdentifierMapEntry* entry = mIdentifierMap.GetEntry(aID);
+  if (!entry) {
+    return;
+  }
+
+  entry->RemoveContentChangeCallback(aObserver, aData, aForImage);
+}
+
+void
+DocumentOrShadowRoot::ReportEmptyGetElementByIdArg()
+{
+  nsContentUtils::ReportEmptyGetElementByIdArg(AsNode().OwnerDoc());
+}
+
 }
 }
--- a/dom/base/DocumentOrShadowRoot.h
+++ b/dom/base/DocumentOrShadowRoot.h
@@ -134,16 +134,64 @@ public:
     IGNORE_ROOT_SCROLL_FRAME = 1,
     FLUSH_LAYOUT = 2,
     IS_ELEMENT_FROM_POINT = 4
   };
 
   void ElementsFromPointHelper(float aX, float aY, uint32_t aFlags,
                                nsTArray<RefPtr<mozilla::dom::Element>>& aElements);
 
+  /**
+   * This gets fired when the element that an id refers to changes.
+   * This fires at difficult times. It is generally not safe to do anything
+   * which could modify the DOM in any way. Use
+   * nsContentUtils::AddScriptRunner.
+   * @return true to keep the callback in the callback set, false
+   * to remove it.
+   */
+  typedef bool (* IDTargetObserver)(Element* aOldElement,
+                                    Element* aNewelement, void* aData);
+
+  /**
+   * Add an IDTargetObserver for a specific ID. The IDTargetObserver
+   * will be fired whenever the content associated with the ID changes
+   * in the future. If aForImage is true, mozSetImageElement can override
+   * what content is associated with the ID. In that case the IDTargetObserver
+   * will be notified at those times when the result of LookupImageElement
+   * changes.
+   * At most one (aObserver, aData, aForImage) triple can be
+   * registered for each ID.
+   * @return the content currently associated with the ID.
+   */
+  Element* AddIDTargetObserver(nsAtom* aID, IDTargetObserver aObserver,
+                               void* aData, bool aForImage);
+
+  /**
+   * Remove the (aObserver, aData, aForImage) triple for a specific ID, if
+   * registered.
+   */
+  void RemoveIDTargetObserver(nsAtom* aID, IDTargetObserver aObserver,
+                              void* aData, bool aForImage);
+
+  /**
+   * Check that aId is not empty and log a message to the console
+   * service if it is.
+   * @returns true if aId looks correct, false otherwise.
+   */
+  inline bool CheckGetElementByIdArg(const nsAString& aId)
+  {
+    if (aId.IsEmpty()) {
+      ReportEmptyGetElementByIdArg();
+      return false;
+    }
+    return true;
+  }
+
+  void ReportEmptyGetElementByIdArg();
+
 protected:
   nsIContent* Retarget(nsIContent* aContent) const;
 
   /**
    * If focused element's subtree root is this document or shadow root, return
    * focused element, otherwise, get the shadow host recursively until the
    * shadow host's subtree root is this document or shadow root.
    */
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -9,16 +9,17 @@
  * of DOM Core's Element, implements nsIContent, provides
  * utility methods for subclasses, and so forth.
  */
 
 #include "mozilla/dom/ElementInlines.h"
 
 #include "AnimationCommon.h"
 #include "mozilla/DebugOnly.h"
+#include "mozilla/StaticPrefs.h"
 #include "mozilla/dom/Animation.h"
 #include "mozilla/dom/Attr.h"
 #include "mozilla/dom/Flex.h"
 #include "mozilla/dom/Grid.h"
 #include "mozilla/gfx/Matrix.h"
 #include "nsAtom.h"
 #include "nsCSSFrameConstructor.h"
 #include "nsDOMAttributeMap.h"
@@ -1242,16 +1243,20 @@ Element::AttachShadow(const ShadowRootIn
    *    context object’s node document, host is context object,
    *    and mode is init’s mode.
    */
   RefPtr<ShadowRoot> shadowRoot =
     new ShadowRoot(this, aInit.mMode, nodeInfo.forget());
 
   shadowRoot->SetIsComposedDocParticipant(IsInComposedDoc());
 
+  if (StaticPrefs::dom_webcomponents_shadowdom_report_usage()) {
+    OwnerDoc()->ReportShadowDOMUsage();
+  }
+
   /**
    * 5. Set context object’s shadow root to shadow.
    */
   SetShadowRoot(shadowRoot);
 
   /**
    * 6. Return shadow.
    */
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -1451,16 +1451,17 @@ nsIDocument::nsIDocument()
     mAllowZoom(false),
     mAllowDoubleTapZoom(false),
     mValidScaleFloat(false),
     mValidMaxScale(false),
     mScaleStrEmpty(false),
     mWidthStrEmpty(false),
     mParserAborted(false),
     mReportedUseCounters(false),
+    mHasReportedShadowDOMUsage(false),
 #ifdef DEBUG
     mWillReparent(false),
 #endif
     mPendingFullscreenRequests(0),
     mXMLDeclarationBits(0),
     mOnloadBlockCount(0),
     mAsyncOnloadBlockCount(0),
     mCompatMode(eCompatibility_FullStandards),
@@ -5087,55 +5088,16 @@ nsDocument::BeginLoad()
   if (mScriptLoader) {
     mScriptLoader->BeginDeferringScripts();
   }
 
   NS_DOCUMENT_NOTIFY_OBSERVERS(BeginLoad, (this));
 }
 
 void
-nsIDocument::ReportEmptyGetElementByIdArg()
-{
-  nsContentUtils::ReportEmptyGetElementByIdArg(this);
-}
-
-Element*
-nsIDocument::AddIDTargetObserver(nsAtom* aID, IDTargetObserver aObserver,
-                                void* aData, bool aForImage)
-{
-  nsDependentAtomString id(aID);
-
-  if (!CheckGetElementByIdArg(id))
-    return nullptr;
-
-  nsIdentifierMapEntry* entry = mIdentifierMap.PutEntry(aID);
-  NS_ENSURE_TRUE(entry, nullptr);
-
-  entry->AddContentChangeCallback(aObserver, aData, aForImage);
-  return aForImage ? entry->GetImageIdElement() : entry->GetIdElement();
-}
-
-void
-nsIDocument::RemoveIDTargetObserver(nsAtom* aID, IDTargetObserver aObserver,
-                                   void* aData, bool aForImage)
-{
-  nsDependentAtomString id(aID);
-
-  if (!CheckGetElementByIdArg(id))
-    return;
-
-  nsIdentifierMapEntry* entry = mIdentifierMap.GetEntry(aID);
-  if (!entry) {
-    return;
-  }
-
-  entry->RemoveContentChangeCallback(aObserver, aData, aForImage);
-}
-
-void
 nsIDocument::MozSetImageElement(const nsAString& aImageElementId,
                                Element* aElement)
 {
   if (aImageElementId.IsEmpty())
     return;
 
   // Hold a script blocker while calling SetImageElement since that can call
   // out to id-observers
@@ -13254,8 +13216,32 @@ nsIDocument::ModuleScriptsEnabled()
   static bool sCachedPref = false;
   if (!sCachedPref) {
     sCachedPref = true;
     Preferences::AddBoolVarCache(&sEnabledForContent, "dom.moduleScripts.enabled", false);
   }
 
   return nsContentUtils::IsChromeDoc(this) || sEnabledForContent;
 }
+
+void
+nsIDocument::ReportShadowDOMUsage()
+{
+  if (mHasReportedShadowDOMUsage) {
+    return;
+  }
+
+  nsIDocument* topLevel = GetTopLevelContentDocument();
+  if (topLevel && !topLevel->mHasReportedShadowDOMUsage) {
+    topLevel->mHasReportedShadowDOMUsage = true;
+    nsString uri;
+    Unused << topLevel->GetDocumentURI(uri);
+    if (!uri.IsEmpty()) {
+      nsAutoString msg = NS_LITERAL_STRING("Shadow DOM used in [") + uri +
+        NS_LITERAL_STRING("] or in some of its subdocuments.");
+      nsContentUtils::ReportToConsoleNonLocalized(msg, nsIScriptError::infoFlag,
+                                                  NS_LITERAL_CSTRING("DOM"),
+                                                  topLevel);
+    }
+  }
+
+  mHasReportedShadowDOMUsage = true;
+}
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -795,63 +795,16 @@ public:
   // This method MUST be called before SetDocumentCharacterSet if
   // you're planning to call both.
   void SetDocumentCharacterSetSource(int32_t aCharsetSource)
   {
     mCharacterSetSource = aCharsetSource;
   }
 
   /**
-   * This gets fired when the element that an id refers to changes.
-   * This fires at difficult times. It is generally not safe to do anything
-   * which could modify the DOM in any way. Use
-   * nsContentUtils::AddScriptRunner.
-   * @return true to keep the callback in the callback set, false
-   * to remove it.
-   */
-  typedef bool (* IDTargetObserver)(Element* aOldElement,
-                                    Element* aNewelement, void* aData);
-
-  /**
-   * Add an IDTargetObserver for a specific ID. The IDTargetObserver
-   * will be fired whenever the content associated with the ID changes
-   * in the future. If aForImage is true, mozSetImageElement can override
-   * what content is associated with the ID. In that case the IDTargetObserver
-   * will be notified at those times when the result of LookupImageElement
-   * changes.
-   * At most one (aObserver, aData, aForImage) triple can be
-   * registered for each ID.
-   * @return the content currently associated with the ID.
-   */
-  Element* AddIDTargetObserver(nsAtom* aID, IDTargetObserver aObserver,
-                               void* aData, bool aForImage);
-  /**
-   * Remove the (aObserver, aData, aForImage) triple for a specific ID, if
-   * registered.
-   */
-  void RemoveIDTargetObserver(nsAtom* aID, IDTargetObserver aObserver,
-                              void* aData, bool aForImage);
-
-  /**
-   * Check that aId is not empty and log a message to the console
-   * service if it is.
-   * @returns true if aId looks correct, false otherwise.
-   */
-  inline bool CheckGetElementByIdArg(const nsAString& aId)
-  {
-    if (aId.IsEmpty()) {
-      ReportEmptyGetElementByIdArg();
-      return false;
-    }
-    return true;
-  }
-
-  void ReportEmptyGetElementByIdArg();
-
-  /**
    * Get the Content-Type of this document.
    */
   void GetContentType(nsAString& aContentType);
 
   /**
    * Set the Content-Type of this document.
    */
   virtual void SetContentType(const nsAString& aContentType);
@@ -3578,16 +3531,18 @@ public:
    * be aFrame's content node if that content is in this document and not
    * anonymous. Otherwise, when aFrame is in a subdocument, we use the frame
    * element containing the subdocument containing aFrame, and/or find the
    * nearest non-anonymous ancestor in this document.
    * Returns null if there is no such element.
    */
   nsIContent* GetContentInThisDocument(nsIFrame* aFrame) const;
 
+  void ReportShadowDOMUsage();
+
 protected:
   already_AddRefed<nsIPrincipal> MaybeDowngradePrincipal(nsIPrincipal* aPrincipal);
 
   void EnsureOnloadBlocker();
 
   void SendToConsole(nsCOMArray<nsISecurityConsoleMessage>& aMessages);
 
   // Returns true if the scheme for the url for this document is "about".
@@ -4094,16 +4049,18 @@ protected:
   // Whether we have reported use counters for this document with Telemetry yet.
   // Normally this is only done at document destruction time, but for image
   // documents (SVG documents) that are not guaranteed to be destroyed, we
   // report use counters when the image cache no longer has any imgRequestProxys
   // pointing to them.  We track whether we ever reported use counters so
   // that we only report them once for the document.
   bool mReportedUseCounters: 1;
 
+  bool mHasReportedShadowDOMUsage: 1;
+
 #ifdef DEBUG
 public:
   bool mWillReparent: 1;
 protected:
 #endif
 
   uint8_t mPendingFullscreenRequests;
 
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -2263,20 +2263,19 @@ nsPluginHost::UpdatePluginBlocklistState
 {
   nsCOMPtr<nsIBlocklistService> blocklist =
     do_GetService("@mozilla.org/extensions/blocklist;1");
   MOZ_ASSERT(blocklist, "Should be able to access the blocklist");
   if (!blocklist) {
     return;
   }
   // Asynchronously get the blocklist state.
-  nsCOMPtr<nsISupports> result;
+  RefPtr<Promise> promise;
   blocklist->GetPluginBlocklistState(aPluginTag, EmptyString(),
-                                     EmptyString(), getter_AddRefs(result));
-  RefPtr<Promise> promise = do_QueryObject(result);
+                                     EmptyString(), getter_AddRefs(promise));
   MOZ_ASSERT(promise, "Should always get a promise for plugin blocklist state.");
   if (promise) {
     promise->AppendNativeHandler(new mozilla::plugins::BlocklistPromiseHandler(aPluginTag, aShouldSoftblock));
   }
 }
 
 nsresult nsPluginHost::ScanPluginsDirectoryList(nsISimpleEnumerator *dirEnum,
                                                 bool aCreatePluginList,
--- a/dom/presentation/PresentationService.cpp
+++ b/dom/presentation/PresentationService.cpp
@@ -536,24 +536,23 @@ PresentationService::HandleSessionReques
 
   // Notify the receiver to launch.
   nsCOMPtr<nsIPresentationRequestUIGlue> glue =
     do_CreateInstance(PRESENTATION_REQUEST_UI_GLUE_CONTRACTID);
   if (NS_WARN_IF(!glue)) {
     ctrlChannel->Disconnect(NS_ERROR_DOM_OPERATION_ERR);
     return info->ReplyError(NS_ERROR_DOM_OPERATION_ERR);
   }
-  nsCOMPtr<nsISupports> promise;
+  RefPtr<Promise> promise;
   rv = glue->SendRequest(url, sessionId, device, getter_AddRefs(promise));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     ctrlChannel->Disconnect(rv);
     return info->ReplyError(NS_ERROR_DOM_OPERATION_ERR);
   }
-  nsCOMPtr<Promise> realPromise = do_QueryInterface(promise);
-  static_cast<PresentationPresentingInfo*>(info.get())->SetPromise(realPromise);
+  static_cast<PresentationPresentingInfo*>(info.get())->SetPromise(promise);
 
   return NS_OK;
 }
 
 nsresult
 PresentationService::HandleTerminateRequest(nsIPresentationTerminateRequest* aRequest)
 {
   nsCOMPtr<nsIPresentationControlChannel> ctrlChannel;
--- a/dom/presentation/interfaces/nsIPresentationRequestUIGlue.idl
+++ b/dom/presentation/interfaces/nsIPresentationRequestUIGlue.idl
@@ -18,12 +18,12 @@ interface nsIPresentationRequestUIGlue :
    * This method is called to open the responding app/page when
    * a presentation request comes in at receiver side.
    *
    * @param url       The url of the request.
    * @param sessionId The session ID of the request.
    *
    * @return A promise that resolves to the opening frame.
    */
-  nsISupports sendRequest(in DOMString url,
-                          in DOMString sessionId,
-                          in nsIPresentationDevice device);
+  Promise sendRequest(in DOMString url,
+                      in DOMString sessionId,
+                      in nsIPresentationDevice device);
 };
--- a/dom/promise/Promise.cpp
+++ b/dom/promise/Promise.cpp
@@ -58,23 +58,18 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Promise)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Promise)
   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mPromiseObj);
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
-NS_IMPL_CYCLE_COLLECTING_ADDREF(Promise)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(Promise)
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Promise)
-  NS_INTERFACE_MAP_ENTRY(nsISupports)
-  NS_INTERFACE_MAP_ENTRY(Promise)
-NS_INTERFACE_MAP_END
+NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(Promise, AddRef);
+NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(Promise, Release);
 
 Promise::Promise(nsIGlobalObject* aGlobal)
   : mGlobal(aGlobal)
   , mPromiseObj(nullptr)
 {
   MOZ_ASSERT(mGlobal);
 
   mozilla::HoldJSObjects(this);
--- a/dom/promise/Promise.h
+++ b/dom/promise/Promise.h
@@ -27,31 +27,25 @@ namespace mozilla {
 namespace dom {
 
 class AnyCallback;
 class MediaStreamError;
 class PromiseInit;
 class PromiseNativeHandler;
 class PromiseDebugging;
 
-#define NS_PROMISE_IID \
-  { 0x1b8d6215, 0x3e67, 0x43ba, \
-    { 0x8a, 0xf9, 0x31, 0x5e, 0x8f, 0xce, 0x75, 0x65 } }
-
-class Promise : public nsISupports,
-                public SupportsWeakPtr<Promise>
+class Promise : public SupportsWeakPtr<Promise>
 {
   friend class PromiseTask;
   friend class PromiseWorkerProxy;
   friend class PromiseWorkerProxyRunnable;
 
 public:
-  NS_DECLARE_STATIC_IID_ACCESSOR(NS_PROMISE_IID)
-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Promise)
+  NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(Promise)
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(Promise)
   MOZ_DECLARE_WEAKREFERENCE_TYPENAME(Promise)
 
   // Promise creation tries to create a JS reflector for the Promise, so is
   // fallible.  Furthermore, we don't want to do JS-wrapping on a 0-refcount
   // object, so we addref before doing that and return the addrefed pointer
   // here.
   static already_AddRefed<Promise>
   Create(nsIGlobalObject* aGlobal, ErrorResult& aRv);
@@ -199,14 +193,12 @@ private:
 
   void HandleException(JSContext* aCx);
 
   RefPtr<nsIGlobalObject> mGlobal;
 
   JS::Heap<JSObject*> mPromiseObj;
 };
 
-NS_DEFINE_STATIC_IID_ACCESSOR(Promise, NS_PROMISE_IID)
-
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_Promise_h
--- a/dom/tests/mochitest/general/test_interfaces.js
+++ b/dom/tests/mochitest/general/test_interfaces.js
@@ -1240,18 +1240,16 @@ var interfaceNamesInGlobalScope =
     {name: "XMLHttpRequest", insecureContext: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "XMLHttpRequestEventTarget", insecureContext: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "XMLHttpRequestUpload", insecureContext: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "XMLSerializer", insecureContext: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "XMLStylesheetProcessingInstruction", insecureContext: true},
-// IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "XPathEvaluator", insecureContext: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "XPathExpression", insecureContext: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "XPathResult", insecureContext: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "XSLTProcessor", insecureContext: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
--- a/dom/webidl/ProcessingInstruction.webidl
+++ b/dom/webidl/ProcessingInstruction.webidl
@@ -1,15 +1,20 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  *
  * The origin of this IDL file is
- * http://www.w3.org/TR/2012/WD-dom-20120105/
+ * https://dom.spec.whatwg.org/#interface-processinginstruction
+ * https://drafts.csswg.org/cssom/#requirements-on-user-agents-implementing-the-xml-stylesheet-processing-instruction
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
+// https://dom.spec.whatwg.org/#interface-processinginstruction
 interface ProcessingInstruction : CharacterData {
   readonly attribute DOMString target;
 };
+
+// https://drafts.csswg.org/cssom/#requirements-on-user-agents-implementing-the-xml-stylesheet-processing-instruction
+ProcessingInstruction implements LinkStyle;
deleted file mode 100644
--- a/dom/webidl/XMLStylesheetProcessingInstruction.webidl
+++ /dev/null
@@ -1,9 +0,0 @@
-/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/.
- */
-
-interface XMLStylesheetProcessingInstruction : ProcessingInstruction {
-  readonly attribute StyleSheet? sheet;
-};
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -936,17 +936,16 @@ WEBIDL_FILES = [
     'WorkerNavigator.webidl',
     'Worklet.webidl',
     'WorkletGlobalScope.webidl',
     'XMLDocument.webidl',
     'XMLHttpRequest.webidl',
     'XMLHttpRequestEventTarget.webidl',
     'XMLHttpRequestUpload.webidl',
     'XMLSerializer.webidl',
-    'XMLStylesheetProcessingInstruction.webidl',
     'XPathEvaluator.webidl',
     'XPathExpression.webidl',
     'XPathNSResolver.webidl',
     'XPathResult.webidl',
     'XSLTProcessor.webidl',
     'XULCommandEvent.webidl',
     'XULDocument.webidl',
     'XULElement.webidl',
--- a/dom/xml/ProcessingInstruction.cpp
+++ b/dom/xml/ProcessingInstruction.cpp
@@ -57,16 +57,18 @@ ProcessingInstruction::ProcessingInstruc
                   aData.BeginReading(), aData.Length(),
                   false);  // Don't notify (bug 420429).
 }
 
 ProcessingInstruction::~ProcessingInstruction()
 {
 }
 
+// If you add nsIStyleSheetLinkingElement here, make sure we actually
+// implement the nsStyleLinkElement methods.
 NS_IMPL_ISUPPORTS_INHERITED(ProcessingInstruction, CharacterData, nsIDOMNode)
 
 JSObject*
 ProcessingInstruction::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return ProcessingInstructionBinding::Wrap(aCx, this, aGivenProto);
 }
 
@@ -84,16 +86,26 @@ ProcessingInstruction::CloneDataNode(moz
                                      bool aCloneText) const
 {
   nsAutoString data;
   GetData(data);
   RefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo;
   return do_AddRef(new ProcessingInstruction(ni.forget(), data));
 }
 
+Maybe<nsStyleLinkElement::SheetInfo>
+ProcessingInstruction::GetStyleSheetInfo()
+{
+  MOZ_ASSERT_UNREACHABLE("XMLStylesheetProcessingInstruction should override "
+                         "this and we don't try to do stylesheet stuff.  In "
+                         "particular, we do not implement "
+                         "nsIStyleSheetLinkingElement");
+  return Nothing();
+}
+
 #ifdef DEBUG
 void
 ProcessingInstruction::List(FILE* out, int32_t aIndent) const
 {
   int32_t index;
   for (index = aIndent; --index >= 0; ) fputs("  ", out);
 
   fprintf(out, "Processing instruction refcount=%" PRIuPTR "<", mRefCnt.get());
--- a/dom/xml/ProcessingInstruction.h
+++ b/dom/xml/ProcessingInstruction.h
@@ -6,22 +6,27 @@
 
 #ifndef mozilla_dom_ProcessingInstruction_h
 #define mozilla_dom_ProcessingInstruction_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/CharacterData.h"
 #include "nsIDOMNode.h"
 #include "nsAString.h"
+#include "nsStyleLinkElement.h"
+
+class nsIPrincipal;
+class nsIURI;
 
 namespace mozilla {
 namespace dom {
 
-class ProcessingInstruction : public CharacterData,
-                              public nsIDOMNode
+class ProcessingInstruction : public CharacterData
+                            , public nsStyleLinkElement
+                            , public nsIDOMNode
 {
 public:
   ProcessingInstruction(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
                         const nsAString& aData);
 
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
 
@@ -55,16 +60,19 @@ protected:
    *
    * @param aName the name of the attribute to get the value for
    * @param aValue [out] the value for the attribute with name specified in
    *                     aAttribute. Empty if the attribute isn't present.
    */
   bool GetAttrValue(nsAtom *aName, nsAString& aValue);
 
   virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override;
+
+  // nsStyleLinkElement overrides, because we can't leave them pure virtual.
+  Maybe<SheetInfo> GetStyleSheetInfo() override;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 /**
  * aNodeInfoManager must not be null.
  */
--- a/dom/xml/XMLStylesheetProcessingInstruction.cpp
+++ b/dom/xml/XMLStylesheetProcessingInstruction.cpp
@@ -1,16 +1,15 @@
 /* -*- 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 "XMLStylesheetProcessingInstruction.h"
-#include "mozilla/dom/XMLStylesheetProcessingInstructionBinding.h"
 #include "nsContentUtils.h"
 #include "nsNetUtil.h"
 
 namespace mozilla {
 namespace dom {
 
 // nsISupports implementation
 
@@ -31,22 +30,16 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_IN
   tmp->nsStyleLinkElement::Unlink();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 
 XMLStylesheetProcessingInstruction::~XMLStylesheetProcessingInstruction()
 {
 }
 
-JSObject*
-XMLStylesheetProcessingInstruction::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
-{
-  return XMLStylesheetProcessingInstructionBinding::Wrap(aCx, this, aGivenProto);
-}
-
 // nsIContent
 
 nsresult
 XMLStylesheetProcessingInstruction::BindToTree(nsIDocument* aDocument,
                                                nsIContent* aParent,
                                                nsIContent* aBindingParent,
                                                bool aCompileEventHandlers)
 {
--- a/dom/xml/XMLStylesheetProcessingInstruction.h
+++ b/dom/xml/XMLStylesheetProcessingInstruction.h
@@ -12,18 +12,17 @@
 #include "mozilla/dom/ProcessingInstruction.h"
 #include "nsIURI.h"
 #include "nsStyleLinkElement.h"
 
 namespace mozilla {
 namespace dom {
 
 class XMLStylesheetProcessingInstruction final
-: public ProcessingInstruction
-, public nsStyleLinkElement
+  : public ProcessingInstruction
 {
 public:
   XMLStylesheetProcessingInstruction(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
                                      const nsAString& aData)
     : ProcessingInstruction(Move(aNodeInfo), aData)
   {
   }
 
@@ -32,18 +31,16 @@ public:
     : ProcessingInstruction(aNodeInfoManager->GetNodeInfo(
                                        nsGkAtoms::processingInstructionTagName,
                                        nullptr, kNameSpaceID_None,
                                        PROCESSING_INSTRUCTION_NODE,
                                        nsGkAtoms::xml_stylesheet), aData)
   {
   }
 
-  virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override;
-
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
 
   // CC
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(XMLStylesheetProcessingInstruction,
                                            ProcessingInstruction)
 
   // nsIDOMNode
--- a/ipc/glue/CrashReporterHost.cpp
+++ b/ipc/glue/CrashReporterHost.cpp
@@ -1,16 +1,17 @@
 /* -*- 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/dom/Promise.h"
 #include "mozilla/ipc/GeckoChildProcessHost.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/SyncRunnable.h"
 #include "mozilla/Telemetry.h"
 #include "nsExceptionHandler.h"
 #include "nsIAsyncShutdown.h"
 #include "nsICrashService.h"
 
@@ -270,17 +271,17 @@ CrashReporterHost::NotifyCrashService(Ge
       processType = nsICrashService::PROCESS_TYPE_GPU;
       telemetryKey.AssignLiteral("gpu");
       break;
     default:
       NS_ERROR("unknown process type");
       return;
   }
 
-  nsCOMPtr<nsISupports> promise;
+  RefPtr<Promise> 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/js/public/HeapAPI.h
+++ b/js/public/HeapAPI.h
@@ -9,16 +9,18 @@
 
 #include <limits.h>
 
 #include "jspubtd.h"
 
 #include "js/TraceKind.h"
 #include "js/Utility.h"
 
+struct JSStringFinalizer;
+
 /* These values are private to the JS engine. */
 namespace js {
 
 JS_FRIEND_API(bool)
 CurrentThreadCanAccessZone(JS::Zone* zone);
 
 namespace gc {
 
@@ -196,16 +198,57 @@ struct Zone
     bool isGCMarking() const { return gcState_ == Mark || gcState_ == MarkGray; }
     bool isGCSweepingOrCompacting() const { return gcState_ == Sweep || gcState_ == Compact; }
 
     static MOZ_ALWAYS_INLINE JS::shadow::Zone* asShadowZone(JS::Zone* zone) {
         return reinterpret_cast<JS::shadow::Zone*>(zone);
     }
 };
 
+struct String
+{
+    static const uint32_t NON_ATOM_BIT     = JS_BIT(0);
+    static const uint32_t LINEAR_BIT       = JS_BIT(1);
+    static const uint32_t INLINE_CHARS_BIT = JS_BIT(3);
+    static const uint32_t LATIN1_CHARS_BIT = JS_BIT(6);
+    static const uint32_t EXTERNAL_FLAGS   = LINEAR_BIT | NON_ATOM_BIT | JS_BIT(5);
+    static const uint32_t TYPE_FLAGS_MASK  = JS_BIT(6) - 1;
+    static const uint32_t PERMANENT_ATOM_MASK    = NON_ATOM_BIT | JS_BIT(5);
+    static const uint32_t PERMANENT_ATOM_FLAGS   = JS_BIT(5);
+
+    uint32_t flags;
+    uint32_t length;
+    union {
+        const JS::Latin1Char* nonInlineCharsLatin1;
+        const char16_t* nonInlineCharsTwoByte;
+        JS::Latin1Char inlineStorageLatin1[1];
+        char16_t inlineStorageTwoByte[1];
+    };
+    const JSStringFinalizer* externalFinalizer;
+
+    static bool nurseryCellIsString(const js::gc::Cell* cell) {
+        MOZ_ASSERT(IsInsideNursery(cell));
+        return reinterpret_cast<const String*>(cell)->flags & NON_ATOM_BIT;
+    }
+
+    static bool isPermanentAtom(const js::gc::Cell* cell) {
+        uint32_t flags = reinterpret_cast<const String*>(cell)->flags;
+        return (flags & PERMANENT_ATOM_MASK) == PERMANENT_ATOM_FLAGS;
+    }
+};
+
+struct Symbol {
+    uint32_t code_;
+    static const uint32_t WellKnownAPILimit = 0x80000000;
+
+    static bool isWellKnownSymbol(const js::gc::Cell* cell) {
+        return reinterpret_cast<const Symbol*>(cell)->code_ < WellKnownAPILimit;
+    }
+};
+
 } /* namespace shadow */
 
 /**
  * A GC pointer, tagged with the trace kind.
  *
  * In general, a GC pointer should be stored with an exact type. This class
  * is for use when that is not possible because a single pointer must point
  * to several kinds of GC thing.
@@ -267,19 +310,22 @@ class JS_FRIEND_API(GCCellPtr)
     // Inline mark bitmap access requires direct pointer arithmetic.
     uintptr_t unsafeAsUIntPtr() const {
         MOZ_ASSERT(asCell());
         MOZ_ASSERT(!js::gc::IsInsideNursery(asCell()));
         return reinterpret_cast<uintptr_t>(asCell());
     }
 
     MOZ_ALWAYS_INLINE bool mayBeOwnedByOtherRuntime() const {
-        if (is<JSString>() || is<JS::Symbol>())
-            return mayBeOwnedByOtherRuntimeSlow();
-        return false;
+        if (!is<JSString>() && !is<JS::Symbol>())
+            return false;
+        if (is<JSString>())
+            return JS::shadow::String::isPermanentAtom(asCell());
+        MOZ_ASSERT(is<JS::Symbol>());
+        return JS::shadow::Symbol::isWellKnownSymbol(asCell());
     }
 
   private:
     static uintptr_t checkedCast(void* p, JS::TraceKind traceKind) {
         js::gc::Cell* cell = static_cast<js::gc::Cell*>(p);
         MOZ_ASSERT((uintptr_t(p) & OutOfLineTraceKindMask) == 0);
         AssertGCThingHasType(cell, traceKind);
         // Note: the OutOfLineTraceKindMask bits are set on all out-of-line kinds
--- a/js/src/build/Makefile.in
+++ b/js/src/build/Makefile.in
@@ -37,44 +37,44 @@ endif
 	cp $^ $@
 
 # Install versioned file, for parallel installability in Linux distributions
 install:: $(LIBRARY_NAME).pc
 	cp $^ $(JS_LIBRARY_NAME).pc
 	$(SYSINSTALL) $(JS_LIBRARY_NAME).pc $(DESTDIR)$(libdir)/pkgconfig
 
 install:: ../js-config.h
-	$(SYSINSTALL) $^ $(DESTDIR)$(includedir)
+	$(SYSINSTALL) $^ $(DESTDIR)$(includedir)/$(JS_LIBRARY_NAME)
 
 ######################################################
 # BEGIN SpiderMonkey header installation
 #
 # Mozilla/Gecko/Firefox mostly doesn't concern itself with defining a sensible
 # install target, because it's shipping primarily packaged builds.  And even if
 # it did, those builds wouldn't really have reason to include header files.  So
 # we have to install public headers ourselves, rather than using something from
 # config/rules.mk or similar.
 #
 # The overall directory structure of the installed headers looks like so:
 #
 #   $(includedir)/
-#      $(LIBRARY_NAME)/
+#      $(JS_LIBRARY_NAME)/
 #        jsapi.h, jspubtd.h, etc. (all of EXPORTS)
 #        js/
 #          js/public/* headers (all are public)
 #        ds/
 #          js/src/ds/* public headers
 #        gc/
 #          js/src/gc/* public headers
 #        mozilla/
 #          mfbt headers
 #
 
 install::
-	$(call py_action,process_install_manifest,--track install_dist_include.track --no-symlinks $(DESTDIR)$(includedir) $(DEPTH)/_build_manifests/install/dist_include)
+	$(call py_action,process_install_manifest,--track install_dist_include.track --no-symlinks $(DESTDIR)$(includedir)/$(JS_LIBRARY_NAME) $(DEPTH)/_build_manifests/install/dist_include)
 
 #
 # END SpiderMonkey header installation
 #############################################
 
 # Install versioned script, for parallel installability in Linux distributions
 install:: js-config
 	cp $^ js$(MOZJS_MAJOR_VERSION)-config
--- a/js/src/gc/Cell.h
+++ b/js/src/gc/Cell.h
@@ -250,17 +250,17 @@ Cell::storeBuffer() const
     return chunk()->trailer.storeBuffer;
 }
 
 inline JS::TraceKind
 Cell::getTraceKind() const
 {
     if (isTenured())
         return asTenured().getTraceKind();
-    if (js::shadow::String::nurseryCellIsString(this))
+    if (JS::shadow::String::nurseryCellIsString(this))
         return JS::TraceKind::String;
     return JS::TraceKind::Object;
 }
 
 /* static */ MOZ_ALWAYS_INLINE bool
 Cell::needWriteBarrierPre(JS::Zone* zone) {
     return JS::shadow::Zone::asShadowZone(zone)->needsIncrementalBarrier();
 }
--- a/js/src/gc/GC.cpp
+++ b/js/src/gc/GC.cpp
@@ -8443,24 +8443,16 @@ JS::GCCellPtr::GCCellPtr(const Value& v)
 JS::TraceKind
 JS::GCCellPtr::outOfLineKind() const
 {
     MOZ_ASSERT((ptr & OutOfLineTraceKindMask) == OutOfLineTraceKindMask);
     MOZ_ASSERT(asCell()->isTenured());
     return MapAllocToTraceKind(asCell()->asTenured().getAllocKind());
 }
 
-bool
-JS::GCCellPtr::mayBeOwnedByOtherRuntimeSlow() const
-{
-    if (is<JSString>())
-        return as<JSString>().isPermanentAtom();
-    return as<Symbol>().isWellKnownSymbol();
-}
-
 #ifdef JSGC_HASH_TABLE_CHECKS
 void
 js::gc::CheckHashTablesAfterMovingGC(JSRuntime* rt)
 {
     /*
      * Check that internal hash tables no longer have any pointers to things
      * that have been moved.
      */
--- a/js/src/gc/GC.h
+++ b/js/src/gc/GC.h
@@ -106,17 +106,18 @@ IterateHeapUnbarrieredForZone(JSContext*
                               IterateCellCallback cellCallback);
 
 /*
  * Invoke chunkCallback on every in-use chunk.
  */
 extern void
 IterateChunks(JSContext* cx, void* data, IterateChunkCallback chunkCallback);
 
-typedef void (*IterateScriptCallback)(JSRuntime* rt, void* data, JSScript* script);
+typedef void (*IterateScriptCallback)(JSRuntime* rt, void* data, JSScript* script,
+                                      const JS::AutoRequireNoGC& nogc);
 
 /*
  * Invoke scriptCallback on every in-use script for
  * the given compartment or for all compartments if it is null.
  */
 extern void
 IterateScripts(JSContext* cx, JSCompartment* compartment,
                void* data, IterateScriptCallback scriptCallback);
--- a/js/src/gc/PublicIterators.cpp
+++ b/js/src/gc/PublicIterators.cpp
@@ -81,27 +81,28 @@ js::IterateChunks(JSContext* cx, void* d
 
 void
 js::IterateScripts(JSContext* cx, JSCompartment* compartment,
                    void* data, IterateScriptCallback scriptCallback)
 {
     MOZ_ASSERT(!cx->suppressGC);
     AutoEmptyNursery empty(cx);
     AutoPrepareForTracing prep(cx);
+    JS::AutoSuppressGCAnalysis nogc;
 
     if (compartment) {
         Zone* zone = compartment->zone();
         for (auto script = zone->cellIter<JSScript>(empty); !script.done(); script.next()) {
             if (script->compartment() == compartment)
-                scriptCallback(cx->runtime(), data, script);
+                scriptCallback(cx->runtime(), data, script, nogc);
         }
     } else {
         for (ZonesIter zone(cx->runtime(), SkipAtoms); !zone.done(); zone.next()) {
             for (auto script = zone->cellIter<JSScript>(empty); !script.done(); script.next())
-                scriptCallback(cx->runtime(), data, script);
+                scriptCallback(cx->runtime(), data, script, nogc);
         }
     }
 }
 
 static void
 IterateGrayObjects(Zone* zone, GCThingCallback cellCallback, void* data)
 {
     for (auto kind : ObjectAllocKinds()) {
--- a/js/src/jit-test/tests/wasm/memory-bulk.js
+++ b/js/src/jit-test/tests/wasm/memory-bulk.js
@@ -1,12 +1,127 @@
 
 if (!wasmBulkMemSupported())
     quit(0);
 
+//---------------------------------------------------------------------//
+//---------------------------------------------------------------------//
+// Validation tests
+
+//-----------------------------------------------------------
+// Test helpers.  Copied and simplified from binary.js.
+
+load(libdir + "wasm-binary.js");
+
+function toU8(array) {
+    for (let b of array)
+        assertEq(b < 256, true);
+    return Uint8Array.from(array);
+}
+
+function varU32(u32) {
+    assertEq(u32 >= 0, true);
+    assertEq(u32 < Math.pow(2,32), true);
+    var bytes = [];
+    do {
+        var byte = u32 & 0x7f;
+        u32 >>>= 7;
+        if (u32 != 0)
+            byte |= 0x80;
+        bytes.push(byte);
+    } while (u32 != 0);
+    return bytes;
+}
+
+function moduleHeaderThen(...rest) {
+    return [magic0, magic1, magic2, magic3, ver0, ver1, ver2, ver3, ...rest];
+}
+
+function moduleWithSections(sectionArray) {
+    var bytes = moduleHeaderThen();
+    for (let section of sectionArray) {
+        bytes.push(section.name);
+        bytes.push(...varU32(section.body.length));
+        bytes.push(...section.body);
+    }
+    return toU8(bytes);
+}
+
+function sigSection(sigs) {
+    var body = [];
+    body.push(...varU32(sigs.length));
+    for (let sig of sigs) {
+        body.push(...varU32(FuncCode));
+        body.push(...varU32(sig.args.length));
+        for (let arg of sig.args)
+            body.push(...varU32(arg));
+        body.push(...varU32(sig.ret == VoidCode ? 0 : 1));
+        if (sig.ret != VoidCode)
+            body.push(...varU32(sig.ret));
+    }
+    return { name: typeId, body };
+}
+
+function declSection(decls) {
+    var body = [];
+    body.push(...varU32(decls.length));
+    for (let decl of decls)
+        body.push(...varU32(decl));
+    return { name: functionId, body };
+}
+
+function funcBody(func) {
+    var body = varU32(func.locals.length);
+    for (let local of func.locals)
+        body.push(...varU32(local));
+    body = body.concat(...func.body);
+    body.push(EndCode);
+    body.splice(0, 0, ...varU32(body.length));
+    return body;
+}
+
+function bodySection(bodies) {
+    var body = varU32(bodies.length).concat(...bodies);
+    return { name: codeId, body };
+}
+
+const v2vSig = {args:[], ret:VoidCode};
+const v2vSigSection = sigSection([v2vSig]);
+
+// Prefixed opcodes
+
+function checkMiscPrefixed(opcode, expect_failure) {
+    let binary = moduleWithSections(
+           [v2vSigSection, declSection([0]),
+            bodySection(
+                [funcBody(
+                    {locals:[],
+                     body:[0x41, 0x0, 0x41, 0x0, 0x41, 0x0, // 3 x const.i32 0
+                           MiscPrefix, opcode]})])]);
+    if (expect_failure) {
+        assertErrorMessage(() => new WebAssembly.Module(binary),
+                           WebAssembly.CompileError, /unrecognized opcode/);
+    } else {
+        assertEq(true, WebAssembly.validate(binary));
+    }
+}
+
+//-----------------------------------------------------------
+// Verification cases for memory.copy/fill
+
+checkMiscPrefixed(0x3f, true);  // unassigned
+checkMiscPrefixed(0x40, false); // memory.copy
+checkMiscPrefixed(0x41, false); // memory.fill
+checkMiscPrefixed(0x42, true);  // unassigned
+
+
+//---------------------------------------------------------------------//
+//---------------------------------------------------------------------//
+// Run tests
+
 //-----------------------------------------------------------
 // Test helpers
 function checkRange(arr, minIx, maxIxPlusOne, expectedValue)
 {
     for (let i = minIx; i < maxIxPlusOne; i++) {
         assertEq(arr[i], expectedValue);
     }
 }
--- a/js/src/jsapi-tests/testPreserveJitCode.cpp
+++ b/js/src/jsapi-tests/testPreserveJitCode.cpp
@@ -5,17 +5,17 @@
 // For js::jit::IsIonEnabled().
 #include "jit/Ion.h"
 
 #include "jsapi-tests/tests.h"
 
 using namespace JS;
 
 static void
-ScriptCallback(JSRuntime* rt, void* data, JSScript* script)
+ScriptCallback(JSRuntime* rt, void* data, JSScript* script, const JS::AutoRequireNoGC& nogc)
 {
     unsigned& count = *static_cast<unsigned*>(data);
     if (script->hasIonScript())
         ++count;
 }
 
 BEGIN_TEST(test_PreserveJitCode)
 {
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -5038,16 +5038,17 @@ GetSymbolDescription(HandleSymbol symbol
     macro(asyncIterator)
 
 enum class SymbolCode : uint32_t {
     // There is one SymbolCode for each well-known symbol.
 #define JS_DEFINE_SYMBOL_ENUM(name) name,
     JS_FOR_EACH_WELL_KNOWN_SYMBOL(JS_DEFINE_SYMBOL_ENUM)  // SymbolCode::iterator, etc.
 #undef JS_DEFINE_SYMBOL_ENUM
     Limit,
+    WellKnownAPILimit = 0x80000000, // matches JS::shadow::Symbol::WellKnownAPILimit for inline use
     InSymbolRegistry = 0xfffffffe,  // created by Symbol.for() or JS::GetSymbolFor()
     UniqueSymbol = 0xffffffff       // created by Symbol() or JS::NewSymbol()
 };
 
 /* For use in loops that iterate over the well-known symbols. */
 const size_t WellKnownSymbolLimit = size_t(SymbolCode::Limit);
 
 /**
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -619,40 +619,16 @@ struct Function {
     uint16_t nargs;
     uint16_t flags;
     /* Used only for natives */
     JSNative native;
     const JSJitInfo* jitinfo;
     void* _1;
 };
 
-struct String
-{
-    static const uint32_t NON_ATOM_BIT     = JS_BIT(0);
-    static const uint32_t LINEAR_BIT       = JS_BIT(1);
-    static const uint32_t INLINE_CHARS_BIT = JS_BIT(3);
-    static const uint32_t LATIN1_CHARS_BIT = JS_BIT(6);
-    static const uint32_t EXTERNAL_FLAGS   = LINEAR_BIT | NON_ATOM_BIT | JS_BIT(5);
-    static const uint32_t TYPE_FLAGS_MASK  = JS_BIT(6) - 1;
-    uint32_t flags;
-    uint32_t length;
-    union {
-        const JS::Latin1Char* nonInlineCharsLatin1;
-        const char16_t* nonInlineCharsTwoByte;
-        JS::Latin1Char inlineStorageLatin1[1];
-        char16_t inlineStorageTwoByte[1];
-    };
-    const JSStringFinalizer* externalFinalizer;
-
-    static bool nurseryCellIsString(const js::gc::Cell* cell) {
-        MOZ_ASSERT(IsInsideNursery(cell));
-        return reinterpret_cast<const String*>(cell)->flags & NON_ATOM_BIT;
-    }
-};
-
 } /* namespace shadow */
 
 // This is equal to |&JSObject::class_|.  Use it in places where you don't want
 // to #include vm/JSObject.h.
 extern JS_FRIEND_DATA(const js::Class* const) ObjectClassPtr;
 
 inline const js::Class*
 GetObjectClass(const JSObject* obj)
@@ -801,75 +777,75 @@ GetObjectSlot(JSObject* obj, size_t slot
 {
     MOZ_ASSERT(slot < GetObjectSlotSpan(obj));
     return reinterpret_cast<const shadow::Object*>(obj)->slotRef(slot);
 }
 
 MOZ_ALWAYS_INLINE size_t
 GetAtomLength(JSAtom* atom)
 {
-    return reinterpret_cast<shadow::String*>(atom)->length;
+    return reinterpret_cast<JS::shadow::String*>(atom)->length;
 }
 
 static const uint32_t MaxStringLength = (1 << 28) - 1;
 
 MOZ_ALWAYS_INLINE size_t
 GetStringLength(JSString* s)
 {
-    return reinterpret_cast<shadow::String*>(s)->length;
+    return reinterpret_cast<JS::shadow::String*>(s)->length;
 }
 
 MOZ_ALWAYS_INLINE size_t
 GetFlatStringLength(JSFlatString* s)
 {
-    return reinterpret_cast<shadow::String*>(s)->length;
+    return reinterpret_cast<JS::shadow::String*>(s)->length;
 }
 
 MOZ_ALWAYS_INLINE size_t
 GetLinearStringLength(JSLinearString* s)
 {
-    return reinterpret_cast<shadow::String*>(s)->length;
+    return reinterpret_cast<JS::shadow::String*>(s)->length;
 }
 
 MOZ_ALWAYS_INLINE bool
 LinearStringHasLatin1Chars(JSLinearString* s)
 {
-    return reinterpret_cast<shadow::String*>(s)->flags & shadow::String::LATIN1_CHARS_BIT;
+    return reinterpret_cast<JS::shadow::String*>(s)->flags & JS::shadow::String::LATIN1_CHARS_BIT;
 }
 
 MOZ_ALWAYS_INLINE bool
 AtomHasLatin1Chars(JSAtom* atom)
 {
-    return reinterpret_cast<shadow::String*>(atom)->flags & shadow::String::LATIN1_CHARS_BIT;
+    return reinterpret_cast<JS::shadow::String*>(atom)->flags & JS::shadow::String::LATIN1_CHARS_BIT;
 }
 
 MOZ_ALWAYS_INLINE bool
 StringHasLatin1Chars(JSString* s)
 {
-    return reinterpret_cast<shadow::String*>(s)->flags & shadow::String::LATIN1_CHARS_BIT;
+    return reinterpret_cast<JS::shadow::String*>(s)->flags & JS::shadow::String::LATIN1_CHARS_BIT;
 }
 
 MOZ_ALWAYS_INLINE const JS::Latin1Char*
 GetLatin1LinearStringChars(const JS::AutoRequireNoGC& nogc, JSLinearString* linear)
 {
     MOZ_ASSERT(LinearStringHasLatin1Chars(linear));
 
-    using shadow::String;
+    using JS::shadow::String;
     String* s = reinterpret_cast<String*>(linear);
     if (s->flags & String::INLINE_CHARS_BIT)
         return s->inlineStorageLatin1;
     return s->nonInlineCharsLatin1;
 }
 
 MOZ_ALWAYS_INLINE const char16_t*
 GetTwoByteLinearStringChars(const JS::AutoRequireNoGC& nogc, JSLinearString* linear)
 {
     MOZ_ASSERT(!LinearStringHasLatin1Chars(linear));
 
-    using shadow::String;
+    using JS::shadow::String;
     String* s = reinterpret_cast<String*>(linear);
     if (s->flags & String::INLINE_CHARS_BIT)
         return s->inlineStorageTwoByte;
     return s->nonInlineCharsTwoByte;
 }
 
 MOZ_ALWAYS_INLINE JSLinearString*
 AtomToLinearString(JSAtom* atom)
@@ -899,17 +875,17 @@ MOZ_ALWAYS_INLINE const char16_t*
 GetTwoByteAtomChars(const JS::AutoRequireNoGC& nogc, JSAtom* atom)
 {
     return GetTwoByteLinearStringChars(nogc, AtomToLinearString(atom));
 }
 
 MOZ_ALWAYS_INLINE bool
 IsExternalString(JSString* str, const JSStringFinalizer** fin, const char16_t** chars)
 {
-    using shadow::String;
+    using JS::shadow::String;
     String* s = reinterpret_cast<String*>(str);
 
     if ((s->flags & String::TYPE_FLAGS_MASK) != String::EXTERNAL_FLAGS)
         return false;
 
     MOZ_ASSERT(JS_IsExternalString(str));
     *fin = s->externalFinalizer;
     *chars = s->nonInlineCharsTwoByte;
@@ -917,17 +893,17 @@ IsExternalString(JSString* str, const JS
 }
 
 JS_FRIEND_API(JSLinearString*)
 StringToLinearStringSlow(JSContext* cx, JSString* str);
 
 MOZ_ALWAYS_INLINE JSLinearString*
 StringToLinearString(JSContext* cx, JSString* str)
 {
-    using shadow::String;
+    using JS::shadow::String;
     String* s = reinterpret_cast<String*>(str);
     if (MOZ_UNLIKELY(!(s->flags & String::LINEAR_BIT)))
         return StringToLinearStringSlow(cx, str);
     return reinterpret_cast<JSLinearString*>(str);
 }
 
 template<typename CharType>
 MOZ_ALWAYS_INLINE void
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -4584,27 +4584,28 @@ class MOZ_STACK_CLASS Debugger::ScriptQu
         for (auto r = compartments.all(); !r.empty(); r.popFront()) {
             JSCompartment* comp = r.front();
             if (!comp->ensureDelazifyScriptsForDebugger(cx))
                 return false;
         }
         return true;
     }
 
-    static void considerScript(JSRuntime* rt, void* data, JSScript* script) {
+    static void considerScript(JSRuntime* rt, void* data, JSScript* script,
+                               const JS::AutoRequireNoGC& nogc) {
         ScriptQuery* self = static_cast<ScriptQuery*>(data);
-        self->consider(script);
+        self->consider(script, nogc);
     }
 
     /*
      * If |script| matches this query, append it to |vector| or place it in
      * |innermostForCompartment|, as appropriate. Set |oom| if an out of memory
      * condition occurred.
      */
-    void consider(JSScript* script) {
+    void consider(JSScript* script, const JS::AutoRequireNoGC& nogc) {
         // We check for presence of script->code() because it is possible that
         // the script was created and thus exposed to GC, but *not* fully
         // initialized from fullyInit{FromEmitter,Trivial} due to errors.
         if (oom || script->selfHosted() || !script->code())
             return;
         JSCompartment* compartment = script->compartment();
         if (!compartments.has(compartment))
             return;
--- a/js/src/vm/StringType.h
+++ b/js/src/vm/StringType.h
@@ -311,17 +311,17 @@ class JSString : public js::gc::Cell
                        NUM_INLINE_CHARS_LATIN1 * sizeof(char)),
                       "Inline Latin1 chars must fit in a JSString");
         static_assert(sizeof(JSString) ==
                       (offsetof(JSString, d.inlineStorageTwoByte) +
                        NUM_INLINE_CHARS_TWO_BYTE * sizeof(char16_t)),
                       "Inline char16_t chars must fit in a JSString");
 
         /* Ensure js::shadow::String has the same layout. */
-        using js::shadow::String;
+        using JS::shadow::String;
         static_assert(offsetof(JSString, d.u1.length) == offsetof(String, length),
                       "shadow::String length offset must match JSString");
         static_assert(offsetof(JSString, d.u1.flags) == offsetof(String, flags),
                       "shadow::String flags offset must match JSString");
         static_assert(offsetof(JSString, d.s.u2.nonInlineCharsLatin1) == offsetof(String, nonInlineCharsLatin1),
                       "shadow::String nonInlineChars offset must match JSString");
         static_assert(offsetof(JSString, d.s.u2.nonInlineCharsTwoByte) == offsetof(String, nonInlineCharsTwoByte),
                       "shadow::String nonInlineChars offset must match JSString");
--- a/js/src/vm/SymbolType.h
+++ b/js/src/vm/SymbolType.h
@@ -12,16 +12,17 @@
 #include <stdio.h>
 
 #include "jsapi.h"
 
 #include "gc/Barrier.h"
 #include "gc/Tracer.h"
 #include "js/AllocPolicy.h"
 #include "js/GCHashTable.h"
+#include "js/HeapAPI.h"
 #include "js/RootingAPI.h"
 #include "js/TypeDecls.h"
 #include "js/Utility.h"
 #include "vm/Printer.h"
 #include "vm/StringType.h"
 
 namespace js {
 class AutoLockForExclusiveAccess;
@@ -45,16 +46,18 @@ class Symbol : public js::gc::TenuredCel
     // the minimum size on both.
     size_t unused_;
 
     Symbol(SymbolCode code, js::HashNumber hash, JSAtom* desc)
         : code_(code), hash_(hash), description_(desc)
     {
         // Silence warnings about unused_ being... unused.
         (void)unused_;
+        static_assert(uint32_t(SymbolCode::WellKnownAPILimit) == JS::shadow::Symbol::WellKnownAPILimit,
+                      "JS::shadow::Symbol::WellKnownAPILimit must match SymbolCode::WellKnownAPILimit");
     }
 
     Symbol(const Symbol&) = delete;
     void operator=(const Symbol&) = delete;
 
     static Symbol*
     newInternal(JSContext* cx, SymbolCode code, js::HashNumber hash,
                 JSAtom* description, js::AutoLockForExclusiveAccess& lock);
--- a/js/src/wasm/WasmBaselineCompile.cpp
+++ b/js/src/wasm/WasmBaselineCompile.cpp
@@ -9195,17 +9195,17 @@ BaseCompiler::emitWake()
 
 #ifdef ENABLE_WASM_BULKMEM_OPS
 bool
 BaseCompiler::emitMemCopy()
 {
     uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
 
     Nothing nothing;
-    if (!iter_.readMemCopy(ValType::I32, &nothing, &nothing, &nothing))
+    if (!iter_.readMemCopy(&nothing, &nothing, &nothing))
         return false;
 
     if (deadCode_)
         return true;
 
     emitInstanceCall(lineOrBytecode, SigPIII_, ExprType::Void, SymbolicAddress::MemCopy);
 
     Label ok;
@@ -9217,17 +9217,17 @@ BaseCompiler::emitMemCopy()
 }
 
 bool
 BaseCompiler::emitMemFill()
 {
     uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
 
     Nothing nothing;
-    if (!iter_.readMemFill(ValType::I32, &nothing, &nothing, &nothing))
+    if (!iter_.readMemFill(&nothing, &nothing, &nothing))
         return false;
 
     if (deadCode_)
         return true;
 
     emitInstanceCall(lineOrBytecode, SigPIII_, ExprType::Void, SymbolicAddress::MemFill);
 
     Label ok;
--- a/js/src/wasm/WasmBinaryToAST.cpp
+++ b/js/src/wasm/WasmBinaryToAST.cpp
@@ -1248,17 +1248,17 @@ AstDecodeWake(AstDecodeContext& c)
 
     return true;
 }
 
 #ifdef ENABLE_WASM_BULKMEM_OPS
 static bool
 AstDecodeMemCopy(AstDecodeContext& c)
 {
-    if (!c.iter().readMemCopy(ValType::I32, nullptr, nullptr, nullptr))
+    if (!c.iter().readMemCopy(nullptr, nullptr, nullptr))
         return false;
 
     AstDecodeStackItem dest = c.popCopy();
     AstDecodeStackItem src  = c.popCopy();
     AstDecodeStackItem len  = c.popCopy();
 
     AstMemCopy* mc = new(c.lifo) AstMemCopy(dest.expr, src.expr, len.expr);
 
@@ -1269,17 +1269,17 @@ AstDecodeMemCopy(AstDecodeContext& c)
         return false;
 
     return true;
 }
 
 static bool
 AstDecodeMemFill(AstDecodeContext& c)
 {
-    if (!c.iter().readMemFill(ValType::I32, nullptr, nullptr, nullptr))
+    if (!c.iter().readMemFill(nullptr, nullptr, nullptr))
         return false;
 
     AstDecodeStackItem len   = c.popCopy();
     AstDecodeStackItem val   = c.popCopy();
     AstDecodeStackItem start = c.popCopy();
 
     AstMemFill* mf = new(c.lifo) AstMemFill(start.expr, val.expr, len.expr);
 
--- a/js/src/wasm/WasmIonCompile.cpp
+++ b/js/src/wasm/WasmIonCompile.cpp
@@ -3598,17 +3598,17 @@ EmitAtomicXchg(FunctionCompiler& f, ValT
 
 #endif // ENABLE_WASM_THREAD_OPS
 
 #ifdef ENABLE_WASM_BULKMEM_OPS
 static bool
 EmitMemCopy(FunctionCompiler& f)
 {
     MDefinition *dest, *src, *len;
-    if (!f.iter().readMemCopy(ValType::I32, &dest, &src, &len))
+    if (!f.iter().readMemCopy(&dest, &src, &len))
         return false;
 
     if (f.inDeadCode())
         return false;
 
     uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
 
     CallCompileState args(f, lineOrBytecode);
@@ -3637,17 +3637,17 @@ EmitMemCopy(FunctionCompiler& f)
 
     return true;
 }
 
 static bool
 EmitMemFill(FunctionCompiler& f)
 {
     MDefinition *start, *val, *len;
-    if (!f.iter().readMemFill(ValType::I32, &start, &val, &len))
+    if (!f.iter().readMemFill(&start, &val, &len))
         return false;
 
     if (f.inDeadCode())
         return false;
 
     uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
 
     CallCompileState args(f, lineOrBytecode);
--- a/js/src/wasm/WasmOpIter.h
+++ b/js/src/wasm/WasmOpIter.h
@@ -653,18 +653,18 @@ class MOZ_STACK_CLASS OpIter : private P
     MOZ_MUST_USE bool readSwizzle(ValType simdType, uint8_t (* lanes)[16], Value* vector);
     MOZ_MUST_USE bool readShuffle(ValType simdType, uint8_t (* lanes)[16],
                                   Value* lhs, Value* rhs);
     MOZ_MUST_USE bool readSimdSelect(ValType simdType, Value* trueValue,
                                      Value* falseValue,
                                      Value* condition);
     MOZ_MUST_USE bool readSimdCtor(ValType elementType, uint32_t numElements, ValType simdType,
                                    ValueVector* argValues);
-    MOZ_MUST_USE bool readMemCopy(ValType argType, Value* dest, Value* src, Value* len);
-    MOZ_MUST_USE bool readMemFill(ValType argType, Value* start, Value* val, Value* len);
+    MOZ_MUST_USE bool readMemCopy(Value* dest, Value* src, Value* len);
+    MOZ_MUST_USE bool readMemFill(Value* start, Value* val, Value* len);
 
     // At a location where readOp is allowed, peek at the next opcode
     // without consuming it or updating any internal state.
     // Never fails: returns uint16_t(Op::Limit) in op->b0 if it can't read.
     void peekOp(OpBytes* op);
 
     // ------------------------------------------------------------------------
     // Stack management.
@@ -2234,47 +2234,45 @@ OpIter<Policy>::readSimdCtor(ValType ele
 
     infalliblePush(simdType);
 
     return true;
 }
 
 template <typename Policy>
 inline bool
-OpIter<Policy>::readMemCopy(ValType argType,
-                            Value* dest, Value* src, Value* len)
+OpIter<Policy>::readMemCopy(Value* dest, Value* src, Value* len)
 {
     MOZ_ASSERT(Classify(op_) == OpKind::MemCopy);
 
-    if (!popWithType(argType, len))
+    if (!popWithType(ValType::I32, len))
         return false;
 
-    if (!popWithType(argType, src))
+    if (!popWithType(ValType::I32, src))
         return false;
 
-    if (!popWithType(argType, dest))
+    if (!popWithType(ValType::I32, dest))
         return false;
 
     return true;
 }
 
 template <typename Policy>
 inline bool
-OpIter<Policy>::readMemFill(ValType argType,
-                            Value* start, Value* val, Value* len)
+OpIter<Policy>::readMemFill(Value* start, Value* val, Value* len)
 {
     MOZ_ASSERT(Classify(op_) == OpKind::MemFill);
 
-    if (!popWithType(argType, len))
+    if (!popWithType(ValType::I32, len))
         return false;
 
-    if (!popWithType(argType, val))
+    if (!popWithType(ValType::I32, val))
         return false;
 
-    if (!popWithType(argType, start))
+    if (!popWithType(ValType::I32, start))
         return false;
 
     return true;
 }
 
 } // namespace wasm
 } // namespace js
 
--- a/js/src/wasm/WasmValidate.cpp
+++ b/js/src/wasm/WasmValidate.cpp
@@ -812,16 +812,22 @@ DecodeFunctionBodyExprs(const ModuleEnvi
                 CHECK(iter.readConversion(ValType::F64, ValType::I32, &nothing));
               case uint16_t(MiscOp::I64TruncSSatF32):
               case uint16_t(MiscOp::I64TruncUSatF32):
                 CHECK(iter.readConversion(ValType::F32, ValType::I64, &nothing));
               case uint16_t(MiscOp::I64TruncSSatF64):
               case uint16_t(MiscOp::I64TruncUSatF64):
                 CHECK(iter.readConversion(ValType::F64, ValType::I64, &nothing));
 #endif
+#ifdef ENABLE_WASM_BULKMEM_OPS
+              case uint16_t(MiscOp::MemCopy):
+                CHECK(iter.readMemCopy(&nothing, &nothing, &nothing));
+              case uint16_t(MiscOp::MemFill):
+                CHECK(iter.readMemFill(&nothing, &nothing, &nothing));
+#endif
               default:
                 return iter.unrecognizedOpcode(&op);
             }
             break;
           }
 #ifdef ENABLE_WASM_GC
           case uint16_t(Op::RefNull): {
             if (env.gcTypesEnabled == HasGcTypes::False)
--- a/js/xpconnect/src/XPCConvert.cpp
+++ b/js/xpconnect/src/XPCConvert.cpp
@@ -94,17 +94,18 @@ XPCConvert::GetISupportsFromJSObject(JSO
     return !!*iface;
 }
 
 /***************************************************************************/
 
 // static
 bool
 XPCConvert::NativeData2JS(MutableHandleValue d, const void* s,
-                          const nsXPTType& type, const nsID* iid, nsresult* pErr)
+                          const nsXPTType& type, const nsID* iid,
+                          uint32_t arrlen, nsresult* pErr)
 {
     MOZ_ASSERT(s, "bad param");
 
     AutoJSContext cx;
     if (pErr)
         *pErr = NS_ERROR_XPC_BAD_CONVERT_NATIVE;
 
     switch (type.TagPart()) {
@@ -195,73 +196,86 @@ XPCConvert::NativeData2JS(MutableHandleV
         return true;
     }
 
     case nsXPTType::T_ASTRING:
         // Fall through to T_DOMSTRING case
 
     case nsXPTType::T_DOMSTRING:
     {
-        const nsAString* p = *static_cast<const nsAString* const*>(s);
+        const nsAString* p = static_cast<const nsAString*>(s);
         if (!p || p->IsVoid()) {
             d.setNull();
             return true;
         }
 
         nsStringBuffer* buf;
         if (!XPCStringConvert::ReadableToJSVal(cx, *p, &buf, d))
             return false;
         if (buf)
             buf->AddRef();
         return true;
     }
 
     case nsXPTType::T_CHAR_STR:
     {
         const char* p = *static_cast<const char* const*>(s);
+        arrlen = p ? strlen(p) : 0;
+        MOZ_FALLTHROUGH;
+    }
+    case nsXPTType::T_PSTRING_SIZE_IS:
+    {
+        const char* p = *static_cast<const char* const*>(s);
         if (!p) {
             d.setNull();
             return true;
         }
 
 #ifdef STRICT_CHECK_OF_UNICODE
         bool isAscii = true;
-        for (char* t = p; *t && isAscii; t++) {
-          if (ILLEGAL_CHAR_RANGE(*t))
-              isAscii = false;
+        for (uint32_t i = 0; i < arrlen; i++) {
+            if (ILLEGAL_CHAR_RANGE(p[i]))
+                isAscii = false;
         }
         MOZ_ASSERT(isAscii, "passing non ASCII data");
 #endif // STRICT_CHECK_OF_UNICODE
 
-        JSString* str = JS_NewStringCopyZ(cx, p);
+        JSString* str = JS_NewStringCopyN(cx, p, arrlen);
         if (!str)
             return false;
 
         d.setString(str);
         return true;
     }
 
     case nsXPTType::T_WCHAR_STR:
     {
         const char16_t* p = *static_cast<const char16_t* const*>(s);
+        arrlen = p ? nsCharTraits<char16_t>::length(p) : 0;
+        MOZ_FALLTHROUGH;
+    }
+    case nsXPTType::T_PWSTRING_SIZE_IS:
+    {
+        const char16_t* p = *static_cast<const char16_t* const*>(s);
         if (!p) {
             d.setNull();
             return true;
         }
 
-        JSString* str = JS_NewUCStringCopyZ(cx, p);
+        JSString* str = JS_NewUCStringCopyN(cx, p, arrlen);
         if (!str)
             return false;
 
         d.setString(str);
         return true;
     }
+
     case nsXPTType::T_UTF8STRING:
     {
-        const nsACString* utf8String = *static_cast<const nsACString* const*>(s);
+        const nsACString* utf8String = static_cast<const nsACString*>(s);
 
         if (!utf8String || utf8String->IsVoid()) {
             d.setNull();
             return true;
         }
 
         if (utf8String->IsEmpty()) {
             d.set(JS_GetEmptyStringValue(cx));
@@ -298,17 +312,17 @@ XPCConvert::NativeData2JS(MutableHandleV
             return false;
         }
 
         d.setString(str);
         return true;
     }
     case nsXPTType::T_CSTRING:
     {
-        const nsACString* cString = *static_cast<const nsACString* const*>(s);
+        const nsACString* cString = static_cast<const nsACString*>(s);
 
         if (!cString || cString->IsVoid()) {
             d.setNull();
             return true;
         }
 
         // c-strings (binary blobs) are deliberately not converted from
         // UTF-8 to UTF-16. T_UTF8Sting is for UTF-8 encoded strings
@@ -350,16 +364,36 @@ XPCConvert::NativeData2JS(MutableHandleV
         if (!ptr) {
             d.setNull();
             return true;
         }
 
         return type.GetDOMObjectInfo().Wrap(cx, ptr, d);
     }
 
+    case nsXPTType::T_PROMISE:
+    {
+        Promise* promise = *static_cast<Promise* const*>(s);
+        if (!promise) {
+            d.setNull();
+            return true;
+        }
+
+        RootedObject jsobj(cx, promise->PromiseObj());
+        if (!JS_WrapObject(cx, &jsobj)) {
+            return false;
+        }
+        d.setObject(*jsobj);
+        return true;
+    }
+
+    case nsXPTType::T_ARRAY:
+        return NativeArray2JS(d, static_cast<const void* const*>(s),
+                              type.ArrayElementType(), iid, arrlen, pErr);
+
     default:
         NS_ERROR("bad type");
         return false;
     }
     return true;
 }
 
 /***************************************************************************/
@@ -397,24 +431,28 @@ bool ConvertToPrimitive(JSContext* cx, H
     return ValueToPrimitive<T, eDefault>(cx, v, retval);
 }
 
 // static
 bool
 XPCConvert::JSData2Native(void* d, HandleValue s,
                           const nsXPTType& type,
                           const nsID* iid,
+                          uint32_t arrlen,
                           nsresult* pErr)
 {
     MOZ_ASSERT(d, "bad param");
 
     AutoJSContext cx;
     if (pErr)
         *pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
 
+    bool sizeis = type.Tag() == TD_PSTRING_SIZE_IS ||
+        type.Tag() == TD_PWSTRING_SIZE_IS;
+
     switch (type.TagPart()) {
     case nsXPTType::T_I8     :
         return ConvertToPrimitive(cx, s, static_cast<int8_t*>(d));
     case nsXPTType::T_I16    :
         return ConvertToPrimitive(cx, s, static_cast<int16_t*>(d));
     case nsXPTType::T_I32    :
         return ConvertToPrimitive(cx, s, static_cast<int32_t*>(d));
     case nsXPTType::T_I64    :
@@ -496,43 +534,42 @@ XPCConvert::JSData2Native(void* d, Handl
         }
         *((const nsID**)d) = pid->Clone();
         return true;
     }
 
     case nsXPTType::T_ASTRING:
     {
         if (s.isUndefined()) {
-            (**((nsAString**)d)).SetIsVoid(true);
+            ((nsAString*)d)->SetIsVoid(true);
             return true;
         }
         MOZ_FALLTHROUGH;
     }
     case nsXPTType::T_DOMSTRING:
     {
+        nsAString* ws = (nsAString*)d;
         if (s.isNull()) {
-            (**((nsAString**)d)).SetIsVoid(true);
+            ws->SetIsVoid(true);
             return true;
         }
         size_t length = 0;
         JSString* str = nullptr;
         if (!s.isUndefined()) {
             str = ToString(cx, s);
             if (!str)
                 return false;
 
             length = JS_GetStringLength(str);
             if (!length) {
-                (**((nsAString**)d)).Truncate();
+                ws->Truncate();
                 return true;
             }
         }
 
-        nsAString* ws = *((nsAString**)d);
-
         if (!str) {
             ws->AssignLiteral(u"undefined");
         } else if (XPCStringConvert::IsDOMString(str)) {
             // The characters represent an existing nsStringBuffer that
             // was shared by XPCStringConvert::ReadableToJSVal.
             const char16_t* chars = JS_GetTwoByteExternalStringChars(str);
             if (chars[length] == '\0') {
                 // Safe to share the buffer.
@@ -549,140 +586,171 @@ XPCConvert::JSData2Native(void* d, Handl
         } else {
             if (!AssignJSString(cx, *ws, str))
                 return false;
         }
         return true;
     }
 
     case nsXPTType::T_CHAR_STR:
+    case nsXPTType::T_PSTRING_SIZE_IS:
     {
         if (s.isUndefined() || s.isNull()) {
+            if (sizeis && 0 != arrlen) {
+                if (pErr)
+                    *pErr = NS_ERROR_XPC_NOT_ENOUGH_CHARS_IN_STRING;
+                return false;
+            }
             *((char**)d) = nullptr;
             return true;
         }
 
         JSString* str = ToString(cx, s);
         if (!str) {
             return false;
         }
+
 #ifdef DEBUG
         if (JS_StringHasLatin1Chars(str)) {
             size_t len;
             AutoCheckCannotGC nogc;
             const Latin1Char* chars = JS_GetLatin1StringCharsAndLength(cx, nogc, str, &len);
             if (chars)
                 CheckCharsInCharRange(chars, len);
         } else {
             size_t len;
             AutoCheckCannotGC nogc;
             const char16_t* chars = JS_GetTwoByteStringCharsAndLength(cx, nogc, str, &len);
             if (chars)
                 CheckCharsInCharRange(chars, len);
         }
 #endif // DEBUG
+
         size_t length = JS_GetStringEncodingLength(cx, str);
         if (length == size_t(-1)) {
             return false;
         }
+        if (sizeis) {
+            if (length > arrlen) {
+                if (pErr)
+                    *pErr = NS_ERROR_XPC_NOT_ENOUGH_CHARS_IN_STRING;
+                return false;
+            }
+            if (length < arrlen) {
+                length = arrlen;
+            }
+        }
         char* buffer = static_cast<char*>(moz_xmalloc(length + 1));
         if (!buffer) {
             return false;
         }
         JS_EncodeStringToBuffer(cx, str, buffer, length);
         buffer[length] = '\0';
         *((void**)d) = buffer;
         return true;
     }
 
     case nsXPTType::T_WCHAR_STR:
+    case nsXPTType::T_PWSTRING_SIZE_IS:
     {
         JSString* str;
 
         if (s.isUndefined() || s.isNull()) {
+            if (sizeis && 0 != arrlen) {
+                if (pErr)
+                    *pErr = NS_ERROR_XPC_NOT_ENOUGH_CHARS_IN_STRING;
+                return false;
+            }
             *((char16_t**)d) = nullptr;
             return true;
         }
 
         if (!(str = ToString(cx, s))) {
             return false;
         }
-        int len = JS_GetStringLength(str);
-        int byte_len = (len+1)*sizeof(char16_t);
+        size_t len = JS_GetStringLength(str);
+        if (sizeis) {
+            if (len > arrlen) {
+                if (pErr)
+                    *pErr = NS_ERROR_XPC_NOT_ENOUGH_CHARS_IN_STRING;
+                return false;
+            }
+            if (len < arrlen) {
+                len = arrlen;
+            }
+        }
+
+        size_t byte_len = (len+1)*sizeof(char16_t);
         if (!(*((void**)d) = moz_xmalloc(byte_len))) {
             // XXX should report error
             return false;
         }
         mozilla::Range<char16_t> destChars(*((char16_t**)d), len + 1);
         if (!JS_CopyStringChars(cx, destChars, str))
             return false;
         destChars[len] = 0;
 
         return true;
     }
 
     case nsXPTType::T_UTF8STRING:
     {
+        nsACString* rs = (nsACString*)d;
         if (s.isNull() || s.isUndefined()) {
-            nsCString* rs = *((nsCString**)d);
             rs->SetIsVoid(true);
             return true;
         }
 
         // The JS val is neither null nor void...
         JSString* str = ToString(cx, s);
         if (!str)
             return false;
 
         size_t length = JS_GetStringLength(str);
         if (!length) {
-            nsCString* rs = *((nsCString**)d);
             rs->Truncate();
             return true;
         }
 
         JSFlatString* flat = JS_FlattenString(cx, str);
         if (!flat)
             return false;
 
         size_t utf8Length = JS::GetDeflatedUTF8StringLength(flat);
-        nsACString* rs = *((nsACString**)d);
         rs->SetLength(utf8Length);
 
         JS::DeflateStringToUTF8Buffer(flat, mozilla::RangedPtr<char>(rs->BeginWriting(), utf8Length));
 
         return true;
     }
 
     case nsXPTType::T_CSTRING:
     {
+        nsACString* rs = (nsACString*)d;
         if (s.isNull() || s.isUndefined()) {
-            nsACString* rs = *((nsACString**)d);
             rs->SetIsVoid(true);
             return true;
         }
 
         // The JS val is neither null nor void...
         JSString* str = ToString(cx, s);
         if (!str) {
             return false;
         }
 
         size_t length = JS_GetStringEncodingLength(cx, str);
         if (length == size_t(-1)) {
             return false;
         }
 
         if (!length) {
-            nsCString* rs = *((nsCString**)d);
             rs->Truncate();
             return true;
         }
 
-        nsACString* rs = *((nsACString**)d);
         rs->SetLength(uint32_t(length));
         if (rs->Length() != uint32_t(length)) {
             return false;
         }
         JS_EncodeStringToBuffer(cx, str, rs->BeginWriting(), length);
 
         return true;
     }
@@ -731,16 +799,43 @@ XPCConvert::JSData2Native(void* d, Handl
 
         nsresult err = type.GetDOMObjectInfo().Unwrap(s, (void**)d);
         if (pErr) {
             *pErr = err;
         }
         return NS_SUCCEEDED(err);
     }
 
+    case nsXPTType::T_PROMISE:
+    {
+        nsIGlobalObject* glob = NativeGlobal(CurrentGlobalOrNull(cx));
+        if (!glob) {
+            if (pErr) {
+                *pErr = NS_ERROR_UNEXPECTED;
+            }
+            return false;
+        }
+
+        // Call Promise::Resolve to create a Promise object. This allows us to
+        // support returning non-promise values from Promise-returning functions
+        // in JS.
+        IgnoredErrorResult err;
+        *(Promise**)d = Promise::Resolve(glob, cx, s, err).take();
+        bool ok = !err.Failed();
+        if (pErr) {
+            *pErr = err.StealNSResult();
+        }
+
+        return ok;
+    }
+
+    case nsXPTType::T_ARRAY:
+        return JSArray2Native((void**)d, s, arrlen,
+                              type.ArrayElementType(), iid, pErr);
+
     default:
         NS_ERROR("bad type");
         return false;
     }
     return true;
 }
 
 /***************************************************************************/
@@ -791,30 +886,16 @@ XPCConvert::NativeInterface2JSObject(Mut
     }
     if (flat) {
         if (allowNativeWrapper && !JS_WrapObject(cx, &flat))
             return false;
         d.setObjectOrNull(flat);
         return true;
     }
 
-    if (iid->Equals(NS_GET_IID(nsISupports))) {
-        // Check for a Promise being returned via nsISupports.  In that
-        // situation, we want to dig out its underlying JS object and return
-        // that.
-        RefPtr<Promise> promise = do_QueryObject(aHelper.Object());
-        if (promise) {
-            flat = promise->PromiseObj();
-            if (!JS_WrapObject(cx, &flat))
-                return false;
-            d.setObjectOrNull(flat);
-            return true;
-        }
-    }
-
     // Don't double wrap CPOWs. This is a temporary measure for compatibility
     // with objects that don't provide necessary QIs (such as objects under
     // the new DOM bindings). We expect the other side of the CPOW to have
     // the appropriate wrappers in place.
     RootedObject cpow(cx, UnwrapNativeCPOW(aHelper.Object()));
     if (cpow) {
         if (!JS_WrapObject(cx, &cpow))
             return false;
@@ -931,27 +1012,16 @@ XPCConvert::JSObject2NativeInterface(voi
                 if (nsCOMPtr<mozIDOMWindow> inner = do_QueryInterface(iface)) {
                     iface = nsPIDOMWindowInner::From(inner)->GetOuterWindow();
                     return NS_SUCCEEDED(iface->QueryInterface(*iid, dest));
                 }
             }
 
             return false;
         }
-
-        // Deal with Promises being passed as nsISupports.  In that situation we
-        // want to create a dom::Promise and use that.
-        if (iid->Equals(NS_GET_IID(nsISupports))) {
-            RootedObject innerObj(cx, inner);
-            if (IsPromiseObject(innerObj)) {
-                nsIGlobalObject* glob = NativeGlobal(innerObj);
-                RefPtr<Promise> p = Promise::CreateFromExisting(glob, innerObj);
-                return p && NS_SUCCEEDED(p->QueryInterface(*iid, dest));
-            }
-        }
     }
 
     RefPtr<nsXPCWrappedJS> wrapper;
     nsresult rv = nsXPCWrappedJS::GetNewOrUsed(src, *iid, getter_AddRefs(wrapper));
     if (pErr)
         *pErr = rv;
 
     if (NS_FAILED(rv) || !wrapper)
@@ -1227,23 +1297,38 @@ XPCConvert::JSValToXPCException(MutableH
     }
     return NS_ERROR_FAILURE;
 }
 
 /***************************************************************************/
 
 // array fun...
 
-#ifdef POPULATE
-#undef POPULATE
-#endif
+static bool
+ValidArrayType(const nsXPTType& type)
+{
+    switch (type.Tag()) {
+        // These types aren't allowed to be in arrays.
+        case nsXPTType::T_VOID:
+        case nsXPTType::T_DOMSTRING:
+        case nsXPTType::T_UTF8STRING:
+        case nsXPTType::T_CSTRING:
+        case nsXPTType::T_ASTRING:
+        case nsXPTType::T_PSTRING_SIZE_IS:
+        case nsXPTType::T_PWSTRING_SIZE_IS:
+        case nsXPTType::T_ARRAY:
+            return false;
+        default:
+            return true;
+    }
+}
 
 // static
 bool
-XPCConvert::NativeArray2JS(MutableHandleValue d, const void** s,
+XPCConvert::NativeArray2JS(MutableHandleValue d, const void* const* s,
                            const nsXPTType& type, const nsID* iid,
                            uint32_t count, nsresult* pErr)
 {
     MOZ_ASSERT(s, "bad param");
 
     AutoJSContext cx;
 
     // XXX add support for putting chars in a string rather than an array
@@ -1252,121 +1337,33 @@ XPCConvert::NativeArray2JS(MutableHandle
 
     RootedObject array(cx, JS_NewArrayObject(cx, count));
     if (!array)
         return false;
 
     if (pErr)
         *pErr = NS_ERROR_XPC_BAD_CONVERT_NATIVE;
 
-    uint32_t i;
-    RootedValue current(cx, JS::NullValue());
-
-#define POPULATE(_t)                                                                    \
-    PR_BEGIN_MACRO                                                                      \
-        for (i = 0; i < count; i++) {                                                   \
-            if (!NativeData2JS(&current, ((_t*)*s)+i, type, iid, pErr) ||               \
-                !JS_DefineElement(cx, array, i, current, JSPROP_ENUMERATE))             \
-                return false;                                                           \
-        }                                                                               \
-    PR_END_MACRO
-
-    // XXX check IsPtr - esp. to handle array of nsID (as opposed to nsID*)
+    if (!ValidArrayType(type))
+        return false;
 
-    switch (type.TagPart()) {
-    case nsXPTType::T_I8            : POPULATE(int8_t);         break;
-    case nsXPTType::T_I16           : POPULATE(int16_t);        break;
-    case nsXPTType::T_I32           : POPULATE(int32_t);        break;
-    case nsXPTType::T_I64           : POPULATE(int64_t);        break;
-    case nsXPTType::T_U8            : POPULATE(uint8_t);        break;
-    case nsXPTType::T_U16           : POPULATE(uint16_t);       break;
-    case nsXPTType::T_U32           : POPULATE(uint32_t);       break;
-    case nsXPTType::T_U64           : POPULATE(uint64_t);       break;
-    case nsXPTType::T_FLOAT         : POPULATE(float);          break;
-    case nsXPTType::T_DOUBLE        : POPULATE(double);         break;
-    case nsXPTType::T_BOOL          : POPULATE(bool);           break;
-    case nsXPTType::T_CHAR          : POPULATE(char);           break;
-    case nsXPTType::T_WCHAR         : POPULATE(char16_t);       break;
-    case nsXPTType::T_VOID          : NS_ERROR("bad type");     return false;
-    case nsXPTType::T_IID           : POPULATE(nsID*);          break;
-    case nsXPTType::T_DOMSTRING     : NS_ERROR("bad type");     return false;
-    case nsXPTType::T_CHAR_STR      : POPULATE(char*);          break;
-    case nsXPTType::T_WCHAR_STR     : POPULATE(char16_t*);      break;
-    case nsXPTType::T_INTERFACE     : POPULATE(nsISupports*);   break;
-    case nsXPTType::T_INTERFACE_IS  : POPULATE(nsISupports*);   break;
-    case nsXPTType::T_DOMOBJECT     : POPULATE(void*);          break;
-    case nsXPTType::T_UTF8STRING    : NS_ERROR("bad type");     return false;
-    case nsXPTType::T_CSTRING       : NS_ERROR("bad type");     return false;
-    case nsXPTType::T_ASTRING       : NS_ERROR("bad type");     return false;
-    default                         : NS_ERROR("bad type");     return false;
+    RootedValue current(cx, JS::NullValue());
+    for (uint32_t i = 0; i < count; ++i) {
+        if (!NativeData2JS(&current, type.ElementPtr(*s, i), type, iid, 0, pErr) ||
+            !JS_DefineElement(cx, array, i, current, JSPROP_ENUMERATE))
+            return false;
     }
 
     if (pErr)
         *pErr = NS_OK;
     d.setObject(*array);
     return true;
-
-#undef POPULATE
 }
 
 
-
-// Check that the tag part of the type matches the type
-// of the array. If the check succeeds, check that the size
-// of the output does not exceed UINT32_MAX bytes. Allocate
-// the memory and copy the elements by memcpy.
-static void*
-CheckTargetAndPopulate(const nsXPTType& type,
-                       uint8_t requiredType,
-                       size_t typeSize,
-                       uint32_t count,
-                       JSObject* tArr,
-                       nsresult* pErr)
-{
-    // Check that the element type expected by the interface matches
-    // the type of the elements in the typed array exactly, including
-    // signedness.
-    if (type.TagPart() != requiredType) {
-        if (pErr)
-            *pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
-
-        return nullptr;
-    }
-
-    // Calculate the maximum number of elements that can fit in
-    // UINT32_MAX bytes.
-    size_t max = UINT32_MAX / typeSize;
-
-    // This could overflow on 32-bit systems so check max first.
-    size_t byteSize = count * typeSize;
-    if (count > max) {
-        if (pErr)
-            *pErr = NS_ERROR_OUT_OF_MEMORY;
-
-        return nullptr;
-    }
-
-    JS::AutoCheckCannotGC nogc;
-    bool isShared;
-    void* buf = JS_GetArrayBufferViewData(tArr, &isShared, nogc);
-
-    // Require opting in to shared memory - a future project.
-    if (isShared) {
-        if (pErr)
-            *pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
-
-        return nullptr;
-    }
-
-    void* output = moz_xmalloc(byteSize);
-
-    memcpy(output, buf, byteSize);
-    return output;
-}
-
 // Fast conversion of typed arrays to native using memcpy.
 // No float or double canonicalization is done. Called by
 // JSarray2Native whenever a TypedArray is met. ArrayBuffers
 // are not accepted; create a properly typed array view on them
 // first. The element type of array must match the XPCOM
 // type in size, type and signedness exactly. As an exception,
 // Uint8ClampedArray is allowed for arrays of uint8_t. DataViews
 // are not supported.
@@ -1388,130 +1385,135 @@ XPCConvert::JSTypedArray2Native(void** d
     uint32_t len = JS_GetTypedArrayLength(jsArray);
     if (len < count) {
         if (pErr)
             *pErr = NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY;
 
         return false;
     }
 
-    void* output = nullptr;
-
+    uint8_t expected;
     switch (JS_GetArrayBufferViewType(jsArray)) {
     case js::Scalar::Int8:
-        output = CheckTargetAndPopulate({ nsXPTType::T_I8 }, type,
-                                        sizeof(int8_t), count,
-                                        jsArray, pErr);
-        if (!output) {
-            return false;
-        }
+        expected = nsXPTType::T_I8;
         break;
 
     case js::Scalar::Uint8:
     case js::Scalar::Uint8Clamped:
-        output = CheckTargetAndPopulate({ nsXPTType::T_U8 }, type,
-                                        sizeof(uint8_t), count,
-                                        jsArray, pErr);
-        if (!output) {
-            return false;
-        }
+        expected = nsXPTType::T_U8;
         break;
 
     case js::Scalar::Int16:
-        output = CheckTargetAndPopulate({ nsXPTType::T_I16 }, type,
-                                        sizeof(int16_t), count,
-                                        jsArray, pErr);
-        if (!output) {
-            return false;
-        }
+        expected = nsXPTType::T_I16;
         break;
 
     case js::Scalar::Uint16:
-        output = CheckTargetAndPopulate({ nsXPTType::T_U16 }, type,
-                                        sizeof(uint16_t), count,
-                                        jsArray, pErr);
-        if (!output) {
-            return false;
-        }
+        expected = nsXPTType::T_U16;
         break;
 
     case js::Scalar::Int32:
-        output = CheckTargetAndPopulate({ nsXPTType::T_I32 }, type,
-                                        sizeof(int32_t), count,
-                                        jsArray, pErr);
-        if (!output) {
-            return false;
-        }
+        expected = nsXPTType::T_I32;
         break;
 
     case js::Scalar::Uint32:
-        output = CheckTargetAndPopulate({ nsXPTType::T_U32 }, type,
-                                        sizeof(uint32_t), count,
-                                        jsArray, pErr);
-        if (!output) {
-            return false;
-        }
+        expected = nsXPTType::T_U32;
         break;
 
     case js::Scalar::Float32:
-        output = CheckTargetAndPopulate({ nsXPTType::T_FLOAT }, type,
-                                        sizeof(float), count,
-                                        jsArray, pErr);
-        if (!output) {
-            return false;
-        }
+        expected = nsXPTType::T_FLOAT;
         break;
 
     case js::Scalar::Float64:
-        output = CheckTargetAndPopulate({ nsXPTType::T_DOUBLE }, type,
-                                        sizeof(double), count,
-                                        jsArray, pErr);
-        if (!output) {
-            return false;
-        }
+        expected = nsXPTType::T_DOUBLE;
         break;
 
     // Yet another array type was defined? It is not supported yet...
     default:
         if (pErr)
             *pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
+        return false;
+    }
 
+    // Check that the type we got is the type we expected.
+    if (expected != type.Tag()) {
+        if (pErr)
+            *pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
+        return false;
+    }
+
+    // Check if the size would overflow uint32_t.
+    if (count > (UINT32_MAX / type.Stride())) {
+        if (pErr)
+            *pErr = NS_ERROR_OUT_OF_MEMORY;
         return false;
     }
 
-    *d = output;
+    // Get the backing memory buffer to copy out of.
+    JS::AutoCheckCannotGC nogc;
+    bool isShared;
+    void* buf = JS_GetArrayBufferViewData(jsArray, &isShared, nogc);
+
+    // Require opting in to shared memory - a future project.
+    if (isShared) {
+        if (pErr)
+            *pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
+        return false;
+    }
+
+    // Allocate the buffer, and copy.
+    *d = moz_xmalloc(count * type.Stride());
+    if (!*d) {
+        if (pErr)
+            *pErr = NS_ERROR_OUT_OF_MEMORY;
+        return false;
+    }
+
+    memcpy(*d, buf, count * type.Stride());
+
     if (pErr)
         *pErr = NS_OK;
 
     return true;
 }
 
 // static
 bool
 XPCConvert::JSArray2Native(void** d, HandleValue s,
                            uint32_t count, const nsXPTType& type,
                            const nsID* iid, nsresult* pErr)
 {
     MOZ_ASSERT(d, "bad param");
 
     AutoJSContext cx;
 
+    // FIXME: XPConnect historically has shortcut the JSArray2Native codepath in
+    // its caller if count is 0, allowing arbitrary values to be passed as
+    // arrays and interpreted as the empty array (bug 1458987).
+    //
+    // NOTE: Once this is fixed, null/undefined should be allowed for arrays if
+    // count is 0.
+    if (count == 0) {
+        *d = nullptr;
+        return true;
+    }
+
     // XXX add support for getting chars from strings
 
     // XXX add support to indicate *which* array element was not convertable
 
+    if (pErr)
+        *pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
+
+    if (!ValidArrayType(type))
+        return false;
+
     if (s.isNullOrUndefined()) {
-        if (0 != count) {
-            if (pErr)
-                *pErr = NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY;
-            return false;
-        }
-
-        *d = nullptr;
-        return true;
+        if (pErr)
+            *pErr = NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY;
+        return false;
     }
 
     if (!s.isObject()) {
         if (pErr)
             *pErr = NS_ERROR_XPC_CANT_CONVERT_PRIMITIVE_TO_ARRAY;
         return false;
     }
 
@@ -1531,251 +1533,115 @@ XPCConvert::JSArray2Native(void** d, Han
 
     uint32_t len;
     if (!JS_GetArrayLength(cx, jsarray, &len) || len < count) {
         if (pErr)
             *pErr = NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY;
         return false;
     }
 
-    if (pErr)
-        *pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
+    if (count > (UINT32_MAX / type.Stride())) {
+        if (pErr)
+            *pErr = NS_ERROR_OUT_OF_MEMORY;
+        return false;
+    }
 
-#define POPULATE(_mode, _t)                                                    \
-    PR_BEGIN_MACRO                                                             \
-        cleanupMode = _mode;                                                   \
-        size_t max = UINT32_MAX / sizeof(_t);                                  \
-        if (count > max ||                                                     \
-            nullptr == (array = moz_xmalloc(count * sizeof(_t)))) {            \
-            if (pErr)                                                          \
-                *pErr = NS_ERROR_OUT_OF_MEMORY;                                \
-            goto failure;                                                      \
-        }                                                                      \
-        for (initedCount = 0; initedCount < count; initedCount++) {            \
-            if (!JS_GetElement(cx, jsarray, initedCount, &current) ||          \
-                !JSData2Native(((_t*)array)+initedCount, current, type,        \
-                               iid, pErr))                                     \
-                goto failure;                                                  \
-        }                                                                      \
-    PR_END_MACRO
-
-    // No Action, FRee memory, RElease object, CLeanup object
-    enum CleanupMode {na, fr, re, cl};
-
-    CleanupMode cleanupMode;
-
-    void* array = nullptr;
-    uint32_t initedCount;
-    RootedValue current(cx);
+    // Allocate the destination array, and begin converting elements.
+    *d = moz_xmalloc(count * type.Stride());
+    if (!*d) {
+        if (pErr)
+            *pErr = NS_ERROR_OUT_OF_MEMORY;
+        return false;
+    }
 
-    // XXX check IsPtr - esp. to handle array of nsID (as opposed to nsID*)
-    // XXX make extra space at end of char* and wchar* and null termintate
+    RootedValue current(cx);
+    uint32_t initedCount;
+    for (initedCount = 0; initedCount < count; ++initedCount) {
+        if (!JS_GetElement(cx, jsarray, initedCount, &current) ||
+            !JSData2Native(type.ElementPtr(*d, initedCount),
+                           current, type, iid, 0, pErr))
+            break;
+    }
 
-    switch (type.TagPart()) {
-    case nsXPTType::T_I8            : POPULATE(na, int8_t);         break;
-    case nsXPTType::T_I16           : POPULATE(na, int16_t);        break;
-    case nsXPTType::T_I32           : POPULATE(na, int32_t);        break;
-    case nsXPTType::T_I64           : POPULATE(na, int64_t);        break;
-    case nsXPTType::T_U8            : POPULATE(na, uint8_t);        break;
-    case nsXPTType::T_U16           : POPULATE(na, uint16_t);       break;
-    case nsXPTType::T_U32           : POPULATE(na, uint32_t);       break;
-    case nsXPTType::T_U64           : POPULATE(na, uint64_t);       break;
-    case nsXPTType::T_FLOAT         : POPULATE(na, float);          break;
-    case nsXPTType::T_DOUBLE        : POPULATE(na, double);         break;
-    case nsXPTType::T_BOOL          : POPULATE(na, bool);           break;
-    case nsXPTType::T_CHAR          : POPULATE(na, char);           break;
-    case nsXPTType::T_WCHAR         : POPULATE(na, char16_t);       break;
-    case nsXPTType::T_VOID          : NS_ERROR("bad type");         goto failure;
-    case nsXPTType::T_IID           : POPULATE(fr, nsID*);          break;
-    case nsXPTType::T_DOMSTRING     : NS_ERROR("bad type");         goto failure;
-    case nsXPTType::T_CHAR_STR      : POPULATE(fr, char*);          break;
-    case nsXPTType::T_WCHAR_STR     : POPULATE(fr, char16_t*);      break;
-    case nsXPTType::T_INTERFACE     : POPULATE(re, nsISupports*);   break;
-    case nsXPTType::T_INTERFACE_IS  : POPULATE(re, nsISupports*);   break;
-    case nsXPTType::T_DOMOBJECT     : POPULATE(cl, void*);          break;
-    case nsXPTType::T_UTF8STRING    : NS_ERROR("bad type");         goto failure;
-    case nsXPTType::T_CSTRING       : NS_ERROR("bad type");         goto failure;
-    case nsXPTType::T_ASTRING       : NS_ERROR("bad type");         goto failure;
-    default                         : NS_ERROR("bad type");         goto failure;
+    // Check if we handled every array element.
+    if (initedCount == count) {
+        if (pErr)
+            *pErr = NS_OK;
+        return true;
     }
 
-    *d = array;
-    if (pErr)
-        *pErr = NS_OK;
-    return true;
-
-failure:
-    // we may need to cleanup the partially filled array of converted stuff
-    if (array) {
-        if (cleanupMode == re) {
-            nsISupports** a = (nsISupports**) array;
-            for (uint32_t i = 0; i < initedCount; i++) {
-                nsISupports* p = a[i];
-                NS_IF_RELEASE(p);
-            }
-        } else if (cleanupMode == fr) {
-            void** a = (void**) array;
-            for (uint32_t i = 0; i < initedCount; i++) {
-                void* p = a[i];
-                if (p) free(p);
-            }
-        } else if (cleanupMode == cl) {
-            void** a = (void**) array;
-            for (uint32_t i = 0; i < initedCount; i++) {
-                void* p = a[i];
-                if (p) type.GetDOMObjectInfo().Cleanup(p);
-            }
-        }
-        free(array);
+    // Something failed! Clean up after ourselves.
+    for (uint32_t i = 0; i < initedCount; ++i) {
+        CleanupValue(type, type.ElementPtr(*d, i));
     }
-
+    free(*d);
+    *d = nullptr;
     return false;
-
-#undef POPULATE
 }
 
-// static
-bool
-XPCConvert::NativeStringWithSize2JS(MutableHandleValue d, const void* s,
-                                    const nsXPTType& type,
-                                    uint32_t count,
-                                    nsresult* pErr)
+/***************************************************************************/
+
+// Internal implementation details for xpc::CleanupValue.
+
+void
+xpc::InnerCleanupValue(const nsXPTType& aType, void* aValue, uint32_t aArrayLen)
 {
-    MOZ_ASSERT(s, "bad param");
+    MOZ_ASSERT(!aType.IsArithmetic(),
+               "Arithmetic types should not get to InnerCleanupValue!");
+    MOZ_ASSERT(aArrayLen == 0 ||
+               aType.Tag() == nsXPTType::T_PSTRING_SIZE_IS ||
+               aType.Tag() == nsXPTType::T_PWSTRING_SIZE_IS ||
+               aType.Tag() == nsXPTType::T_ARRAY,
+               "Array lengths may only appear for certain types!");
+
+    switch (aType.Tag()) {
+        // Pointer types
+        case nsXPTType::T_DOMOBJECT:
+            aType.GetDOMObjectInfo().Cleanup(*(void**)aValue);
+            break;
+
+        case nsXPTType::T_PROMISE:
+            (*(mozilla::dom::Promise**)aValue)->Release();
+            break;
+
+        case nsXPTType::T_INTERFACE:
+        case nsXPTType::T_INTERFACE_IS:
+            (*(nsISupports**)aValue)->Release();
+            break;
 
-    AutoJSContext cx;
-    if (pErr)
-        *pErr = NS_ERROR_XPC_BAD_CONVERT_NATIVE;
+        // String types
+        case nsXPTType::T_DOMSTRING:
+        case nsXPTType::T_ASTRING:
+            ((nsAString*)aValue)->Truncate();
+            break;
+        case nsXPTType::T_UTF8STRING:
+        case nsXPTType::T_CSTRING:
+            ((nsACString*)aValue)->Truncate();
+            break;
 
-    switch (type.TagPart()) {
+        // Pointer Types
+        case nsXPTType::T_IID:
+        case nsXPTType::T_CHAR_STR:
+        case nsXPTType::T_WCHAR_STR:
         case nsXPTType::T_PSTRING_SIZE_IS:
+        case nsXPTType::T_PWSTRING_SIZE_IS:
+            free(*(void**)aValue);
+            break;
+
+        // Array Types
+        case nsXPTType::T_ARRAY:
         {
-            char* p = *((char**)s);
-            if (!p)
-                break;
-            JSString* str;
-            if (!(str = JS_NewStringCopyN(cx, p, count)))
-                return false;
-            d.setString(str);
+            const nsXPTType& elty = aType.ArrayElementType();
+            void* elements = *(void**)aValue;
+
+            for (uint32_t i = 0; i < aArrayLen; ++i) {
+                CleanupValue(elty, elty.ElementPtr(elements, i));
+            }
+            free(elements);
             break;
         }
-        case nsXPTType::T_PWSTRING_SIZE_IS:
-        {
-            char16_t* p = *((char16_t**)s);
-            if (!p)
-                break;
-            JSString* str;
-            if (!(str = JS_NewUCStringCopyN(cx, p, count)))
-                return false;
-            d.setString(str);
-            break;
-        }
-        default:
-            XPC_LOG_ERROR(("XPCConvert::NativeStringWithSize2JS : unsupported type"));
-            return false;
     }
-    return true;
-}
-
-// static
-bool
-XPCConvert::JSStringWithSize2Native(void* d, HandleValue s,
-                                    uint32_t count, const nsXPTType& type,
-                                    nsresult* pErr)
-{
-    MOZ_ASSERT(!s.isNull(), "bad param");
-    MOZ_ASSERT(d, "bad param");
-
-    AutoJSContext cx;
-    uint32_t len;
-
-    if (pErr)
-        *pErr = NS_ERROR_XPC_BAD_CONVERT_NATIVE;
-
-    switch (type.TagPart()) {
-        case nsXPTType::T_PSTRING_SIZE_IS:
-        {
-            if (s.isUndefined() || s.isNull()) {
-                if (0 != count) {
-                    if (pErr)
-                        *pErr = NS_ERROR_XPC_NOT_ENOUGH_CHARS_IN_STRING;
-                    return false;
-                }
-                *((char**)d) = nullptr;
-                return true;
-            }
-
-            JSString* str = ToString(cx, s);
-            if (!str) {
-                return false;
-            }
 
-            size_t length = JS_GetStringEncodingLength(cx, str);
-            if (length == size_t(-1)) {
-                return false;
-            }
-            if (length > count) {
-                if (pErr)
-                    *pErr = NS_ERROR_XPC_NOT_ENOUGH_CHARS_IN_STRING;
-                return false;
-            }
-            len = uint32_t(length);
-
-            if (len < count)
-                len = count;
-
-            uint32_t alloc_len = (len + 1) * sizeof(char);
-            char* buffer = static_cast<char*>(moz_xmalloc(alloc_len));
-            if (!buffer) {
-                return false;
-            }
-            JS_EncodeStringToBuffer(cx, str, buffer, len);
-            buffer[len] = '\0';
-            *((char**)d) = buffer;
-
-            return true;
-        }
-
-        case nsXPTType::T_PWSTRING_SIZE_IS:
-        {
-            JSString* str;
-
-            if (s.isUndefined() || s.isNull()) {
-                if (0 != count) {
-                    if (pErr)
-                        *pErr = NS_ERROR_XPC_NOT_ENOUGH_CHARS_IN_STRING;
-                    return false;
-                }
-
-                *((const char16_t**)d) = nullptr;
-                return true;
-            }
-
-            if (!(str = ToString(cx, s))) {
-                return false;
-            }
-
-            len = JS_GetStringLength(str);
-            if (len > count) {
-                if (pErr)
-                    *pErr = NS_ERROR_XPC_NOT_ENOUGH_CHARS_IN_STRING;
-                return false;
-            }
-
-            len = count;
-
-            uint32_t alloc_len = (len + 1) * sizeof(char16_t);
-            if (!(*((void**)d) = moz_xmalloc(alloc_len))) {
-                // XXX should report error
-                return false;
-            }
-            mozilla::Range<char16_t> destChars(*((char16_t**)d), len + 1);
-            if (!JS_CopyStringChars(cx, destChars, str))
-                return false;
-            destChars[count] = 0;
-
-            return true;
-        }
-        default:
-            XPC_LOG_ERROR(("XPCConvert::JSStringWithSize2Native : unsupported type"));
-            return false;
+    // Null out the pointer if we have it.
+    if (aType.HasPointerRepr()) {
+        *(void**)aValue = nullptr;
     }
 }
--- a/js/xpconnect/src/XPCInlines.h
+++ b/js/xpconnect/src/XPCInlines.h
@@ -517,9 +517,25 @@ inline
 void ThrowBadResult(nsresult result, XPCCallContext& ccx)
 {
     XPCThrower::ThrowBadResult(NS_ERROR_XPC_NATIVE_RETURNED_FAILURE,
                                result, ccx);
 }
 
 /***************************************************************************/
 
+inline void
+xpc::CleanupValue(const nsXPTType& aType,
+                  void* aValue,
+                  uint32_t aArrayLen)
+{
+    // Check if we can do a cheap early return, and only perform the inner call
+    // if we can't. We never have to clean up null pointer types or arithmetic
+    // types.
+    if (aType.IsArithmetic() || (aType.HasPointerRepr() && !*(void**)aValue)) {
+        return;
+    }
+    xpc::InnerCleanupValue(aType, aValue, aArrayLen);
+}
+
+/***************************************************************************/
+
 #endif /* xpcinlines_h___ */
--- a/js/xpconnect/src/XPCVariant.cpp
+++ b/js/xpconnect/src/XPCVariant.cpp
@@ -213,38 +213,38 @@ XPCArrayHomogenizer::GetTypeForArray(JSC
         MOZ_ASSERT(state != tUnk, "bad state table!");
 
         if (state == tVar)
             break;
     }
 
     switch (state) {
         case tInt :
-            *resultType = TD_INT32;
+            *resultType = nsXPTType::MkArrayType(nsXPTType::Idx::INT32);
             break;
         case tDbl :
-            *resultType = TD_DOUBLE;
+            *resultType = nsXPTType::MkArrayType(nsXPTType::Idx::DOUBLE);
             break;
         case tBool:
-            *resultType = TD_BOOL;
+            *resultType = nsXPTType::MkArrayType(nsXPTType::Idx::BOOL);
             break;
         case tStr :
-            *resultType = TD_PWSTRING;
+            *resultType = nsXPTType::MkArrayType(nsXPTType::Idx::PWSTRING);
             break;
         case tID  :
-            *resultType = TD_PNSIID;
+            *resultType = nsXPTType::MkArrayType(nsXPTType::Idx::PNSIID);
             break;
         case tISup:
-            *resultType = TD_INTERFACE_IS_TYPE;
+            *resultType = nsXPTType::MkArrayType(nsXPTType::Idx::INTERFACE_IS_TYPE);
             *resultID = NS_GET_IID(nsISupports);
             break;
         case tNull:
             // FALL THROUGH
         case tVar :
-            *resultType = TD_INTERFACE_IS_TYPE;
+            *resultType = nsXPTType::MkArrayType(nsXPTType::Idx::INTERFACE_IS_TYPE);
             *resultID = NS_GET_IID(nsIVariant);
             break;
         case tArr :
             // FALL THROUGH
         case tUnk :
             // FALL THROUGH
         case tErr :
             // FALL THROUGH
@@ -334,25 +334,26 @@ bool XPCVariant::InitializeData(JSContex
         }
 
         nsXPTType type;
         nsID id;
 
         if (!XPCArrayHomogenizer::GetTypeForArray(cx, jsobj, len, &type, &id))
             return false;
 
-        if (!XPCConvert::JSArray2Native(&mData.u.array.mArrayValue,
-                                        val, len, type, &id, nullptr))
+        if (!XPCConvert::JSData2Native(&mData.u.array.mArrayValue,
+                                       val, type, &id, len, nullptr))
             return false;
 
+        const nsXPTType& elty = type.ArrayElementType();
         mData.mType = nsIDataType::VTYPE_ARRAY;
-        if (type.IsInterfacePointer())
+        if (elty.IsInterfacePointer())
             mData.u.array.mArrayInterfaceID = id;
         mData.u.array.mArrayCount = len;
-        mData.u.array.mArrayType = type.TagPart();
+        mData.u.array.mArrayType = elty.Tag();
 
         return true;
     }
 
     // XXX This could be smarter and pick some more interesting iface.
 
     nsXPConnect*  xpc = nsXPConnect::XPConnect();
     nsCOMPtr<nsISupports> wrapper;
@@ -448,122 +449,118 @@ XPCVariant::VariantDataToJS(nsIVariant* 
             pJSVal.setBoolean(b);
             return true;
         }
         case nsIDataType::VTYPE_CHAR:
         {
             char c;
             if (NS_FAILED(variant->GetAsChar(&c)))
                 return false;
-            return XPCConvert::NativeData2JS(pJSVal, (const void*)&c, { TD_CHAR }, &iid, pErr);
+            return XPCConvert::NativeData2JS(pJSVal, (const void*)&c, { TD_CHAR }, &iid, 0, pErr);
         }
         case nsIDataType::VTYPE_WCHAR:
         {
             char16_t wc;
             if (NS_FAILED(variant->GetAsWChar(&wc)))
                 return false;
-            return XPCConvert::NativeData2JS(pJSVal, (const void*)&wc, { TD_WCHAR }, &iid, pErr);
+            return XPCConvert::NativeData2JS(pJSVal, (const void*)&wc, { TD_WCHAR }, &iid, 0, pErr);
         }
         case nsIDataType::VTYPE_ID:
         {
             if (NS_FAILED(variant->GetAsID(&iid)))
                 return false;
             nsID* v = &iid;
-            return XPCConvert::NativeData2JS(pJSVal, (const void*)&v, { TD_PNSIID }, &iid, pErr);
+            return XPCConvert::NativeData2JS(pJSVal, (const void*)&v, { TD_PNSIID }, &iid, 0, pErr);
         }
         case nsIDataType::VTYPE_ASTRING:
         {
             nsAutoString astring;
             if (NS_FAILED(variant->GetAsAString(astring)))
                 return false;
-            nsAutoString* v = &astring;
-            return XPCConvert::NativeData2JS(pJSVal, (const void*)&v, { TD_ASTRING }, &iid, pErr);
+            return XPCConvert::NativeData2JS(pJSVal, &astring, { TD_ASTRING }, &iid, 0, pErr);
         }
         case nsIDataType::VTYPE_DOMSTRING:
         {
             nsAutoString astring;
             if (NS_FAILED(variant->GetAsAString(astring)))
                 return false;
-            nsAutoString* v = &astring;
-            return XPCConvert::NativeData2JS(pJSVal, (const void*)&v,
-                                             { TD_DOMSTRING }, &iid, pErr);
+            return XPCConvert::NativeData2JS(pJSVal, &astring, { TD_DOMSTRING }, &iid, 0, pErr);
         }
         case nsIDataType::VTYPE_CSTRING:
         {
             nsAutoCString cString;
             if (NS_FAILED(variant->GetAsACString(cString)))
                 return false;
-            nsAutoCString* v = &cString;
-            return XPCConvert::NativeData2JS(pJSVal, (const void*)&v,
-                                             { TD_CSTRING }, &iid, pErr);
+            return XPCConvert::NativeData2JS(pJSVal, &cString, { TD_CSTRING }, &iid, 0, pErr);
         }
         case nsIDataType::VTYPE_UTF8STRING:
         {
             nsUTF8String utf8String;
             if (NS_FAILED(variant->GetAsAUTF8String(utf8String)))
                 return false;
-            nsUTF8String* v = &utf8String;
-            return XPCConvert::NativeData2JS(pJSVal, (const void*)&v,
-                                             { TD_UTF8STRING }, &iid, pErr);
+            return XPCConvert::NativeData2JS(pJSVal, &utf8String, { TD_UTF8STRING }, &iid, 0, pErr);
         }
         case nsIDataType::VTYPE_CHAR_STR:
         {
             char* pc;
             if (NS_FAILED(variant->GetAsString(&pc)))
                 return false;
             bool success = XPCConvert::NativeData2JS(pJSVal, (const void*)&pc,
-                                                     { TD_PSTRING }, &iid, pErr);
+                                                     { TD_PSTRING }, &iid, 0, pErr);
             free(pc);
             return success;
         }
         case nsIDataType::VTYPE_STRING_SIZE_IS:
         {
             char* pc;
             uint32_t size;
             if (NS_FAILED(variant->GetAsStringWithSize(&size, &pc)))
                 return false;
-            bool success = XPCConvert::NativeStringWithSize2JS(pJSVal, (const void*)&pc,
-                                                               { TD_PSTRING_SIZE_IS }, size, pErr);
+            bool success = XPCConvert::NativeData2JS(pJSVal, (const void*)&pc,
+                                                     { TD_PSTRING_SIZE_IS },
+                                                     &iid, size, pErr);
             free(pc);
             return success;
         }
         case nsIDataType::VTYPE_WCHAR_STR:
         {
             char16_t* pwc;
             if (NS_FAILED(variant->GetAsWString(&pwc)))
                 return false;
             bool success = XPCConvert::NativeData2JS(pJSVal, (const void*)&pwc,
-                                                     { TD_PSTRING }, &iid, pErr);
+                                                     { TD_PSTRING }, &iid, 0, pErr);
             free(pwc);
             return success;
         }
         case nsIDataType::VTYPE_WSTRING_SIZE_IS:
         {
             char16_t* pwc;
             uint32_t size;
             if (NS_FAILED(variant->GetAsWStringWithSize(&size, &pwc)))
                 return false;
-            bool success = XPCConvert::NativeStringWithSize2JS(pJSVal, (const void*)&pwc,
-                                                               { TD_PWSTRING_SIZE_IS }, size, pErr);
+            bool success = XPCConvert::NativeData2JS(pJSVal, (const void*)&pwc,
+                                                     { TD_PWSTRING_SIZE_IS },
+                                                     &iid, size, pErr);
             free(pwc);
             return success;
         }
         case nsIDataType::VTYPE_INTERFACE:
         case nsIDataType::VTYPE_INTERFACE_IS:
         {
             nsISupports* pi;
             nsID* piid;
             if (NS_FAILED(variant->GetAsInterface(&piid, (void**)&pi)))
                 return false;
 
             iid = *piid;
             free((char*)piid);
 
             bool success = XPCConvert::NativeData2JS(pJSVal, (const void*)&pi,
-                                                     { TD_INTERFACE_IS_TYPE }, &iid, pErr);
+                                                     { TD_INTERFACE_IS_TYPE },
+                                                     &iid, 0, pErr);
             if (pi)
                 pi->Release();
             return success;
         }
         case nsIDataType::VTYPE_ARRAY:
         {
             nsDiscriminatedUnion du;
             nsresult rv;
@@ -573,51 +570,76 @@ XPCVariant::VariantDataToJS(nsIVariant* 
                                      &du.u.array.mArrayCount,
                                      &du.u.array.mArrayValue);
             if (NS_FAILED(rv))
                 return false;
 
             // must exit via VARIANT_DONE from here on...
             du.mType = nsIDataType::VTYPE_ARRAY;
 
-            nsXPTType conversionType;
             uint16_t elementType = du.u.array.mArrayType;
             const nsID* pid = nullptr;
 
+            nsXPTType::Idx xptIndex;
             switch (elementType) {
                 case nsIDataType::VTYPE_INT8:
+                    xptIndex = nsXPTType::Idx::INT8;
+                    break;
                 case nsIDataType::VTYPE_INT16:
+                    xptIndex = nsXPTType::Idx::INT16;
+                    break;
                 case nsIDataType::VTYPE_INT32:
+                    xptIndex = nsXPTType::Idx::INT32;
+                    break;
                 case nsIDataType::VTYPE_INT64:
+                    xptIndex = nsXPTType::Idx::INT64;
+                    break;
                 case nsIDataType::VTYPE_UINT8:
+                    xptIndex = nsXPTType::Idx::UINT8;
+                    break;
                 case nsIDataType::VTYPE_UINT16:
+                    xptIndex = nsXPTType::Idx::UINT16;
+                    break;
                 case nsIDataType::VTYPE_UINT32:
+                    xptIndex = nsXPTType::Idx::UINT32;
+                    break;
                 case nsIDataType::VTYPE_UINT64:
+                    xptIndex = nsXPTType::Idx::UINT64;
+                    break;
                 case nsIDataType::VTYPE_FLOAT:
+                    xptIndex = nsXPTType::Idx::FLOAT;
+                    break;
                 case nsIDataType::VTYPE_DOUBLE:
+                    xptIndex = nsXPTType::Idx::DOUBLE;
+                    break;
                 case nsIDataType::VTYPE_BOOL:
+                    xptIndex = nsXPTType::Idx::BOOL;
+                    break;
                 case nsIDataType::VTYPE_CHAR:
+                    xptIndex = nsXPTType::Idx::CHAR;
+                    break;
                 case nsIDataType::VTYPE_WCHAR:
-                    conversionType = (uint8_t)elementType;
+                    xptIndex = nsXPTType::Idx::WCHAR;
                     break;
-
                 case nsIDataType::VTYPE_ID:
+                    xptIndex = nsXPTType::Idx::PNSIID;
+                    break;
                 case nsIDataType::VTYPE_CHAR_STR:
+                    xptIndex = nsXPTType::Idx::PSTRING;
+                    break;
                 case nsIDataType::VTYPE_WCHAR_STR:
-                    conversionType = (uint8_t)elementType;
+                    xptIndex = nsXPTType::Idx::PWSTRING;
                     break;
-
                 case nsIDataType::VTYPE_INTERFACE:
                     pid = &NS_GET_IID(nsISupports);
-                    conversionType = (uint8_t)elementType;
+                    xptIndex = nsXPTType::Idx::INTERFACE_IS_TYPE;
                     break;
-
                 case nsIDataType::VTYPE_INTERFACE_IS:
                     pid = &du.u.array.mArrayInterfaceID;
-                    conversionType = (uint8_t)elementType;
+                    xptIndex = nsXPTType::Idx::INTERFACE_IS_TYPE;
                     break;
 
                 // The rest are illegal.
                 case nsIDataType::VTYPE_VOID:
                 case nsIDataType::VTYPE_ASTRING:
                 case nsIDataType::VTYPE_DOMSTRING:
                 case nsIDataType::VTYPE_CSTRING:
                 case nsIDataType::VTYPE_UTF8STRING:
@@ -627,20 +649,19 @@ XPCVariant::VariantDataToJS(nsIVariant* 
                 case nsIDataType::VTYPE_EMPTY_ARRAY:
                 case nsIDataType::VTYPE_EMPTY:
                 default:
                     NS_ERROR("bad type in array!");
                     return false;
             }
 
             bool success =
-                XPCConvert::NativeArray2JS(pJSVal,
-                                           (const void**)&du.u.array.mArrayValue,
-                                           conversionType, pid,
-                                           du.u.array.mArrayCount, pErr);
+                XPCConvert::NativeData2JS(pJSVal, (const void*)&du.u.array.mArrayValue,
+                                          nsXPTType::MkArrayType(xptIndex), pid,
+                                          du.u.array.mArrayCount, pErr);
 
             return success;
         }
         case nsIDataType::VTYPE_EMPTY_ARRAY:
         {
             JSObject* array = JS_NewArrayObject(cx, 0);
             if (!array)
                 return false;
--- a/js/xpconnect/src/XPCWrappedJSClass.cpp
+++ b/js/xpconnect/src/XPCWrappedJSClass.cpp
@@ -299,17 +299,17 @@ GetNamedPropertyAsVariantRaw(XPCCallCont
                              nsIVariant** aResult,
                              nsresult* pErr)
 {
     nsXPTType type = { TD_INTERFACE_TYPE };
     RootedValue val(ccx);
 
     return JS_GetPropertyById(ccx, aJSObj, aName, &val) &&
            XPCConvert::JSData2Native(aResult, val, type,
-                                     &NS_GET_IID(nsIVariant), pErr);
+                                     &NS_GET_IID(nsIVariant), 0, pErr);
 }
 
 // static
 nsresult
 nsXPCWrappedJSClass::GetNamedPropertyAsVariant(XPCCallContext& ccx,
                                                JSObject* aJSObjArg,
                                                const nsAString& aName,
                                                nsIVariant** aResult)
@@ -487,20 +487,20 @@ GetFunctionName(JSContext* cx, HandleObj
     if (filenameSuffix) {
         filenameSuffix++;
     } else {
         filenameSuffix = filename;
     }
 
     nsCString displayName("anonymous");
     if (funName) {
-        nsCString* displayNamePtr = &displayName;
         RootedValue funNameVal(cx, StringValue(funName));
-        if (!XPCConvert::JSData2Native(&displayNamePtr, funNameVal, { nsXPTType::T_UTF8STRING },
-                                       nullptr, nullptr))
+        if (!XPCConvert::JSData2Native(&displayName, funNameVal,
+                                       { nsXPTType::T_UTF8STRING },
+                                       nullptr, 0, nullptr))
         {
             JS_ClearPendingException(cx);
             return nsCString("anonymous");
         }
     }
 
     displayName.Append('[');
     displayName.Append(filenameSuffix, strlen(filenameSuffix));
@@ -658,172 +658,118 @@ nsXPCWrappedJSClass::GetRootJSObject(JSC
         result = aJSObj;
     JSObject* inner = js::UncheckedUnwrap(result);
     if (inner)
         return inner;
     return result;
 }
 
 bool
-nsXPCWrappedJSClass::GetArraySizeFromParam(JSContext* cx,
-                                           const nsXPTMethodInfo* method,
-                                           const nsXPTParamInfo& param,
-                                           uint16_t methodIndex,
-                                           uint8_t paramIndex,
+nsXPCWrappedJSClass::GetArraySizeFromParam(const nsXPTMethodInfo* method,
+                                           const nsXPTType& type,
                                            nsXPTCMiniVariant* nativeParams,
                                            uint32_t* result) const
 {
-    uint8_t argnum;
-    nsresult rv;
+    if (type.Tag() != nsXPTType::T_ARRAY &&
+        type.Tag() != nsXPTType::T_PSTRING_SIZE_IS &&
+        type.Tag() != nsXPTType::T_PWSTRING_SIZE_IS) {
+        *result = 0;
+        return true;
+    }
 
-    rv = mInfo->GetSizeIsArgNumberForParam(methodIndex, &param, 0, &argnum);
-    if (NS_FAILED(rv))
-        return false;
-
-    const nsXPTParamInfo& arg_param = method->GetParam(argnum);
+    uint8_t argnum = type.ArgNum();
+    const nsXPTParamInfo& param = method->Param(argnum);
 
     // This should be enforced by the xpidl compiler, but it's not.
     // See bug 695235.
-    MOZ_ASSERT(arg_param.GetType().TagPart() == nsXPTType::T_U32,
-               "size_is references parameter of invalid type.");
+    if (param.Type().Tag() != nsXPTType::T_U32) {
+        return false;
+    }
 
-    if (arg_param.IsIndirect())
+    // If the length is passed indirectly (as an outparam), dereference by an
+    // extra level.
+    if (param.IsIndirect()) {
         *result = *(uint32_t*)nativeParams[argnum].val.p;
-    else
+    } else {
         *result = nativeParams[argnum].val.u32;
-
+    }
     return true;
 }
 
 bool
-nsXPCWrappedJSClass::GetInterfaceTypeFromParam(JSContext* cx,
-                                               const nsXPTMethodInfo* method,
-                                               const nsXPTParamInfo& param,
-                                               uint16_t methodIndex,
+nsXPCWrappedJSClass::GetInterfaceTypeFromParam(const nsXPTMethodInfo* method,
                                                const nsXPTType& type,
                                                nsXPTCMiniVariant* nativeParams,
                                                nsID* result) const
 {
-    uint8_t type_tag = type.TagPart();
+    result->Clear();
 
-    if (type_tag == nsXPTType::T_INTERFACE) {
-        if (NS_SUCCEEDED(GetInterfaceInfo()->
-                         GetIIDForParamNoAlloc(methodIndex, &param, result))) {
-            return true;
+    const nsXPTType& inner = type.InnermostType();
+    if (inner.Tag() == nsXPTType::T_INTERFACE) {
+        // Directly get IID from nsXPTInterfaceInfo.
+        if (!inner.GetInterface()) {
+            return false;
         }
-    } else if (type_tag == nsXPTType::T_INTERFACE_IS) {
-        uint8_t argnum;
-        nsresult rv;
-        rv = mInfo->GetInterfaceIsArgNumberForParam(methodIndex,
-                                                    &param, &argnum);
-        if (NS_FAILED(rv))
-            return false;
 
-        const nsXPTParamInfo& arg_param = method->GetParam(argnum);
-        const nsXPTType& arg_type = arg_param.GetType();
+        *result = inner.GetInterface()->IID();
+    } else if (inner.Tag() == nsXPTType::T_INTERFACE_IS) {
+        // Get IID from a passed parameter.
+        const nsXPTParamInfo& param = method->Param(inner.ArgNum());
+        if (param.Type().Tag() != nsXPTType::T_IID) {
+            return false;
+        }
+
+        void* ptr = nativeParams[inner.ArgNum()].val.p;
 
-        if (arg_type.TagPart() == nsXPTType::T_IID) {
-            if (arg_param.IsIndirect()) {
-                nsID** p = (nsID**) nativeParams[argnum].val.p;
-                if (!p || !*p)
-                    return false;
-                *result = **p;
-            } else {
-                nsID* p = (nsID*) nativeParams[argnum].val.p;
-                if (!p)
-                    return false;
-                *result = *p;
-            }
-            return true;
+        // If the IID is passed indirectly (as an outparam), dereference by an
+        // extra level.
+        if (ptr && param.IsIndirect()) {
+            ptr = *(nsID**) ptr;
         }
+
+        if (!ptr) {
+            return false;
+        }
+
+        *result = *(nsID*) ptr;
     }
-    return false;
-}
-
-/* static */ void
-nsXPCWrappedJSClass::CleanupPointerArray(const nsXPTType& datum_type,
-                                         uint32_t array_count,
-                                         void** arrayp)
-{
-    if (datum_type.IsInterfacePointer()) {
-        nsISupports** pp = (nsISupports**) arrayp;
-        for (uint32_t k = 0; k < array_count; k++) {
-            nsISupports* p = pp[k];
-            NS_IF_RELEASE(p);
-        }
-    } else {
-        void** pp = (void**) arrayp;
-        for (uint32_t k = 0; k < array_count; k++) {
-            void* p = pp[k];
-            if (p) free(p);
-        }
-    }
-}
-
-/* static */ void
-nsXPCWrappedJSClass::CleanupPointerTypeObject(const nsXPTType& type,
-                                              void** pp)
-{
-    MOZ_ASSERT(pp,"null pointer");
-    if (type.IsInterfacePointer()) {
-        nsISupports* p = *((nsISupports**)pp);
-        if (p) p->Release();
-    } else {
-        void* p = *((void**)pp);
-        if (p) free(p);
-    }
+    return true;
 }
 
 void
-nsXPCWrappedJSClass::CleanupOutparams(JSContext* cx, uint16_t methodIndex,
-                                      const nsXPTMethodInfo* info, nsXPTCMiniVariant* nativeParams,
-                                      bool inOutOnly, uint8_t n) const
+nsXPCWrappedJSClass::CleanupOutparams(const nsXPTMethodInfo* info,
+                                      nsXPTCMiniVariant* nativeParams,
+                                      bool inOutOnly, uint8_t count) const
 {
     // clean up any 'out' params handed in
-    for (uint8_t i = 0; i < n; i++) {
+    for (uint8_t i = 0; i < count; i++) {
         const nsXPTParamInfo& param = info->GetParam(i);
         if (!param.IsOut())
             continue;
 
-        const nsXPTType& type = param.GetType();
-        if (!type.deprecated_IsPointer())
-            continue;
-        void* p = nativeParams[i].val.p;
-        if (!p)
+        // Extract the array length so we can use it in CleanupValue.
+        uint32_t arrayLen = 0;
+        if (!GetArraySizeFromParam(info, param.Type(), nativeParams, &arrayLen))
             continue;
 
-        // The inOutOnly flag was introduced when consolidating two very
-        // similar code paths in CallMethod in bug 1175513. I don't know
-        // if and why the difference is necessary.
-        if (!inOutOnly || param.IsIn()) {
-            if (type.IsArray()) {
-                void** pp = *static_cast<void***>(p);
-                if (pp) {
-                    // we need to get the array length and iterate the items
-                    uint32_t array_count;
-                    nsXPTType datum_type;
+        MOZ_ASSERT(param.IsIndirect(), "Outparams are always indirect");
 
-                    if (NS_SUCCEEDED(mInfo->GetTypeForParam(methodIndex, &param,
-                                                            1, &datum_type)) &&
-                        datum_type.deprecated_IsPointer() &&
-                        GetArraySizeFromParam(cx, info, param, methodIndex,
-                                              i, nativeParams, &array_count) &&
-                        array_count) {
+        // The inOutOnly flag is necessary because full outparams may contain
+        // uninitialized junk before the call is made, and we don't want to try
+        // to clean up uninitialized junk.
+        if (!inOutOnly || param.IsIn()) {
+            xpc::CleanupValue(param.Type(), nativeParams[i].val.p, arrayLen);
+        }
 
-                        CleanupPointerArray(datum_type, array_count, pp);
-                    }
-
-                    // always release the array if it is inout
-                    free(pp);
-                }
-            } else {
-                CleanupPointerTypeObject(type, static_cast<void**>(p));
-            }
+        // Even if we didn't call CleanupValue, null out any pointers. This is
+        // just to protect C++ callers which may read garbage if they forget to
+        // check the error value.
+        if (param.Type().HasPointerRepr()) {
+            *(void**)nativeParams[i].val.p = nullptr;
         }
-        *static_cast<void**>(p) = nullptr;
     }
 }
 
 nsresult
 nsXPCWrappedJSClass::CheckForException(XPCCallContext & ccx,
                                        AutoEntryScript& aes,
                                        const char * aPropertyName,
                                        const char * anInterfaceName,
@@ -1097,79 +1043,44 @@ nsXPCWrappedJSClass::CallMethod(nsXPCWra
     // NB: This assignment *looks* wrong because we haven't yet called our
     // function. However, we *have* already entered the compartmen that we're
     // about to call, and that's the global that we want here. In other words:
     // we're trusting the JS engine to come up with a good global to use for
     // our object (whatever it was).
     for (i = 0; i < argc; i++) {
         const nsXPTParamInfo& param = info->GetParam(i);
         const nsXPTType& type = param.GetType();
-        nsXPTType datum_type;
         uint32_t array_count;
-        bool isArray = type.IsArray();
         RootedValue val(cx, NullValue());
-        bool isSizedString = isArray ?
-                false :
-                type.TagPart() == nsXPTType::T_PSTRING_SIZE_IS ||
-                type.TagPart() == nsXPTType::T_PWSTRING_SIZE_IS;
 
 
         // verify that null was not passed for 'out' param
         if (param.IsOut() && !nativeParams[i].val.p) {
             retval = NS_ERROR_INVALID_ARG;
             goto pre_call_clean_up;
         }
 
-        if (isArray) {
-            if (NS_FAILED(mInfo->GetTypeForParam(methodIndex, &param, 1,
-                                                 &datum_type)))
-                goto pre_call_clean_up;
-        } else
-            datum_type = type;
-
         if (param.IsIn()) {
-            nsXPTCMiniVariant* pv;
-
+            const void* pv;
             if (param.IsIndirect())
-                pv = (nsXPTCMiniVariant*) nativeParams[i].val.p;
+                pv = nativeParams[i].val.p;
             else
                 pv = &nativeParams[i];
 
-            if (datum_type.IsInterfacePointer() &&
-                !GetInterfaceTypeFromParam(cx, info, param, methodIndex,
-                                           datum_type, nativeParams,
-                                           &param_iid))
+            if (!GetInterfaceTypeFromParam(info, type, nativeParams, &param_iid) ||
+                !GetArraySizeFromParam(info, type, nativeParams, &array_count))
                 goto pre_call_clean_up;
 
-            if (isArray || isSizedString) {
-                if (!GetArraySizeFromParam(cx, info, param, methodIndex,
-                                           i, nativeParams, &array_count))
-                    goto pre_call_clean_up;
-            }
-
-            if (isArray) {
-                if (!XPCConvert::NativeArray2JS(&val,
-                                                (const void**)&pv->val,
-                                                datum_type, &param_iid,
-                                                array_count, nullptr))
-                    goto pre_call_clean_up;
-            } else if (isSizedString) {
-                if (!XPCConvert::NativeStringWithSize2JS(&val,
-                                                         (const void*)&pv->val,
-                                                         datum_type,
-                                                         array_count, nullptr))
-                    goto pre_call_clean_up;
-            } else {
-                if (!XPCConvert::NativeData2JS(&val, &pv->val, type,
-                                               &param_iid, nullptr))
-                    goto pre_call_clean_up;
-            }
+            if (!XPCConvert::NativeData2JS(&val, pv, type,
+                                            &param_iid, array_count,
+                                            nullptr))
+                goto pre_call_clean_up;
         }
 
-        if (param.IsOut() || param.IsDipper()) {
+        if (param.IsOut()) {
             // create an 'out' object
             RootedObject out_obj(cx, NewOutObject(cx));
             if (!out_obj) {
                 retval = NS_ERROR_OUT_OF_MEMORY;
                 goto pre_call_clean_up;
             }
 
             if (param.IsIn()) {
@@ -1183,17 +1094,17 @@ nsXPCWrappedJSClass::CallMethod(nsXPCWra
         } else
             *sp++ = val;
     }
 
     readyToDoTheCall = true;
 
 pre_call_clean_up:
     // clean up any 'out' params handed in
-    CleanupOutparams(cx, methodIndex, info, nativeParams, /* inOutOnly = */ true, paramCount);
+    CleanupOutparams(info, nativeParams, /* inOutOnly = */ true, paramCount);
 
     // Make sure "this" doesn't get deleted during this call.
     nsCOMPtr<nsIXPCWrappedJSClass> kungFuDeathGrip(this);
 
     if (!readyToDoTheCall)
         return retval;
 
     // do the deed - note exceptions
@@ -1242,139 +1153,95 @@ pre_call_clean_up:
     // When we later convert the dependent params (if any) we will know that
     // the params upon which they depend will have already been converted -
     // regardless of ordering.
 
     foundDependentParam = false;
     for (i = 0; i < paramCount; i++) {
         const nsXPTParamInfo& param = info->GetParam(i);
         MOZ_ASSERT(!param.IsShared(), "[shared] implies [noscript]!");
-        if (!param.IsOut() && !param.IsDipper())
+        if (!param.IsOut())
             continue;
 
         const nsXPTType& type = param.GetType();
         if (type.IsDependent()) {
             foundDependentParam = true;
             continue;
         }
 
         RootedValue val(cx);
-        uint8_t type_tag = type.TagPart();
-        nsXPTCMiniVariant* pv;
-
-        if (param.IsDipper())
-            pv = (nsXPTCMiniVariant*) &nativeParams[i].val.p;
-        else
-            pv = (nsXPTCMiniVariant*) nativeParams[i].val.p;
 
         if (&param == info->GetRetval())
             val = rval;
         else if (argv[i].isPrimitive())
             break;
         else {
             RootedObject obj(cx, &argv[i].toObject());
             if (!JS_GetPropertyById(cx, obj,
                                     mRuntime->GetStringID(XPCJSContext::IDX_VALUE),
                                     &val))
                 break;
         }
 
         // setup allocator and/or iid
 
-        if (type_tag == nsXPTType::T_INTERFACE) {
-            if (NS_FAILED(GetInterfaceInfo()->
-                          GetIIDForParamNoAlloc(methodIndex, &param,
-                                                &param_iid)))
+        const nsXPTType& inner = type.InnermostType();
+        if (inner.Tag() == nsXPTType::T_INTERFACE) {
+            if (!inner.GetInterface())
                 break;
+            param_iid = inner.GetInterface()->IID();
         }
 
-        if (!XPCConvert::JSData2Native(&pv->val, val, type,
-                                       &param_iid, nullptr))
+        MOZ_ASSERT(param.IsIndirect(), "outparams are always indirect");
+        if (!XPCConvert::JSData2Native(nativeParams[i].val.p, val, type,
+                                       &param_iid, 0, nullptr))
             break;
     }
 
     // if any params were dependent, then we must iterate again to convert them.
     if (foundDependentParam && i == paramCount) {
         for (i = 0; i < paramCount; i++) {
             const nsXPTParamInfo& param = info->GetParam(i);
             if (!param.IsOut())
                 continue;
 
             const nsXPTType& type = param.GetType();
             if (!type.IsDependent())
                 continue;
 
             RootedValue val(cx);
-            nsXPTCMiniVariant* pv;
-            nsXPTType datum_type;
             uint32_t array_count;
-            bool isArray = type.IsArray();
-            bool isSizedString = isArray ?
-                    false :
-                    type.TagPart() == nsXPTType::T_PSTRING_SIZE_IS ||
-                    type.TagPart() == nsXPTType::T_PWSTRING_SIZE_IS;
-
-            pv = (nsXPTCMiniVariant*) nativeParams[i].val.p;
 
             if (&param == info->GetRetval())
                 val = rval;
             else {
                 RootedObject obj(cx, &argv[i].toObject());
                 if (!JS_GetPropertyById(cx, obj,
                                         mRuntime->GetStringID(XPCJSContext::IDX_VALUE),
                                         &val))
                     break;
             }
 
             // setup allocator and/or iid
 
-            if (isArray) {
-                if (NS_FAILED(mInfo->GetTypeForParam(methodIndex, &param, 1,
-                                                     &datum_type)))
-                    break;
-            } else
-                datum_type = type;
-
-            if (datum_type.IsInterfacePointer()) {
-               if (!GetInterfaceTypeFromParam(cx, info, param, methodIndex,
-                                              datum_type, nativeParams,
-                                              &param_iid))
-                   break;
-            }
+            if (!GetInterfaceTypeFromParam(info, type, nativeParams, &param_iid) ||
+                !GetArraySizeFromParam(info, type, nativeParams, &array_count))
+                break;
 
-            if (isArray || isSizedString) {
-                if (!GetArraySizeFromParam(cx, info, param, methodIndex,
-                                           i, nativeParams, &array_count))
-                    break;
-            }
-
-            if (isArray) {
-                if (array_count &&
-                    !XPCConvert::JSArray2Native((void**)&pv->val, val,
-                                                array_count, datum_type,
-                                                &param_iid, nullptr))
-                    break;
-            } else if (isSizedString) {
-                if (!XPCConvert::JSStringWithSize2Native((void*)&pv->val, val,
-                                                         array_count, datum_type,
-                                                         nullptr))
-                    break;
-            } else {
-                if (!XPCConvert::JSData2Native(&pv->val, val, type,
-                                               &param_iid,
-                                               nullptr))
-                    break;
-            }
+            MOZ_ASSERT(param.IsIndirect(), "outparams are always indirect");
+            if (!XPCConvert::JSData2Native(nativeParams[i].val.p, val, type,
+                                           &param_iid, array_count, nullptr))
+                break;
         }
     }
 
     if (i != paramCount) {
         // We didn't manage all the result conversions!
         // We have to cleanup any junk that *did* get converted.
-        CleanupOutparams(cx, methodIndex, info, nativeParams, /* inOutOnly = */ false, i);
+        CleanupOutparams(info, nativeParams, /* inOutOnly = */ false, i);
     } else {
         // set to whatever the JS code might have set as the result
         retval = xpccx->GetPendingResult();
     }
 
     return retval;
 }
 
--- a/js/xpconnect/src/XPCWrappedNative.cpp
+++ b/js/xpconnect/src/XPCWrappedNative.cpp
@@ -1128,17 +1128,17 @@ XPCWrappedNative::InitTearOffJSObject(XP
 static bool Throw(nsresult errNum, XPCCallContext& ccx)
 {
     XPCThrower::Throw(errNum, ccx);
     return false;
 }
 
 /***************************************************************************/
 
-class MOZ_STACK_CLASS CallMethodHelper
+class MOZ_STACK_CLASS CallMethodHelper final
 {
     XPCCallContext& mCallContext;
     nsresult mInvokeResult;
     const nsXPTInterfaceInfo* const mIFaceInfo;
     const nsXPTMethodInfo* mMethodInfo;
     nsISupports* const mCallee;
     const uint16_t mVTableIndex;
     HandleId mIdxValueId;
@@ -1146,22 +1146,20 @@ class MOZ_STACK_CLASS CallMethodHelper
     AutoTArray<nsXPTCVariant, 8> mDispatchParams;
     uint8_t mJSContextIndex; // TODO make const
     uint8_t mOptArgcIndex; // TODO make const
 
     Value* const mArgv;
     const uint32_t mArgc;
 
     MOZ_ALWAYS_INLINE bool
-    GetArraySizeFromParam(uint8_t paramIndex, HandleValue maybeArray, uint32_t* result);
+    GetArraySizeFromParam(const nsXPTType& type, HandleValue maybeArray, uint32_t* result);
 
     MOZ_ALWAYS_INLINE bool
-    GetInterfaceTypeFromParam(uint8_t paramIndex,
-                              const nsXPTType& datum_type,
-                              nsID* result) const;
+    GetInterfaceTypeFromParam(const nsXPTType& type, nsID* result) const;
 
     MOZ_ALWAYS_INLINE bool
     GetOutParamSource(uint8_t paramIndex, MutableHandleValue srcp) const;
 
     MOZ_ALWAYS_INLINE bool
     GatherAndConvertResults();
 
     MOZ_ALWAYS_INLINE bool
@@ -1184,21 +1182,16 @@ class MOZ_STACK_CLASS CallMethodHelper
 
     MOZ_ALWAYS_INLINE bool InitializeDispatchParams();
 
     MOZ_ALWAYS_INLINE bool ConvertIndependentParams(bool* foundDependentParam);
     MOZ_ALWAYS_INLINE bool ConvertIndependentParam(uint8_t i);
     MOZ_ALWAYS_INLINE bool ConvertDependentParams();
     MOZ_ALWAYS_INLINE bool ConvertDependentParam(uint8_t i);
 
-    MOZ_ALWAYS_INLINE void CleanupParam(nsXPTCMiniVariant& param, nsXPTType& type);
-
-    MOZ_ALWAYS_INLINE bool AllocateStringClass(nsXPTCVariant* dp,
-                                               const nsXPTParamInfo& paramInfo);
-
     MOZ_ALWAYS_INLINE nsresult Invoke();
 
 public:
 
     explicit CallMethodHelper(XPCCallContext& ccx)
         : mCallContext(ccx)
         , mInvokeResult(NS_ERROR_UNEXPECTED)
         , mIFaceInfo(ccx.GetInterface()->GetInterfaceInfo())
@@ -1215,29 +1208,33 @@ public:
         // Success checked later.
         mIFaceInfo->GetMethodInfo(mVTableIndex, &mMethodInfo);
     }
 
     ~CallMethodHelper();
 
     MOZ_ALWAYS_INLINE bool Call();
 
+    // Trace implementation so we can put our CallMethodHelper in a Rooted<T>.
+    void trace(JSTracer* aTrc);
+
 };
 
 // static
 bool
 XPCWrappedNative::CallMethod(XPCCallContext& ccx,
                              CallMode mode /*= CALL_METHOD */)
 {
     nsresult rv = ccx.CanCallNow();
     if (NS_FAILED(rv)) {
         return Throw(rv, ccx);
     }
 
-    return CallMethodHelper(ccx).Call();
+    JS::Rooted<CallMethodHelper> helper(ccx, /* init = */ ccx);
+    return helper.get().Call();
 }
 
 bool
 CallMethodHelper::Call()
 {
     mCallContext.SetRetVal(JS::UndefinedValue());
 
     mCallContext.GetContext()->SetPendingException(nullptr);
@@ -1276,143 +1273,104 @@ CallMethodHelper::Call()
         return false;
     }
 
     return GatherAndConvertResults();
 }
 
 CallMethodHelper::~CallMethodHelper()
 {
-    uint8_t paramCount = mMethodInfo->GetParamCount();
-    if (mDispatchParams.Length()) {
-        for (uint8_t i = 0; i < paramCount; i++) {
-            nsXPTCVariant* dp = GetDispatchParam(i);
-            const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(i);
-
-            if (paramInfo.GetType().IsArray()) {
-                void* p = dp->val.p;
-                if (!p)
-                    continue;
+    for (nsXPTCVariant& param : mDispatchParams) {
+        // Only clean up values which need cleanup.
+        if (!param.DoesValNeedCleanup())
+            continue;
 
-                // Clean up the array contents if necessary.
-                if (dp->DoesValNeedCleanup()) {
-                    // We need some basic information to properly destroy the array.
-                    uint32_t array_count = 0;
-                    nsXPTType datum_type;
-                    if (!GetArraySizeFromParam(i, UndefinedHandleValue, &array_count) ||
-                        !NS_SUCCEEDED(mIFaceInfo->GetTypeForParam(mVTableIndex,
-                                                                  &paramInfo,
-                                                                  1, &datum_type))) {
-                        // XXXbholley - I'm not convinced that the above calls will
-                        // ever fail.
-                        NS_ERROR("failed to get array information, we'll leak here");
-                        continue;
-                    }
+        uint32_t arraylen = 0;
+        if (!GetArraySizeFromParam(param.type, UndefinedHandleValue, &arraylen))
+            continue;
 
-                    // Loop over the array contents. For each one, we create a
-                    // dummy 'val' and pass it to the cleanup helper.
-                    for (uint32_t k = 0; k < array_count; k++) {
-                        nsXPTCMiniVariant v;
-                        v.val.p = static_cast<void**>(p)[k];
-                        CleanupParam(v, datum_type);
-                    }
-                }
-
-                // always free the array itself
-                free(p);
-            } else {
-                // Clean up single parameters (if requested).
-                if (dp->DoesValNeedCleanup())
-                    CleanupParam(*dp, dp->type);
-            }
-        }
+        xpc::CleanupValue(param.type, &param.val, arraylen);
     }
 }
 
 bool
-CallMethodHelper::GetArraySizeFromParam(uint8_t paramIndex,
+CallMethodHelper::GetArraySizeFromParam(const nsXPTType& type,
                                         HandleValue maybeArray,
                                         uint32_t* result)
 {
-    nsresult rv;
-    const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(paramIndex);
+    if (type.Tag() != nsXPTType::T_ARRAY &&
+        type.Tag() != nsXPTType::T_PSTRING_SIZE_IS &&
+        type.Tag() != nsXPTType::T_PWSTRING_SIZE_IS) {
+        *result = 0;
+        return true;
+    }
+
+    uint8_t argnum = type.ArgNum();
+    uint32_t* lengthp = &GetDispatchParam(argnum)->val.u32;
 
     // TODO fixup the various exceptions that are thrown
 
-    rv = mIFaceInfo->GetSizeIsArgNumberForParam(mVTableIndex, &paramInfo, 0, &paramIndex);
-    if (NS_FAILED(rv))
-        return Throw(NS_ERROR_XPC_CANT_GET_ARRAY_INFO, mCallContext);
-
     // If the array length wasn't passed, it might have been listed as optional.
     // When converting arguments from JS to C++, we pass the array as |maybeArray|,
     // and give ourselves the chance to infer the length. Once we have it, we stick
     // it in the right slot so that we can find it again when cleaning up the params.
     // from the array.
-    if (paramIndex >= mArgc && maybeArray.isObject()) {
-        MOZ_ASSERT(mMethodInfo->GetParam(paramIndex).IsOptional());
-        RootedObject arrayOrNull(mCallContext, maybeArray.isObject() ? &maybeArray.toObject()
-                                                                     : nullptr);
+    if (argnum >= mArgc && maybeArray.isObject()) {
+        MOZ_ASSERT(mMethodInfo->Param(argnum).IsOptional());
+        RootedObject arrayOrNull(mCallContext, &maybeArray.toObject());
 
         bool isArray;
         bool ok = false;
         if (JS_IsArrayObject(mCallContext, maybeArray, &isArray) && isArray) {
-            ok = JS_GetArrayLength(mCallContext, arrayOrNull, &GetDispatchParam(paramIndex)->val.u32);
+            ok = JS_GetArrayLength(mCallContext, arrayOrNull, lengthp);
         } else if (JS_IsTypedArrayObject(&maybeArray.toObject())) {
-            GetDispatchParam(paramIndex)->val.u32 = JS_GetTypedArrayLength(&maybeArray.toObject());
+            *lengthp = JS_GetTypedArrayLength(&maybeArray.toObject());
             ok = true;
         }
 
         if (!ok) {
             return Throw(NS_ERROR_XPC_CANT_CONVERT_OBJECT_TO_ARRAY, mCallContext);
         }
     }
 
-    *result = GetDispatchParam(paramIndex)->val.u32;
-
+    *result = *lengthp;
     return true;
 }
 
 bool
-CallMethodHelper::GetInterfaceTypeFromParam(uint8_t paramIndex,
-                                            const nsXPTType& datum_type,
+CallMethodHelper::GetInterfaceTypeFromParam(const nsXPTType& type,
                                             nsID* result) const
 {
-    nsresult rv;
-    const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(paramIndex);
-    uint8_t tag = datum_type.TagPart();
+    result->Clear();
 
-    // TODO fixup the various exceptions that are thrown
+    const nsXPTType& inner = type.InnermostType();
+    if (inner.Tag() == nsXPTType::T_INTERFACE) {
+        if (!inner.GetInterface()) {
+            return Throw(NS_ERROR_XPC_CANT_GET_PARAM_IFACE_INFO, mCallContext);
+        }
 
-    if (tag == nsXPTType::T_INTERFACE) {
-        rv = mIFaceInfo->GetIIDForParamNoAlloc(mVTableIndex, &paramInfo, result);
-        if (NS_FAILED(rv))
+        *result = inner.GetInterface()->IID();
+    } else if (inner.Tag() == nsXPTType::T_INTERFACE_IS) {
+        nsID* id = (nsID*) GetDispatchParam(inner.ArgNum())->val.p;
+        if (!id) {
             return ThrowBadParam(NS_ERROR_XPC_CANT_GET_PARAM_IFACE_INFO,
-                                 paramIndex, mCallContext);
-    } else if (tag == nsXPTType::T_INTERFACE_IS) {
-        rv = mIFaceInfo->GetInterfaceIsArgNumberForParam(mVTableIndex, &paramInfo,
-                                                         &paramIndex);
-        if (NS_FAILED(rv))
-            return Throw(NS_ERROR_XPC_CANT_GET_ARRAY_INFO, mCallContext);
+                                 inner.ArgNum(), mCallContext);
+        }
 
-        nsID* p = (nsID*) GetDispatchParam(paramIndex)->val.p;
-        if (!p)
-            return ThrowBadParam(NS_ERROR_XPC_CANT_GET_PARAM_IFACE_INFO,
-                                 paramIndex, mCallContext);
-        *result = *p;
+        *result = *id;
     }
     return true;
 }
 
 bool
 CallMethodHelper::GetOutParamSource(uint8_t paramIndex, MutableHandleValue srcp) const
 {
     const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(paramIndex);
     bool isRetval = &paramInfo == mMethodInfo->GetRetval();
 
-    MOZ_ASSERT(!paramInfo.IsDipper(), "Dipper params are handled separately");
     if (paramInfo.IsOut() && !isRetval) {
         MOZ_ASSERT(paramIndex < mArgc || paramInfo.IsOptional(),
                    "Expected either enough arguments or an optional argument");
         Value arg = paramIndex < mArgc ? mArgv[paramIndex] : JS::NullValue();
         if (paramIndex < mArgc) {
             RootedObject obj(mCallContext);
             if (!arg.isPrimitive())
                 obj = &arg.toObject();
@@ -1432,72 +1390,34 @@ CallMethodHelper::GetOutParamSource(uint
 
 bool
 CallMethodHelper::GatherAndConvertResults()
 {
     // now we iterate through the native params to gather and convert results
     uint8_t paramCount = mMethodInfo->GetParamCount();
     for (uint8_t i = 0; i < paramCount; i++) {
         const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(i);
-        if (!paramInfo.IsOut() && !paramInfo.IsDipper())
+        if (!paramInfo.IsOut())
             continue;
 
         const nsXPTType& type = paramInfo.GetType();
         nsXPTCVariant* dp = GetDispatchParam(i);
         RootedValue v(mCallContext, NullValue());
+
         uint32_t array_count = 0;
-        nsXPTType datum_type;
-        bool isArray = type.IsArray();
-        bool isSizedString = isArray ?
-                false :
-                type.TagPart() == nsXPTType::T_PSTRING_SIZE_IS ||
-                type.TagPart() == nsXPTType::T_PWSTRING_SIZE_IS;
-
-        if (isArray) {
-            if (NS_FAILED(mIFaceInfo->GetTypeForParam(mVTableIndex, &paramInfo, 1,
-                                                      &datum_type))) {
-                Throw(NS_ERROR_XPC_CANT_GET_ARRAY_INFO, mCallContext);
-                return false;
-            }
-        } else
-            datum_type = type;
-
-        if (isArray || isSizedString) {
-            if (!GetArraySizeFromParam(i, UndefinedHandleValue, &array_count))
-                return false;
-        }
-
         nsID param_iid;
-        if (datum_type.IsInterfacePointer() &&
-            !GetInterfaceTypeFromParam(i, datum_type, &param_iid))
+        if (!GetInterfaceTypeFromParam(type, &param_iid) ||
+            !GetArraySizeFromParam(type, UndefinedHandleValue, &array_count))
             return false;
 
         nsresult err;
-        if (isArray) {
-            if (!XPCConvert::NativeArray2JS(&v, (const void**)&dp->val,
-                                            datum_type, &param_iid,
-                                            array_count, &err)) {
-                // XXX need exception scheme for arrays to indicate bad element
-                ThrowBadParam(err, i, mCallContext);
-                return false;
-            }
-        } else if (isSizedString) {
-            if (!XPCConvert::NativeStringWithSize2JS(&v,
-                                                     (const void*)&dp->val,
-                                                     datum_type,
-                                                     array_count, &err)) {
-                ThrowBadParam(err, i, mCallContext);
-                return false;
-            }
-        } else {
-            if (!XPCConvert::NativeData2JS(&v, &dp->val, datum_type,
-                                           &param_iid, &err)) {
-                ThrowBadParam(err, i, mCallContext);
-                return false;
-            }
+        if (!XPCConvert::NativeData2JS(&v, &dp->val, type,
+                                        &param_iid, array_count, &err)) {
+            ThrowBadParam(err, i, mCallContext);
+            return false;
         }
 
         if (&paramInfo == mMethodInfo->GetRetval()) {
             mCallContext.SetRetVal(v);
         } else if (i < mArgc) {
             // we actually assured this before doing the invoke
             MOZ_ASSERT(mArgv[i].isObject(), "out var is not object");
             RootedObject obj(mCallContext, &mArgv[i].toObject());
@@ -1544,17 +1464,17 @@ CallMethodHelper::QueryInterfaceFastPath
         return false;
     }
 
     RootedValue v(mCallContext, NullValue());
     nsresult err;
     bool success =
         XPCConvert::NativeData2JS(&v, &qiresult,
                                   { nsXPTType::T_INTERFACE_IS },
-                                  iid, &err);
+                                  iid, 0, &err);
     NS_IF_RELEASE(qiresult);
 
     if (!success) {
         ThrowBadParam(err, 0, mCallContext);
         return false;
     }
 
     mCallContext.SetRetVal(v);
@@ -1642,58 +1562,44 @@ CallMethodHelper::ConvertIndependentPara
     return true;
 }
 
 bool
 CallMethodHelper::ConvertIndependentParam(uint8_t i)
 {
     const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(i);
     const nsXPTType& type = paramInfo.GetType();
-    uint8_t type_tag = type.TagPart();
     nsXPTCVariant* dp = GetDispatchParam(i);
     dp->type = type;
     MOZ_ASSERT(!paramInfo.IsShared(), "[shared] implies [noscript]!");
 
-    // String classes are always "in" - those that are marked "out" are converted
-    // by the XPIDL compiler to "in+dipper". See the note above IsDipper() in
-    // xptinfo.h.
-    //
-    // Also note that the fact that we bail out early for dipper parameters means
-    // that "inout" dipper parameters don't work - see bug 687612.
-    if (paramInfo.IsStringClass()) {
-        if (!AllocateStringClass(dp, paramInfo))
-            return false;
-        if (paramInfo.IsDipper()) {
-            // We've allocated our string class explicitly, so we don't need
-            // to do any conversions on the incoming argument. However, we still
-            // need to verify that it's an object, so that we don't get surprised
-            // later on when trying to assign the result to .value.
-            if (i < mArgc && !mArgv[i].isObject()) {
-                ThrowBadParam(NS_ERROR_XPC_NEED_OUT_OBJECT, i, mCallContext);
-                return false;
-            }
-            return true;
-        }
-    }
-
     // Specify the correct storage/calling semantics.
     if (paramInfo.IsIndirect())
         dp->SetIndirect();
 
-    // The JSVal proper is always stored within the 'val' union and passed
-    // indirectly, regardless of in/out-ness.
-    if (type_tag == nsXPTType::T_JSVAL) {
-        // Root the value.
-        new (&dp->val.j) JS::Value();
-        MOZ_ASSERT(dp->val.j.isUndefined());
-        if (!js::AddRawValueRoot(mCallContext, &dp->val.j,
-                                 "XPCWrappedNative::CallMethod param"))
-        {
-            return false;
-        }
+    // Some types are always stored within the nsXPTCVariant, and passed
+    // indirectly, regardless of in/out-ness. These types are stored in the
+    // nsXPTCVariant's extended value.
+    switch (type.Tag()) {
+        // Ensure that the jsval has a valid value.
+        case nsXPTType::T_JSVAL:
+            new (&dp->ext.jsval) JS::Value();
+            MOZ_ASSERT(dp->ext.jsval.isUndefined());
+            break;
+
+        // Initialize our temporary string class values so they can be assigned
+        // to by the XPCConvert logic.
+        case nsXPTType::T_ASTRING:
+        case nsXPTType::T_DOMSTRING:
+            new (&dp->ext.nsstr) nsString();
+            break;
+        case nsXPTType::T_CSTRING:
+        case nsXPTType::T_UTF8STRING:
+            new (&dp->ext.nscstr) nsCString();
+            break;
     }
 
     // Flag cleanup for anything that isn't self-contained.
     if (!type.IsArithmetic())
         dp->SetValNeedsCleanup();
 
     // Even if there's nothing to convert, we still need to examine the
     // JSObject container for out-params. If it's null or otherwise invalid,
@@ -1714,47 +1620,49 @@ CallMethodHelper::ConvertIndependentPara
     // dealing with an 'in' or an 'inout' parameter. 'inout' was handled above,
     // so all that's left is 'in'.
     if (!paramInfo.IsOut()) {
         // Handle the 'in' case.
         MOZ_ASSERT(i < mArgc || paramInfo.IsOptional(),
                    "Expected either enough arguments or an optional argument");
         if (i < mArgc)
             src = mArgv[i];
-        else if (type_tag == nsXPTType::T_JSVAL)
+        else if (type.Tag() == nsXPTType::T_JSVAL)
             src.setUndefined();
         else
             src.setNull();
     }
 
-    nsID param_iid;
-    if (type_tag == nsXPTType::T_INTERFACE &&
-        NS_FAILED(mIFaceInfo->GetIIDForParamNoAlloc(mVTableIndex, &paramInfo,
-                                                    &param_iid))) {
-        ThrowBadParam(NS_ERROR_XPC_CANT_GET_PARAM_IFACE_INFO, i, mCallContext);
-        return false;
+    nsID param_iid = { 0 };
+    const nsXPTType& inner = type.InnermostType();
+    if (inner.Tag() == nsXPTType::T_INTERFACE) {
+        if (!inner.GetInterface()) {
+            return ThrowBadParam(NS_ERROR_XPC_CANT_GET_PARAM_IFACE_INFO,
+                                 i, mCallContext);
+        }
+        param_iid = inner.GetInterface()->IID();
     }
 
     // Don't allow CPOWs to be passed to native code (in case they try to cast
     // to a concrete type).
     if (src.isObject() &&
         jsipc::IsWrappedCPOW(&src.toObject()) &&
-        type_tag == nsXPTType::T_INTERFACE &&
+        type.Tag() == nsXPTType::T_INTERFACE &&
         !param_iid.Equals(NS_GET_IID(nsISupports)))
     {
         // Allow passing CPOWs to XPCWrappedJS.
         nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS(do_QueryInterface(mCallee));
         if (!wrappedJS) {
             ThrowBadParam(NS_ERROR_XPC_CANT_PASS_CPOW_TO_NATIVE, i, mCallContext);
             return false;
         }
     }
 
     nsresult err;
-    if (!XPCConvert::JSData2Native(&dp->val, src, type, &param_iid, &err)) {
+    if (!XPCConvert::JSData2Native(&dp->val, src, type, &param_iid, 0, &err)) {
         ThrowBadParam(err, i, mCallContext);
         return false;
     }
 
     return true;
 }
 
 bool
@@ -1773,51 +1681,27 @@ CallMethodHelper::ConvertDependentParams
     return true;
 }
 
 bool
 CallMethodHelper::ConvertDependentParam(uint8_t i)
 {
     const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(i);
     const nsXPTType& type = paramInfo.GetType();
-    nsXPTType datum_type;
-    uint32_t array_count = 0;
-    bool isArray = type.IsArray();
-
-    bool isSizedString = isArray ?
-        false :
-        type.TagPart() == nsXPTType::T_PSTRING_SIZE_IS ||
-        type.TagPart() == nsXPTType::T_PWSTRING_SIZE_IS;
 
     nsXPTCVariant* dp = GetDispatchParam(i);
     dp->type = type;
 
-    if (isArray) {
-        if (NS_FAILED(mIFaceInfo->GetTypeForParam(mVTableIndex, &paramInfo, 1,
-                                                  &datum_type))) {
-            Throw(NS_ERROR_XPC_CANT_GET_ARRAY_INFO, mCallContext);
-            return false;
-        }
-        MOZ_ASSERT(datum_type.TagPart() != nsXPTType::T_JSVAL,
-                   "Arrays of JSVals not currently supported - see bug 693337.");
-    } else {
-        datum_type = type;
-    }
-
     // Specify the correct storage/calling semantics.
     if (paramInfo.IsIndirect())
         dp->SetIndirect();
 
-    // We have 3 possible type of dependent parameters: Arrays, Sized Strings,
-    // and iid_is Interface pointers. The latter two always need cleanup, and
-    // arrays need cleanup for all non-arithmetic types. Since the latter two
-    // cases also happen to be non-arithmetic, we can just inspect datum_type
-    // here.
-    if (!datum_type.IsArithmetic())
-        dp->SetValNeedsCleanup();
+    // Make sure we clean up all of our dependent types. All of them require
+    // allocations of some kind.
+    dp->SetValNeedsCleanup();
 
     // Even if there's nothing to convert, we still need to examine the
     // JSObject container for out-params. If it's null or otherwise invalid,
     // we want to know before the call, rather than after.
     //
     // This is a no-op for 'in' params.
     RootedValue src(mCallContext);
     if (!GetOutParamSource(i, &src))
@@ -1835,138 +1719,79 @@ CallMethodHelper::ConvertDependentParam(
     if (!paramInfo.IsOut()) {
         // Handle the 'in' case.
         MOZ_ASSERT(i < mArgc || paramInfo.IsOptional(),
                      "Expected either enough arguments or an optional argument");
         src = i < mArgc ? mArgv[i] : JS::NullValue();
     }
 
     nsID param_iid;
-    if (datum_type.IsInterfacePointer() &&
-        !GetInterfaceTypeFromParam(i, datum_type, &param_iid))
+    uint32_t array_count;
+    if (!GetInterfaceTypeFromParam(type, &param_iid) ||
+        !GetArraySizeFromParam(type, src, &array_count))
         return false;
 
     nsresult err;
 
-    if (isArray || isSizedString) {
-        if (!GetArraySizeFromParam(i, src, &array_count))
-            return false;
-
-        if (isArray) {
-            if (array_count &&
-                !XPCConvert::JSArray2Native((void**)&dp->val, src,
-                                            array_count, datum_type, &param_iid,
-                                            &err)) {
-                // XXX need exception scheme for arrays to indicate bad element
-                ThrowBadParam(err, i, mCallContext);
-                return false;
-            }
-        } else // if (isSizedString)
-        {
-            if (!XPCConvert::JSStringWithSize2Native((void*)&dp->val,
-                                                     src, array_count,
-                                                     datum_type, &err)) {
-                ThrowBadParam(err, i, mCallContext);
-                return false;
-            }
-        }
-    } else {
-        if (!XPCConvert::JSData2Native(&dp->val, src, type,
-                                       &param_iid, &err)) {
-            ThrowBadParam(err, i, mCallContext);
-            return false;
-        }
-    }
-
-    return true;
-}
-
-// Performs all necessary teardown on a parameter after method invocation.
-//
-// This method should only be called if the value in question was flagged
-// for cleanup (ie, if dp->DoesValNeedCleanup()).
-void
-CallMethodHelper::CleanupParam(nsXPTCMiniVariant& param, nsXPTType& type)
-{
-    // We handle array elements, but not the arrays themselves.
-    MOZ_ASSERT(type.TagPart() != nsXPTType::T_ARRAY, "Can't handle arrays.");
-
-    // Pointers may sometimes be null even if cleanup was requested. Combine
-    // the null checking for all the different types into one check here.
-    if (type.TagPart() != nsXPTType::T_JSVAL && param.val.p == nullptr)
-        return;
-
-    switch (type.TagPart()) {
-        case nsXPTType::T_JSVAL:
-            js::RemoveRawValueRoot(mCallContext, &param.val.j);
-            break;
-        case nsXPTType::T_INTERFACE:
-        case nsXPTType::T_INTERFACE_IS:
-            ((nsISupports*)param.val.p)->Release();
-            break;
-        case nsXPTType::T_DOMOBJECT:
-            type.GetDOMObjectInfo().Cleanup(param.val.p);
-            break;
-        case nsXPTType::T_ASTRING:
-        case nsXPTType::T_DOMSTRING:
-            mCallContext.GetContext()->mScratchStrings.Destroy((nsString*)param.val.p);
-            break;
-        case nsXPTType::T_UTF8STRING:
-        case nsXPTType::T_CSTRING:
-            mCallContext.GetContext()->mScratchCStrings.Destroy((nsCString*)param.val.p);
-            break;
-        default:
-            MOZ_ASSERT(!type.IsArithmetic(), "Cleanup requested on unexpected type.");
-            free(param.val.p);
-            break;
-    }
-}
-
-bool
-CallMethodHelper::AllocateStringClass(nsXPTCVariant* dp,
-                                      const nsXPTParamInfo& paramInfo)
-{
-    // Get something we can make comparisons with.
-    uint8_t type_tag = paramInfo.GetType().TagPart();
-
-    // There should be 4 cases, all strings. Verify that here.
-    MOZ_ASSERT(type_tag == nsXPTType::T_ASTRING ||
-               type_tag == nsXPTType::T_DOMSTRING ||
-               type_tag == nsXPTType::T_UTF8STRING ||
-               type_tag == nsXPTType::T_CSTRING,
-               "Unexpected string class type!");
-
-    // ASTRING and DOMSTRING are very similar, and both use nsString.
-    // UTF8_STRING and CSTRING are also quite similar, and both use nsCString.
-    if (type_tag == nsXPTType::T_ASTRING || type_tag == nsXPTType::T_DOMSTRING)
-        dp->val.p = mCallContext.GetContext()->mScratchStrings.Create();
-    else
-        dp->val.p = mCallContext.GetContext()->mScratchCStrings.Create();
-
-    // Check for OOM, in either case.
-    if (!dp->val.p) {
-        JS_ReportOutOfMemory(mCallContext);
+    if (!XPCConvert::JSData2Native(&dp->val, src, type,
+                                   &param_iid, array_count, &err)) {
+        ThrowBadParam(err, i, mCallContext);
         return false;
     }
 
-    // We allocated, so we need to deallocate after the method call completes.
-    dp->SetValNeedsCleanup();
-
     return true;
 }
 
 nsresult
 CallMethodHelper::Invoke()
 {
     uint32_t argc = mDispatchParams.Length();
     nsXPTCVariant* argv = mDispatchParams.Elements();
 
     return NS_InvokeByIndex(mCallee, mVTableIndex, argc, argv);
 }
 
+static void
+TraceParam(JSTracer* aTrc, void* aVal, const nsXPTType& aType,
+           uint32_t aArrayLen = 0)
+{
+    if (aType.Tag() == nsXPTType::T_JSVAL) {
+        JS::UnsafeTraceRoot(aTrc, (JS::Value*)aVal,
+                            "XPCWrappedNative::CallMethod param");
+    } else if (aType.Tag() == nsXPTType::T_ARRAY && *(void**)aVal) {
+        const nsXPTType& elty = aType.ArrayElementType();
+        if (elty.Tag() != nsXPTType::T_JSVAL) {
+            return;
+        }
+
+        for (uint32_t i = 0; i < aArrayLen; ++i) {
+            TraceParam(aTrc, elty.ElementPtr(aVal, i), elty);
+        }
+    }
+}
+
+void
+CallMethodHelper::trace(JSTracer* aTrc)
+{
+    // We need to note each of our initialized parameters which contain jsvals.
+    for (nsXPTCVariant& param : mDispatchParams) {
+        if (!param.DoesValNeedCleanup()) {
+            MOZ_ASSERT(param.type.Tag() != nsXPTType::T_JSVAL,
+                       "JSVals are marked as needing cleanup (even though they don't)");
+            continue;
+        }
+
+        uint32_t arrayLen = 0;
+        if (!GetArraySizeFromParam(param.type, UndefinedHandleValue, &arrayLen))
+            continue;
+
+        TraceParam(aTrc, &param.val, param.type, arrayLen);
+    }
+}
+
 /***************************************************************************/
 // interface methods
 
 JSObject*
 XPCWrappedNative::GetJSObject()
 {
     return GetFlatJSObject();
 }
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -327,62 +327,16 @@ MOZ_DEFINE_ENUM(WatchdogTimestampCategor
     TimestampWatchdogWakeup,
     TimestampWatchdogHibernateStart,
     TimestampWatchdogHibernateStop,
     TimestampContextStateChange
 ));
 
 class AsyncFreeSnowWhite;
 
-template <class StringType>
-class ShortLivedStringBuffer
-{
-public:
-    StringType* Create()
-    {
-        for (uint32_t i = 0; i < ArrayLength(mStrings); ++i) {
-            if (!mStrings[i]) {
-                mStrings[i].emplace();
-                return mStrings[i].ptr();
-            }
-        }
-
-        // All our internal string wrappers are used, allocate a new string.
-        return new StringType();
-    }
-
-    void Destroy(StringType* string)
-    {
-        for (uint32_t i = 0; i < ArrayLength(mStrings); ++i) {
-            if (mStrings[i] && mStrings[i].ptr() == string) {
-                // One of our internal strings is no longer in use, mark
-                // it as such and free its data.
-                mStrings[i].reset();
-                return;
-            }
-        }
-
-        // We're done with a string that's not one of our internal
-        // strings, delete it.
-        delete string;
-    }
-
-    ~ShortLivedStringBuffer()
-    {
-#ifdef DEBUG
-        for (uint32_t i = 0; i < ArrayLength(mStrings); ++i) {
-            MOZ_ASSERT(!mStrings[i], "Short lived string still in use");
-        }
-#endif
-    }
-
-private:
-    mozilla::Maybe<StringType> mStrings[2];
-};
-
 class XPCJSContext final : public mozilla::CycleCollectedJSContext
                          , public mozilla::LinkedListElement<XPCJSContext>
 {
 public:
     static void InitTLS();
     static XPCJSContext* NewXPCJSContext(XPCJSContext* aPrimaryContext);
     static XPCJSContext* Get();
 
@@ -465,19 +419,16 @@ public:
         IDX_THEN                    ,
         IDX_ISINSTANCE              ,
         IDX_TOTAL_COUNT // just a count of the above
     };
 
     inline JS::HandleId GetStringID(unsigned index) const;
     inline const char* GetStringName(unsigned index) const;
 
-    ShortLivedStringBuffer<nsString> mScratchStrings;
-    ShortLivedStringBuffer<nsCString> mScratchCStrings;
-
 private:
     XPCJSContext();
 
     MOZ_IS_CLASS_INIT
     nsresult Initialize(XPCJSContext* aPrimaryContext);
 
     XPCCallContext*          mCallContext;
     AutoMarkingPtr*          mAutoRoots;
@@ -1794,41 +1745,29 @@ private:
                         const nsXPTInterfaceInfo* aInfo);
 
     bool IsReflectable(uint16_t i) const
         {return (bool)(mDescriptors[i/32] & (1 << (i%32)));}
     void SetReflectable(uint16_t i, bool b)
         {if (b) mDescriptors[i/32] |= (1 << (i%32));
          else mDescriptors[i/32] &= ~(1 << (i%32));}
 
-    bool GetArraySizeFromParam(JSContext* cx,
-                               const nsXPTMethodInfo* method,
-                               const nsXPTParamInfo& param,
-                               uint16_t methodIndex,
-                               uint8_t paramIndex,
+    bool GetArraySizeFromParam(const nsXPTMethodInfo* method,
+                               const nsXPTType& type,
                                nsXPTCMiniVariant* params,
                                uint32_t* result) const;
 
-    bool GetInterfaceTypeFromParam(JSContext* cx,
-                                   const nsXPTMethodInfo* method,
-                                   const nsXPTParamInfo& param,
-                                   uint16_t methodIndex,
+    bool GetInterfaceTypeFromParam(const nsXPTMethodInfo* method,
                                    const nsXPTType& type,
                                    nsXPTCMiniVariant* params,
                                    nsID* result) const;
 
-    static void CleanupPointerArray(const nsXPTType& datum_type,
-                                    uint32_t array_count,
-                                    void** arrayp);
-
-    static void CleanupPointerTypeObject(const nsXPTType& type,
-                                         void** pp);
-
-    void CleanupOutparams(JSContext* cx, uint16_t methodIndex, const nsXPTMethodInfo* info,
-                          nsXPTCMiniVariant* nativeParams, bool inOutOnly, uint8_t n) const;
+    void CleanupOutparams(const nsXPTMethodInfo* info,
+                          nsXPTCMiniVariant* nativeParams,
+                          bool inOutOnly, uint8_t n) const;
 
 private:
     XPCJSRuntime* mRuntime;
     const nsXPTInterfaceInfo* mInfo;
     char* mName;
     nsIID mIID;
     uint32_t* mDescriptors;
 };
@@ -1994,21 +1933,23 @@ public:
      * @param iid the interface of s that we want
      * @param scope the default scope to put on the new JSObject's parent
      *        chain
      * @param pErr [out] relevant error code, if any.
      */
 
     static bool NativeData2JS(JS::MutableHandleValue d,
                               const void* s, const nsXPTType& type,
-                              const nsID* iid, nsresult* pErr);
+                              const nsID* iid, uint32_t arrlen,
+                              nsresult* pErr);
 
     static bool JSData2Native(void* d, JS::HandleValue s,
                               const nsXPTType& type,
                               const nsID* iid,
+                              uint32_t arrlen,
                               nsresult* pErr);
 
     /**
      * Convert a native nsISupports into a JSObject.
      *
      * @param dest [out] the resulting JSObject
      * @param src the native object we're working with
      * @param iid the interface of src that we want (may be null)
@@ -2044,39 +1985,30 @@ public:
      * @param d [out] the resulting JS::Value
      * @param s the native array we're working with
      * @param type the type of objects in the array
      * @param iid the interface of each object in the array that we want
      * @param count the number of items in the array
      * @param scope the default scope to put on the new JSObjects' parent chain
      * @param pErr [out] relevant error code, if any.
      */
-    static bool NativeArray2JS(JS::MutableHandleValue d, const void** s,
+    static bool NativeArray2JS(JS::MutableHandleValue d, const void* const* s,
                                const nsXPTType& type, const nsID* iid,
                                uint32_t count, nsresult* pErr);
 
     static bool JSArray2Native(void** d, JS::HandleValue s,
                                uint32_t count, const nsXPTType& type,
                                const nsID* iid, nsresult* pErr);
 
     static bool JSTypedArray2Native(void** d,
                                     JSObject* jsarray,
                                     uint32_t count,
                                     const nsXPTType& type,
                                     nsresult* pErr);
 
-    static bool NativeStringWithSize2JS(JS::MutableHandleValue d, const void* s,
-                                        const nsXPTType& type,
-                                        uint32_t count,
-                                        nsresult* pErr);
-
-    static bool JSStringWithSize2Native(void* d, JS::HandleValue s,
-                                        uint32_t count, const nsXPTType& type,
-                                        nsresult* pErr);
-
     static nsresult JSValToXPCException(JS::MutableHandleValue s,
                                         const char* ifaceName,
                                         const char* methodName,
                                         mozilla::dom::Exception** exception);
 
     static nsresult ConstructException(nsresult rv, const char* message,
                                        const char* ifaceName,
                                        const char* methodName,
@@ -3104,16 +3036,49 @@ ObjectScope(JSObject* obj)
 
 JSObject* NewOutObject(JSContext* cx);
 bool IsOutObject(JSContext* cx, JSObject* obj);
 
 nsresult HasInstance(JSContext* cx, JS::HandleObject objArg, const nsID* iid, bool* bp);
 
 nsIPrincipal* GetObjectPrincipal(JSObject* obj);
 
+// Attempt to clean up the passed in value pointer. The pointer `value` must be
+// a pointer to a value described by the type `nsXPTType`.
+//
+// This method expects a value of the following types:
+//   TD_PNSIID
+//     value : nsID* (free)
+//   TD_DOMSTRING, TD_ASTRING, TD_CSTRING, TD_UTF8STRING
+//     value : ns[C]String* (truncate)
+//   TD_PSTRING, TD_PWSTRING, TD_PSTRING_SIZE_IS, TD_PWSTRING_SIZE_IS
+//     value : char[16_t]** (free)
+//   TD_INTERFACE_TYPE, TD_INTERFACE_IS_TYPE
+//     value : nsISupports** (release)
+//   TD_ARRAY (NOTE: aArrayLen should be passed)
+//     value : void** (cleanup elements & free)
+//   TD_DOMOBJECT
+//     value : T** (cleanup)
+//   TD_PROMISE
+//     value : dom::Promise** (release)
+//
+// Other types are ignored.
+//
+// Custom behaviour may be desired in some situations:
+//  - This method Truncate()s nsStrings, it does not free them.
+//  - This method does not unroot JSValues.
+inline void CleanupValue(const nsXPTType& aType,
+                         void* aValue,
+                         uint32_t aArrayLen = 0);
+
+// Out-of-line internals for xpc::CleanupValue. Defined in XPCConvert.cpp.
+void InnerCleanupValue(const nsXPTType& aType,
+                       void* aValue,
+                       uint32_t aArrayLen);
+
 } // namespace xpc
 
 namespace mozilla {
 namespace dom {
 extern bool
 DefineStaticJSVals(JSContext* cx);
 } // namespace dom
 } // namespace mozilla
--- a/modules/libpref/init/StaticPrefList.h
+++ b/modules/libpref/init/StaticPrefList.h
@@ -83,16 +83,26 @@
 
 VARCACHE_PREF(
   "accessibility.monoaudio.enable",
    accessibility_monoaudio_enable,
   bool, false
 )
 
 //---------------------------------------------------------------------------
+// DOM prefs
+//---------------------------------------------------------------------------
+
+VARCACHE_PREF(
+  "dom.webcomponents.shadowdom.report_usage",
+   dom_webcomponents_shadowdom_report_usage,
+  bool, false
+)
+
+//---------------------------------------------------------------------------
 // Full-screen prefs
 //---------------------------------------------------------------------------
 
 #ifdef RELEASE_OR_BETA
 # define PREF_VALUE false
 #else
 # define PREF_VALUE true
 #endif
--- a/security/manager/ssl/SecretDecoderRing.cpp
+++ b/security/manager/ssl/SecretDecoderRing.cpp
@@ -140,17 +140,17 @@ SecretDecoderRing::EncryptString(const n
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 SecretDecoderRing::AsyncEncryptStrings(uint32_t plaintextsCount,
                                        const char16_t** plaintexts,
                                        JSContext* aCx,
-                                       nsISupports** aPromise) {
+                                       Promise** aPromise) {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
   NS_ENSURE_ARG(plaintextsCount);
   NS_ENSURE_ARG_POINTER(plaintexts);
   NS_ENSURE_ARG_POINTER(aCx);
 
   nsIGlobalObject* globalObject =
     xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx));
 
--- a/security/manager/ssl/nsISecretDecoderRing.idl
+++ b/security/manager/ssl/nsISecretDecoderRing.idl
@@ -28,18 +28,18 @@ interface nsISecretDecoderRing: nsISuppo
    * all at once. This method accepts an array of wstrings which it will convert
    * to UTF-8 internally before encrypting.
    *
    * @param plaintextsCount the number of strings to encrypt.
    * @param plaintexts the strings to encrypt.
    * @return A promise for the list of encrypted strings, encoded as Base64.
    */
   [implicit_jscontext, must_use]
-  nsISupports asyncEncryptStrings(in unsigned long plaintextsCount,
-                                  [array, size_is(plaintextsCount)] in wstring plaintexts);
+  Promise asyncEncryptStrings(in unsigned long plaintextsCount,
+                              [array, size_is(plaintextsCount)] in wstring plaintexts);
 
   /**
    * Decrypt Base64 input.
    * See the encryptString() documentation - this method has basically the same
    * limitations.
    *
    * @param encryptedBase64Text Encrypted input text, encoded as Base64.
    * @return The decoded text.
--- a/security/nss/TAG-INFO
+++ b/security/nss/TAG-INFO
@@ -1,1 +1,1 @@
-NSS_3_37_RTM
+6e4b0141df2f
--- a/security/nss/automation/abi-check/previous-nss-release
+++ b/security/nss/automation/abi-check/previous-nss-release
@@ -1,1 +1,1 @@
-NSS_3_36_BRANCH
+NSS_3_37_BRANCH
--- a/security/nss/automation/taskcluster/docker-saw/Dockerfile
+++ b/security/nss/automation/taskcluster/docker-saw/Dockerfile
@@ -1,9 +1,9 @@
-FROM ubuntu:latest
+FROM ubuntu:16.04
 MAINTAINER Tim Taubert <ttaubert@mozilla.com>
 
 RUN useradd -d /home/worker -s /bin/bash -m worker
 WORKDIR /home/worker
 
 ENV DEBIAN_FRONTEND noninteractive
 
 RUN apt-get update && apt-get install -y \
--- a/security/nss/coreconf/coreconf.dep
+++ b/security/nss/coreconf/coreconf.dep
@@ -5,8 +5,9 @@
 
 /*
  * A dummy header file that is a dependency for all the object files.
  * Used to force a full recompilation of NSS in Mozilla's Tinderbox
  * depend builds.  See comments in rules.mk.
  */
 
 #error "Do not include this header file."
+
--- a/security/nss/gtests/ssl_gtest/ssl_drop_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_drop_unittest.cc
@@ -879,16 +879,55 @@ TEST_P(TlsConnectDatagram12Plus, MissAWi
   GetCipherAndLimit(version_, &cipher);
   server_->EnableSingleCipher(cipher);
   Connect();
 
   EXPECT_EQ(SECSuccess, SSLInt_AdvanceWriteSeqByAWindow(client_->ssl_fd(), 1));
   SendReceive();
 }
 
+// This filter replaces the first record it sees with junk application data.
+class TlsReplaceFirstRecordWithJunk : public TlsRecordFilter {
+ public:
+  TlsReplaceFirstRecordWithJunk(const std::shared_ptr<TlsAgent>& a)
+      : TlsRecordFilter(a), replaced_(false) {}
+
+ protected:
+  PacketFilter::Action FilterRecord(const TlsRecordHeader& header,
+                                    const DataBuffer& record, size_t* offset,
+                                    DataBuffer* output) override {
+    if (replaced_) {
+      return KEEP;
+    }
+    replaced_ = true;
+    TlsRecordHeader out_header(header.variant(), header.version(),
+                               kTlsApplicationDataType,
+                               header.sequence_number());
+
+    static const uint8_t junk[] = {1, 2, 3, 4};
+    *offset = out_header.Write(output, *offset, DataBuffer(junk, sizeof(junk)));
+    return CHANGE;
+  }
+
+ private:
+  bool replaced_;
+};
+
+// DTLS needs to discard application_data that it receives prior to handshake
+// completion, not generate an error.
+TEST_P(TlsConnectDatagram, ReplaceFirstServerRecordWithApplicationData) {
+  MakeTlsFilter<TlsReplaceFirstRecordWithJunk>(server_);
+  Connect();
+}
+
+TEST_P(TlsConnectDatagram, ReplaceFirstClientRecordWithApplicationData) {
+  MakeTlsFilter<TlsReplaceFirstRecordWithJunk>(client_);
+  Connect();
+}
+
 INSTANTIATE_TEST_CASE_P(Datagram12Plus, TlsConnectDatagram12Plus,
                         TlsConnectTestBase::kTlsV12Plus);
 INSTANTIATE_TEST_CASE_P(DatagramPre13, TlsConnectDatagramPre13,
                         TlsConnectTestBase::kTlsV11V12);
 INSTANTIATE_TEST_CASE_P(DatagramDrop13, TlsDropDatagram13,
                         ::testing::Values(true, false));
 INSTANTIATE_TEST_CASE_P(DatagramReorder13, TlsReorderDatagram13,
                         ::testing::Values(true, false));
--- a/security/nss/lib/dev/devtoken.c
+++ b/security/nss/lib/dev/devtoken.c
@@ -523,17 +523,19 @@ nssToken_ImportCertificate(
         }
         /* according to PKCS#11, label, ID, issuer, and serial number
          * may change after the object has been created.  For PKIX, the
          * last two attributes can't change, so for now we'll only worry
          * about the first two.
          */
         NSS_CK_TEMPLATE_START(cert_tmpl, attr, ctsize);
         NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_ID, id);
-        NSS_CK_SET_ATTRIBUTE_UTF8(attr, CKA_LABEL, nickname);
+        if (!rvObject->label && nickname) {
+            NSS_CK_SET_ATTRIBUTE_UTF8(attr, CKA_LABEL, nickname);
+        }
         NSS_CK_TEMPLATE_FINISH(cert_tmpl, attr, ctsize);
         /* reset the mutable attributes on the token */
         nssCKObject_SetAttributes(rvObject->handle,
                                   cert_tmpl, ctsize,
                                   session, slot);
         if (!rvObject->label && nickname) {
             rvObject->label = nssUTF8_Duplicate(nickname, NULL);
         }
--- a/security/nss/lib/freebl/blake2b.c
+++ b/security/nss/lib/freebl/blake2b.c
@@ -175,17 +175,17 @@ blake2b_Begin(BLAKE2BContext* ctx, uint8
         PORT_Memcpy(block, key, keylen);
         BLAKE2B_Update(ctx, block, BLAKE2B_BLOCK_LENGTH);
         PORT_Memset(block, 0, BLAKE2B_BLOCK_LENGTH);
     }
 
     return SECSuccess;
 
 failure:
-    PORT_Memset(&ctx, 0, sizeof(ctx));
+    PORT_Memset(ctx, 0, sizeof(*ctx));
     PORT_SetError(SEC_ERROR_INVALID_ARGS);
     return SECFailure;
 }
 
 SECStatus
 BLAKE2B_Begin(BLAKE2BContext* ctx)
 {
     return blake2b_Begin(ctx, BLAKE2B512_LENGTH, NULL, 0);
--- a/security/nss/lib/nss/nss.h
+++ b/security/nss/lib/nss/nss.h
@@ -17,22 +17,22 @@
 
 /*
  * NSS's major version, minor version, patch level, build number, and whether
  * this is a beta release.
  *
  * The format of the version string should be
  *     "<major version>.<minor version>[.<patch level>[.<build number>]][ <ECC>][ <Beta>]"
  */
-#define NSS_VERSION "3.37" _NSS_CUSTOMIZED
+#define NSS_VERSION "3.38" _NSS_CUSTOMIZED " Beta"
 #define NSS_VMAJOR 3
-#define NSS_VMINOR 37
+#define NSS_VMINOR 38
 #define NSS_VPATCH 0
 #define NSS_VBUILD 0
-#define NSS_BETA PR_FALSE
+#define NSS_BETA PR_TRUE
 
 #ifndef RC_INVOKED
 
 #include "seccomon.h"
 
 typedef struct NSSInitParametersStr NSSInitParameters;
 
 /*
--- a/security/nss/lib/softoken/sdb.c
+++ b/security/nss/lib/softoken/sdb.c
@@ -32,16 +32,20 @@
 #include "prsystem.h" /* for PR_GetDirectorySeparator() */
 #include <sys/stat.h>
 #if defined(_WIN32)
 #include <io.h>
 #include <windows.h>
 #elif defined(XP_UNIX)
 #include <unistd.h>
 #endif
+#if defined(LINUX) && !defined(ANDROID)
+#include <linux/magic.h>
+#include <sys/vfs.h>
+#endif
 #include "utilpars.h"
 
 #ifdef SQLITE_UNSAFE_THREADS
 #include "prlock.h"
 /*
  * SQLite can be compiled to be thread safe or not.
  * turn on SQLITE_UNSAFE_THREADS if the OS does not support
  * a thread safe version of sqlite.
@@ -1758,16 +1762,18 @@ sdb_init(char *dbname, char *table, sdbD
     SDBPrivate *sdb_p = NULL;
     sqlite3 *sqlDB = NULL;
     int sqlerr = SQLITE_OK;
     CK_RV error = CKR_OK;
     char *cacheTable = NULL;
     PRIntervalTime now = 0;
     char *env;
     PRBool enableCache = PR_FALSE;
+    PRBool checkFSType = PR_FALSE;
+    PRBool measureSpeed = PR_FALSE;
     PRBool create;
     int flags = inFlags & 0x7;
 
     *pSdb = NULL;
     *inUpdate = 0;
 
     /* sqlite3 doesn't have a flag to specify that we want to
      * open the database read only. If the db doesn't exist,
@@ -1918,21 +1924,58 @@ sdb_init(char *dbname, char *table, sdbD
      *   always be used.
      *
      * It is expected that most applications will not need this feature, and
      * thus it is disabled by default.
      */
 
     env = PR_GetEnvSecure("NSS_SDB_USE_CACHE");
 
-    if (!env || PORT_Strcasecmp(env, "no") == 0) {
-        enableCache = PR_FALSE;
+    /* Variables enableCache, checkFSType, measureSpeed are PR_FALSE by default,
+     * which is the expected behavior for NSS_SDB_USE_CACHE="no".
+     * We don't need to check for "no" here. */
+    if (!env) {
+        /* By default, with no variable set, we avoid expensive measuring for
+         * most FS types. We start with inexpensive FS type checking, and
+         * might perform measuring for some types. */
+        checkFSType = PR_TRUE;
     } else if (PORT_Strcasecmp(env, "yes") == 0) {
         enableCache = PR_TRUE;
-    } else {
+    } else if (PORT_Strcasecmp(env, "no") != 0) { /* not "no" => "auto" */
+        measureSpeed = PR_TRUE;
+    }
+
+    if (checkFSType) {
+#if defined(LINUX) && !defined(ANDROID)
+        struct statfs statfs_s;
+        if (statfs(dbname, &statfs_s) == 0) {
+            switch (statfs_s.f_type) {
+                case SMB_SUPER_MAGIC:
+                case 0xff534d42: /* CIFS_MAGIC_NUMBER */
+                case NFS_SUPER_MAGIC:
+                    /* We assume these are slow. */
+                    enableCache = PR_TRUE;
+                    break;
+                case CODA_SUPER_MAGIC:
+                case 0x65735546: /* FUSE_SUPER_MAGIC */
+                case NCP_SUPER_MAGIC:
+                    /* It's uncertain if this FS is fast or slow.
+                     * It seems reasonable to perform slow measuring for users
+                     * with questionable FS speed. */
+                    measureSpeed = PR_TRUE;
+                    break;
+                case AFS_SUPER_MAGIC: /* Already implements caching. */
+                default:
+                    break;
+            }
+        }
+#endif
+    }
+
+    if (measureSpeed) {
         char *tempDir = NULL;
         PRUint32 tempOps = 0;
         /*
          *  Use PR_Access to determine how expensive it
          * is to check for the existance of a local file compared to the same
          * check in the temp directory. If the temp directory is faster, cache
          * the database there. */
         tempDir = sdb_getTempDir(sqlDB);
--- a/security/nss/lib/softoken/softkver.h
+++ b/security/nss/lib/softoken/softkver.h
@@ -12,16 +12,16 @@
 
 /*
  * Softoken's major version, minor version, patch level, build number,
  * and whether this is a beta release.
  *
  * The format of the version string should be
  *     "<major version>.<minor version>[.<patch level>[.<build number>]][ <ECC>][ <Beta>]"
  */
-#define SOFTOKEN_VERSION "3.37" SOFTOKEN_ECC_STRING
+#define SOFTOKEN_VERSION "3.38" SOFTOKEN_ECC_STRING " Beta"
 #define SOFTOKEN_VMAJOR 3
-#define SOFTOKEN_VMINOR 37
+#define SOFTOKEN_VMINOR 38
 #define SOFTOKEN_VPATCH 0
 #define SOFTOKEN_VBUILD 0
-#define SOFTOKEN_BETA PR_FALSE
+#define SOFTOKEN_BETA PR_TRUE
 
 #endif /* _SOFTKVER_H_ */
--- a/security/nss/lib/ssl/ssl3con.c
+++ b/security/nss/lib/ssl/ssl3con.c
@@ -12161,16 +12161,24 @@ ssl3_HandleRecord(sslSocket *ss, SSL3Cip
         PORT_SetError(SSL_ERROR_TOKEN_INSERTION_REMOVAL);
         return SECFailure;
     }
 
     /* Clear out the buffer in case this exits early.  Any data then won't be
      * processed twice. */
     plaintext->len = 0;
 
+    /* We're waiting for another ClientHello, which will appear unencrypted.
+     * Use the content type to tell whether this should be discarded. */
+    if (ss->ssl3.hs.zeroRttIgnore == ssl_0rtt_ignore_hrr &&
+        cText->hdr[0] == content_application_data) {
+        PORT_Assert(ss->ssl3.hs.ws == wait_client_hello);
+        return SECSuccess;
+    }
+
     ssl_GetSpecReadLock(ss); /******************************************/
     spec = ssl3_GetCipherSpec(ss, cText);
     if (!spec) {
         PORT_Assert(IS_DTLS(ss));
         ssl_ReleaseSpecReadLock(ss); /*****************************/
         return SECSuccess;
     }
     if (spec != ss->ssl3.crSpec) {
@@ -12191,91 +12199,90 @@ ssl3_HandleRecord(sslSocket *ss, SSL3Cip
     if (cText->seqNum >= spec->cipherDef->max_records) {
         ssl_ReleaseSpecReadLock(ss); /*****************************/
         SSL_TRC(3, ("%d: SSL[%d]: read sequence number at limit 0x%0llx",
                     SSL_GETPID(), ss->fd, cText->seqNum));
         PORT_SetError(SSL_ERROR_TOO_MANY_RECORDS);
         return SECFailure;
     }
 
-    /* We're waiting for another ClientHello, which will appear unencrypted.
-     * Use the content type to tell whether this is should be discarded.
-     *
-     * XXX If we decide to remove the content type from encrypted records, this
-     *     will become much more difficult to manage. */
-    if (ss->ssl3.hs.zeroRttIgnore == ssl_0rtt_ignore_hrr &&
-        cText->hdr[0] == content_application_data) {
-        ssl_ReleaseSpecReadLock(ss); /*****************************/
-        PORT_Assert(ss->ssl3.hs.ws == wait_client_hello);
-        return SECSuccess;
-    }
-
     if (plaintext->space < MAX_FRAGMENT_LENGTH) {
         rv = sslBuffer_Grow(plaintext, MAX_FRAGMENT_LENGTH + 2048);
         if (rv != SECSuccess) {
             ssl_ReleaseSpecReadLock(ss); /*************************/
             SSL_DBG(("%d: SSL3[%d]: HandleRecord, tried to get %d bytes",
                      SSL_GETPID(), ss->fd, MAX_FRAGMENT_LENGTH + 2048));
             /* sslBuffer_Grow has set a memory error code. */
             /* Perhaps we should send an alert. (but we have no memory!) */
             return SECFailure;
         }
     }
 
-#ifdef UNSAFE_FUZZER_MODE
+    /* Most record types aside from protected TLS 1.3 records carry the content
+     * type in the first octet. TLS 1.3 will override this value later. */
     rType = cText->hdr[0];
-    rv = Null_Cipher(NULL, plaintext->buf, (int *)&plaintext->len,
-                     plaintext->space, cText->buf->buf, cText->buf->len);
+    /* Encrypted application data records could arrive before the handshake
+     * completes in DTLS 1.3. These can look like valid TLS 1.2 application_data
+     * records in epoch 0, which is never valid. Pretend they didn't decrypt. */
+    if (spec->epoch == 0 && rType == content_application_data) {
+        PORT_SetError(SSL_ERROR_RX_UNEXPECTED_APPLICATION_DATA);
+        alert = unexpected_message;
+        rv = SECFailure;
+    } else {
+#ifdef UNSAFE_FUZZER_MODE
+        rv = Null_Cipher(NULL, plaintext->buf, (int *)&plaintext->len,
+                         plaintext->space, cText->buf->buf, cText->buf->len);
 #else
-    /* IMPORTANT: Unprotect functions MUST NOT send alerts
-     * because we still hold the spec read lock. Instead, if they
-     * return SECFailure, they set *alert to the alert to be sent. */
-    if (spec->version < SSL_LIBRARY_VERSION_TLS_1_3 ||
-        spec->cipherDef->calg == ssl_calg_null) {
-        /* Unencrypted TLS 1.3 records use the pre-TLS 1.3 format. */
-        rType = cText->hdr[0];
-        rv = ssl3_UnprotectRecord(ss, spec, cText, plaintext, &alert);
-    } else {
-        rv = tls13_UnprotectRecord(ss, spec, cText, plaintext, &rType, &alert);
-    }
+        /* IMPORTANT: Unprotect functions MUST NOT send alerts
+         * because we still hold the spec read lock. Instead, if they
+         * return SECFailure, they set *alert to the alert to be sent. */
+        if (spec->version < SSL_LIBRARY_VERSION_TLS_1_3 ||
+            spec->epoch == 0) {
+            rv = ssl3_UnprotectRecord(ss, spec, cText, plaintext, &alert);
+        } else {
+            rv = tls13_UnprotectRecord(ss, spec, cText, plaintext, &rType,
+                                       &alert);
+        }
 #endif
+    }
 
     if (rv != SECSuccess) {
         ssl_ReleaseSpecReadLock(ss); /***************************/
 
         SSL_DBG(("%d: SSL3[%d]: decryption failed", SSL_GETPID(), ss->fd));
 
         /* Ensure that we don't process this data again. */
         plaintext->len = 0;
 
-        /* Ignore a CCS if the alternative handshake is negotiated.  Note that
-         * this will fail if the server fails to negotiate the alternative
-         * handshake type in a 0-RTT session that is resumed from a session that
-         * did negotiate it.  We don't care about that corner case right now. */
+        /* Ignore a CCS if compatibility mode is negotiated.  Note that this
+         * will fail if the server fails to negotiate compatibility mode in a
+         * 0-RTT session that is resumed from a session that did negotiate it.
+         * We don't care about that corner case right now. */
         if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3 &&
             cText->hdr[0] == content_change_cipher_spec &&
             ss->ssl3.hs.ws != idle_handshake &&
             cText->buf->len == 1 &&
             cText->buf->buf[0] == change_cipher_spec_choice) {
             /* Ignore the CCS. */
             return SECSuccess;
         }
+
         if (IS_DTLS(ss) ||
             (ss->sec.isServer &&
              ss->ssl3.hs.zeroRttIgnore == ssl_0rtt_ignore_trial)) {
             /* Silently drop the packet */
             return SECSuccess;
-        } else {
-            int errCode = PORT_GetError();
-            SSL3_SendAlert(ss, alert_fatal, alert);
-            /* Reset the error code in case SSL3_SendAlert called
-             * PORT_SetError(). */
-            PORT_SetError(errCode);
-            return SECFailure;
-        }
+        }
+
+        int errCode = PORT_GetError();
+        SSL3_SendAlert(ss, alert_fatal, alert);
+        /* Reset the error code in case SSL3_SendAlert called
+         * PORT_SetError(). */
+        PORT_SetError(errCode);
+        return SECFailure;
     }
 
     /* SECSuccess */
     if (IS_DTLS(ss)) {
         dtls_RecordSetRecvd(&spec->recvdRecords, cText->seqNum);
         spec->nextSeqNum = PR_MAX(spec->nextSeqNum, cText->seqNum + 1);
     } else {
         ++spec->nextSeqNum;
--- a/security/nss/lib/util/nssutil.h
+++ b/security/nss/lib/util/nssutil.h
@@ -14,22 +14,22 @@
 
 /*
  * NSS utilities's major version, minor version, patch level, build number,
  * and whether this is a beta release.
  *
  * The format of the version string should be
  *     "<major version>.<minor version>[.<patch level>[.<build number>]][ <Beta>]"
  */
-#define NSSUTIL_VERSION "3.37"
+#define NSSUTIL_VERSION "3.38 Beta"
 #define NSSUTIL_VMAJOR 3
-#define NSSUTIL_VMINOR 37
+#define NSSUTIL_VMINOR 38
 #define NSSUTIL_VPATCH 0
 #define NSSUTIL_VBUILD 0
-#define NSSUTIL_BETA PR_FALSE
+#define NSSUTIL_BETA PR_TRUE
 
 SEC_BEGIN_PROTOS
 
 /*
  * Returns a const string of the UTIL library version.
  */
 extern const char *NSSUTIL_GetVersion(void);
 
--- a/security/nss/tests/cert/cert.sh
+++ b/security/nss/tests/cert/cert.sh
@@ -1055,16 +1055,35 @@ cert_extended_ssl()
       certu -A -n "${CERTNAME}-ecmixed" -t "u,u,u" -d "${PROFILEDIR}" \
 	  -f "${R_PWFILE}" -i "${CERTNAME}-ecmixed.cert" 2>&1
 
 #      CU_ACTION="Import Client mixed EC Root CA -t T,, for $CERTNAME (ext.)"
 #      certu -A -n "clientCA-ecmixed" -t "T,," -f "${R_PWFILE}" \
 #	  -d "${PROFILEDIR}" -i "${CLIENT_CADIR}/clientCA-ecmixed.ca.cert" \
 #	  2>&1
 
+  # Check that a repeated import with a different nickname doesn't change the
+  # nickname of the existing cert (bug 1458518).
+  # We want to search for the results using grep, to avoid subset matches,
+  # we'll use one of the longer nicknames for testing.
+  # (Because "grep -w hostname" matches "grep -w hostname-dsamixed")
+  MYDBPASS="-d ${PROFILEDIR} -f ${R_PWFILE}"
+  TESTNAME="Ensure there's exactly one match for ${CERTNAME}-dsamixed"
+  cert_check_nickname_exists "$MYDBPASS" "${CERTNAME}-dsamixed" 0 1 "${TESTNAME}"
+
+  CU_ACTION="Repeated import of $CERTNAME's mixed DSA Cert with different nickname"
+  certu -A -n "${CERTNAME}-repeated-dsamixed" -t "u,u,u" -d "${PROFILEDIR}" \
+        -f "${R_PWFILE}" -i "${CERTNAME}-dsamixed.cert" 2>&1
+
+  TESTNAME="Ensure there's still exactly one match for ${CERTNAME}-dsamixed"
+  cert_check_nickname_exists "$MYDBPASS" "${CERTNAME}-dsamixed" 0 1 "${TESTNAME}"
+
+  TESTNAME="Ensure there's zero matches for ${CERTNAME}-repeated-dsamixed"
+  cert_check_nickname_exists "$MYDBPASS" "${CERTNAME}-repeated-dsamixed" 0 0 "${TESTNAME}"
+
   echo "Importing all the server's own CA chain into the servers DB"
   for CA in `find ${SERVER_CADIR} -name "?*.ca.cert"` ;
   do
       N=`basename $CA | sed -e "s/.ca.cert//"`
       if [ $N = "serverCA" -o $N = "serverCA-ec" -o $N = "serverCA-dsa" ] ; then
           T="-t C,C,C"
       else
           T="-t u,u,u"
@@ -1527,16 +1546,47 @@ cert_make_with_param()
         cert_log "ERROR: ${TESTNAME} - ${EXTRA} failed"
         return 1
     fi
 
     html_passed "${TESTNAME} (${COUNT})"
     return 0
 }
 
+cert_check_nickname_exists()
+{
+    MYDIRPASS="$1"
+    MYCERTNAME="$2"
+    EXPECT="$3"
+    EXPECTCOUNT="$4"
+    MYTESTNAME="$5"
+
+    echo certutil ${MYDIRPASS} -L
+    ${BINDIR}/certutil ${MYDIRPASS} -L
+
+    RET=$?
+    if [ "${RET}" -ne "${EXPECT}" ]; then
+        CERTFAILED=1
+        html_failed "${MYTESTNAME} - list"
+        cert_log "ERROR: ${MYTESTNAME} - list"
+        return 1
+    fi
+
+    LISTCOUNT=`${BINDIR}/certutil ${MYDIRPASS} -L | grep -wc ${MYCERTNAME}`
+    if [ "${LISTCOUNT}" -ne "${EXPECTCOUNT}" ]; then
+        CERTFAILED=1
+        html_failed "${MYTESTNAME} - list and count"
+        cert_log "ERROR: ${MYTESTNAME} - list and count failed"
+        return 1
+    fi
+
+    html_passed "${MYTESTNAME}"
+    return 0
+}
+
 cert_list_and_count_dns()
 {
     DIRPASS="$1"
     CERTNAME="$2"
     EXPECT="$3"
     EXPECTCOUNT="$4"
     TESTNAME="$5"
 
--- a/taskcluster/ci/test/xpcshell.yml
+++ b/taskcluster/ci/test/xpcshell.yml
@@ -37,16 +37,17 @@ xpcshell:
         by-test-platform:
             windows10-64-asan/opt: []  # No XPCShell on ASAN yet
             default: built-projects
     chunks:
         by-test-platform:
             linux32/debug: 12
             linux64/debug: 10
             android-4.2-x86/opt: 6
+            android-4.3-arm7-api-16/debug: 12
             macosx.*: 1
             windows.*: 1
             windows10-64-ccov/debug: 8
             default: 8
     instance-size:
         by-test-platform:
             android.*: xlarge
             default: default
--- a/testing/web-platform/meta/content-security-policy/navigate-to/child-navigates-parent-blocked.html.ini
+++ b/testing/web-platform/meta/content-security-policy/navigate-to/child-navigates-parent-blocked.html.ini
@@ -1,4 +1,5 @@
 [child-navigates-parent-blocked.html]
+  disabled: if not debug && (os == "linux") https://bugzilla.mozilla.org/show_bug.cgi?id=1450864
   [Test that the child can't navigate the parent because the relevant policy belongs to the navigation initiator (in this case the child)]
     expected: FAIL
 
--- a/testing/web-platform/meta/css/cssom/interfaces.html.ini
+++ b/testing/web-platform/meta/css/cssom/interfaces.html.ini
@@ -1,18 +1,15 @@
 [interfaces.html]
   [Document must be primary interface of document]
     expected: FAIL
 
   [Stringification of document]
     expected: FAIL
 
-  [ProcessingInstruction interface: attribute sheet]
-    expected: FAIL
-
   [CSSRule interface: constant MARGIN_RULE on interface object]
     expected: FAIL
 
   [CSSRule interface: constant MARGIN_RULE on interface prototype object]
     expected: FAIL
 
   [CSSRule interface: style_element.sheet.cssRules[0\] must inherit property "MARGIN_RULE" with the proper type (6)]
     expected: FAIL
--- a/toolkit/components/crashes/nsICrashService.idl
+++ b/toolkit/components/crashes/nsICrashService.idl
@@ -12,19 +12,19 @@ interface nsICrashService : nsISupports
    *
    * @param processType
    *        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
+   * @return A promise that resolves after the crash has been stored
    */
-  nsISupports addCrash(in long processType, in long crashType, in AString id);
+  Promise 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;
--- a/toolkit/components/telemetry/Telemetry.cpp
+++ b/toolkit/components/telemetry/Telemetry.cpp
@@ -971,17 +971,17 @@ public:
   {
     nsCOMPtr<nsIRunnable> resultRunnable = new GetLoadedModulesResultRunnable(mPromise, SharedLibraryInfo::GetInfoForSelf());
     return NS_DispatchToMainThread(resultRunnable);
   }
 };
 #endif // MOZ_GECKO_PROFILER
 
 NS_IMETHODIMP
-TelemetryImpl::GetLoadedModules(JSContext *cx, nsISupports** aPromise)
+TelemetryImpl::GetLoadedModules(JSContext *cx, Promise** aPromise)
 {
 #if defined(MOZ_GECKO_PROFILER)
   nsIGlobalObject* global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(cx));
   if (NS_WARN_IF(!global)) {
     return NS_ERROR_FAILURE;
   }
 
   ErrorResult result;
--- a/toolkit/components/telemetry/nsITelemetry.idl
+++ b/toolkit/components/telemetry/nsITelemetry.idl
@@ -193,17 +193,17 @@ interface nsITelemetry : nsISupports
    *   ...
    * ]
    *
    * @return A promise that resolves to an array of modules or rejects with
              NS_ERROR_FAILURE on failure.
    * @throws NS_ERROR_NOT_IMPLEMENTED if the Gecko profiler is not enabled.
    */
   [implicit_jscontext]
-  nsISupports getLoadedModules();
+  Promise getLoadedModules();
 
   /*
    * An object with two fields: memoryMap and stacks.
    * * memoryMap is a list of loaded libraries.
    * * stacks is a list of stacks. Each stack is a list of pairs of the form
    *   [moduleIndex, offset]. The moduleIndex is an index into the memoryMap and
    *   offset is an offset in the library at memoryMap[moduleIndex].
    * This format is used to make it easier to send the stacks to the
--- a/toolkit/mozapps/extensions/content/extensions.js
+++ b/toolkit/mozapps/extensions/content/extensions.js
@@ -1,16 +1,16 @@
 /* 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";
 
 /* import-globals-from ../../../content/contentAreaUtils.js */
-/* globals XMLStylesheetProcessingInstruction */
+/* globals ProcessingInstruction */
 /* exported UPDATES_RELEASENOTES_TRANSFORMFILE, XMLURI_PARSE_ERROR, loadView, gBrowser */
 
 ChromeUtils.import("resource://gre/modules/DeferredTask.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/DownloadUtils.jsm");
 ChromeUtils.import("resource://gre/modules/AddonManager.jsm");
 ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
@@ -104,17 +104,17 @@ function promiseEvent(event, target, cap
 var gPendingInitializations = 1;
 Object.defineProperty(this, "gIsInitializing", {
   get: () => gPendingInitializations > 0
 });
 
 function initialize(event) {
   // XXXbz this listener gets _all_ load events for all nodes in the
   // document... but relies on not being called "too early".
-  if (event.target instanceof XMLStylesheetProcessingInstruction) {
+  if (event.target instanceof ProcessingInstruction) {
     return;
   }
   document.removeEventListener("load", initialize, true);
 
   let globalCommandSet = document.getElementById("globalCommandSet");
   globalCommandSet.addEventListener("command", function(event) {
     gViewController.doCommand(event.target.id);
   });
--- a/tools/profiler/gecko/nsIProfiler.idl
+++ b/tools/profiler/gecko/nsIProfiler.idl
@@ -51,27 +51,27 @@ interface nsIProfiler : nsISupports
   /*
    * Returns a JS object of the profile. If aSinceTime is passed, only report
    * samples taken at >= aSinceTime.
    */
   [implicit_jscontext]
   jsval getProfileData([optional] in double aSinceTime);
 
   [implicit_jscontext]
-  nsISupports getProfileDataAsync([optional] in double aSinceTime);
+  Promise getProfileDataAsync([optional] in double aSinceTime);
 
   [implicit_jscontext]
-  nsISupports getProfileDataAsArrayBuffer([optional] in double aSinceTime);
+  Promise getProfileDataAsArrayBuffer([optional] in double aSinceTime);
 
   /**
    * Returns a promise that resolves once the file has been written.
    */
   [implicit_jscontext]
-  nsISupports dumpProfileToFileAsync(in ACString aFilename,
-                                     [optional] in double aSinceTime);
+  Promise dumpProfileToFileAsync(in ACString aFilename,
+                                 [optional] in double aSinceTime);
 
   boolean IsActive();
 
   /**
    * Returns an array of the features that are supported in this build.
    * Features may vary depending on platform and build flags.
    */
   void GetFeatures(out uint32_t aCount, [retval, array, size_is(aCount)] out string aFeatures);
--- a/tools/profiler/gecko/nsProfiler.cpp
+++ b/tools/profiler/gecko/nsProfiler.cpp
@@ -217,17 +217,17 @@ nsProfiler::GetProfileData(double aSince
   MOZ_ALWAYS_TRUE(JS_ParseJSON(aCx, profile16, js_string.Length(), &val));
 
   aResult.set(val);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsProfiler::GetProfileDataAsync(double aSinceTime, JSContext* aCx,
-                                nsISupports** aPromise)
+                                Promise** aPromise)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!profiler_is_active()) {
     return NS_ERROR_FAILURE;
   }
 
   if (NS_WARN_IF(!aCx)) {
@@ -285,17 +285,17 @@ nsProfiler::GetProfileDataAsync(double a
     });
 
   promise.forget(aPromise);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsProfiler::GetProfileDataAsArrayBuffer(double aSinceTime, JSContext* aCx,
-                                        nsISupports** aPromise)
+                                        Promise** aPromise)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!profiler_is_active()) {
     return NS_ERROR_FAILURE;
   }
 
   if (NS_WARN_IF(!aCx)) {
@@ -342,17 +342,17 @@ nsProfiler::GetProfileDataAsArrayBuffer(
 
   promise.forget(aPromise);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsProfiler::DumpProfileToFileAsync(const nsACString& aFilename,
                                    double aSinceTime, JSContext* aCx,
-                                   nsISupports** aPromise)
+                                   Promise** aPromise)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!profiler_is_active()) {
     return NS_ERROR_FAILURE;
   }
 
   if (NS_WARN_IF(!aCx)) {
--- a/xpcom/base/nsrootidl.idl
+++ b/xpcom/base/nsrootidl.idl
@@ -12,17 +12,26 @@
 #include "nscore.h"
 typedef int64_t PRTime;
 
 /*
  * Forward declarations for new string types
  */
 #include "nsStringFwd.h"
 
-/* 
+/*
+ * Forward declaration of mozilla::dom::Promise
+ */
+namespace mozilla {
+namespace dom {
+class Promise;
+} // namespace dom
+} // namespace mozilla
+
+/*
  * Start commenting out the C++ versions of the below in the output header
  */
 #if 0
 %}
 
 typedef boolean             bool   ;
 typedef octet               uint8_t  ;
 typedef unsigned short      uint16_t ;
@@ -83,14 +92,16 @@ typedef unsigned long       size_t;
 
 [ref, astring] native AString(ignored);
 [ref, astring] native AStringRef(ignored);
 [ptr, astring] native AStringPtr(ignored);
 
 [ref, jsval]  native jsval(jsval);
               native jsid(jsid);
 
+[ptr, promise] native Promise(ignored);
+
 %{C++
 /* 
  * End commenting out the C++ versions of the above in the output header
  */
 #endif
 %}
--- a/xpcom/build/XPCOMInit.cpp
+++ b/xpcom/build/XPCOMInit.cpp
@@ -48,18 +48,16 @@
 
 #include "nsComponentManager.h"
 #include "nsCategoryManagerUtils.h"
 #include "nsIServiceManager.h"
 
 #include "nsThreadManager.h"
 #include "nsThreadPool.h"
 
-#include "xptinfo.h"
-
 #include "nsTimerImpl.h"
 #include "TimerThread.h"
 
 #include "nsThread.h"
 #include "nsProcess.h"
 #include "nsEnvironment.h"
 #include "nsVersionComparatorImpl.h"
 
--- a/xpcom/build/nsXPCOMPrivate.h
+++ b/xpcom/build/nsXPCOMPrivate.h
@@ -4,17 +4,16 @@
  * 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 nsXPCOMPrivate_h__
 #define nsXPCOMPrivate_h__
 
 #include "nscore.h"
 #include "nsXPCOM.h"
-#include "xptcall.h"
 
 /**
  * During this shutdown notification all threads which run XPCOM code must
  * be joined.
  */
 #define NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID "xpcom-shutdown-threads"
 
 /**
--- a/xpcom/idl-parser/xpidl/header.py
+++ b/xpcom/idl-parser/xpidl/header.py
@@ -173,16 +173,17 @@ include = """
 #endif
 """
 
 jsvalue_include = """
 #include "js/Value.h"
 """
 
 infallible_includes = """
+#include "mozilla/AlreadyAddRefed.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/DebugOnly.h"
 """
 
 header_end = """/* For IDL files that don't want to include root IDL files. */
 #ifndef NS_NO_VTABLE
 #define NS_NO_VTABLE
 #endif
@@ -301,26 +302,39 @@ iface_forward = """
 /* Use this macro to declare functions that forward the behavior of this interface to another object. */
 #define NS_FORWARD_%(macroname)s(_to) """
 
 iface_forward_safe = """
 
 /* Use this macro to declare functions that forward the behavior of this interface to another object in a safe way. */
 #define NS_FORWARD_SAFE_%(macroname)s(_to) """
 
-attr_infallible_tmpl = """\
+attr_builtin_infallible_tmpl = """\
   inline %(realtype)s%(nativename)s(%(args)s)
   {
     %(realtype)sresult;
     mozilla::DebugOnly<nsresult> rv = %(nativename)s(%(argnames)s&result);
     MOZ_ASSERT(NS_SUCCEEDED(rv));
     return result;
   }
 """
 
+# NOTE: We don't use RefPtr::forget here because we don't want to need the
+# definition of %(realtype)s in scope, which we would need for the
+# AddRef/Release calls.
+attr_refcnt_infallible_tmpl = """\
+  inline already_AddRefed<%(realtype)s>%(nativename)s(%(args)s)
+  {
+    %(realtype)s* result = nullptr;
+    mozilla::DebugOnly<nsresult> rv = %(nativename)s(%(argnames)s&result);
+    MOZ_ASSERT(NS_SUCCEEDED(rv));
+    return already_AddRefed<%(realtype)s>(result);
+  }
+"""
+
 
 def write_interface(iface, fd):
     if iface.namemap is None:
         raise Exception("Interface was not resolved.")
 
     # Confirm that no names of methods will overload in this interface
     names = set()
     def record_name(name):
@@ -358,21 +372,28 @@ def write_interface(iface, fd):
 
     def write_attr_decl(a):
         printComments(fd, a.doccomments, '  ')
 
         fd.write("  /* %s */\n" % a.toIDL())
 
         fd.write("  %s = 0;\n" % attributeAsNative(a, True))
         if a.infallible:
-            fd.write(attr_infallible_tmpl %
-                     {'realtype': a.realtype.nativeType('in'),
-                      'nativename': attributeNativeName(a, getter=True),
-                      'args': '' if not a.implicit_jscontext else 'JSContext* cx',
-                      'argnames': '' if not a.implicit_jscontext else 'cx, '})
+            realtype = a.realtype.nativeType('in')
+            tmpl = attr_builtin_infallible_tmpl
+
+            if a.realtype.kind != 'builtin':
+                assert realtype.endswith(' *'), "bad infallible type"
+                tmpl = attr_refcnt_infallible_tmpl
+                realtype = realtype[:-2] # strip trailing pointer
+
+            fd.write(tmpl % {'realtype': realtype,
+                             'nativename': attributeNativeName(a, getter=True),
+                             'args': '' if not a.implicit_jscontext else 'JSContext* cx',
+                             'argnames': '' if not a.implicit_jscontext else 'cx, '})
 
         if not a.readonly:
             fd.write("  %s = 0;\n" % attributeAsNative(a, False))
         fd.write("\n")
 
     defname = iface.name.upper()
     if iface.name[0:2] == 'ns':
         defname = 'NS_' + defname[2:]
--- a/xpcom/idl-parser/xpidl/jsonxpt.py
+++ b/xpcom/idl-parser/xpidl/jsonxpt.py
@@ -40,16 +40,17 @@ TypeMap = {
     'wstring':            'TD_PWSTRING',
     # special types
     'nsid':               'TD_PNSIID',
     'domstring':          'TD_DOMSTRING',
     'astring':            'TD_ASTRING',
     'utf8string':         'TD_UTF8STRING',
     'cstring':            'TD_CSTRING',
     'jsval':              'TD_JSVAL',
+    'promise':            'TD_PROMISE',
 }
 
 
 def flags(*flags):
     return [flag for flag, cond in flags if cond]
 
 
 def get_type(type, calltype, iid_is=None, size_is=None):
--- a/xpcom/idl-parser/xpidl/rust.py
+++ b/xpcom/idl-parser/xpidl/rust.py
@@ -240,17 +240,18 @@ def attrAsWrapper(iface, m, getter):
         if m.implicit_jscontext:
             raise xpidl.RustNoncompat("jscontext is unsupported")
 
         if m.nostdcall:
             raise xpidl.RustNoncompat("nostdcall is unsupported")
 
         name = attributeParamName(m)
 
-        if getter and m.infallible:
+        if getter and m.infallible and m.realtype.kind == 'builtin':
+            # NOTE: We don't support non-builtin infallible getters in Rust code.
             return infallible_impl_tmpl % {
                 'name': attributeNativeName(m, getter),
                 'realtype': m.realtype.rustType('in'),
             }
 
         rust_type = m.realtype.rustType('out' if getter else 'in')
         return method_impl_tmpl % {
             'name': attributeNativeName(m, getter),
--- a/xpcom/idl-parser/xpidl/xpidl.py
+++ b/xpcom/idl-parser/xpidl/xpidl.py
@@ -450,18 +450,19 @@ class Native(object):
     specialtype = None
 
     specialtypes = {
         'nsid': None,
         'domstring': 'nsAString',
         'utf8string': 'nsACString',
         'cstring': 'nsACString',
         'astring': 'nsAString',
-        'jsval': 'JS::Value'
-        }
+        'jsval': 'JS::Value',
+        'promise': '::mozilla::dom::Promise',
+    }
 
     # Mappings from C++ native name types to rust native names. Types which
     # aren't listed here are incompatible with rust code.
     rust_nativenames = {
         'void': "libc::c_void",
         'char': "u8",
         'char16_t': "u16",
         'nsID': "nsID",
@@ -500,16 +501,19 @@ class Native(object):
 
     def resolve(self, parent):
         parent.setName(self)
 
     def isScriptable(self):
         if self.specialtype is None:
             return False
 
+        if self.specialtype == 'promise':
+            return self.modifier == 'ptr'
+
         if self.specialtype == 'nsid':
             return self.modifier is not None
 
         return self.modifier == 'ref'
 
     def isPtr(self, calltype):
         return self.modifier == 'ptr'
 
@@ -517,17 +521,17 @@ class Native(object):
         return self.modifier == 'ref'
 
     def nativeType(self, calltype, const=False, shared=False):
         if shared:
             if calltype != 'out':
                 raise IDLError("[shared] only applies to out parameters.")
             const = True
 
-        if self.specialtype is not None and calltype == 'in':
+        if self.specialtype not in [None, 'promise'] and calltype == 'in':
             const = True
 
         if self.specialtype == 'jsval':
             if calltype == 'out' or calltype == 'inout':
                 return "JS::MutableHandleValue "
             return "JS::HandleValue "
 
         if self.isRef(calltype):
@@ -931,18 +935,18 @@ class Attribute(object):
         if (self.null is not None and
                 getBuiltinOrNativeTypeName(self.realtype) != '[domstring]'):
             raise IDLError("'Null' attribute can only be used on DOMString",
                            self.location)
         if (self.undefined is not None and
                 getBuiltinOrNativeTypeName(self.realtype) != '[domstring]'):
             raise IDLError("'Undefined' attribute can only be used on DOMString",
                            self.location)
-        if self.infallible and not self.realtype.kind == 'builtin':
-            raise IDLError('[infallible] only works on builtin types '
+        if self.infallible and not self.realtype.kind in ['builtin', 'interface', 'forward', 'webidl']:
+            raise IDLError('[infallible] only works on interfaces, domobjects, and builtin types '
                            '(numbers, booleans, and raw char types)',
                            self.location)
         if self.infallible and not iface.attributes.builtinclass:
             raise IDLError('[infallible] attributes are only allowed on '
                            '[builtinclass] interfaces',
                            self.location)
 
     def toIDL(self):
--- a/xpcom/reflect/xptcall/xptcall.h
+++ b/xpcom/reflect/xptcall/xptcall.h
@@ -12,52 +12,75 @@
 #include "nsISupports.h"
 #include "xptinfo.h"
 #include "js/Value.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/MemoryReporting.h"
 
 struct nsXPTCMiniVariant
 {
-    union U
+// No ctors or dtors so that we can use arrays of these on the stack
+// with no penalty.
+    union Union
     {
         int8_t    i8;
         int16_t   i16;
         int32_t   i32;
         int64_t   i64;
         uint8_t   u8;
         uint16_t  u16;
         uint32_t  u32;
         uint64_t  u64;
         float     f;
         double    d;
         bool      b;
         char      c;
         char16_t wc;
         void*     p;
-
-        // Types below here are unknown to the assembly implementations, and
-        // therefore _must_ be passed with indirect semantics. We put them in
-        // the union here for type safety, so that we can avoid void* tricks.
-        JS::Value j;
+    };
 
-        // |j| has a non-trivial constructor and therefore MUST be
-        // placement-new'd into existence.
-        MOZ_PUSH_DISABLE_NONTRIVIAL_UNION_WARNINGS
-        U() {}
-        MOZ_POP_DISABLE_NONTRIVIAL_UNION_WARNINGS
-    } val;
+    Union val;
 };
 
-struct nsXPTCVariant : public nsXPTCMiniVariant
+static_assert(offsetof(nsXPTCMiniVariant, val) == 0,
+              "nsXPTCMiniVariant must be a thin wrapper");
+
+struct nsXPTCVariant
 {
-// No ctors or dtors so that we can use arrays of these on the stack
-// with no penalty.
+// No ctors or dtors so that we can use arrays of these on the stack with no
+// penalty.
+    union ExtendedVal
+    {
+    // ExtendedVal is an extension on nsXPTCMiniVariant. It contains types
+    // unknown to the assembly implementations which must be passed by indirect
+    // semantics.
+    //
+    // nsXPTCVariant contains enough space to store ExtendedVal inline, which
+    // can be used to store these types when IsIndirect() is true.
+        nsXPTCMiniVariant mini;
+
+        nsCString  nscstr;
+        nsString   nsstr;
+        JS::Value  jsval;
 
-    // inherits 'val' here
+        // This type contains non-standard-layout types, so needs an explicit
+        // Ctor/Dtor - we'll just delete them.
+        ExtendedVal() = delete;
+        ~ExtendedVal() = delete;
+    };
+
+    union
+    {
+        // The `val` field from nsXPTCMiniVariant.
+        nsXPTCMiniVariant::Union val;
+
+        // Storage for any extended variants.
+        ExtendedVal ext;
+    };
+
     void*     ptr;
     nsXPTType type;
     uint8_t   flags;
 
     enum
     {
         //
         // Bitflag definitions
@@ -93,63 +116,33 @@ struct nsXPTCVariant : public nsXPTCMini
     void SetValNeedsCleanup() {flags |= VAL_NEEDS_CLEANUP;}
 
     bool IsIndirect()         const  {return 0 != (flags & PTR_IS_DATA);}
     bool DoesValNeedCleanup() const  {return 0 != (flags & VAL_NEEDS_CLEANUP);}
 
     // Internal use only. Use IsIndirect() instead.
     bool IsPtrData()       const  {return 0 != (flags & PTR_IS_DATA);}
 
-    void Init(const nsXPTCMiniVariant& mv, const nsXPTType& t, uint8_t f)
-    {
-        type = t;
-        flags = f;
+    // Implicitly convert to nsXPTCMiniVariant.
+    operator nsXPTCMiniVariant&() {
+        return *(nsXPTCMiniVariant*) &val;
+    }
+    operator const nsXPTCMiniVariant&() const {
+        return *(const nsXPTCMiniVariant*) &val;
+    }
 
-        if(f & PTR_IS_DATA)
-        {
-            ptr = mv.val.p;
-            val.p = nullptr;
-        }
-        else
-        {
-            ptr = nullptr;
-            val.p = nullptr; // make sure 'val.p' is always initialized
-            switch(t.TagPart()) {
-              case nsXPTType::T_I8:                val.i8  = mv.val.i8;  break;
-              case nsXPTType::T_I16:               val.i16 = mv.val.i16; break;
-              case nsXPTType::T_I32:               val.i32 = mv.val.i32; break;
-              case nsXPTType::T_I64:               val.i64 = mv.val.i64; break;
-              case nsXPTType::T_U8:                val.u8  = mv.val.u8;  break;
-              case nsXPTType::T_U16:               val.u16 = mv.val.u16; break;
-              case nsXPTType::T_U32:               val.u32 = mv.val.u32; break;
-              case nsXPTType::T_U64:               val.u64 = mv.val.u64; break;
-              case nsXPTType::T_FLOAT:             val.f   = mv.val.f;   break;
-              case nsXPTType::T_DOUBLE:            val.d   = mv.val.d;   break;
-              case nsXPTType::T_BOOL:              val.b   = mv.val.b;   break;
-              case nsXPTType::T_CHAR:              val.c   = mv.val.c;   break;
-              case nsXPTType::T_WCHAR:             val.wc  = mv.val.wc;  break;
-              case nsXPTType::T_VOID:              /* fall through */
-              case nsXPTType::T_IID:               /* fall through */
-              case nsXPTType::T_DOMSTRING:         /* fall through */
-              case nsXPTType::T_CHAR_STR:          /* fall through */
-              case nsXPTType::T_WCHAR_STR:         /* fall through */
-              case nsXPTType::T_INTERFACE:         /* fall through */
-              case nsXPTType::T_INTERFACE_IS:      /* fall through */
-              case nsXPTType::T_DOMOBJECT:         /* fall through */
-              case nsXPTType::T_ARRAY:             /* fall through */
-              case nsXPTType::T_PSTRING_SIZE_IS:   /* fall through */
-              case nsXPTType::T_PWSTRING_SIZE_IS:  /* fall through */
-              case nsXPTType::T_UTF8STRING:        /* fall through */
-              case nsXPTType::T_CSTRING:           /* fall through */
-              default:                             val.p   = mv.val.p;   break;
-            }
-        }
-    }
+    // As this type contains an anonymous union, we need to provide explicit
+    // constructors & destructors.
+    nsXPTCVariant() { }
+    ~nsXPTCVariant() { }
 };
 
+static_assert(offsetof(nsXPTCVariant, val) == offsetof(nsXPTCVariant, ext),
+              "nsXPTCVariant::{ext,val} must have matching offsets");
+
 class nsIXPTCProxy : public nsISupports
 {
 public:
     NS_IMETHOD CallMethod(uint16_t aMethodIndex,
                           const nsXPTMethodInfo *aInfo,
                           nsXPTCMiniVariant *aParams) = 0;
 };
 
--- a/xpcom/reflect/xptinfo/xptcodegen.py
+++ b/xpcom/reflect/xptinfo/xptcodegen.py
@@ -145,16 +145,43 @@ def iid_bytes(iid): # Get the byte repre
     return bs
 
 # Split a 16-bit integer into its high and low 8 bits
 def splitint(i):
     assert i < 2**16
     return (i >> 8, i & 0xff)
 
 
+# Occasionally in xpconnect, we need to fabricate types to pass into the
+# conversion methods. In some cases, these types need to be arrays, which hold
+# indicies into the extra types array.
+#
+# These are some types which should have known indexes into the extra types
+# array.
+utility_types = [
+    { 'tag': 'TD_INT8' },
+    { 'tag': 'TD_UINT8' },
+    { 'tag': 'TD_INT16' },
+    { 'tag': 'TD_UINT16' },
+    { 'tag': 'TD_INT32' },
+    { 'tag': 'TD_UINT32' },
+    { 'tag': 'TD_INT64' },
+    { 'tag': 'TD_UINT64' },
+    { 'tag': 'TD_FLOAT' },
+    { 'tag': 'TD_DOUBLE' },
+    { 'tag': 'TD_BOOL' },
+    { 'tag': 'TD_CHAR' },
+    { 'tag': 'TD_WCHAR' },
+    { 'tag': 'TD_PNSIID' },
+    { 'tag': 'TD_PSTRING' },
+    { 'tag': 'TD_PWSTRING' },
+    { 'tag': 'TD_INTERFACE_IS_TYPE', 'iid_is': 0 },
+]
+
+
 # Core of the code generator. Takes a list of raw JSON XPT interfaces, and
 # writes out a file containing the necessary static declarations into fd.
 def link_to_cpp(interfaces, fd):
     # Perfect Hash from IID into the ifaces array.
     iid_phf = PerfectHash(PHFSIZE, [
         (iid_bytes(iface['uuid']), iface)
         for iface in interfaces
     ])
@@ -215,16 +242,24 @@ def link_to_cpp(interfaces, fd):
         elif len(strings):
             # Get the last string we inserted (should be O(1) on OrderedDict).
             last_s = next(reversed(strings))
             strings[s] = strings[last_s] + len(last_s) + 1
         else:
             strings[s] = 0
         return strings[s]
 
+    def lower_extra_type(type):
+        key = describe_type(type)
+        idx = type_cache.get(key)
+        if idx is None:
+            idx = type_cache[key] = len(types)
+            types.append(lower_type(type))
+        return idx
+
     def describe_type(type): # Create the type's documentation comment.
         tag = type['tag'][3:].lower()
         if tag == 'array':
             return '%s[size_is=%d]' % (
                 describe_type(type['element']), type['size_is'])
         elif tag == 'interface_type' or tag == 'domobject':
             return type['name']
         elif tag == 'interface_is_type':
@@ -234,23 +269,17 @@ def link_to_cpp(interfaces, fd):
         return tag
 
     def lower_type(type, in_=False, out=False, optional=False):
         tag = type['tag']
         d1 = d2 = 0
 
         if tag == 'TD_ARRAY':
             d1 = type['size_is']
-
-            # index of element in extra types list
-            key = describe_type(type['element'])
-            d2 = type_cache.get(key)
-            if d2 is None:
-                d2 = type_cache[key] = len(types)
-                types.append(lower_type(type['element']))
+            d2 = lower_extra_type(type['element'])
 
         elif tag == 'TD_INTERFACE_TYPE':
             d1, d2 = splitint(interface_idx(type['name']))
 
         elif tag == 'TD_INTERFACE_IS_TYPE':
             d1 = type['iid_is']
 
         elif tag == 'TD_DOMOBJECT':
@@ -406,16 +435,22 @@ def link_to_cpp(interfaces, fd):
             return
 
         # Lower the methods and constants used by this interface
         for method in iface['methods']:
             lower_method(method, iface['name'])
         for const in iface['consts']:
             lower_const(const, iface['name'])
 
+    # Lower the types which have fixed indexes first, and check that the indexes
+    # seem correct.
+    for expected, ty in enumerate(utility_types):
+        got = lower_extra_type(ty)
+        assert got == expected, "Wrong index when lowering"
+
     # Lower interfaces in the order of the IID phf's values lookup.
     for iface in iid_phf.values:
         lower_iface(iface)
 
     # Write out the final output file
     fd.write("/* THIS FILE WAS GENERATED BY xptcodegen.py - DO NOT EDIT */\n\n")
 
     # Include any bindings files which we need to include due to XPT shims.
@@ -481,18 +516,24 @@ namespace detail {
             if idx % 8 == 0:
                 fd.write('\n ')
             fd.write(" 0x%04x," % v)
         fd.write("\n};\n\n")
     phfarr("sPHF_IIDs", "uint32_t", iid_phf.intermediate)
     phfarr("sPHF_Names", "uint32_t", name_phf.intermediate)
     phfarr("sPHF_NamesIdxs", "uint16_t", name_phf.values)
 
+    # Generate some checks that the indexes for the utility types match the
+    # declared ones in xptinfo.h
+    for idx, ty in enumerate(utility_types):
+        fd.write("static_assert(%d == (uint8_t)nsXPTType::Idx::%s, \"Bad idx\");\n" %
+                 (idx, ty['tag'][3:]))
+
     # The footer contains some checks re: the size of the generated arrays.
-    fd.write("""\
+    fd.write("""
 const uint16_t sInterfacesSize = mozilla::ArrayLength(sInterfaces);
 static_assert(sInterfacesSize == mozilla::ArrayLength(sPHF_NamesIdxs),
               "sPHF_NamesIdxs must have same size as sInterfaces");
 
 static_assert(kPHFSize == mozilla::ArrayLength(sPHF_Names),
               "sPHF_IIDs must have size kPHFSize");
 static_assert(kPHFSize == mozilla::ArrayLength(sPHF_IIDs),
               "sPHF_Names must have size kPHFSize");
--- a/xpcom/reflect/xptinfo/xptinfo.cpp
+++ b/xpcom/reflect/xptinfo/xptinfo.cpp
@@ -257,80 +257,16 @@ nsXPTInterfaceInfo::GetConstant(uint16_t
 {
   *aName = aIndex < ConstantCount()
     ? moz_xstrdup(Constant(aIndex, aConstant))
     : nullptr;
   return *aName ? NS_OK : NS_ERROR_FAILURE;
 }
 
 nsresult
-nsXPTInterfaceInfo::GetTypeForParam(uint16_t /* UNUSED aMethodIndex */,
-                                    const nsXPTParamInfo* aParam,
-                                    uint16_t aDimension,
-                                    nsXPTType* aRetval) const
-{
-  const nsXPTType* type = &aParam->Type();
-  for (uint16_t i = 0; i < aDimension; ++i) {
-    if (type->Tag() != TD_ARRAY) {
-      NS_ERROR("bad dimension");
-      return NS_ERROR_INVALID_ARG;
-    }
-    type = &type->ArrayElementType();
-  }
-
-  *aRetval = *type; // NOTE: This copies the type, which is fine I guess?
-  return NS_OK;
-}
-
-nsresult
-nsXPTInterfaceInfo::GetSizeIsArgNumberForParam(uint16_t /* UNUSED aMethodIndex */,
-                                               const nsXPTParamInfo* aParam,
-                                               uint16_t aDimension,
-                                               uint8_t* aRetval) const
-{
-  const nsXPTType* type = &aParam->Type();
-  for (uint16_t i = 0; i < aDimension; ++i) {
-    if (type->Tag() != TD_ARRAY) {
-      NS_ERROR("bad dimension");
-      return NS_ERROR_INVALID_ARG;
-    }
-    type = &type->ArrayElementType();
-  }
-
-  if (type->Tag() != TD_ARRAY &&
-      type->Tag() != TD_PSTRING_SIZE_IS &&
-      type->Tag() != TD_PWSTRING_SIZE_IS) {
-    NS_ERROR("not a size_is");
-    return NS_ERROR_INVALID_ARG;
-  }
-
-  *aRetval = type->ArgNum();
-  return NS_OK;
-}
-
-nsresult
-nsXPTInterfaceInfo::GetInterfaceIsArgNumberForParam(uint16_t /* UNUSED aMethodIndex */,
-                                                    const nsXPTParamInfo* aParam,
-                                                    uint8_t* aRetval) const
-{
-  const nsXPTType* type = &aParam->Type();
-  while (type->Tag() == TD_ARRAY) {
-    type = &type->ArrayElementType();
-  }
-
-  if (type->Tag() != TD_INTERFACE_IS_TYPE) {
-    NS_ERROR("not an iid_is");
-    return NS_ERROR_INVALID_ARG;
-  }
-
-  *aRetval = type->ArgNum();
-  return NS_OK;
-}
-
-nsresult
 nsXPTInterfaceInfo::IsIID(const nsIID* aIID, bool* aIs) const
 {
   *aIs = mIID == *aIID;
   return NS_OK;
 }
 
 nsresult
 nsXPTInterfaceInfo::GetNameShared(const char** aName) const
@@ -356,33 +292,13 @@ nsXPTInterfaceInfo::IsFunction(bool* aRe
 nsresult
 nsXPTInterfaceInfo::HasAncestor(const nsIID* aIID, bool* aRetval) const
 {
   *aRetval = HasAncestor(*aIID);
   return NS_OK;
 }
 
 nsresult
-nsXPTInterfaceInfo::GetIIDForParamNoAlloc(uint16_t aMethodIndex,
-                                          const nsXPTParamInfo* aParam,
-                                          nsIID* aIID) const
-{
-  const nsXPTType* type = &aParam->Type();
-  while (type->Tag() == TD_ARRAY) {
-    type = &type->ArrayElementType();
-  }
-
-  if (type->Tag() == TD_INTERFACE_TYPE) {
-    const nsXPTInterfaceInfo* info = type->GetInterface();
-    if (info) {
-      *aIID = info->IID();
-      return NS_OK;
-    }
-  }
-  return NS_ERROR_FAILURE;
-}
-
-nsresult
 nsXPTInterfaceInfo::IsMainProcessScriptableOnly(bool* aRetval) const
 {
   *aRetval = IsMainProcessScriptableOnly();
   return NS_OK;
 }
--- a/xpcom/reflect/xptinfo/xptinfo.h
+++ b/xpcom/reflect/xptinfo/xptinfo.h
@@ -12,16 +12,17 @@
 
 #ifndef xptinfo_h
 #define xptinfo_h
 
 #include <stdint.h>
 #include "nsID.h"
 #include "mozilla/Assertions.h"
 #include "js/Value.h"
+#include "nsString.h"
 
 // Forward Declarations
 namespace mozilla {
 namespace dom {
 struct NativePropertyHooks;
 } // namespace dom
 } // namespace mozilla
 
@@ -101,33 +102,21 @@ struct nsXPTInterfaceInfo
   nsresult IsBuiltinClass(bool* aRes) const;
   nsresult GetParent(const nsXPTInterfaceInfo** aParent) const;
   nsresult GetMethodCount(uint16_t* aMethodCount) const;
   nsresult GetConstantCount(uint16_t* aConstantCount) const;
   nsresult GetMethodInfo(uint16_t aIndex, const nsXPTMethodInfo** aInfo) const;
   nsresult GetConstant(uint16_t aIndex,
                        JS::MutableHandleValue constant,
                        char** aName) const;
-  nsresult GetTypeForParam(uint16_t aMethodIndex, const nsXPTParamInfo* aParam,
-                           uint16_t aDimension, nsXPTType* aRetval) const;
-  nsresult GetSizeIsArgNumberForParam(uint16_t aMethodIndex,
-                                      const nsXPTParamInfo* aParam,
-                                      uint16_t aDimension,
-                                      uint8_t* aRetval) const;
-  nsresult GetInterfaceIsArgNumberForParam(uint16_t aMethodIndex,
-                                           const nsXPTParamInfo* aParam,
-                                           uint8_t* aRetval) const;
   nsresult IsIID(const nsIID* aIID, bool* aIs) const;
   nsresult GetNameShared(const char** aName) const;
   nsresult GetIIDShared(const nsIID** aIID) const;
   nsresult IsFunction(bool* aRetval) const;
   nsresult HasAncestor(const nsIID* aIID, bool* aRetval) const;
-  nsresult GetIIDForParamNoAlloc(uint16_t aMethodIndex,
-                                 const nsXPTParamInfo* aParam,
-                                 nsIID* aIID) const;
   nsresult IsMainProcessScriptableOnly(bool* aRetval) const;
 
   // XXX: We can probably get away with removing this method. A shim interface
   // _should_ never show up in code which calls EnsureResolved().
   bool EnsureResolved() const { return !mIsShim; }
 
   ////////////////////////////////////////////////////////////////
   // Ensure these fields are in the same order as xptcodegen.py //
@@ -185,30 +174,33 @@ enum nsXPTTypeTag : uint8_t
   TD_INTERFACE_IS_TYPE = 19,
   TD_ARRAY             = 20,
   TD_PSTRING_SIZE_IS   = 21,
   TD_PWSTRING_SIZE_IS  = 22,
   TD_UTF8STRING        = 23,
   TD_CSTRING           = 24,
   TD_ASTRING           = 25,
   TD_JSVAL             = 26,
-  TD_DOMOBJECT         = 27
+  TD_DOMOBJECT         = 27,
+  TD_PROMISE           = 28
 };
 
 
 /*
  * A nsXPTType is a union used to identify the type of a method argument or
  * return value. The internal data is stored as an 5-bit tag, and two 8-bit
  * integers, to keep alignment requirements low.
  *
  * nsXPTType contains 3 extra bits, reserved for use by nsXPTParamInfo.
  */
 struct nsXPTType
 {
-  nsXPTTypeTag Tag() const { return static_cast<nsXPTTypeTag>(mTag); }
+  // NOTE: This is uint8_t instead of nsXPTTypeTag so that it can be compared
+  // with the nsXPTType::* re-exports.
+  uint8_t Tag() const { return mTag; }
 
   uint8_t ArgNum() const {
     MOZ_ASSERT(Tag() == TD_INTERFACE_IS_TYPE ||
                Tag() == TD_PSTRING_SIZE_IS ||
                Tag() == TD_PWSTRING_SIZE_IS ||
                Tag() == TD_ARRAY);
     return mData1;
   }
@@ -239,39 +231,74 @@ public:
   // doesn't depend on anything else in memory (ie: not a pointer, not an
   // XPCOM object, not a jsval, etc).
   //
   // Supposedly this terminology comes from Harbison/Steele, but it's still
   // a rather crappy name. We'd change it if it wasn't used all over the
   // place in xptcall. :-(
   bool IsArithmetic() const { return Tag() <= TD_WCHAR; }
 
-  // We used to abuse 'pointer' flag bit in typelib format quite extensively.
-  // We've gotten rid of most of the cases, but there's still a fair amount
-  // of refactoring to be done in XPCWrappedJSClass before we can safely stop
-  // asking about this. In the mean time, we've got a temporary version of
-  // IsPointer() that should do the right thing.
-  bool deprecated_IsPointer() const {
-    return !IsArithmetic() && Tag() != TD_JSVAL;
-  }
-
   bool IsInterfacePointer() const {
     return Tag() == TD_INTERFACE_TYPE || Tag() == TD_INTERFACE_IS_TYPE;
   }
 
   bool IsArray() const { return Tag() == TD_ARRAY; }
 
   bool IsDependent() const {
     return Tag() == TD_INTERFACE_IS_TYPE || Tag() == TD_ARRAY ||
            Tag() == TD_PSTRING_SIZE_IS || Tag() == TD_PWSTRING_SIZE_IS;
   }
 
-  bool IsStringClass() const {
-    return Tag() == TD_DOMSTRING || Tag() == TD_ASTRING ||
-           Tag() == TD_CSTRING || Tag() == TD_UTF8STRING;
+  // Unwrap a nested type to its innermost value (e.g. through arrays).
+  const nsXPTType& InnermostType() const {
+    if (Tag() == TD_ARRAY) {
+      return ArrayElementType().InnermostType();
+    }
+    return *this;
+  }
+
+  // Helper methods for working with the type's native representation.
+  inline size_t Stride() const;
+  inline bool HasPointerRepr() const;
+
+  // Offset the given base pointer to reference the element at the given index.
+  void* ElementPtr(const void* aBase, uint32_t aIndex) const {
+    return (char*)aBase + (aIndex * Stride());
+  }
+
+  // Indexes into the extra types array of a small set of known types.
+  enum class Idx : uint8_t
+  {
+    INT8 = 0,
+    UINT8,
+    INT16,
+    UINT16,
+    INT32,
+    UINT32,
+    INT64,
+    UINT64,
+    FLOAT,
+    DOUBLE,
+    BOOL,
+    CHAR,
+    WCHAR,
+    PNSIID,
+    PSTRING,
+    PWSTRING,
+    INTERFACE_IS_TYPE
+  };
+
+  // Helper methods for fabricating nsXPTType values used by xpconnect.
+  static nsXPTType MkArrayType(Idx aInner) {
+    MOZ_ASSERT(aInner <= Idx::INTERFACE_IS_TYPE);
+    return { TD_ARRAY, false, false, false, 0, (uint8_t)aInner };
+  }
+  static const nsXPTType& Get(Idx aInner) {
+    MOZ_ASSERT(aInner <= Idx::INTERFACE_IS_TYPE);
+    return xpt::detail::GetType((uint8_t)aInner);
   }
 
   ///////////////////////////////////////
   // nsXPTType backwards compatibility //
   ///////////////////////////////////////
 
   nsXPTType& operator=(uint8_t aPrefix) { mTag = aPrefix; return *this; }
   operator uint8_t() const { return TagPart(); };
@@ -301,17 +328,18 @@ public:
     T_INTERFACE_IS      = TD_INTERFACE_IS_TYPE,
     T_ARRAY             = TD_ARRAY            ,
     T_PSTRING_SIZE_IS   = TD_PSTRING_SIZE_IS  ,
     T_PWSTRING_SIZE_IS  = TD_PWSTRING_SIZE_IS ,
     T_UTF8STRING        = TD_UTF8STRING       ,
     T_CSTRING           = TD_CSTRING          ,
     T_ASTRING           = TD_ASTRING          ,
     T_JSVAL             = TD_JSVAL            ,
-    T_DOMOBJECT         = TD_DOMOBJECT
+    T_DOMOBJECT         = TD_DOMOBJECT        ,
+    T_PROMISE           = TD_PROMISE
   };
 
   ////////////////////////////////////////////////////////////////
   // Ensure these fields are in the same order as xptcodegen.py //
   ////////////////////////////////////////////////////////////////
 
   uint8_t mTag : 5;
 
@@ -334,48 +362,35 @@ static_assert(sizeof(nsXPTType) == 3, "w
 
 /*
  * A nsXPTParamInfo is used to describe either a single argument to a method or
  * a method's result. It stores its flags in the type descriptor to save space.
  */
 struct nsXPTParamInfo
 {
   bool IsIn() const { return mType.mInParam; }
-  bool IsOut() const { return mType.mOutParam && !IsDipper(); }
+  bool IsOut() const { return mType.mOutParam; }
   bool IsOptional() const { return mType.mOptionalParam; }
   bool IsShared() const { return false; } // XXX remove (backcompat)
 
   // Get the type of this parameter.
   const nsXPTType& Type() const { return mType; }
   const nsXPTType& GetType() const { return Type(); } // XXX remove (backcompat)
 
-  // Dipper types are one of the more inscrutable aspects of xpidl. In a
-  // nutshell, dippers are empty container objects, created and passed by the
-  // caller, and filled by the callee. The callee receives a fully- formed
-  // object, and thus does not have to construct anything. But the object is
-  // functionally empty, and the callee is responsible for putting something
-  // useful inside of it.
-  //
-  // Dipper types are treated as `in` parameters when declared as an `out`
-  // parameter. For this reason, dipper types are sometimes referred to as 'out
-  // parameters masquerading as in'. The burden of maintaining this illusion
-  // falls mostly on XPConnect, which creates the empty containers, and harvest
-  // the results after the call.
-  //
-  // Currently, the only dipper types are the string classes.
-  //
-  // XXX: Dipper types may be able to go away? (bug 677784)
-  bool IsDipper() const { return mType.mOutParam && IsStringClass(); }
-
-  // Whether this parameter is passed indirectly on the stack. This mainly
-  // applies to out/inout params, but we use it unconditionally for certain
-  // types.
-  bool IsIndirect() const { return IsOut() || mType.Tag() == TD_JSVAL; }
-
-  bool IsStringClass() const { return mType.IsStringClass(); }
+  // Whether this parameter is passed indirectly on the stack. All out/inout
+  // params are passed indirectly, although some types are passed indirectly
+  // unconditionally.
+  bool IsIndirect() const {
+    return IsOut() ||
+      mType.Tag() == TD_JSVAL ||
+      mType.Tag() == TD_ASTRING ||
+      mType.Tag() == TD_DOMSTRING ||
+      mType.Tag() == TD_CSTRING ||
+      mType.Tag() == TD_UTF8STRING;
+  }
 
   ////////////////////////////////////////////////////////////////
   // Ensure these fields are in the same order as xptcodegen.py //
   ////////////////////////////////////////////////////////////////
 
   nsXPTType mType;
 };
 
@@ -557,9 +572,76 @@ inline const char*
 GetString(uint32_t aIndex)
 {
   return &sStrings[aIndex];
 }
 
 } // namespace detail
 } // namespace xpt
 
+inline bool
+nsXPTType::HasPointerRepr() const
+{
+  // This method should return `true` if the given type would be represented as
+  // a pointer when not passed indirectly.
+  switch (Tag()) {
+    case TD_VOID:
+    case TD_PNSIID:
+    case TD_PSTRING:
+    case TD_PWSTRING:
+    case TD_INTERFACE_TYPE:
+    case TD_INTERFACE_IS_TYPE:
+    case TD_ARRAY:
+    case TD_PSTRING_SIZE_IS:
+    case TD_PWSTRING_SIZE_IS:
+    case TD_DOMOBJECT:
+    case TD_PROMISE:
+        return true;
+    default:
+        return false;
+  }
+}
+
+inline size_t
+nsXPTType::Stride() const
+{
+  // Compute the stride to use when walking an array of the given type.
+  //
+  // NOTE: We cast to nsXPTTypeTag here so we get a warning if a type is missed
+  // in this switch statement. It's important that this method returns a value
+  // for every possible type.
+
+  switch (static_cast<nsXPTTypeTag>(Tag())) {
+    case TD_INT8:              return sizeof(int8_t);
+    case TD_INT16:             return sizeof(int16_t);
+    case TD_INT32:             return sizeof(int32_t);
+    case TD_INT64:             return sizeof(int64_t);
+    case TD_UINT8:             return sizeof(uint8_t);
+    case TD_UINT16:            return sizeof(uint16_t);
+    case TD_UINT32:            return sizeof(uint32_t);
+    case TD_UINT64:            return sizeof(uint64_t);
+    case TD_FLOAT:             return sizeof(float);
+    case TD_DOUBLE:            return sizeof(double);
+    case TD_BOOL:              return sizeof(bool);
+    case TD_CHAR:              return sizeof(char);
+    case TD_WCHAR:             return sizeof(char16_t);
+    case TD_VOID:              return sizeof(void*);
+    case TD_PNSIID:            return sizeof(nsIID*);
+    case TD_DOMSTRING:         return sizeof(nsString);
+    case TD_PSTRING:           return sizeof(char*);
+    case TD_PWSTRING:          return sizeof(char16_t*);
+    case TD_INTERFACE_TYPE:    return sizeof(nsISupports*);
+    case TD_INTERFACE_IS_TYPE: return sizeof(nsISupports*);
+    case TD_ARRAY:             return sizeof(void*);
+    case TD_PSTRING_SIZE_IS:   return sizeof(char*);
+    case TD_PWSTRING_SIZE_IS:  return sizeof(char16_t*);
+    case TD_UTF8STRING:        return sizeof(nsCString);
+    case TD_CSTRING:           return sizeof(nsCString);
+    case TD_ASTRING:           return sizeof(nsString);
+    case TD_JSVAL:             return sizeof(JS::Value);
+    case TD_DOMOBJECT:         return sizeof(void*);
+    case TD_PROMISE:           return sizeof(void*);
+  }
+
+  MOZ_CRASH("Unknown type");
+}
+
 #endif /* xptinfo_h */
--- a/xpcom/system/nsIBlocklistService.idl
+++ b/xpcom/system/nsIBlocklistService.idl
@@ -39,14 +39,14 @@ interface nsIBlocklistService : nsISuppo
    *          If this parameter is null, the version of the running application
    *          is used.
    * @param   toolkitVersion
    *          The version of the toolkit we are checking in the blocklist.
    *          If this parameter is null, the version of the running toolkit
    *          is used.
    * @returns Promise that resolves to the STATE constant.
    */
-  nsISupports getPluginBlocklistState(in nsIPluginTag plugin,
-                                      [optional] in AString appVersion,
-                                      [optional] in AString toolkitVersion);
+  Promise getPluginBlocklistState(in nsIPluginTag plugin,
+                                  [optional] in AString appVersion,
+                                  [optional] in AString toolkitVersion);
 
   readonly attribute boolean isLoaded;
 };