Merge inbound to mozilla-central. a=merge
authorCsoregi Natalia <ncsoregi@mozilla.com>
Tue, 15 May 2018 12:53:24 +0300
changeset 795256 cf3ee14023483cbbb57129479537c713e22c1980
parent 795249 2fa8aedae0ffbed270af75dd62489bc33f589b0f (current diff)
parent 795255 c81dfed2fa9fbde91af9d33a929461105102b92e (diff)
child 795257 33a5f529478d5f1c2194c17b6553725223e5a614
child 795258 588b3250262b4866a7ac01762526f71284c4c7a2
child 795264 20250b6ab1132d14b4c4df99f96e3fe2de89381f
child 795267 849ff7539d26e00183af1baf7708ce3f9bac2892
child 795269 d5dbb2d3a5a1d9eff4eea8f181ed4ea7f21c6f3c
child 795281 185266b047fb2ce8a0c4d6b76454e0bdcf2d4358
child 795285 c079e47afb74b2c4b7da271efcaf20dd45d1eb60
child 795288 2a9a5e3f430d34b7408e2751aabde9ba8e2aa22c
child 795290 edafeac6a144ee15ad6aa8cbe798c0fc94743b4b
child 795298 fa3d082b2075cc4f8b91e064d48f796d823dea1c
child 795304 48d6f36d141287c064af3a24e9968b6cc4ac92a0
child 795317 6c64bd1dee70dcaceb05561014c744c7ac9b0859
child 795323 4c19ba1a9a0c48baba8d0458553fba841ce4efc8
child 795324 5ae702b3c46447ad27bdb80a9328375185aa55f4
child 795327 e5b1aeb7339327587d411d34e31f8faa5e836bf7
child 795328 9b78328a27dc2ed3d68ea63143befca5b1db0c53
child 795333 ed3783e41e7d9b1b61b26c95b894dfdc1859d732
child 795334 5a8ffd692f5d557ecc25e8ade472373122bf8058
child 795338 dc74cb75292584188e3c08095b816e6eee20bead
child 795349 74b79313ce4362c31aa4052ddeef97541920e205
child 795351 6503e1cab6358594123e24b479e32cad8382dfce
child 795352 3c08a5aab5398eb3b5685b18e5fe06e926db5f85
child 795355 b72061d2fa1872a9d33e5f2f32042aeeb878d9ad
child 795359 0c373e28eeb08ed993bbf40106c3328bfb0c8a13
child 795403 199132d5d7c82d0d26ff5f303d3dac930802974e
child 795405 c07339a96312eada209fe83ba2a30a3ae1193d11
child 795408 b6b4ce595172f21a61789acf474a1c66aca97fe4
child 795414 de9dfd10556a2e35ffab7a9d10435b235146282d
child 795415 4cd4caf2d4d7bf38d24fc778245f430abc32bc74
child 795416 edc52176b047807e89c5449ce051a271dc2b3077
child 795422 e278b6639ca1517dbcefa2a307a59b41d85e28f8
child 795451 9b367bafb01537eaebcdc877eb5744d7fe86b391
child 795455 aebc03899ad88aa2838825c14a45c59e1e59be05
child 795457 4cdeef3ac57e18b7f7a415cf9abdf746c76dfe7f
child 795473 8caca89c7e8e63c178e49b97d6abc37f80d59129
child 795474 9166196d04f4ae36efa59756206a472227e735e8
child 795479 04d0bded0426716c80da3fd2546fc40221f82ab4
child 795482 87a2d999619c768e9b1b53aeed5b3f41923ad74a
child 795484 96388e20d1819fea2bd83f452d543f52c300f839
child 795486 4e3b6648fc02d85d3b01c648dcebabc4f18fd9f5
child 795493 ec6914fb9d8948113ad70e415e57552cf88d80f5
child 795504 fa83b2d9e6b9e26cde42fedcc599288975ae31ab
child 795555 6bb76b128f9fb63cddaecb00c65f512e0d631bec
child 795559 605eb68fdb5f3bd216322a4b7e4d5c322b2eac5e
child 795562 af550005fa29f4d85655ea67932aa465851eb20b
child 795570 8d9a51486bddd88bc05227b9e8dcbc45330bf20c
child 795597 c422438e6767e4b165a05b8e0a712ab8997e73b4
child 795690 71c3bfb35a777f23d168c3f16637a69557a21231
child 795737 6631f0848a84bcb015042543d1b8aaf4edcb60bc
child 795738 3979f1aaa043bb035f8947db2c4c525c12204a7c
child 795764 902c604dcc08930f82ca20edbfe3bb23244e78e2
child 795774 39cee3a82c147f62e34d970d08fae8c5b14a9855
child 795811 f950de93d8b8707dc9b7dd4bfcb1e79566837991
child 795833 f222e764471baf49eb0eeb4fa1e0c821810b0973
child 795925 68496a39f39361d4ca0e990083bc19366393c35d
child 795926 986add321c25e233702f1540127c19babd365797
child 795951 2f711baf98d9e4c183a4ccf96afcfa0b5c0e0e8a
child 795970 675bd3fa996b9c7ee57a5e83575a69ebd29f6eec
child 796338 44707daeb1a83bcc9c11d1b05d76c4a96fab2a03
child 796373 eb0498cba1499579685b2e30a2626280647f5c16
child 796515 30e0ebeaae2bd8bc2f4c6c5f9c4cc62ff8e9810f
child 796580 a73eb6b17967504d35da1b5f79e50272dc6cbf4a
child 796596 91e9875f9e4fc7a0d0c72af1b52ddaca80a52175
child 796623 535c4b07298021578d45c2733004a703ca3dacac
child 796723 b583290c6101f502b83ddddddcb795cac2951b87
child 796908 47d7fdf28573f1976b4f3ea45d5a1d231c0ff3ac
child 797236 5e05114c19b9b8d4402541c18beeeed50c65a8f4
child 797240 46aaf26aa4a2c7a2399012f879c4477faa23506a
child 797244 64be181f08d0956fb972b00d6c89cbbf52ca1198
child 797286 6d4dba13913c4238984f9055565a5228d16ff793
child 797429 dceaeb403c131903358f2c2277971339041f6984
child 797526 cb6c45ccb0070d72337767944e32013e19691a39
child 797867 2126f70c7b17b3105098527d92b40fe0871872a2
child 798912 9622e3caf7bb7886e858263f370cf2d49fa10e24
child 799005 8d164eedfee1e68ad6c46b2b189f60ace625acf5
child 799130 0a7302e2690ccc2b809e2eb3df655756839f5425
child 799595 d40c219c5e73c598af35a42cc6a147bb64f5cab4
child 799828 238454588981e2c2d11ad7d44c15bad4d717fb1b
child 799879 11a5ed37aaf6bbf97b85b24ff29a395ac5f05950
child 799885 e1d7f9703ca24f857bb88efdb39a36819ed0a7c4
child 799886 4965efcac1ce0302e2f35ad81de03f2a65fdbfdf
child 799888 7a7d5fae9e3537dff247911a28f048ebcfc1c6c4
child 799903 d0ad2208ab9c3e91ac91e7d756be1d3d016569e0
child 799947 e9326aec06fda9d53ca22827155b7c927d92ef12
push id109898
push userplingurar@mozilla.com
push dateTue, 15 May 2018 10:04:48 +0000
reviewersmerge
milestone62.0a1
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;
 };