merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Tue, 11 Oct 2016 12:01:35 +0200
changeset 317401 7ae377917236
parent 317379 8f4c28b59a8c (current diff)
parent 317400 3b920f1c673a (diff)
child 317438 51dc2ac80cf1
child 317502 8654fba1417d
child 317511 6e861a1843cb
push id30804
push usercbook@mozilla.com
push dateTue, 11 Oct 2016 10:01:46 +0000
treeherdermozilla-central@7ae377917236 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone52.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
--- a/browser/base/content/newtab/dragDataHelper.js
+++ b/browser/base/content/newtab/dragDataHelper.js
@@ -6,17 +6,17 @@
 
 var gDragDataHelper = {
   get mimeType() {
     return "text/x-moz-url";
   },
 
   getLinkFromDragEvent: function DragDataHelper_getLinkFromDragEvent(aEvent) {
     let dt = aEvent.dataTransfer;
-    if (!dt || !dt.types.contains(this.mimeType)) {
+    if (!dt || !dt.types.includes(this.mimeType)) {
       return null;
     }
 
     let data = dt.getData(this.mimeType) || "";
     let [url, title] = data.split(/[\r\n]+/);
     return {url: url, title: title};
   }
 };
--- a/browser/base/content/test/general/browser_clipboard_pastefile.js
+++ b/browser/base/content/test/general/browser_clipboard_pastefile.js
@@ -39,17 +39,17 @@ add_task(function*() {
   searchbar.focus();
 
   yield new Promise((resolve, reject) => {
     searchbar.addEventListener("paste", function copyEvent(event) {
       searchbar.removeEventListener("paste", copyEvent, true);
 
       let dt = event.clipboardData;
       is(dt.types.length, 3, "number of types");
-      ok(dt.types.contains("text/plain"), "text/plain exists in types");
+      ok(dt.types.includes("text/plain"), "text/plain exists in types");
       ok(dt.mozTypesAt(0).contains("text/plain"), "text/plain exists in mozTypesAt");
       is(dt.getData("text/plain"), "Alternate", "text/plain returned in getData");
       is(dt.mozGetDataAt("text/plain", 0), "Alternate", "text/plain returned in mozGetDataAt");
 
       resolve();
     }, true);
 
     EventUtils.synthesizeKey("v", { accelKey: true });
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -651,20 +651,20 @@ file, You can obtain one at http://mozil
           this.inputField.removeAttribute("tooltiptext");
         ]]></body>
       </method>
 
       <method name="onDragOver">
         <parameter name="aEvent"/>
         <body>
           var types = aEvent.dataTransfer.types;
-          if (types.contains("application/x-moz-file") ||
-              types.contains("text/x-moz-url") ||
-              types.contains("text/uri-list") ||
-              types.contains("text/unicode"))
+          if (types.includes("application/x-moz-file") ||
+              types.includes("text/x-moz-url") ||
+              types.includes("text/uri-list") ||
+              types.includes("text/unicode"))
             aEvent.preventDefault();
         </body>
       </method>
 
       <method name="onDrop">
         <parameter name="aEvent"/>
         <body><![CDATA[
           let links = browserDragAndDrop.dropLinks(aEvent);
--- a/browser/components/downloads/content/allDownloadsViewOverlay.js
+++ b/browser/components/downloads/content/allDownloadsViewOverlay.js
@@ -1386,19 +1386,19 @@ DownloadsPlacesView.prototype = {
     dt.setData("text/uri-list", url);
     dt.setData("text/plain", url);
     dt.effectAllowed = "copyMove";
     dt.addElement(selectedItem);
   },
 
   onDragOver(aEvent) {
     let types = aEvent.dataTransfer.types;
-    if (types.contains("text/uri-list") ||
-        types.contains("text/x-moz-url") ||
-        types.contains("text/plain")) {
+    if (types.includes("text/uri-list") ||
+        types.includes("text/x-moz-url") ||
+        types.includes("text/plain")) {
       aEvent.preventDefault();
     }
   },
 
   onDrop(aEvent) {
     let dt = aEvent.dataTransfer;
     // If dragged item is from our source, do not try to
     // redownload already downloaded file.
--- a/browser/components/originattributes/test/browser/test.html
+++ b/browser/components/originattributes/test/browser/test.html
@@ -7,19 +7,14 @@
   <script>
   window.onmessage = function (evt) {
     if (evt.data != "HI") {
       return;
     }
 
     window.parent.postMessage("OK", "http://mochi.test:8888");
   };
-
-  setTimeout(function() {
-    window.parent.postMessage("KO", "http://mochi.test:8888");
-  }, 1000);
-
   </script>
 </head>
 <body>
   Hello World.
 </body>
 </html>
--- a/browser/components/search/content/search.xml
+++ b/browser/components/search/content/search.xml
@@ -851,17 +851,17 @@
 
       <handler event="keypress" keycode="VK_UP" modifiers="alt"
                phase="capturing"
                action="return this.openSearch();"/>
 
       <handler event="dragover">
       <![CDATA[
         var types = event.dataTransfer.types;
-        if (types.contains("text/plain") || types.contains("text/x-moz-text-internal"))
+        if (types.includes("text/plain") || types.includes("text/x-moz-text-internal"))
           event.preventDefault();
       ]]>
       </handler>
 
       <handler event="drop">
       <![CDATA[
         var dataTransfer = event.dataTransfer;
         var data = dataTransfer.getData("text/plain");
--- a/dom/base/Location.h
+++ b/dom/base/Location.h
@@ -39,184 +39,183 @@ public:
 
   void SetDocShell(nsIDocShell *aDocShell);
   nsIDocShell *GetDocShell();
 
   // nsIDOMLocation
   NS_DECL_NSIDOMLOCATION
 
   #define THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME() { \
-    MOZ_ASSERT(aSubjectPrincipal.isSome()); \
-    if (!CallerSubsumes(aSubjectPrincipal.value())) { \
+    if (!CallerSubsumes(&aSubjectPrincipal)) { \
       aError.Throw(NS_ERROR_DOM_SECURITY_ERR); \
       return; \
     } \
   }
 
   // WebIDL API:
   void Assign(const nsAString& aUrl,
-              const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+              nsIPrincipal& aSubjectPrincipal,
               ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = Assign(aUrl);
   }
 
   void Replace(const nsAString& aUrl,
-               const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+               nsIPrincipal& aSubjectPrincipal,
                ErrorResult& aError)
   {
     aError = Replace(aUrl);
   }
 
   void Reload(bool aForceget,
-              const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+              nsIPrincipal& aSubjectPrincipal,
               ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = Reload(aForceget);
   }
 
   void GetHref(nsAString& aHref,
-               const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+               nsIPrincipal& aSubjectPrincipal,
                ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = GetHref(aHref);
   }
 
   void SetHref(const nsAString& aHref,
-               const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+               nsIPrincipal& aSubjectPrincipal,
                ErrorResult& aError)
   {
     aError = SetHref(aHref);
   }
 
   void GetOrigin(nsAString& aOrigin,
-                 const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                 nsIPrincipal& aSubjectPrincipal,
                  ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = GetOrigin(aOrigin);
   }
 
   void GetProtocol(nsAString& aProtocol,
-                   const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                   nsIPrincipal& aSubjectPrincipal,
                    ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = GetProtocol(aProtocol);
   }
 
   void SetProtocol(const nsAString& aProtocol,
-                   const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                   nsIPrincipal& aSubjectPrincipal,
                    ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = SetProtocol(aProtocol);
   }
 
   void GetHost(nsAString& aHost,
-               const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+               nsIPrincipal& aSubjectPrincipal,
                ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = GetHost(aHost);
   }
 
   void SetHost(const nsAString& aHost,
-               const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+               nsIPrincipal& aSubjectPrincipal,
                ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = SetHost(aHost);
   }
 
   void GetHostname(nsAString& aHostname,
-                   const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                   nsIPrincipal& aSubjectPrincipal,
                    ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = GetHostname(aHostname);
   }
 
   void SetHostname(const nsAString& aHostname,
-                   const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                   nsIPrincipal& aSubjectPrincipal,
                    ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = SetHostname(aHostname);
   }
 
   void GetPort(nsAString& aPort,
-               const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+               nsIPrincipal& aSubjectPrincipal,
                ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = GetPort(aPort);
   }
 
   void SetPort(const nsAString& aPort,
-               const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+               nsIPrincipal& aSubjectPrincipal,
                ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = SetPort(aPort);
   }
 
   void GetPathname(nsAString& aPathname,
-                   const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                   nsIPrincipal& aSubjectPrincipal,
                    ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = GetPathname(aPathname);
   }
 
   void SetPathname(const nsAString& aPathname,
-                   const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                   nsIPrincipal& aSubjectPrincipal,
                    ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = SetPathname(aPathname);
   }
 
   void GetSearch(nsAString& aSeach,
-                 const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                 nsIPrincipal& aSubjectPrincipal,
                  ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = GetSearch(aSeach);
   }
 
   void SetSearch(const nsAString& aSeach,
-                 const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                 nsIPrincipal& aSubjectPrincipal,
                  ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = SetSearch(aSeach);
   }
 
   void GetHash(nsAString& aHash,
-               const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+               nsIPrincipal& aSubjectPrincipal,
                ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = GetHash(aHash);
   }
 
   void SetHash(const nsAString& aHash,
-               const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+               nsIPrincipal& aSubjectPrincipal,
                ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = SetHash(aHash);
   }
 
   void Stringify(nsAString& aRetval,
-                 const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                 nsIPrincipal& aSubjectPrincipal,
                  ErrorResult& aError)
   {
     // GetHref checks CallerSubsumes.
     GetHref(aRetval, aSubjectPrincipal, aError);
   }
 
   nsPIDOMWindowInner* GetParentObject() const
   {
--- a/dom/base/contentAreaDropListener.js
+++ b/dom/base/contentAreaDropListener.js
@@ -133,21 +133,21 @@ ContentAreaDropListener.prototype =
 
   canDropLink: function(aEvent, aAllowSameDocument)
   {
     if (this._eventTargetIsDisabled(aEvent))
       return false;
 
     let dataTransfer = aEvent.dataTransfer;
     let types = dataTransfer.types;
-    if (!types.contains("application/x-moz-file") &&
-        !types.contains("text/x-moz-url") &&
-        !types.contains("text/uri-list") &&
-        !types.contains("text/x-moz-text-internal") &&
-        !types.contains("text/plain"))
+    if (!types.includes("application/x-moz-file") &&
+        !types.includes("text/x-moz-url") &&
+        !types.includes("text/uri-list") &&
+        !types.includes("text/x-moz-text-internal") &&
+        !types.includes("text/plain"))
       return false;
 
     if (aAllowSameDocument)
       return true;
 
     let sourceNode = dataTransfer.mozSourceNode;
     if (!sourceNode)
       return true;
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -6238,16 +6238,24 @@ nsDocument::LoadBindingDocument(const ns
                                      ? Some(nsContentUtils::SubjectPrincipal())
                                      : Nothing(),
                                    rv);
   return rv.StealNSResult();
 }
 
 void
 nsIDocument::LoadBindingDocument(const nsAString& aURI,
+                                 nsIPrincipal& aSubjectPrincipal,
+                                 ErrorResult& rv)
+{
+  LoadBindingDocument(aURI, Some(&aSubjectPrincipal), rv);
+}
+
+void
+nsIDocument::LoadBindingDocument(const nsAString& aURI,
                                  const Maybe<nsIPrincipal*>& aSubjectPrincipal,
                                  ErrorResult& rv)
 {
   nsCOMPtr<nsIURI> uri;
   rv = NS_NewURI(getter_AddRefs(uri), aURI,
                  mCharacterSet.get(),
                  GetDocBaseURI());
   if (rv.Failed()) {
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -6812,23 +6812,22 @@ nsGlobalWindow::CanMoveResizeWindows(boo
     }
   }
   return true;
 }
 
 bool
 nsGlobalWindow::AlertOrConfirm(bool aAlert,
                                const nsAString& aMessage,
-                               const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                               nsIPrincipal& aSubjectPrincipal,
                                ErrorResult& aError)
 {
   // XXX This method is very similar to nsGlobalWindow::Prompt, make
   // sure any modifications here don't need to happen over there!
   MOZ_ASSERT(IsOuterWindow());
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
 
   if (!AreDialogsEnabled()) {
     // Just silently return.  In the case of alert(), the return value is
     // ignored.  In the case of confirm(), returning false is the same thing as
     // would happen if the user cancels.
     return false;
   }
 
@@ -6837,17 +6836,17 @@ nsGlobalWindow::AlertOrConfirm(bool aAle
   // the whole time a modal dialog is open.
   nsAutoPopupStatePusher popupStatePusher(openAbused, true);
 
   // Before bringing up the window, unsuppress painting and flush
   // pending reflows.
   EnsureReflowFlushAndPaint();
 
   nsAutoString title;
-  MakeScriptDialogTitle(title, aSubjectPrincipal.value());
+  MakeScriptDialogTitle(title, &aSubjectPrincipal);
 
   // Remove non-terminating null characters from the
   // string. See bug #310037.
   nsAutoString final;
   nsContentUtils::StripNullChars(aMessage, final);
 
   nsresult rv;
   nsCOMPtr<nsIPromptFactory> promptFac =
@@ -6890,55 +6889,55 @@ nsGlobalWindow::AlertOrConfirm(bool aAle
                prompt->Alert(title.get(), final.get()) :
                prompt->Confirm(title.get(), final.get(), &result);
   }
 
   return result;
 }
 
 void
-nsGlobalWindow::Alert(const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+nsGlobalWindow::Alert(nsIPrincipal& aSubjectPrincipal,
                       ErrorResult& aError)
 {
   MOZ_ASSERT(IsInnerWindow());
   Alert(EmptyString(), aSubjectPrincipal, aError);
 }
 
 void
 nsGlobalWindow::AlertOuter(const nsAString& aMessage,
-                           const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                           nsIPrincipal& aSubjectPrincipal,
                            ErrorResult& aError)
 {
   MOZ_RELEASE_ASSERT(IsOuterWindow());
   AlertOrConfirm(/* aAlert = */ true, aMessage, aSubjectPrincipal, aError);
 }
 
 void
 nsGlobalWindow::Alert(const nsAString& aMessage,
-                      const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                      nsIPrincipal& aSubjectPrincipal,
                       ErrorResult& aError)
 {
   FORWARD_TO_OUTER_OR_THROW(AlertOuter, (aMessage, aSubjectPrincipal, aError),
                             aError, );
 }
 
 bool
 nsGlobalWindow::ConfirmOuter(const nsAString& aMessage,
-                             const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                             nsIPrincipal& aSubjectPrincipal,
                              ErrorResult& aError)
 {
   MOZ_RELEASE_ASSERT(IsOuterWindow());
 
   return AlertOrConfirm(/* aAlert = */ false, aMessage, aSubjectPrincipal,
                         aError);
 }
 
 bool
 nsGlobalWindow::Confirm(const nsAString& aMessage,
-                        const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                        nsIPrincipal& aSubjectPrincipal,
                         ErrorResult& aError)
 {
   FORWARD_TO_OUTER_OR_THROW(ConfirmOuter, (aMessage, aSubjectPrincipal, aError),
                             aError, false);
 }
 
 already_AddRefed<Promise>
 nsGlobalWindow::Fetch(const RequestOrUSVString& aInput,
@@ -6946,23 +6945,22 @@ nsGlobalWindow::Fetch(const RequestOrUSV
 {
   return FetchRequest(this, aInput, aInit, aRv);
 }
 
 void
 nsGlobalWindow::PromptOuter(const nsAString& aMessage,
                             const nsAString& aInitial,
                             nsAString& aReturn,
-                            const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                            nsIPrincipal& aSubjectPrincipal,
                             ErrorResult& aError)
 {
   // XXX This method is very similar to nsGlobalWindow::AlertOrConfirm, make
   // sure any modifications here don't need to happen over there!
   MOZ_RELEASE_ASSERT(IsOuterWindow());
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
 
   SetDOMStringToNull(aReturn);
 
   if (!AreDialogsEnabled()) {
     // Return null, as if the user just canceled the prompt.
     return;
   }
 
@@ -6971,17 +6969,17 @@ nsGlobalWindow::PromptOuter(const nsAStr
   // the whole time a modal dialog is open.
   nsAutoPopupStatePusher popupStatePusher(openAbused, true);
 
   // Before bringing up the window, unsuppress painting and flush
   // pending reflows.
   EnsureReflowFlushAndPaint();
 
   nsAutoString title;
-  MakeScriptDialogTitle(title, aSubjectPrincipal.value());
+  MakeScriptDialogTitle(title, &aSubjectPrincipal);
 
   // Remove non-terminating null characters from the
   // string. See bug #310037.
   nsAutoString fixedMessage, fixedInitial;
   nsContentUtils::StripNullChars(aMessage, fixedMessage);
   nsContentUtils::StripNullChars(aInitial, fixedInitial);
 
   nsresult rv;
@@ -7032,17 +7030,17 @@ nsGlobalWindow::PromptOuter(const nsAStr
   if (ok && outValue) {
     aReturn.Assign(outValue);
   }
 }
 
 void
 nsGlobalWindow::Prompt(const nsAString& aMessage, const nsAString& aInitial,
                        nsAString& aReturn,
-                       const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                       nsIPrincipal& aSubjectPrincipal,
                        ErrorResult& aError)
 {
   FORWARD_TO_OUTER_OR_THROW(PromptOuter,
                             (aMessage, aInitial, aReturn, aSubjectPrincipal,
                              aError),
                             aError, );
 }
 
@@ -8278,21 +8276,20 @@ nsGlobalWindow::CallerInnerWindow()
   nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(global);
   return nsGlobalWindow::Cast(win);
 }
 
 void
 nsGlobalWindow::PostMessageMozOuter(JSContext* aCx, JS::Handle<JS::Value> aMessage,
                                     const nsAString& aTargetOrigin,
                                     JS::Handle<JS::Value> aTransfer,
-                                    const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                                    nsIPrincipal& aSubjectPrincipal,
                                     ErrorResult& aError)
 {
   MOZ_RELEASE_ASSERT(IsOuterWindow());
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
 
   //
   // Window.postMessage is an intentional subversion of the same-origin policy.
   // As such, this code must be particularly careful in the information it
   // exposes to calling code.
   //
   // http://www.whatwg.org/specs/web-apps/current-work/multipage/section-crossDocumentMessages.html
   //
@@ -8365,17 +8362,17 @@ nsGlobalWindow::PostMessageMozOuter(JSCo
     }
 
     if (NS_FAILED(originURI->SetUserPass(EmptyCString())) ||
         NS_FAILED(originURI->SetPath(EmptyCString()))) {
       return;
     }
 
     PrincipalOriginAttributes attrs =
-      BasePrincipal::Cast(aSubjectPrincipal.value())->OriginAttributesRef();
+      BasePrincipal::Cast(&aSubjectPrincipal)->OriginAttributesRef();
     // Create a nsIPrincipal inheriting the app/browser attributes from the
     // caller.
     providedPrincipal = BasePrincipal::CreateCodebasePrincipal(originURI, attrs);
     if (NS_WARN_IF(!providedPrincipal)) {
       return;
     }
   }
 
@@ -8403,30 +8400,30 @@ nsGlobalWindow::PostMessageMozOuter(JSCo
 
   aError = NS_DispatchToCurrentThread(event);
 }
 
 void
 nsGlobalWindow::PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
                                const nsAString& aTargetOrigin,
                                JS::Handle<JS::Value> aTransfer,
-                               const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                               nsIPrincipal& aSubjectPrincipal,
                                ErrorResult& aError)
 {
   FORWARD_TO_OUTER_OR_THROW(PostMessageMozOuter,
                             (aCx, aMessage, aTargetOrigin, aTransfer,
                              aSubjectPrincipal, aError),
                             aError, );
 }
 
 void
 nsGlobalWindow::PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
                                const nsAString& aTargetOrigin,
                                const Optional<Sequence<JS::Value>>& aTransfer,
-                               const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                               nsIPrincipal& aSubjectPrincipal,
                                ErrorResult& aError)
 {
   JS::Rooted<JS::Value> transferArray(aCx, JS::UndefinedValue());
   if (aTransfer.WasPassed()) {
     const Sequence<JS::Value >& values = aTransfer.Value();
 
     // The input sequence only comes from the generated bindings code, which
     // ensures it is rooted.
@@ -9044,41 +9041,39 @@ nsGlobalWindow::CacheXBLPrototypeHandler
     mCachedXBLPrototypeHandlers = new nsJSThingHashtable<nsPtrHashKey<nsXBLPrototypeHandler>, JSObject*>();
     PreserveWrapper(ToSupports(this));
   }
 
   mCachedXBLPrototypeHandlers->Put(aKey, aHandler);
 }
 
 Element*
-nsGlobalWindow::GetFrameElementOuter(const Maybe<nsIPrincipal*>& aSubjectPrincipal)
+nsGlobalWindow::GetFrameElementOuter(nsIPrincipal& aSubjectPrincipal)
 {
   MOZ_RELEASE_ASSERT(IsOuterWindow());
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
 
   if (!mDocShell || mDocShell->GetIsMozBrowserOrApp()) {
     return nullptr;
   }
 
   // Per HTML5, the frameElement getter returns null in cross-origin situations.
   Element* element = GetRealFrameElementOuter();
   if (!element) {
     return nullptr;
   }
 
-  if (!aSubjectPrincipal.value()->
-         SubsumesConsideringDomain(element->NodePrincipal())) {
+  if (!aSubjectPrincipal.SubsumesConsideringDomain(element->NodePrincipal())) {
     return nullptr;
   }
 
   return element;
 }
 
 Element*
-nsGlobalWindow::GetFrameElement(const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+nsGlobalWindow::GetFrameElement(nsIPrincipal& aSubjectPrincipal,
                                 ErrorResult& aError)
 {
   FORWARD_TO_OUTER_OR_THROW(GetFrameElementOuter, (aSubjectPrincipal), aError,
                             nullptr);
 }
 
 Element*
 nsGlobalWindow::GetRealFrameElementOuter()
@@ -9247,33 +9242,32 @@ nsGlobalWindow::ConvertDialogOptions(con
     }
   }
 }
 
 already_AddRefed<nsIVariant>
 nsGlobalWindow::ShowModalDialogOuter(const nsAString& aUrl,
                                      nsIVariant* aArgument,
                                      const nsAString& aOptions,
-                                     const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                                     nsIPrincipal& aSubjectPrincipal,
                                      ErrorResult& aError)
 {
   MOZ_RELEASE_ASSERT(IsOuterWindow());
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
 
   if (mDoc) {
     mDoc->WarnOnceAbout(nsIDocument::eShowModalDialog);
   }
 
   if (!IsShowModalDialogEnabled()) {
     aError.Throw(NS_ERROR_NOT_AVAILABLE);
     return nullptr;
   }
 
   RefPtr<DialogValueHolder> argHolder =
-    new DialogValueHolder(aSubjectPrincipal.value(), aArgument);
+    new DialogValueHolder(&aSubjectPrincipal, aArgument);
 
   // Before bringing up the window/dialog, unsuppress painting and flush
   // pending reflows.
   EnsureReflowFlushAndPaint();
 
   if (!AreDialogsEnabled()) {
     // We probably want to keep throwing here; silently doing nothing is a bit
     // weird given the typical use cases of showModalDialog().
@@ -9320,30 +9314,30 @@ nsGlobalWindow::ShowModalDialogOuter(con
   MOZ_ASSERT(!aError.Failed());
 
   return retVal.forget();
 }
 
 already_AddRefed<nsIVariant>
 nsGlobalWindow::ShowModalDialog(const nsAString& aUrl, nsIVariant* aArgument,
                                 const nsAString& aOptions,
-                                const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                                nsIPrincipal& aSubjectPrincipal,
                                 ErrorResult& aError)
 {
   FORWARD_TO_OUTER_OR_THROW(ShowModalDialogOuter,
                             (aUrl, aArgument, aOptions, aSubjectPrincipal,
                              aError), aError, nullptr);
 }
 
 void
 nsGlobalWindow::ShowModalDialog(JSContext* aCx, const nsAString& aUrl,
                                 JS::Handle<JS::Value> aArgument,
                                 const nsAString& aOptions,
                                 JS::MutableHandle<JS::Value> aRetval,
-                                const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                                nsIPrincipal& aSubjectPrincipal,
                                 ErrorResult& aError)
 {
   MOZ_ASSERT(IsInnerWindow());
 
   nsCOMPtr<nsIVariant> args;
   aError = nsContentUtils::XPConnect()->JSToVariant(aCx,
                                                     aArgument,
                                                     getter_AddRefs(args));
@@ -10582,27 +10576,27 @@ nsGlobalWindow::GetSessionStorage(ErrorR
   if (MOZ_LOG_TEST(gDOMLeakPRLog, LogLevel::Debug)) {
     PR_LogPrint("nsGlobalWindow %p returns %p sessionStorage", this, mSessionStorage.get());
   }
 
   return mSessionStorage;
 }
 
 DOMStorage*
-nsGlobalWindow::GetLocalStorage(const Maybe<nsIPrincipal*>& aSubjectPrincipal,
-                                ErrorResult& aError)
+nsGlobalWindow::GetLocalStorage(ErrorResult& aError)
 {
   MOZ_RELEASE_ASSERT(IsInnerWindow());
 
   if (!Preferences::GetBool(kStorageEnabled)) {
     return nullptr;
   }
 
   if (!mLocalStorage) {
-    if (!DOMStorage::CanUseStorage(AsInner(), aSubjectPrincipal)) {
+    if (nsContentUtils::StorageAllowedForWindow(AsInner()) ==
+          nsContentUtils::StorageAccess::eDeny) {
       aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
       return nullptr;
     }
 
     nsIPrincipal *principal = GetPrincipal();
     if (!principal) {
       return nullptr;
     }
@@ -11658,20 +11652,17 @@ nsGlobalWindow::CloneStorageEvent(const 
   aEvent->GetNewValue(dict.mNewValue);
   aEvent->GetUrl(dict.mUrl);
 
   RefPtr<DOMStorage> storageArea = aEvent->GetStorageArea();
   MOZ_ASSERT(storageArea);
 
   RefPtr<DOMStorage> storage;
   if (storageArea->GetType() == DOMStorage::LocalStorage) {
-    storage = GetLocalStorage(nsContentUtils::GetCurrentJSContext()
-                                ? Some(nsContentUtils::SubjectPrincipal())
-                                : Nothing(),
-                              aRv);
+    storage = GetLocalStorage(aRv);
   } else {
     MOZ_ASSERT(storageArea->GetType() == DOMStorage::SessionStorage);
     storage = GetSessionStorage(aRv);
   }
 
   if (aRv.Failed() || !storage) {
     return nullptr;
   }
@@ -13996,42 +13987,40 @@ NS_INTERFACE_MAP_END_INHERITING(nsGlobal
 
 NS_IMPL_ADDREF_INHERITED(nsGlobalModalWindow, nsGlobalWindow)
 NS_IMPL_RELEASE_INHERITED(nsGlobalModalWindow, nsGlobalWindow)
 
 
 void
 nsGlobalWindow::GetDialogArgumentsOuter(JSContext* aCx,
                                         JS::MutableHandle<JS::Value> aRetval,
-                                        const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                                        nsIPrincipal& aSubjectPrincipal,
                                         ErrorResult& aError)
 {
   MOZ_RELEASE_ASSERT(IsOuterWindow());
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
   MOZ_ASSERT(IsModalContentWindow(),
              "This should only be called on modal windows!");
 
   if (!mDialogArguments) {
     MOZ_ASSERT(mIsClosed, "This window should be closed!");
     aRetval.setUndefined();
     return;
   }
 
   // This does an internal origin check, and returns undefined if the subject
   // does not subsumes the origin of the arguments.
   JS::Rooted<JSObject*> wrapper(aCx, GetWrapper());
   JSAutoCompartment ac(aCx, wrapper);
-  mDialogArguments->Get(aCx, wrapper, aSubjectPrincipal.value(),
-                        aRetval, aError);
+  mDialogArguments->Get(aCx, wrapper, &aSubjectPrincipal, aRetval, aError);
 }
 
 void
 nsGlobalWindow::GetDialogArguments(JSContext* aCx,
                                    JS::MutableHandle<JS::Value> aRetval,
-                                   const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                                   nsIPrincipal& aSubjectPrincipal,
                                    ErrorResult& aError)
 {
   FORWARD_TO_OUTER_OR_THROW(GetDialogArgumentsOuter,
                             (aCx, aRetval, aSubjectPrincipal, aError),
                             aError, );
 }
 
 /* static */ already_AddRefed<nsGlobalModalWindow>
@@ -14065,38 +14054,36 @@ void
 nsGlobalWindow::InitWasOffline()
 {
   mWasOffline = NS_IsOffline() || NS_IsAppOffline(GetPrincipal());
 }
 
 void
 nsGlobalWindow::GetReturnValueOuter(JSContext* aCx,
                                     JS::MutableHandle<JS::Value> aReturnValue,
-                                    const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                                    nsIPrincipal& aSubjectPrincipal,
                                     ErrorResult& aError)
 {
   MOZ_RELEASE_ASSERT(IsOuterWindow());
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
   MOZ_ASSERT(IsModalContentWindow(),
              "This should only be called on modal windows!");
 
   if (mReturnValue) {
     JS::Rooted<JSObject*> wrapper(aCx, GetWrapper());
     JSAutoCompartment ac(aCx, wrapper);
-    mReturnValue->Get(aCx, wrapper, aSubjectPrincipal.value(),
-                      aReturnValue, aError);
+    mReturnValue->Get(aCx, wrapper, &aSubjectPrincipal, aReturnValue, aError);
   } else {
     aReturnValue.setUndefined();
   }
 }
 
 void
 nsGlobalWindow::GetReturnValue(JSContext* aCx,
                                JS::MutableHandle<JS::Value> aReturnValue,
-                               const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                               nsIPrincipal& aSubjectPrincipal,
                                ErrorResult& aError)
 {
   FORWARD_TO_OUTER_OR_THROW(GetReturnValueOuter,
                             (aCx, aReturnValue, aSubjectPrincipal, aError),
                             aError, );
 }
 
 NS_IMETHODIMP
@@ -14110,38 +14097,36 @@ nsGlobalModalWindow::GetReturnValue(nsIV
     return NS_OK;
   }
   return mReturnValue->Get(nsContentUtils::SubjectPrincipal(), aRetVal);
 }
 
 void
 nsGlobalWindow::SetReturnValueOuter(JSContext* aCx,
                                     JS::Handle<JS::Value> aReturnValue,
-                                    const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                                    nsIPrincipal& aSubjectPrincipal,
                                     ErrorResult& aError)
 {
   MOZ_RELEASE_ASSERT(IsOuterWindow());
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
   MOZ_ASSERT(IsModalContentWindow(),
              "This should only be called on modal windows!");
 
   nsCOMPtr<nsIVariant> returnValue;
   aError =
     nsContentUtils::XPConnect()->JSToVariant(aCx, aReturnValue,
                                              getter_AddRefs(returnValue));
   if (!aError.Failed()) {
-    mReturnValue = new DialogValueHolder(aSubjectPrincipal.value(),
-                                         returnValue);
+    mReturnValue = new DialogValueHolder(&aSubjectPrincipal, returnValue);
   }
 }
 
 void
 nsGlobalWindow::SetReturnValue(JSContext* aCx,
                                JS::Handle<JS::Value> aReturnValue,
-                               const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                               nsIPrincipal& aSubjectPrincipal,
                                ErrorResult& aError)
 {
   FORWARD_TO_OUTER_OR_THROW(SetReturnValueOuter,
                             (aCx, aReturnValue, aSubjectPrincipal, aError),
                             aError, );
 }
 
 NS_IMETHODIMP
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -932,19 +932,19 @@ public:
   void SetOpener(JSContext* aCx, JS::Handle<JS::Value> aOpener,
                  mozilla::ErrorResult& aError);
   already_AddRefed<nsPIDOMWindowOuter> GetParentOuter();
   already_AddRefed<nsPIDOMWindowOuter> GetParent(mozilla::ErrorResult& aError);
   already_AddRefed<nsPIDOMWindowOuter> GetParent() override;
   nsPIDOMWindowOuter* GetScriptableParent() override;
   nsPIDOMWindowOuter* GetScriptableParentOrNull() override;
   mozilla::dom::Element*
-  GetFrameElementOuter(const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal);
+  GetFrameElementOuter(nsIPrincipal& aSubjectPrincipal);
   mozilla::dom::Element*
-  GetFrameElement(const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
+  GetFrameElement(nsIPrincipal& aSubjectPrincipal,
                   mozilla::ErrorResult& aError);
   already_AddRefed<nsIDOMElement> GetFrameElement() override;
   already_AddRefed<nsPIDOMWindowOuter>
   OpenOuter(const nsAString& aUrl,
             const nsAString& aName,
             const nsAString& aOptions,
             mozilla::ErrorResult& aError);
   already_AddRefed<nsPIDOMWindowOuter>
@@ -977,58 +977,58 @@ public:
   static bool
   TokenizeDialogOptions(nsAString& aToken, nsAString::const_iterator& aIter,
                         nsAString::const_iterator aEnd);
   static void
   ConvertDialogOptions(const nsAString& aOptions, nsAString& aResult);
 
 protected:
   bool AlertOrConfirm(bool aAlert, const nsAString& aMessage,
-                      const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                      nsIPrincipal& aSubjectPrincipal,
                       mozilla::ErrorResult& aError);
 
 public:
-  void Alert(const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
+  void Alert(nsIPrincipal& aSubjectPrincipal,
              mozilla::ErrorResult& aError);
   void AlertOuter(const nsAString& aMessage,
-                  const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                  nsIPrincipal& aSubjectPrincipal,
                   mozilla::ErrorResult& aError);
   void Alert(const nsAString& aMessage,
-             const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
+             nsIPrincipal& aSubjectPrincipal,
              mozilla::ErrorResult& aError);
   bool ConfirmOuter(const nsAString& aMessage,
-                    const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                    nsIPrincipal& aSubjectPrincipal,
                     mozilla::ErrorResult& aError);
   bool Confirm(const nsAString& aMessage,
-               const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
+               nsIPrincipal& aSubjectPrincipal,
                mozilla::ErrorResult& aError);
   void PromptOuter(const nsAString& aMessage, const nsAString& aInitial,
                    nsAString& aReturn,
-                   const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                   nsIPrincipal& aSubjectPrincipal,
                    mozilla::ErrorResult& aError);
   void Prompt(const nsAString& aMessage, const nsAString& aInitial,
               nsAString& aReturn,
-              const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
+              nsIPrincipal& aSubjectPrincipal,
               mozilla::ErrorResult& aError);
   already_AddRefed<mozilla::dom::cache::CacheStorage> GetCaches(mozilla::ErrorResult& aRv);
   already_AddRefed<mozilla::dom::Promise> Fetch(const mozilla::dom::RequestOrUSVString& aInput,
                                                 const mozilla::dom::RequestInit& aInit,
                                                 mozilla::ErrorResult& aRv);
   void PrintOuter(mozilla::ErrorResult& aError);
   void Print(mozilla::ErrorResult& aError);
   void ShowModalDialog(JSContext* aCx, const nsAString& aUrl,
                        JS::Handle<JS::Value> aArgument,
                        const nsAString& aOptions,
                        JS::MutableHandle<JS::Value> aRetval,
-                       const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                       nsIPrincipal& aSubjectPrincipal,
                        mozilla::ErrorResult& aError);
   void PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
                       const nsAString& aTargetOrigin,
                       const mozilla::dom::Optional<mozilla::dom::Sequence<JS::Value > >& aTransfer,
-                      const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                      nsIPrincipal& aSubjectPrincipal,
                       mozilla::ErrorResult& aError);
   int32_t SetTimeout(JSContext* aCx, mozilla::dom::Function& aFunction,
                      int32_t aTimeout,
                      const mozilla::dom::Sequence<JS::Value>& aArguments,
                      mozilla::ErrorResult& aError);
   int32_t SetTimeout(JSContext* aCx, const nsAString& aHandler,
                      int32_t aTimeout,
                      const mozilla::dom::Sequence<JS::Value>& /* unused */,
@@ -1044,18 +1044,17 @@ public:
                       mozilla::ErrorResult& aError);
   void ClearInterval(int32_t aHandle);
   void Atob(const nsAString& aAsciiBase64String, nsAString& aBinaryData,
             mozilla::ErrorResult& aError);
   void Btoa(const nsAString& aBinaryData, nsAString& aAsciiBase64String,
             mozilla::ErrorResult& aError);
   mozilla::dom::DOMStorage* GetSessionStorage(mozilla::ErrorResult& aError);
   mozilla::dom::DOMStorage*
-  GetLocalStorage(const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
-                  mozilla::ErrorResult& aError);
+  GetLocalStorage(mozilla::ErrorResult& aError);
   mozilla::dom::Selection* GetSelectionOuter();
   mozilla::dom::Selection* GetSelection(mozilla::ErrorResult& aError);
   already_AddRefed<nsISelection> GetSelection() override;
   mozilla::dom::IDBFactory* GetIndexedDB(mozilla::ErrorResult& aError);
   already_AddRefed<nsICSSDeclaration>
     GetComputedStyle(mozilla::dom::Element& aElt, const nsAString& aPseudoElt,
                      mozilla::ErrorResult& aError) override;
   already_AddRefed<mozilla::dom::MediaQueryList> MatchMediaOuter(const nsAString& aQuery);
@@ -1265,32 +1264,32 @@ public:
   nsIMessageBroadcaster* GetMessageManager(mozilla::ErrorResult& aError);
   nsIMessageBroadcaster* GetGroupMessageManager(const nsAString& aGroup,
                                                 mozilla::ErrorResult& aError);
   void BeginWindowMove(mozilla::dom::Event& aMouseDownEvent,
                        mozilla::dom::Element* aPanel,
                        mozilla::ErrorResult& aError);
 
   void GetDialogArgumentsOuter(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval,
-                               const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                               nsIPrincipal& aSubjectPrincipal,
                                mozilla::ErrorResult& aError);
   void GetDialogArguments(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval,
-                          const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                          nsIPrincipal& aSubjectPrincipal,
                           mozilla::ErrorResult& aError);
   void GetReturnValueOuter(JSContext* aCx, JS::MutableHandle<JS::Value> aReturnValue,
-                           const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                           nsIPrincipal& aSubjectPrincipal,
                            mozilla::ErrorResult& aError);
   void GetReturnValue(JSContext* aCx, JS::MutableHandle<JS::Value> aReturnValue,
-                      const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                      nsIPrincipal& aSubjectPrincipal,
                       mozilla::ErrorResult& aError);
   void SetReturnValueOuter(JSContext* aCx, JS::Handle<JS::Value> aReturnValue,
-                           const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                           nsIPrincipal& aSubjectPrincipal,
                            mozilla::ErrorResult& aError);
   void SetReturnValue(JSContext* aCx, JS::Handle<JS::Value> aReturnValue,
-                      const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                      nsIPrincipal& aSubjectPrincipal,
                       mozilla::ErrorResult& aError);
 
   void GetInterface(JSContext* aCx, nsIJSID* aIID,
                     JS::MutableHandle<JS::Value> aRetval,
                     mozilla::ErrorResult& aError);
 
   already_AddRefed<nsWindowRoot> GetWindowRootOuter();
   already_AddRefed<nsWindowRoot> GetWindowRoot(mozilla::ErrorResult& aError);
@@ -1703,34 +1702,34 @@ protected:
   // Returns CSS pixels based on primary screen.  Outer windows only.
   mozilla::CSSIntPoint GetScreenXY(mozilla::ErrorResult& aError);
 
   nsGlobalWindow* InnerForSetTimeoutOrInterval(mozilla::ErrorResult& aError);
 
   void PostMessageMozOuter(JSContext* aCx, JS::Handle<JS::Value> aMessage,
                            const nsAString& aTargetOrigin,
                            JS::Handle<JS::Value> aTransfer,
-                           const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                           nsIPrincipal& aSubjectPrincipal,
                            mozilla::ErrorResult& aError);
   void PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
                       const nsAString& aTargetOrigin,
                       JS::Handle<JS::Value> aTransfer,
-                      const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                      nsIPrincipal& aSubjectPrincipal,
                       mozilla::ErrorResult& aError);
 
   already_AddRefed<nsIVariant>
     ShowModalDialogOuter(const nsAString& aUrl, nsIVariant* aArgument,
                          const nsAString& aOptions,
-                         const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                         nsIPrincipal& aSubjectPrincipal,
                          mozilla::ErrorResult& aError);
 
   already_AddRefed<nsIVariant>
     ShowModalDialog(const nsAString& aUrl, nsIVariant* aArgument,
                     const nsAString& aOptions,
-                    const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                    nsIPrincipal& aSubjectPrincipal,
                     mozilla::ErrorResult& aError);
 
   // Ask the user if further dialogs should be blocked, if dialogs are currently
   // being abused. This is used in the cases where we have no modifiable UI to
   // show, in that case we show a separate dialog to ask this question.
   bool ConfirmDialogIfNeeded();
 
   // Helper called after moving/resizing, to update docShell's presContext
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -2683,16 +2683,19 @@ public:
 
   // QuerySelector and QuerySelectorAll already defined on nsINode
   nsINodeList* GetAnonymousNodes(Element& aElement);
   Element* GetAnonymousElementByAttribute(Element& aElement,
                                           const nsAString& aAttrName,
                                           const nsAString& aAttrValue);
   Element* GetBindingParent(nsINode& aNode);
   void LoadBindingDocument(const nsAString& aURI,
+                           nsIPrincipal& aSubjectPrincipal,
+                           mozilla::ErrorResult& rv);
+  void LoadBindingDocument(const nsAString& aURI,
                            const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
                            mozilla::ErrorResult& rv);
   mozilla::dom::XPathExpression*
     CreateExpression(const nsAString& aExpression,
                      mozilla::dom::XPathNSResolver* aResolver,
                      mozilla::ErrorResult& rv);
   nsINode* CreateNSResolver(nsINode& aNodeResolver);
   already_AddRefed<mozilla::dom::XPathResult>
--- a/dom/base/nsObjectLoadingContent.cpp
+++ b/dom/base/nsObjectLoadingContent.cpp
@@ -3532,34 +3532,32 @@ nsObjectLoadingContent::ShouldPlay(Fallb
     return true;
   case nsIPluginTag::STATE_CLICKTOPLAY:
     return false;
   }
   MOZ_CRASH("Unexpected enabledState");
 }
 
 nsIDocument*
-nsObjectLoadingContent::GetContentDocument(const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal)
+nsObjectLoadingContent::GetContentDocument(nsIPrincipal& aSubjectPrincipal)
 {
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
-
   nsCOMPtr<nsIContent> thisContent =
     do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
 
   if (!thisContent->IsInComposedDoc()) {
     return nullptr;
   }
 
   nsIDocument *sub_doc = thisContent->OwnerDoc()->GetSubDocumentFor(thisContent);
   if (!sub_doc) {
     return nullptr;
   }
 
   // Return null for cross-origin contentDocument.
-  if (!aSubjectPrincipal.value()->SubsumesConsideringDomain(sub_doc->NodePrincipal())) {
+  if (!aSubjectPrincipal.SubsumesConsideringDomain(sub_doc->NodePrincipal())) {
     return nullptr;
   }
 
   return sub_doc;
 }
 
 void
 nsObjectLoadingContent::LegacyCall(JSContext* aCx,
--- a/dom/base/nsObjectLoadingContent.h
+++ b/dom/base/nsObjectLoadingContent.h
@@ -175,17 +175,17 @@ class nsObjectLoadingContent : public ns
     // id.  If in doubt, return true.
     static bool MayResolve(jsid aId);
 
     // Helper for WebIDL enumeration
     void GetOwnPropertyNames(JSContext* aCx, nsTArray<nsString>& /* unused */,
                              mozilla::ErrorResult& aRv);
 
     // WebIDL API
-    nsIDocument* GetContentDocument(const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal);
+    nsIDocument* GetContentDocument(nsIPrincipal& aSubjectPrincipal);
     void GetActualType(nsAString& aType) const
     {
       CopyUTF8toUTF16(mContentType, aType);
     }
     uint32_t DisplayedType() const
     {
       return mType;
     }
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -6902,23 +6902,23 @@ class CGCallGenerator(CGThing):
     isFallible is a boolean indicating whether the call should be fallible.
 
     resultVar: If the returnType is not void, then the result of the call is
     stored in a C++ variable named by resultVar. The caller is responsible for
     declaring the result variable. If the caller doesn't care about the result
     value, resultVar can be omitted.
     """
     def __init__(self, isFallible, needsSubjectPrincipal, arguments, argsPre,
-                 returnType, extendedAttributes, descriptorProvider,
+                 returnType, extendedAttributes, descriptor,
                  nativeMethodName, static, object="self", argsPost=[],
                  resultVar=None):
         CGThing.__init__(self)
 
         result, resultOutParam, resultRooter, resultArgs, resultConversion = \
-            getRetvalDeclarationForType(returnType, descriptorProvider)
+            getRetvalDeclarationForType(returnType, descriptor)
 
         args = CGList([CGGeneric(arg) for arg in argsPre], ", ")
         for a, name in arguments:
             arg = CGGeneric(name)
 
             # Now constify the things that need it
             def needsConst(a):
                 if a.type.isDictionary():
@@ -7003,26 +7003,42 @@ class CGCallGenerator(CGThing):
         elif result is not None:
             assert resultOutParam is None
             call = CGWrapper(call, pre=resultVar + " = ")
 
         call = CGWrapper(call, post=";\n")
         self.cgRoot.append(call)
 
         if needsSubjectPrincipal:
-            self.cgRoot.prepend(CGGeneric(dedent(
-               """
-               Maybe<nsIPrincipal*> subjectPrincipal;
-               if (NS_IsMainThread()) {
-                 JSCompartment* compartment = js::GetContextCompartment(cx);
-                 MOZ_ASSERT(compartment);
-                 JSPrincipals* principals = JS_GetCompartmentPrincipals(compartment);
-                 subjectPrincipal.emplace(nsJSPrincipals::get(principals));
-               }
-               """)))
+            getPrincipal = dedent(
+                """
+                JSCompartment* compartment = js::GetContextCompartment(cx);
+                MOZ_ASSERT(compartment);
+                JSPrincipals* principals = JS_GetCompartmentPrincipals(compartment);
+                """)
+
+            if descriptor.interface.isExposedInAnyWorker():
+                self.cgRoot.prepend(CGGeneric(fill(
+                    """
+                    Maybe<nsIPrincipal*> subjectPrincipal;
+                    if (NS_IsMainThread()) {
+                      $*{getPrincipal}
+                      subjectPrincipal.emplace(nsJSPrincipals::get(principals));
+                    }
+                    """,
+                    getPrincipal=getPrincipal)))
+            else:
+                self.cgRoot.prepend(CGGeneric(fill(
+                    """
+                    $*{getPrincipal}
+                    // Initializing a nonnull is pretty darn annoying...
+                    NonNull<nsIPrincipal> subjectPrincipal;
+                    subjectPrincipal = static_cast<nsIPrincipal*>(nsJSPrincipals::get(principals));
+                    """,
+                    getPrincipal=getPrincipal)))
 
         if isFallible:
             self.cgRoot.prepend(CGGeneric("binding_detail::FastErrorResult rv;\n"))
             self.cgRoot.append(CGGeneric(dedent(
                 """
                 if (MOZ_UNLIKELY(rv.MaybeSetPendingException(cx))) {
                   return false;
                 }
@@ -13988,17 +14004,17 @@ class CGNativeMember(ClassMethod):
                                  "aRetVal"))
         elif returnType.isAny():
             args.append(Argument("JS::MutableHandle<JS::Value>", "aRetVal"))
         elif returnType.isObject() or returnType.isSpiderMonkeyInterface():
             args.append(Argument("JS::MutableHandle<JSObject*>", "aRetVal"))
 
         # And the nsIPrincipal
         if self.member.getExtendedAttribute('NeedsSubjectPrincipal'):
-            args.append(Argument("const Maybe<nsIPrincipal*>&", "aPrincipal"))
+            args.append(Argument("nsIPrincipal&", "aPrincipal"))
         # And the ErrorResult
         if 'infallible' not in self.extendedAttrs:
             # Use aRv so it won't conflict with local vars named "rv"
             args.append(Argument("ErrorResult&", "aRv"))
         # The legacycaller thisval
         if self.member.isMethod() and self.member.isLegacycaller():
             # If it has an identifier, we can't deal with it yet
             assert self.member.isIdentifierLess()
--- a/dom/bindings/test/TestBindingHeader.h
+++ b/dom/bindings/test/TestBindingHeader.h
@@ -928,16 +928,19 @@ public:
   void SetJsonifierShouldSkipThis3(TestCallbackInterface&);
   void ThrowingMethod(ErrorResult& aRv);
   bool GetThrowingAttr(ErrorResult& aRv) const;
   void SetThrowingAttr(bool arg, ErrorResult& aRv);
   bool GetThrowingGetterAttr(ErrorResult& aRv) const;
   void SetThrowingGetterAttr(bool arg);
   bool ThrowingSetterAttr() const;
   void SetThrowingSetterAttr(bool arg, ErrorResult& aRv);
+  void NeedsSubjectPrincipalMethod(nsIPrincipal&);
+  bool NeedsSubjectPrincipalAttr(nsIPrincipal&);
+  void SetNeedsSubjectPrincipalAttr(bool, nsIPrincipal&);
   int16_t LegacyCall(const JS::Value&, uint32_t, TestInterface&);
   void PassArgsWithDefaults(JSContext*, const Optional<int32_t>&,
                             TestInterface*, const Dict&, double,
                             const Optional<float>&);
 
   void SetDashed_attribute(int8_t);
   int8_t Dashed_attribute();
   void Dashed_method();
--- a/dom/bindings/test/TestCodeGen.webidl
+++ b/dom/bindings/test/TestCodeGen.webidl
@@ -938,16 +938,18 @@ interface TestInterface {
   void passRenamedInterface(TestRenamedInterface arg);
   [PutForwards=writableByte] readonly attribute TestInterface putForwardsAttr;
   [PutForwards=writableByte, LenientThis] readonly attribute TestInterface putForwardsAttr2;
   [PutForwards=writableByte, ChromeOnly] readonly attribute TestInterface putForwardsAttr3;
   [Throws] void throwingMethod();
   [Throws] attribute boolean throwingAttr;
   [GetterThrows] attribute boolean throwingGetterAttr;
   [SetterThrows] attribute boolean throwingSetterAttr;
+  [NeedsSubjectPrincipal] void needsSubjectPrincipalMethod();
+  [NeedsSubjectPrincipal] attribute boolean needsSubjectPrincipalAttr;
   legacycaller short(unsigned long arg1, TestInterface arg2);
   void passArgsWithDefaults(optional long arg1,
                             optional TestInterface? arg2 = null,
                             optional Dict arg3, optional double arg4 = 5.0,
                             optional float arg5);
 
   attribute any jsonifierShouldSkipThis;
   attribute TestParentInterface jsonifierShouldSkipThis2;
--- a/dom/bindings/test/TestJSImplGen.webidl
+++ b/dom/bindings/test/TestJSImplGen.webidl
@@ -789,16 +789,20 @@ interface TestJSImplInterface {
   void passRenamedInterface(TestRenamedInterface arg);
   [PutForwards=writableByte] readonly attribute TestJSImplInterface putForwardsAttr;
   [PutForwards=writableByte, LenientThis] readonly attribute TestJSImplInterface putForwardsAttr2;
   [PutForwards=writableByte, ChromeOnly] readonly attribute TestJSImplInterface putForwardsAttr3;
   [Throws] void throwingMethod();
   [Throws] attribute boolean throwingAttr;
   [GetterThrows] attribute boolean throwingGetterAttr;
   [SetterThrows] attribute boolean throwingSetterAttr;
+  // NeedsSubjectPrincipal not supported on JS-implemented things for
+  // now, because we always pass in the caller principal anyway.
+  //  [NeedsSubjectPrincipal] void needsSubjectPrincipalMethod();
+  //  [NeedsSubjectPrincipal] attribute boolean needsSubjectPrincipalAttr;
   // legacycaller short(unsigned long arg1, TestInterface arg2);
   void passArgsWithDefaults(optional long arg1,
                             optional TestInterface? arg2 = null,
                             optional Dict arg3, optional double arg4 = 5.0,
                             optional float arg5);
   attribute any jsonifierShouldSkipThis;
   attribute TestParentInterface jsonifierShouldSkipThis2;
   attribute TestCallbackInterface jsonifierShouldSkipThis3;
--- a/dom/events/ClipboardEvent.cpp
+++ b/dom/events/ClipboardEvent.cpp
@@ -71,17 +71,17 @@ ClipboardEvent::Constructor(const Global
   if (e->mEventIsInternal) {
     InternalClipboardEvent* event = e->mEvent->AsClipboardEvent();
     if (event) {
       // Always create a clipboardData for the copy event. If this is changed to
       // support other types of events, make sure that read/write privileges are
       // checked properly within DataTransfer.
       clipboardData = new DataTransfer(ToSupports(e), eCopy, false, -1);
       clipboardData->SetData(aParam.mDataType, aParam.mData,
-                             Some(aGlobal.GetSubjectPrincipal()), aRv);
+                             *aGlobal.GetSubjectPrincipal(), aRv);
       NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
     }
   }
 
   e->InitClipboardEvent(aType, aParam.mBubbles, aParam.mCancelable,
                         clipboardData);
   e->SetTrusted(trusted);
   e->SetComposed(aParam.mComposed);
--- a/dom/events/DataTransfer.cpp
+++ b/dom/events/DataTransfer.cpp
@@ -279,21 +279,20 @@ DataTransfer::SetEffectAllowedInt(uint32
 NS_IMETHODIMP
 DataTransfer::GetMozUserCancelled(bool* aUserCancelled)
 {
   *aUserCancelled = MozUserCancelled();
   return NS_OK;
 }
 
 already_AddRefed<FileList>
-DataTransfer::GetFiles(const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+DataTransfer::GetFiles(nsIPrincipal& aSubjectPrincipal,
                        ErrorResult& aRv)
 {
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
-  return mItems->Files(aSubjectPrincipal.value());
+  return mItems->Files(&aSubjectPrincipal);
 }
 
 NS_IMETHODIMP
 DataTransfer::GetFiles(nsIDOMFileList** aFileList)
 {
   if (!aFileList) {
     return NS_ERROR_FAILURE;
   }
@@ -305,93 +304,68 @@ DataTransfer::GetFiles(nsIDOMFileList** 
   // This code is also called from C++ code, which expects it to have a System
   // Principal, and thus the SubjectPrincipal cannot be used.
   RefPtr<FileList> files = mItems->Files(nsContentUtils::GetSystemPrincipal());
 
   files.forget(aFileList);
   return NS_OK;
 }
 
-already_AddRefed<DOMStringList>
-DataTransfer::GetTypes(ErrorResult& aRv) const
+void
+DataTransfer::GetTypes(nsTArray<nsString>& aTypes,
+                       nsIPrincipal& aSubjectPrincipal) const
 {
-  RefPtr<DOMStringList> types = new DOMStringList();
-
+  // When called from bindings, aTypes will be empty, but since we might have
+  // Gecko-internal callers too, clear it to be safe.
+  aTypes.Clear();
+  
   const nsTArray<RefPtr<DataTransferItem>>* items = mItems->MozItemsAt(0);
   if (NS_WARN_IF(!items)) {
-    return types.forget();
+    return;
   }
 
   for (uint32_t i = 0; i < items->Length(); i++) {
     DataTransferItem* item = items->ElementAt(i);
     MOZ_ASSERT(item);
 
-    if (item->ChromeOnly() && !nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
+    if (item->ChromeOnly() && !nsContentUtils::IsSystemPrincipal(&aSubjectPrincipal)) {
       continue;
     }
 
     nsAutoString type;
     item->GetType(type);
     if (item->Kind() == DataTransferItem::KIND_STRING || type.EqualsASCII(kFileMime)) {
       // If the entry has kind KIND_STRING, we want to add it to the list.
-      if (NS_WARN_IF(!types->Add(type))) {
-        aRv.Throw(NS_ERROR_FAILURE);
-        return nullptr;
-      }
+      aTypes.AppendElement(type);
     }
   }
 
   for (uint32_t i = 0; i < mItems->Length(); ++i) {
-    ErrorResult rv;
     bool found = false;
-    DataTransferItem* item = mItems->IndexedGetter(i, found, rv);
-    if (!found || rv.Failed() || item->Kind() != DataTransferItem::KIND_FILE) {
-      rv.SuppressException();
+    DataTransferItem* item = mItems->IndexedGetter(i, found);
+    MOZ_ASSERT(found);
+    if (item->Kind() != DataTransferItem::KIND_FILE) {
       continue;
     }
-    if (NS_WARN_IF(!types->Add(NS_LITERAL_STRING("Files")))) {
-      aRv.Throw(NS_ERROR_FAILURE);
-      return nullptr;
-    }
+    aTypes.AppendElement(NS_LITERAL_STRING("Files"));
     break;
   }
-
-  return types.forget();
-}
-
-NS_IMETHODIMP
-DataTransfer::GetTypes(nsISupports** aTypes)
-{
-  if (NS_WARN_IF(!aTypes)) {
-    return NS_ERROR_FAILURE;
-  }
-
-  ErrorResult rv;
-  RefPtr<DOMStringList> types = GetTypes(rv);
-  if (NS_WARN_IF(rv.Failed())) {
-    return rv.StealNSResult();
-  }
-
-  types.forget(aTypes);
-  return NS_OK;
 }
 
 void
 DataTransfer::GetData(const nsAString& aFormat, nsAString& aData,
-                      const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                      nsIPrincipal& aSubjectPrincipal,
                       ErrorResult& aRv)
 {
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
-
   // return an empty string if data for the format was not found
   aData.Truncate();
 
   nsCOMPtr<nsIVariant> data;
   nsresult rv =
-    GetDataAtInternal(aFormat, 0, aSubjectPrincipal.value(),
+    GetDataAtInternal(aFormat, 0, &aSubjectPrincipal,
                       getter_AddRefs(data));
   if (NS_FAILED(rv)) {
     if (rv != NS_ERROR_DOM_INDEX_SIZE_ERR) {
       aRv.Throw(rv);
     }
     return;
   }
 
@@ -429,44 +403,32 @@ DataTransfer::GetData(const nsAString& a
       }
     }
     else {
       aData = stringdata;
     }
   }
 }
 
-NS_IMETHODIMP
-DataTransfer::GetData(const nsAString& aFormat, nsAString& aData)
-{
-  ErrorResult rv;
-  GetData(aFormat, aData, Some(nsContentUtils::SubjectPrincipal()), rv);
-  return rv.StealNSResult();
-}
-
 void
 DataTransfer::SetData(const nsAString& aFormat, const nsAString& aData,
-                      const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                      nsIPrincipal& aSubjectPrincipal,
                       ErrorResult& aRv)
 {
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
-
   RefPtr<nsVariantCC> variant = new nsVariantCC();
   variant->SetAsAString(aData);
 
-  aRv = SetDataAtInternal(aFormat, variant, 0, aSubjectPrincipal.value());
+  aRv = SetDataAtInternal(aFormat, variant, 0, &aSubjectPrincipal);
 }
 
 void
 DataTransfer::ClearData(const Optional<nsAString>& aFormat,
-                        const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                        nsIPrincipal& aSubjectPrincipal,
                         ErrorResult& aRv)
 {
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
-
   if (mReadOnly) {
     aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
     return;
   }
 
   if (MozItemCount() == 0) {
     return;
   }
@@ -643,23 +605,21 @@ DataTransfer::GetDataAtInternal(const ns
   data.forget(aData);
   return NS_OK;
 }
 
 void
 DataTransfer::MozGetDataAt(JSContext* aCx, const nsAString& aFormat,
                            uint32_t aIndex,
                            JS::MutableHandle<JS::Value> aRetval,
-                           const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                           nsIPrincipal& aSubjectPrincipal,
                            mozilla::ErrorResult& aRv)
 {
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
-
   nsCOMPtr<nsIVariant> data;
-  aRv = GetDataAtInternal(aFormat, aIndex, aSubjectPrincipal.value(),
+  aRv = GetDataAtInternal(aFormat, aIndex, &aSubjectPrincipal,
                           getter_AddRefs(data));
   if (aRv.Failed()) {
     return;
   }
 
   if (!data) {
     aRetval.setNull();
     return;
@@ -688,16 +648,22 @@ DataTransfer::PrincipalMaySetData(const 
         aType.EqualsASCII(kFilePromiseMime)) {
       NS_WARNING("Disallowing adding x-moz-file or x-moz-file-promize types to DataTransfer");
       return false;
     }
   }
   return true;
 }
 
+void
+DataTransfer::TypesListMayHaveChanged()
+{
+  DataTransferBinding::ClearCachedTypesValue(this);
+}
+
 nsresult
 DataTransfer::SetDataAtInternal(const nsAString& aFormat, nsIVariant* aData,
                                 uint32_t aIndex,
                                 nsIPrincipal* aSubjectPrincipal)
 {
   if (aFormat.IsEmpty()) {
     return NS_OK;
   }
@@ -729,36 +695,32 @@ DataTransfer::SetDataAtInternal(const ns
   }
 
   return SetDataWithPrincipal(aFormat, aData, aIndex, aSubjectPrincipal);
 }
 
 void
 DataTransfer::MozSetDataAt(JSContext* aCx, const nsAString& aFormat,
                            JS::Handle<JS::Value> aData, uint32_t aIndex,
-                           const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                           nsIPrincipal& aSubjectPrincipal,
                            ErrorResult& aRv)
 {
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
-
   nsCOMPtr<nsIVariant> data;
   aRv = nsContentUtils::XPConnect()->JSValToVariant(aCx, aData,
                                                     getter_AddRefs(data));
   if (!aRv.Failed()) {
-    aRv = SetDataAtInternal(aFormat, data, aIndex, aSubjectPrincipal.value());
+    aRv = SetDataAtInternal(aFormat, data, aIndex, &aSubjectPrincipal);
   }
 }
 
 void
 DataTransfer::MozClearDataAt(const nsAString& aFormat, uint32_t aIndex,
-                             const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                             nsIPrincipal& aSubjectPrincipal,
                              ErrorResult& aRv)
 {
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
-
   if (mReadOnly) {
     aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
     return;
   }
 
   if (aIndex >= MozItemCount()) {
     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     return;
@@ -783,25 +745,24 @@ DataTransfer::MozClearDataAt(const nsASt
   if (aIndex == 0 && mItems->MozItemCount() > 1 &&
       mItems->MozItemsAt(0)->Length() == 0) {
     mItems->PopIndexZero();
   }
 }
 
 void
 DataTransfer::MozClearDataAtHelper(const nsAString& aFormat, uint32_t aIndex,
-                                   const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                                   nsIPrincipal& aSubjectPrincipal,
                                    ErrorResult& aRv)
 {
   MOZ_ASSERT(!mReadOnly);
   MOZ_ASSERT(aIndex < MozItemCount());
   MOZ_ASSERT(aIndex == 0 ||
              (mEventMessage != eCut && mEventMessage != eCopy &&
               mEventMessage != ePaste));
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
 
   nsAutoString format;
   GetRealFormat(aFormat, format);
 
   mItems->MozRemoveByTypeAt(format, aIndex, aSubjectPrincipal, aRv);
 }
 
 void
@@ -825,21 +786,19 @@ DataTransfer::SetDragImage(nsIDOMElement
   nsCOMPtr<Element> image = do_QueryInterface(aImage);
   if (image) {
     SetDragImage(*image, aX, aY, rv);
   }
   return rv.StealNSResult();
 }
 
 already_AddRefed<Promise>
-DataTransfer::GetFilesAndDirectories(const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+DataTransfer::GetFilesAndDirectories(nsIPrincipal& aSubjectPrincipal,
                                      ErrorResult& aRv)
 {
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
-
   nsCOMPtr<nsINode> parentNode = do_QueryInterface(mParent);
   if (!parentNode) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   nsCOMPtr<nsIGlobalObject> global = parentNode->OwnerDoc()->GetScopeObject();
   MOZ_ASSERT(global);
@@ -848,17 +807,17 @@ DataTransfer::GetFilesAndDirectories(con
     return nullptr;
   }
 
   RefPtr<Promise> p = Promise::Create(global, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
-  RefPtr<FileList> files = mItems->Files(aSubjectPrincipal.value());
+  RefPtr<FileList> files = mItems->Files(&aSubjectPrincipal);
   if (NS_WARN_IF(!files)) {
     return nullptr;
   }
 
   Sequence<RefPtr<File>> filesSeq;
   files->ToSequence(filesSeq, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
@@ -866,17 +825,17 @@ DataTransfer::GetFilesAndDirectories(con
 
   p->MaybeResolve(filesSeq);
 
   return p.forget();
 }
 
 already_AddRefed<Promise>
 DataTransfer::GetFiles(bool aRecursiveFlag,
-                       const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                       nsIPrincipal& aSubjectPrincipal,
                        ErrorResult& aRv)
 {
   // Currently we don't support directories.
   return GetFilesAndDirectories(aSubjectPrincipal, aRv);
 }
 
 void
 DataTransfer::AddElement(Element& aElement, ErrorResult& aRv)
@@ -1470,18 +1429,18 @@ DataTransfer::FillAllExternalData()
   }
 }
 
 void
 DataTransfer::FillInExternalCustomTypes(uint32_t aIndex,
                                         nsIPrincipal* aPrincipal)
 {
   RefPtr<DataTransferItem> item = new DataTransferItem(this,
-                                                       NS_LITERAL_STRING(kCustomTypesMime));
-  item->SetKind(DataTransferItem::KIND_STRING);
+                                                       NS_LITERAL_STRING(kCustomTypesMime),
+                                                       DataTransferItem::KIND_STRING);
   item->SetIndex(aIndex);
 
   nsCOMPtr<nsIVariant> variant = item->DataNoSecurityCheck();
   if (!variant) {
     return;
   }
 
   FillInExternalCustomTypes(variant, aIndex, aPrincipal);
--- a/dom/events/DataTransfer.h
+++ b/dom/events/DataTransfer.h
@@ -131,41 +131,42 @@ public:
     } else {
       aEffectAllowed.AssignASCII(sEffects[mEffectAllowed]);
     }
   }
 
   void SetDragImage(Element& aElement, int32_t aX, int32_t aY,
                     ErrorResult& aRv);
 
-  already_AddRefed<DOMStringList> GetTypes(ErrorResult& rv) const;
+  void GetTypes(nsTArray<nsString>& aTypes,
+                nsIPrincipal& aSubjectPrincipal) const;
 
   void GetData(const nsAString& aFormat, nsAString& aData,
-               const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+               nsIPrincipal& aSubjectPrincipal,
                ErrorResult& aRv);
 
   void SetData(const nsAString& aFormat, const nsAString& aData,
-               const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+               nsIPrincipal& aSubjectPrincipal,
                ErrorResult& aRv);
 
   void ClearData(const mozilla::dom::Optional<nsAString>& aFormat,
-                 const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                 nsIPrincipal& aSubjectPrincipal,
                  mozilla::ErrorResult& aRv);
 
   already_AddRefed<FileList>
-  GetFiles(const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+  GetFiles(nsIPrincipal& aSubjectPrincipal,
            mozilla::ErrorResult& aRv);
 
   already_AddRefed<Promise>
-  GetFilesAndDirectories(const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+  GetFilesAndDirectories(nsIPrincipal& aSubjectPrincipal,
                          mozilla::ErrorResult& aRv);
 
   already_AddRefed<Promise>
   GetFiles(bool aRecursiveFlag,
-           const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+           nsIPrincipal& aSubjectPrincipal,
            ErrorResult& aRv);
 
 
   void AddElement(Element& aElement, mozilla::ErrorResult& aRv);
 
   uint32_t MozItemCount() const;
 
   void GetMozCursor(nsString& aCursor)
@@ -176,27 +177,27 @@ public:
       aCursor.AssignLiteral("auto");
     }
   }
 
   already_AddRefed<DOMStringList> MozTypesAt(uint32_t aIndex,
                                              mozilla::ErrorResult& aRv) const;
 
   void MozClearDataAt(const nsAString& aFormat, uint32_t aIndex,
-                      const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                      nsIPrincipal& aSubjectPrincipal,
                       mozilla::ErrorResult& aRv);
 
   void MozSetDataAt(JSContext* aCx, const nsAString& aFormat,
                     JS::Handle<JS::Value> aData, uint32_t aIndex,
-                    const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                    nsIPrincipal& aSubjectPrincipal,
                     mozilla::ErrorResult& aRv);
 
   void MozGetDataAt(JSContext* aCx, const nsAString& aFormat,
                     uint32_t aIndex, JS::MutableHandle<JS::Value> aRetval,
-                    const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                    nsIPrincipal& aSubjectPrincipal,
                     mozilla::ErrorResult& aRv);
 
   bool MozUserCancelled() const
   {
     return mUserCancelled;
   }
 
   already_AddRefed<nsINode> GetMozSourceNode();
@@ -284,16 +285,21 @@ public:
   // converts some formats used for compatibility in aInFormat into aOutFormat.
   // Text and text/unicode become text/plain, and URL becomes text/uri-list
   void GetRealFormat(const nsAString& aInFormat, nsAString& aOutFormat) const;
 
   static bool PrincipalMaySetData(const nsAString& aFormat,
                                   nsIVariant* aData,
                                   nsIPrincipal* aPrincipal);
 
+  // Notify the DataTransfer that the list returned from GetTypes may have
+  // changed.  This can happen due to items we care about for purposes of
+  // GetTypes being added or removed or changing item kinds.
+  void TypesListMayHaveChanged();
+
 protected:
 
   // caches text and uri-list data formats that exist in the drag service or
   // clipboard for retrieval later.
   nsresult CacheExternalData(const char* aFormat, uint32_t aIndex,
                              nsIPrincipal* aPrincipal, bool aHidden);
 
   // caches the formats that exist in the drag service that were added by an
@@ -316,17 +322,17 @@ protected:
   void FillAllExternalData();
 
   void FillInExternalCustomTypes(uint32_t aIndex, nsIPrincipal* aPrincipal);
 
   void FillInExternalCustomTypes(nsIVariant* aData, uint32_t aIndex,
                                  nsIPrincipal* aPrincipal);
 
   void MozClearDataAtHelper(const nsAString& aFormat, uint32_t aIndex,
-                            const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                            nsIPrincipal& aSubjectPrincipal,
                             mozilla::ErrorResult& aRv);
 
   nsCOMPtr<nsISupports> mParent;
 
   // the drop effect and effect allowed
   uint32_t mDropEffect;
   uint32_t mEffectAllowed;
 
--- a/dom/events/DataTransferItem.cpp
+++ b/dom/events/DataTransferItem.cpp
@@ -69,22 +69,16 @@ DataTransferItem::Clone(DataTransfer* aD
   it->mData = mData;
   it->mPrincipal = mPrincipal;
   it->mChromeOnly = mChromeOnly;
 
   return it.forget();
 }
 
 void
-DataTransferItem::SetType(const nsAString& aType)
-{
-  mType = aType;
-}
-
-void
 DataTransferItem::SetData(nsIVariant* aData)
 {
   // Invalidate our file cache, we will regenerate it with the new data
   mCachedFile = nullptr;
 
   if (!aData) {
     // We are holding a temporary null which will later be filled.
     // These are provided by the system, and have guaranteed properties about
@@ -225,36 +219,33 @@ DataTransferItem::FillInExternalData()
         supportscstr->GetData(str);
         variant->SetAsACString(str);
       }
     }
   }
 
   SetData(variant);
 
-#ifdef DEBUG
   if (oldKind != Kind()) {
     NS_WARNING("Clipboard data provided by the OS does not match predicted kind");
+    mDataTransfer->TypesListMayHaveChanged();
   }
-#endif
 }
 
 already_AddRefed<File>
-DataTransferItem::GetAsFile(const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+DataTransferItem::GetAsFile(nsIPrincipal& aSubjectPrincipal,
                             ErrorResult& aRv)
 {
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
-
   if (mKind != KIND_FILE) {
     return nullptr;
   }
 
   // This is done even if we have an mCachedFile, as it performs the necessary
   // permissions checks to ensure that we are allowed to access this type.
-  nsCOMPtr<nsIVariant> data = Data(aSubjectPrincipal.value(), aRv);
+  nsCOMPtr<nsIVariant> data = Data(&aSubjectPrincipal, aRv);
   if (NS_WARN_IF(!data || aRv.Failed())) {
     return nullptr;
   }
 
   // Generate the dom::File from the stored data, caching it so that the
   // same object is returned in the future.
   if (!mCachedFile) {
     nsCOMPtr<nsISupports> supports;
@@ -278,21 +269,19 @@ DataTransferItem::GetAsFile(const Maybe<
     }
   }
 
   RefPtr<File> file = mCachedFile;
   return file.forget();
 }
 
 already_AddRefed<FileSystemEntry>
-DataTransferItem::GetAsEntry(const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+DataTransferItem::GetAsEntry(nsIPrincipal& aSubjectPrincipal,
                              ErrorResult& aRv)
 {
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
-
   RefPtr<File> file = GetAsFile(aSubjectPrincipal, aRv);
   if (NS_WARN_IF(aRv.Failed()) || !file) {
     return nullptr;
   }
 
   nsCOMPtr<nsIGlobalObject> global;
   // This is annoying, but DataTransfer may have various things as parent.
   nsCOMPtr<EventTarget> target =
@@ -381,29 +370,27 @@ DataTransferItem::CreateFileFromInputStr
   }
 
   return File::CreateMemoryFile(mDataTransfer, data, available, fileName,
                                 mType, PR_Now());
 }
 
 void
 DataTransferItem::GetAsString(FunctionStringCallback* aCallback,
-                              const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                              nsIPrincipal& aSubjectPrincipal,
                               ErrorResult& aRv)
 {
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
-
   if (!aCallback || mKind != KIND_STRING) {
     return;
   }
 
   // Theoretically this should be done inside of the runnable, as it might be an
   // expensive operation on some systems, however we wouldn't get access to the
   // NS_ERROR_DOM_SECURITY_ERROR messages which may be raised by this method.
-  nsCOMPtr<nsIVariant> data = Data(aSubjectPrincipal.value(), aRv);
+  nsCOMPtr<nsIVariant> data = Data(&aSubjectPrincipal, aRv);
   if (NS_WARN_IF(!data || aRv.Failed())) {
     return;
   }
 
   nsAutoString stringData;
   nsresult rv = data->GetAsAString(stringData);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return;
--- a/dom/events/DataTransferItem.h
+++ b/dom/events/DataTransferItem.h
@@ -30,33 +30,33 @@ public:
   // we can have other kinds then just FILE and STRING. These others are simply
   // marked as "other" and can only be produced throug the Moz* APIs.
   enum eKind {
     KIND_FILE,
     KIND_STRING,
     KIND_OTHER,
   };
 
-  DataTransferItem(DataTransfer* aDataTransfer, const nsAString& aType)
+  DataTransferItem(DataTransfer* aDataTransfer, const nsAString& aType,
+                   eKind aKind = KIND_OTHER)
     : mIndex(0)
     , mChromeOnly(false)
-    , mKind(KIND_OTHER)
+    , mKind(aKind)
     , mType(aType)
     , mDataTransfer(aDataTransfer)
   {
     MOZ_ASSERT(mDataTransfer, "Must be associated with a DataTransfer");
   }
 
   already_AddRefed<DataTransferItem> Clone(DataTransfer* aDataTransfer) const;
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
-  // NOTE: This accesses the subject principal, and should not be called from C++
   void GetAsString(FunctionStringCallback* aCallback,
-                   const Maybe<nsIPrincipal*>& aPrincipal,
+                   nsIPrincipal& aSubjectPrincipal,
                    ErrorResult& aRv);
 
   void GetKind(nsAString& aKind) const
   {
     switch (mKind) {
     case KIND_FILE:
       aKind = NS_LITERAL_STRING("file");
       return;
@@ -68,32 +68,27 @@ public:
       return;
     }
   }
 
   void GetType(nsAString& aType) const
   {
     aType = mType;
   }
-  void SetType(const nsAString& aType);
 
   eKind Kind() const
   {
     return mKind;
   }
-  void SetKind(eKind aKind)
-  {
-    mKind = aKind;
-  }
 
   already_AddRefed<File>
-  GetAsFile(const Maybe<nsIPrincipal*>& aSubjectPrincipal, ErrorResult& aRv);
+  GetAsFile(nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv);
 
   already_AddRefed<FileSystemEntry>
-  GetAsEntry(const Maybe<nsIPrincipal*>& aSubjectPrincipal, ErrorResult& aRv);
+  GetAsEntry(nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv);
 
   DataTransfer* GetParentObject() const
   {
     return mDataTransfer;
   }
 
   nsIPrincipal* Principal() const
   {
@@ -101,16 +96,19 @@ public:
   }
   void SetPrincipal(nsIPrincipal* aPrincipal)
   {
     mPrincipal = aPrincipal;
   }
 
   already_AddRefed<nsIVariant> DataNoSecurityCheck();
   already_AddRefed<nsIVariant> Data(nsIPrincipal* aPrincipal, ErrorResult& aRv);
+
+  // Note: This can modify the mKind.  Callers of this method must let the
+  // relevant DataTransfer know, because its types list can change as a result.
   void SetData(nsIVariant* aData);
 
   uint32_t Index() const
   {
     return mIndex;
   }
   void SetIndex(uint32_t aIndex)
   {
@@ -133,17 +131,17 @@ private:
   ~DataTransferItem() {}
   already_AddRefed<File> CreateFileFromInputStream(nsIInputStream* aStream);
 
   // The index in the 2d mIndexedItems array
   uint32_t mIndex;
 
   bool mChromeOnly;
   eKind mKind;
-  nsString mType;
+  const nsString mType;
   nsCOMPtr<nsIVariant> mData;
   nsCOMPtr<nsIPrincipal> mPrincipal;
   RefPtr<DataTransfer> mDataTransfer;
 
   // File cache for nsIFile application/x-moz-file entries.
   RefPtr<File> mCachedFile;
 };
 
--- a/dom/events/DataTransferItemList.cpp
+++ b/dom/events/DataTransferItemList.cpp
@@ -77,36 +77,34 @@ DataTransferItemList::Clone(DataTransfer
     list->mItems[i] = list->mIndexedItems[index][subIndex];
   }
 
   return list.forget();
 }
 
 void
 DataTransferItemList::Remove(uint32_t aIndex,
-                             const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                             nsIPrincipal& aSubjectPrincipal,
                              ErrorResult& aRv)
 {
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
-
   if (mDataTransfer->IsReadOnly()) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   if (aIndex >= Length()) {
     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     return;
   }
 
   ClearDataHelper(mItems[aIndex], aIndex, -1, aSubjectPrincipal, aRv);
 }
 
 DataTransferItem*
-DataTransferItemList::IndexedGetter(uint32_t aIndex, bool& aFound, ErrorResult& aRv) const
+DataTransferItemList::IndexedGetter(uint32_t aIndex, bool& aFound) const
 {
   if (aIndex >= mItems.Length()) {
     aFound = false;
     return nullptr;
   }
 
   MOZ_ASSERT(mItems[aIndex]);
   aFound = true;
@@ -121,21 +119,19 @@ DataTransferItemList::MozItemCount() con
   // if it is empty, scripts using the moz* APIs should see it as not existing.
   if (length == 1 && mIndexedItems[0].IsEmpty()) {
     return 0;
   }
   return length;
 }
 
 void
-DataTransferItemList::Clear(const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+DataTransferItemList::Clear(nsIPrincipal& aSubjectPrincipal,
                             ErrorResult& aRv)
 {
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
-
   if (NS_WARN_IF(mDataTransfer->IsReadOnly())) {
     return;
   }
 
   uint32_t count = Length();
   for (uint32_t i = 0; i < count; i++) {
     // We always remove the last item first, to avoid moving items around in
     // memory as much
@@ -144,81 +140,75 @@ DataTransferItemList::Clear(const Maybe<
   }
 
   MOZ_ASSERT(Length() == 0);
 }
 
 DataTransferItem*
 DataTransferItemList::Add(const nsAString& aData,
                           const nsAString& aType,
-                          const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                          nsIPrincipal& aSubjectPrincipal,
                           ErrorResult& aRv)
 {
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
-
   if (NS_WARN_IF(mDataTransfer->IsReadOnly())) {
     return nullptr;
   }
 
   nsCOMPtr<nsIVariant> data(new storage::TextVariant(aData));
 
   nsAutoString format;
   mDataTransfer->GetRealFormat(aType, format);
 
-  if (!DataTransfer::PrincipalMaySetData(format, data,
-                                         aSubjectPrincipal.value())) {
+  if (!DataTransfer::PrincipalMaySetData(format, data, &aSubjectPrincipal)) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return nullptr;
   }
 
   // We add the textual data to index 0. We set aInsertOnly to true, as we don't
   // want to update an existing entry if it is already present, as per the spec.
   RefPtr<DataTransferItem> item =
-    SetDataWithPrincipal(format, data, 0, aSubjectPrincipal.value(),
+    SetDataWithPrincipal(format, data, 0, &aSubjectPrincipal,
                          /* aInsertOnly = */ true,
                          /* aHidden = */ false,
                          aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
   MOZ_ASSERT(item->Kind() != DataTransferItem::KIND_FILE);
 
   return item;
 }
 
 DataTransferItem*
 DataTransferItemList::Add(File& aData,
-                          const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                          nsIPrincipal& aSubjectPrincipal,
                           ErrorResult& aRv)
 {
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
-
   if (mDataTransfer->IsReadOnly()) {
     return nullptr;
   }
 
   nsCOMPtr<nsISupports> supports = do_QueryObject(&aData);
   nsCOMPtr<nsIWritableVariant> data = new nsVariant();
   data->SetAsISupports(supports);
 
   nsAutoString type;
   aData.GetType(type);
 
-  if (!DataTransfer::PrincipalMaySetData(type, data,
-                                         aSubjectPrincipal.value())) {
+  if (!DataTransfer::PrincipalMaySetData(type, data, &aSubjectPrincipal)) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return nullptr;
   }
 
   // We need to add this as a new item, as multiple files can't exist in the
   // same item in the Moz DataTransfer layout. It will be appended at the end of
   // the internal specced layout.
   uint32_t index = mIndexedItems.Length();
   RefPtr<DataTransferItem> item =
-    SetDataWithPrincipal(type, data, index, aSubjectPrincipal.value(),
+    SetDataWithPrincipal(type, data, index, &aSubjectPrincipal,
                          /* aInsertOnly = */ true,
                          /* aHidden = */ false,
                          aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
   MOZ_ASSERT(item->Kind() == DataTransferItem::KIND_FILE);
 
@@ -267,21 +257,19 @@ DataTransferItemList::Files(nsIPrincipal
 
   files = mFiles;
   return files.forget();
 }
 
 void
 DataTransferItemList::MozRemoveByTypeAt(const nsAString& aType,
                                         uint32_t aIndex,
-                                        const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                                        nsIPrincipal& aSubjectPrincipal,
                                         ErrorResult& aRv)
 {
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
-
   if (NS_WARN_IF(mDataTransfer->IsReadOnly() ||
                  aIndex >= mIndexedItems.Length())) {
     return;
   }
 
   bool removeAll = aType.IsEmpty();
 
   nsTArray<RefPtr<DataTransferItem>>& items = mIndexedItems[aIndex];
@@ -365,16 +353,20 @@ DataTransferItemList::SetDataWithPrincip
                         || !subsumes))) {
           aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
           return nullptr;
         }
         item->SetPrincipal(aPrincipal);
 
         DataTransferItem::eKind oldKind = item->Kind();
         item->SetData(aData);
+        if (oldKind != item->Kind()) {
+          // Types list may have changed, even if aIndex == 0.
+          mDataTransfer->TypesListMayHaveChanged();
+        }
 
         if (aIndex != 0) {
           // If the item changes from being a file to not a file or vice-versa,
           // its presence in the mItems array may need to change.
           if (item->Kind() == DataTransferItem::KIND_FILE &&
               oldKind != DataTransferItem::KIND_FILE) {
             // not file => file
             mItems.AppendElement(item);
@@ -428,19 +420,25 @@ DataTransferItemList::AppendNewItem(uint
   item->SetChromeOnly(aHidden);
 
   mIndexedItems[aIndex].AppendElement(item);
 
   // We only want to add the item to the main mItems list if the index we are
   // adding to is 0, or the item we are adding is a file. If we add an item
   // which is not a file to a non-zero index, invariants could be broken.
   // (namely the invariant that there are not 2 non-file entries in the items
-  // array with the same type)
-  if (!aHidden && (item->Kind() == DataTransferItem::KIND_FILE || aIndex == 0)) {
-    mItems.AppendElement(item);
+  // array with the same type).
+  //
+  // We also want to update our DataTransfer's type list any time we're adding a
+  // KIND_FILE item, or an item at index 0.
+  if (item->Kind() == DataTransferItem::KIND_FILE || aIndex == 0) {
+    if (!aHidden) {
+      mItems.AppendElement(item);
+    }
+    mDataTransfer->TypesListMayHaveChanged();
   }
 
   return item;
 }
 
 const nsTArray<RefPtr<DataTransferItem>>*
 DataTransferItemList::MozItemsAt(uint32_t aIndex) // -- INDEXED
 {
@@ -470,60 +468,60 @@ DataTransferItemList::PopIndexZero()
 
 void
 DataTransferItemList::ClearAllItems()
 {
   // We always need to have index 0, so don't delete that one
   mItems.Clear();
   mIndexedItems.Clear();
   mIndexedItems.SetLength(1);
+  mDataTransfer->TypesListMayHaveChanged();
 
   // Re-generate files (into an empty list)
   RegenerateFiles();
 }
 
 void
 DataTransferItemList::ClearDataHelper(DataTransferItem* aItem,
                                       uint32_t aIndexHint,
                                       uint32_t aMozOffsetHint,
-                                      const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                                      nsIPrincipal& aSubjectPrincipal,
                                       ErrorResult& aRv)
 {
   MOZ_ASSERT(aItem);
   if (NS_WARN_IF(mDataTransfer->IsReadOnly())) {
     return;
   }
 
-  if (aItem->Principal() && aSubjectPrincipal.isSome() &&
-      !aSubjectPrincipal.value()->Subsumes(aItem->Principal())) {
+  if (aItem->Principal() && !aSubjectPrincipal.Subsumes(aItem->Principal())) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return;
   }
 
   // Check if the aIndexHint is actually the index, and then remove the item
   // from aItems
-  ErrorResult rv;
   bool found;
-  if (IndexedGetter(aIndexHint, found, rv) == aItem) {
+  if (IndexedGetter(aIndexHint, found) == aItem) {
     mItems.RemoveElementAt(aIndexHint);
   } else {
     mItems.RemoveElement(aItem);
   }
-  rv.SuppressException();
 
   // Check if the aMozIndexHint and aMozOffsetHint are actually the index and
   // offset, and then remove them from mIndexedItems
   MOZ_ASSERT(aItem->Index() < mIndexedItems.Length());
   nsTArray<RefPtr<DataTransferItem>>& items = mIndexedItems[aItem->Index()];
   if (aMozOffsetHint < items.Length() && aItem == items[aMozOffsetHint]) {
     items.RemoveElementAt(aMozOffsetHint);
   } else {
     items.RemoveElement(aItem);
   }
 
+  mDataTransfer->TypesListMayHaveChanged();
+
   // Check if we should remove the index. We never remove index 0.
   if (items.Length() == 0 && aItem->Index() != 0) {
     mIndexedItems.RemoveElementAt(aItem->Index());
 
     // Update the index of every element which has now been shifted
     for (uint32_t i = aItem->Index(); i < mIndexedItems.Length(); i++) {
       nsTArray<RefPtr<DataTransferItem>>& items = mIndexedItems[i];
       for (uint32_t j = 0; j < items.Length(); j++) {
@@ -559,25 +557,23 @@ DataTransferItemList::RegenerateFiles()
 void
 DataTransferItemList::GenerateFiles(FileList* aFiles,
                                     nsIPrincipal* aFilesPrincipal)
 {
   MOZ_ASSERT(aFiles);
   MOZ_ASSERT(aFilesPrincipal);
   uint32_t count = Length();
   for (uint32_t i = 0; i < count; i++) {
-    ErrorResult rv;
     bool found;
-    RefPtr<DataTransferItem> item = IndexedGetter(i, found, rv);
-    if (NS_WARN_IF(!found || rv.Failed())) {
-      continue;
-    }
+    RefPtr<DataTransferItem> item = IndexedGetter(i, found);
+    MOZ_ASSERT(found);
 
     if (item->Kind() == DataTransferItem::KIND_FILE) {
-      RefPtr<File> file = item->GetAsFile(Some(aFilesPrincipal), rv);
+      IgnoredErrorResult rv;
+      RefPtr<File> file = item->GetAsFile(*aFilesPrincipal, rv);
       if (NS_WARN_IF(rv.Failed() || !file)) {
         continue;
       }
       aFiles->Append(file);
     }
   }
 }
 
--- a/dom/events/DataTransferItemList.h
+++ b/dom/events/DataTransferItemList.h
@@ -46,76 +46,86 @@ public:
                                JS::Handle<JSObject*> aGivenProto) override;
 
   uint32_t Length() const
   {
     return mItems.Length();
   };
 
   DataTransferItem* Add(const nsAString& aData, const nsAString& aType,
-                        const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                        nsIPrincipal& aSubjectPrincipal,
                         ErrorResult& rv);
   DataTransferItem* Add(File& aData,
-                        const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                        nsIPrincipal& aSubjectPrincipal,
                         ErrorResult& aRv);
 
   void Remove(uint32_t aIndex,
-              const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+              nsIPrincipal& aSubjectPrincipal,
               ErrorResult& aRv);
 
-  DataTransferItem* IndexedGetter(uint32_t aIndex, bool& aFound,
-                                  ErrorResult& aRv) const;
+  DataTransferItem* IndexedGetter(uint32_t aIndex, bool& aFound) const;
 
   DataTransfer* GetParentObject() const
   {
     return mDataTransfer;
   }
 
-  void Clear(const Maybe<nsIPrincipal*>& aSubjectPrincipal, ErrorResult& aRv);
+  void Clear(nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv);
 
   already_AddRefed<DataTransferItem>
   SetDataWithPrincipal(const nsAString& aType, nsIVariant* aData,
                        uint32_t aIndex, nsIPrincipal* aPrincipal,
                        bool aInsertOnly, bool aHidden, ErrorResult& aRv);
 
   already_AddRefed<FileList> Files(nsIPrincipal* aPrincipal);
 
   // Moz-style helper methods for interacting with the stored data
   void MozRemoveByTypeAt(const nsAString& aType, uint32_t aIndex,
-                         const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                         nsIPrincipal& aSubjectPrincipal,
                          ErrorResult& aRv);
   DataTransferItem* MozItemByTypeAt(const nsAString& aType, uint32_t aIndex);
   const nsTArray<RefPtr<DataTransferItem>>* MozItemsAt(uint32_t aIndex);
   uint32_t MozItemCount() const;
 
   // Causes everything in indexes above 0 to shift down one index.
   void PopIndexZero();
 
   // Delete every item in the DataTransferItemList, without checking for
   // permissions or read-only status (for internal use only).
   void ClearAllItems();
 
 private:
   void ClearDataHelper(DataTransferItem* aItem, uint32_t aIndexHint,
                        uint32_t aMozOffsetHint,
-                       const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                       nsIPrincipal& aSubjectPrincipal,
                        ErrorResult& aRv);
   DataTransferItem* AppendNewItem(uint32_t aIndex, const nsAString& aType,
                                   nsIVariant* aData, nsIPrincipal* aPrincipal,
                                   bool aHidden);
   void RegenerateFiles();
   void GenerateFiles(FileList* aFiles, nsIPrincipal* aFilesPrincipal);
 
   ~DataTransferItemList() {}
 
   RefPtr<DataTransfer> mDataTransfer;
   bool mIsExternal;
   RefPtr<FileList> mFiles;
   // The principal for which mFiles is cached
   nsCOMPtr<nsIPrincipal> mFilesPrincipal;
+  // mItems is the list of items that corresponds to the spec concept of a
+  // DataTransferItemList.  That is, this is the thing the spec's indexed getter
+  // operates on.  The items in here are a subset of the items present in the
+  // arrays that live in mIndexedItems.
   nsTArray<RefPtr<DataTransferItem>> mItems;
+  // mIndexedItems represents all our items.  For any given index, all items at
+  // that index have different types in the GetType() sense.  That means that
+  // representing multiple items with the same type (e.g. multiple files)
+  // requires using multiple indices.
+  //
+  // There is always a (possibly empty) list of items at index 0, so
+  // mIndexedItems.Length() >= 1 at all times.
   nsTArray<nsTArray<RefPtr<DataTransferItem>>> mIndexedItems;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_DataTransferItemList_h
--- a/dom/events/test/test_dragstart.html
+++ b/dom/events/test/test_dragstart.html
@@ -113,17 +113,17 @@ function doDragStartSelection(event)
   is(event.pageX, 14, "dragstart pageX");
   is(event.pageY, 14, "dragstart pageY");
 
   var dt = event.dataTransfer;
   ok(dt instanceof DataTransfer, "dataTransfer is DataTransfer");
   gDataTransfer = dt;
 
   var types = dt.types;
-  is(types instanceof DOMStringList, true, "initial types is a DOMStringList");
+  ok(Array.isArray(types), "initial types is an Array");
   checkTypes(dt, ["text/_moz_htmlcontext", "text/_moz_htmlinfo", "text/html", "text/plain"], 0, "initial selection");
 
   is(dt.getData("text/plain"), "This is a draggable bit of text.", "initial selection text/plain");
   is(dt.getData("text/html"), "<div id=\"draggable\" ondragstart=\"doDragStartSelection(event)\">This is a <em>draggable</em> bit of text.</div>",
      "initial selection text/html");
 
   // text/unicode and Text are available for compatibility. They retrieve the
   // text/plain data
@@ -143,17 +143,17 @@ function doDragStartSelection(event)
   setTimeout(afterDragTests, 0);
 }
 
 function test_DataTransfer(dt)
 {
   is(dt.mozItemCount, 0, "empty itemCount");
 
   var types = dt.types;
-  is(types instanceof DOMStringList, true, "empty types is a DOMStringList");
+  ok(Array.isArray(types), "empty types is an Array");
   checkTypes(dt, [], 0, "empty");
   is(dt.getData("text/plain"), "", "empty data is empty");
 
   // calling setDataAt requires an index that is 0 <= index <= dt.itemCount
   expectError(() => dt.mozSetDataAt("text/plain", "Some Text", 1),
               "IndexSizeError", "setDataAt index too high");
 
   is(dt.mozUserCancelled, false, "userCancelled");
--- a/dom/html/HTMLIFrameElement.h
+++ b/dom/html/HTMLIFrameElement.h
@@ -163,17 +163,17 @@ public:
   {
     SetHTMLAttr(nsGkAtoms::referrerpolicy, aReferrer, aError);
   }
   void GetReferrerPolicy(nsAString& aReferrer)
   {
     GetEnumAttr(nsGkAtoms::referrerpolicy, EmptyCString().get(), aReferrer);
   }
   nsIDocument*
-  GetSVGDocument(const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal)
+  GetSVGDocument(nsIPrincipal& aSubjectPrincipal)
   {
     return GetContentDocument(aSubjectPrincipal);
   }
   bool Mozbrowser() const
   {
     return GetBoolAttr(nsGkAtoms::mozbrowser);
   }
   void SetMozbrowser(bool aAllow, ErrorResult& aError)
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -2836,21 +2836,19 @@ NS_IMETHODIMP
 HTMLInputElement::MozIsTextField(bool aExcludePassword, bool* aResult)
 {
   *aResult = MozIsTextField(aExcludePassword);
   return NS_OK;
 }
 
 void
 HTMLInputElement::SetUserInput(const nsAString& aInput,
-                               const mozilla::Maybe<nsIPrincipal*>& aPrincipal) {
-  MOZ_ASSERT(aPrincipal.isSome());
-
+                               nsIPrincipal& aSubjectPrincipal) {
   if (mType == NS_FORM_INPUT_FILE &&
-      !nsContentUtils::IsSystemPrincipal(aPrincipal.value())) {
+      !nsContentUtils::IsSystemPrincipal(&aSubjectPrincipal)) {
     return;
   }
 
   SetUserInput(aInput);
 }
 
 NS_IMETHODIMP
 HTMLInputElement::SetUserInput(const nsAString& aValue)
--- a/dom/html/HTMLInputElement.h
+++ b/dom/html/HTMLInputElement.h
@@ -816,17 +816,17 @@ public:
     return mNumberControlSpinnerIsSpinning && !mNumberControlSpinnerSpinsUp;
   }
 
   bool MozIsTextField(bool aExcludePassword);
 
   nsIEditor* GetEditor();
 
   void SetUserInput(const nsAString& aInput,
-                    const mozilla::Maybe<nsIPrincipal*>& aPrincipal);
+                    nsIPrincipal& aSubjectPrincipal);
 
   // XPCOM GetPhonetic() is OK
 
   /**
    * If aValue contains a valid floating-point number in the format specified
    * by the HTML 5 spec:
    *
    *   http://www.whatwg.org/specs/web-apps/current-work/multipage/common-microsyntaxes.html#floating-point-numbers
--- a/dom/html/HTMLObjectElement.cpp
+++ b/dom/html/HTMLObjectElement.cpp
@@ -461,23 +461,23 @@ HTMLObjectElement::TabIndexDefault()
 }
 
 NS_IMETHODIMP
 HTMLObjectElement::GetContentDocument(nsIDOMDocument **aContentDocument)
 {
   NS_ENSURE_ARG_POINTER(aContentDocument);
 
   nsCOMPtr<nsIDOMDocument> domDoc =
-    do_QueryInterface(GetContentDocument(Some(nsContentUtils::SubjectPrincipal())));
+    do_QueryInterface(GetContentDocument(*nsContentUtils::SubjectPrincipal()));
   domDoc.forget(aContentDocument);
   return NS_OK;
 }
 
 nsPIDOMWindowOuter*
-HTMLObjectElement::GetContentWindow(const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal)
+HTMLObjectElement::GetContentWindow(nsIPrincipal& aSubjectPrincipal)
 {
   nsIDocument* doc = GetContentDocument(aSubjectPrincipal);
   if (doc) {
     return doc->GetWindow();
   }
 
   return nullptr;
 }
--- a/dom/html/HTMLObjectElement.h
+++ b/dom/html/HTMLObjectElement.h
@@ -153,17 +153,17 @@ public:
   }
   void SetHeight(const nsAString& aValue, ErrorResult& aRv)
   {
     SetHTMLAttr(nsGkAtoms::height, aValue, aRv);
   }
   using nsObjectLoadingContent::GetContentDocument;
 
   nsPIDOMWindowOuter*
-  GetContentWindow(const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal);
+  GetContentWindow(nsIPrincipal& aSubjectPrincipal);
 
   using nsIConstraintValidation::CheckValidity;
   using nsIConstraintValidation::ReportValidity;
   using nsIConstraintValidation::GetValidationMessage;
   void GetAlign(DOMString& aValue)
   {
     GetHTMLAttr(nsGkAtoms::align, aValue);
   }
@@ -234,17 +234,17 @@ public:
     GetHTMLAttr(nsGkAtoms::border, aValue);
   }
   void SetBorder(const nsAString& aValue, ErrorResult& aRv)
   {
     SetHTMLAttr(nsGkAtoms::border, aValue, aRv);
   }
 
   nsIDocument*
-  GetSVGDocument(const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal)
+  GetSVGDocument(nsIPrincipal& aSubjectPrincipal)
   {
     return GetContentDocument(aSubjectPrincipal);
   }
 
 private:
   /**
    * Calls LoadObject with the correct arguments to start the plugin load.
    */
--- a/dom/html/HTMLSharedObjectElement.h
+++ b/dom/html/HTMLSharedObjectElement.h
@@ -183,17 +183,17 @@ public:
   {
     SetHTMLAttr(nsGkAtoms::type, aValue, aRv);
   }
   // width covered by <applet>
   // height covered by <applet>
   // align covered by <applet>
   // name covered by <applet>
   nsIDocument*
-  GetSVGDocument(const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal)
+  GetSVGDocument(nsIPrincipal& aSubjectPrincipal)
   {
     return GetContentDocument(aSubjectPrincipal);
   }
 
   /**
    * Calls LoadObject with the correct arguments to start the plugin load.
    */
   void StartObjectLoad(bool aNotify, bool aForceLoad);
--- a/dom/html/nsGenericHTMLFrameElement.cpp
+++ b/dom/html/nsGenericHTMLFrameElement.cpp
@@ -75,37 +75,36 @@ nsGenericHTMLFrameElement::~nsGenericHTM
   }
 }
 
 nsresult
 nsGenericHTMLFrameElement::GetContentDocument(nsIDOMDocument** aContentDocument)
 {
   NS_PRECONDITION(aContentDocument, "Null out param");
   nsCOMPtr<nsIDOMDocument> document =
-    do_QueryInterface(GetContentDocument(Some(nsContentUtils::SubjectPrincipal())));
+    do_QueryInterface(GetContentDocument(*nsContentUtils::SubjectPrincipal()));
   document.forget(aContentDocument);
   return NS_OK;
 }
 
 nsIDocument*
-nsGenericHTMLFrameElement::GetContentDocument(const Maybe<nsIPrincipal*>& aSubjectPrincipal)
+nsGenericHTMLFrameElement::GetContentDocument(nsIPrincipal& aSubjectPrincipal)
 {
   nsCOMPtr<nsPIDOMWindowOuter> win = GetContentWindow();
   if (!win) {
     return nullptr;
   }
 
   nsIDocument *doc = win->GetDoc();
   if (!doc) {
     return nullptr;
   }
 
   // Return null for cross-origin contentDocument.
-  if (!aSubjectPrincipal.value()
-        ->SubsumesConsideringDomain(doc->NodePrincipal())) {
+  if (!aSubjectPrincipal.SubsumesConsideringDomain(doc->NodePrincipal())) {
     return nullptr;
   }
   return doc;
 }
 
 already_AddRefed<nsPIDOMWindowOuter>
 nsGenericHTMLFrameElement::GetContentWindow()
 {
--- a/dom/html/nsGenericHTMLFrameElement.h
+++ b/dom/html/nsGenericHTMLFrameElement.h
@@ -97,18 +97,17 @@ public:
 
 protected:
   virtual ~nsGenericHTMLFrameElement();
 
   // This doesn't really ensure a frame loader in all cases, only when
   // it makes sense.
   void EnsureFrameLoader();
   nsresult LoadSrc();
-  nsIDocument*
-  GetContentDocument(const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal);
+  nsIDocument* GetContentDocument(nsIPrincipal& aSubjectPrincipal);
   nsresult GetContentDocument(nsIDOMDocument** aContentDocument);
   already_AddRefed<nsPIDOMWindowOuter> GetContentWindow();
 
   RefPtr<nsFrameLoader> mFrameLoader;
 
   /**
    * True when the element is created by the parser using the
    * NS_FROM_PARSER_NETWORK flag.
--- a/dom/html/nsHTMLDocument.cpp
+++ b/dom/html/nsHTMLDocument.cpp
@@ -2838,20 +2838,28 @@ nsHTMLDocument::SetDesignMode(const nsAS
   SetDesignMode(aDesignMode, nsContentUtils::GetCurrentJSContext()
                                ? Some(nsContentUtils::SubjectPrincipal())
                                : Nothing(), rv);
   return rv.StealNSResult();
 }
 
 void
 nsHTMLDocument::SetDesignMode(const nsAString& aDesignMode,
+                              nsIPrincipal& aSubjectPrincipal,
+                              ErrorResult& rv)
+{
+  SetDesignMode(aDesignMode, Some(&aSubjectPrincipal), rv);
+}
+
+void
+nsHTMLDocument::SetDesignMode(const nsAString& aDesignMode,
                               const Maybe<nsIPrincipal*>& aSubjectPrincipal,
                               ErrorResult& rv)
 {
-  if (!nsContentUtils::LegacyIsCallerNativeCode() &&
+  if (aSubjectPrincipal.isSome() &&
       !aSubjectPrincipal.value()->Subsumes(NodePrincipal())) {
     rv.Throw(NS_ERROR_DOM_PROP_ACCESS_DENIED);
     return;
   }
   bool editableMode = HasFlag(NODE_IS_EDITABLE);
   if (aDesignMode.LowerCaseEqualsASCII(editableMode ? "off" : "on")) {
     SetEditableFlag(!editableMode);
 
--- a/dom/html/nsHTMLDocument.h
+++ b/dom/html/nsHTMLDocument.h
@@ -206,21 +206,24 @@ public:
        bool aReplace,
        mozilla::ErrorResult& rv);
   void Close(mozilla::ErrorResult& rv);
   void Write(JSContext* cx, const mozilla::dom::Sequence<nsString>& aText,
              mozilla::ErrorResult& rv);
   void Writeln(JSContext* cx, const mozilla::dom::Sequence<nsString>& aText,
                mozilla::ErrorResult& rv);
   void GetDesignMode(nsAString& aDesignMode,
-                     const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal)
+                     nsIPrincipal& aSubjectPrincipal)
   {
     GetDesignMode(aDesignMode);
   }
   void SetDesignMode(const nsAString& aDesignMode,
+                     nsIPrincipal& aSubjectPrincipal,
+                     mozilla::ErrorResult& rv);
+  void SetDesignMode(const nsAString& aDesignMode,
                      const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
                      mozilla::ErrorResult& rv);
   bool ExecCommand(const nsAString& aCommandID, bool aDoShowUI,
                    const nsAString& aValue, mozilla::ErrorResult& rv);
   bool QueryCommandEnabled(const nsAString& aCommandID,
                            mozilla::ErrorResult& rv);
   bool QueryCommandIndeterm(const nsAString& aCommandID,
                             mozilla::ErrorResult& rv);
--- a/dom/interfaces/events/nsIDOMDataTransfer.idl
+++ b/dom/interfaces/events/nsIDOMDataTransfer.idl
@@ -62,29 +62,16 @@ interface nsIDOMDataTransfer : nsISuppor
   /**
    * Holds a list of all the local files available on this data transfer.
    * A dataTransfer containing no files will return an empty list, and an
    * invalid index access on the resulting file list will return null. 
    */
   readonly attribute nsIDOMFileList files;
 
   /**
-   * Holds a list of the format types of the data that is stored for the first
-   * item, in the same order the data was added. An empty list will be
-   * returned if no data was added.
-   */
-  readonly attribute nsISupports types;
-
-  /**
-   * Retrieves the data for a given format, or an empty string if data for
-   * that format does not exist or the data transfer contains no data.
-   */
-  DOMString getData(in DOMString format);
-
-  /**
    * Set the image to be used for dragging if a custom one is desired. Most of
    * the time, this would not be set, as a default image is created from the
    * node that was dragged.
    *
    * If the node is an HTML img element, an HTML canvas element or a XUL image
    * element, the image data is used. Otherwise, image should be a visible
    * node and the drag image will be created from this. If image is null, any
    * custom drag image is cleared and the default is used instead.
--- a/dom/ipc/ProcessHangMonitor.cpp
+++ b/dom/ipc/ProcessHangMonitor.cpp
@@ -302,17 +302,18 @@ HangMonitorChild::InterruptCallback()
     forcePaintEpoch = mForcePaintEpoch;
 
     mForcePaint = false;
   }
 
   if (forcePaint) {
     RefPtr<TabChild> tabChild = TabChild::FindTabChild(forcePaintTab);
     if (tabChild) {
-      JS::AutoAssertOnGC aaogc(mContext);
+      JS::AutoAssertOnGC nogc(mContext);
+      JS::AutoAssertOnBarrier nobarrier(mContext);
       tabChild->ForcePaint(forcePaintEpoch);
     }
   }
 }
 
 void
 HangMonitorChild::Shutdown()
 {
@@ -384,16 +385,17 @@ HangMonitorChild::RecvForcePaint(const T
   {
     MonitorAutoLock lock(mMonitor);
     mForcePaint = true;
     mForcePaintTab = aTabId;
     mForcePaintEpoch = aLayerObserverEpoch;
   }
 
   JS_RequestInterruptCallback(mContext);
+  JS::RequestGCInterruptCallback(mContext);
 
   return true;
 }
 
 void
 HangMonitorChild::Open(Transport* aTransport, ProcessId aPid,
                        MessageLoop* aIOLoop)
 {
@@ -1182,16 +1184,17 @@ mozilla::CreateHangMonitorParent(Content
 PProcessHangMonitorChild*
 mozilla::CreateHangMonitorChild(mozilla::ipc::Transport* aTransport,
                                 base::ProcessId aOtherPid)
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
 
   JSContext* cx = danger::GetJSContext();
   JS_AddInterruptCallback(cx, InterruptCallback);
+  JS::AddGCInterruptCallback(cx, InterruptCallback);
 
   ProcessHangMonitor* monitor = ProcessHangMonitor::GetOrCreate();
   HangMonitorChild* child = new HangMonitorChild(monitor);
 
   monitor->MonitorLoop()->PostTask(NewNonOwningRunnableMethod
                                    <mozilla::ipc::Transport*,
                                     base::ProcessId,
                                     MessageLoop*>(child,
new file mode 100644
--- /dev/null
+++ b/dom/media/test/crashtests/1223670.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+<script>
+
+function boom()
+{
+
+    var ac = new window.AudioContext("publicnotification");
+
+    setTimeout(function() {
+        document.documentElement.removeAttribute("class");
+        var htmlAudio = new Audio();
+        var stream = htmlAudio.mozCaptureStreamUntilEnded();
+        ac.createMediaStreamSource(stream);
+    }, 0);
+}
+
+</script>
+</head>
+<body onload="boom();">
+</body>
+</html>
--- a/dom/media/test/crashtests/crashtests.list
+++ b/dom/media/test/crashtests/crashtests.list
@@ -76,16 +76,17 @@ load 1041466.html
 load 1045650.html
 load 1080986.html
 load 1122218.html
 load 1127188.html
 load 1157994.html
 skip-if(!B2G) load 1158427.html
 load 1185176.html
 load 1185192.html
+load 1223670.html
 load 1228484.html
 load 1304948.html
 load analyser-channels-1.html
 load audiocontext-double-suspend.html
 load buffer-source-duration-1.html
 load buffer-source-ended-1.html
 load buffer-source-resampling-start-1.html
 load doppler-1.html
--- a/dom/storage/DOMStorage.cpp
+++ b/dom/storage/DOMStorage.cpp
@@ -62,69 +62,61 @@ DOMStorage::~DOMStorage()
 
 /* virtual */ JSObject*
 DOMStorage::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return StorageBinding::Wrap(aCx, this, aGivenProto);
 }
 
 uint32_t
-DOMStorage::GetLength(const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+DOMStorage::GetLength(nsIPrincipal& aSubjectPrincipal,
                       ErrorResult& aRv)
 {
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
-
-  if (!CanUseStorage(nullptr, aSubjectPrincipal, this)) {
+  if (!CanUseStorage(aSubjectPrincipal)) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return 0;
   }
 
   uint32_t length;
   aRv = mCache->GetLength(this, &length);
   return length;
 }
 
 void
 DOMStorage::Key(uint32_t aIndex, nsAString& aResult,
-                const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                nsIPrincipal& aSubjectPrincipal,
                 ErrorResult& aRv)
 {
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
-
-  if (!CanUseStorage(nullptr, aSubjectPrincipal, this)) {
+  if (!CanUseStorage(aSubjectPrincipal)) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return;
   }
 
   aRv = mCache->GetKey(this, aIndex, aResult);
 }
 
 void
 DOMStorage::GetItem(const nsAString& aKey, nsAString& aResult,
-                    const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                    nsIPrincipal& aSubjectPrincipal,
                     ErrorResult& aRv)
 {
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
-
-  if (!CanUseStorage(nullptr, aSubjectPrincipal, this)) {
+  if (!CanUseStorage(aSubjectPrincipal)) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return;
   }
 
   aRv = mCache->GetItem(this, aKey, aResult);
 }
 
 void
 DOMStorage::SetItem(const nsAString& aKey, const nsAString& aData,
-                    const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                    nsIPrincipal& aSubjectPrincipal,
                     ErrorResult& aRv)
 {
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
-
-  if (!CanUseStorage(nullptr, aSubjectPrincipal, this)) {
+  if (!CanUseStorage(aSubjectPrincipal)) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return;
   }
 
   nsString data;
   bool ok = data.Assign(aData, fallible);
   if (!ok) {
     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
@@ -139,44 +131,40 @@ DOMStorage::SetItem(const nsAString& aKe
 
   if (!aRv.ErrorCodeIs(NS_SUCCESS_DOM_NO_OPERATION)) {
     BroadcastChangeNotification(aKey, old, aData);
   }
 }
 
 void
 DOMStorage::RemoveItem(const nsAString& aKey,
-                       const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                       nsIPrincipal& aSubjectPrincipal,
                        ErrorResult& aRv)
 {
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
-
-  if (!CanUseStorage(nullptr, aSubjectPrincipal, this)) {
+  if (!CanUseStorage(aSubjectPrincipal)) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return;
   }
 
   nsAutoString old;
   aRv = mCache->RemoveItem(this, aKey, old);
   if (aRv.Failed()) {
     return;
   }
 
   if (!aRv.ErrorCodeIs(NS_SUCCESS_DOM_NO_OPERATION)) {
     BroadcastChangeNotification(aKey, old, NullString());
   }
 }
 
 void
-DOMStorage::Clear(const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+DOMStorage::Clear(nsIPrincipal& aSubjectPrincipal,
                   ErrorResult& aRv)
 {
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
-
-  if (!CanUseStorage(nullptr, aSubjectPrincipal, this)) {
+  if (!CanUseStorage(aSubjectPrincipal)) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return;
   }
 
   aRv = mCache->Clear(this);
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
@@ -240,47 +228,34 @@ DOMStorage::BroadcastChangeNotification(
                                   ? u"localStorage"
                                   : u"sessionStorage");
   NS_DispatchToMainThread(r);
 }
 
 static const char kPermissionType[] = "cookie";
 static const char kStorageEnabled[] = "dom.storage.enabled";
 
-// static, public
 bool
-DOMStorage::CanUseStorage(nsPIDOMWindowInner* aWindow,
-                          const Maybe<nsIPrincipal*>& aSubjectPrincipal,
-                          DOMStorage* aStorage)
+DOMStorage::CanUseStorage(nsIPrincipal& aSubjectPrincipal)
 {
   // This method is responsible for correct setting of mIsSessionOnly.
 
   if (!mozilla::Preferences::GetBool(kStorageEnabled)) {
     return false;
   }
 
-  nsContentUtils::StorageAccess access = nsContentUtils::StorageAccess::eDeny;
-  if (aWindow) {
-    access = nsContentUtils::StorageAllowedForWindow(aWindow);
-  } else if (aStorage) {
-    access = nsContentUtils::StorageAllowedForPrincipal(aStorage->mPrincipal);
-  }
+  nsContentUtils::StorageAccess access =
+    nsContentUtils::StorageAllowedForPrincipal(mPrincipal);
 
   if (access == nsContentUtils::StorageAccess::eDeny) {
     return false;
   }
 
-  if (aStorage) {
-    aStorage->mIsSessionOnly = access <= nsContentUtils::StorageAccess::eSessionScoped;
-
-    MOZ_ASSERT(aSubjectPrincipal.isSome());
-    return aStorage->CanAccess(aSubjectPrincipal.value());
-  }
-
-  return true;
+  mIsSessionOnly = access <= nsContentUtils::StorageAccess::eSessionScoped;
+  return CanAccess(&aSubjectPrincipal);
 }
 
 DOMStorage::StorageType
 DOMStorage::GetType() const
 {
   return mManager->Type();
 }
 
@@ -315,18 +290,17 @@ bool
 DOMStorage::CanAccess(nsIPrincipal* aPrincipal)
 {
   return !aPrincipal || aPrincipal->Subsumes(mPrincipal);
 }
 
 void
 DOMStorage::GetSupportedNames(nsTArray<nsString>& aKeys)
 {
-  if (!CanUseStorage(nullptr, Some(nsContentUtils::SubjectPrincipal()),
-                     this)) {
+  if (!CanUseStorage(*nsContentUtils::SubjectPrincipal())) {
     // return just an empty array
     aKeys.Clear();
     return;
   }
 
   mCache->GetKeys(this, aKeys);
 }
 
--- a/dom/storage/DOMStorage.h
+++ b/dom/storage/DOMStorage.h
@@ -65,84 +65,83 @@ public:
   // WebIDL
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   nsPIDOMWindowInner* GetParentObject() const
   {
     return mWindow;
   }
 
-  uint32_t GetLength(const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+  uint32_t GetLength(nsIPrincipal& aSubjectPrincipal,
                      ErrorResult& aRv);
 
   void Key(uint32_t aIndex, nsAString& aResult,
-           const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+           nsIPrincipal& aSubjectPrincipal,
            ErrorResult& aRv);
 
   void GetItem(const nsAString& aKey, nsAString& aResult,
-               const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+               nsIPrincipal& aSubjectPrincipal,
                ErrorResult& aRv);
 
   void GetSupportedNames(nsTArray<nsString>& aKeys);
 
   void NamedGetter(const nsAString& aKey, bool& aFound, nsAString& aResult,
-                   const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                   nsIPrincipal& aSubjectPrincipal,
                    ErrorResult& aRv)
   {
     GetItem(aKey, aResult, aSubjectPrincipal, aRv);
     aFound = !aResult.IsVoid();
   }
 
   void SetItem(const nsAString& aKey, const nsAString& aValue,
-               const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+               nsIPrincipal& aSubjectPrincipal,
                ErrorResult& aRv);
 
   void NamedSetter(const nsAString& aKey, const nsAString& aValue,
-                   const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                   nsIPrincipal& aSubjectPrincipal,
                    ErrorResult& aRv)
   {
     SetItem(aKey, aValue, aSubjectPrincipal, aRv);
   }
 
   void RemoveItem(const nsAString& aKey,
-                  const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                  nsIPrincipal& aSubjectPrincipal,
                   ErrorResult& aRv);
 
   void NamedDeleter(const nsAString& aKey, bool& aFound,
-                    const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                    nsIPrincipal& aSubjectPrincipal,
                     ErrorResult& aRv)
   {
     RemoveItem(aKey, aSubjectPrincipal, aRv);
 
     aFound = !aRv.ErrorCodeIs(NS_SUCCESS_DOM_NO_OPERATION);
   }
 
-  void Clear(const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+  void Clear(nsIPrincipal& aSubjectPrincipal,
              ErrorResult& aRv);
 
-  // The method checks whether the caller can use a storage.
-  // CanUseStorage is called before any DOM initiated operation
-  // on a storage is about to happen and ensures that the storage's
-  // session-only flag is properly set according the current settings.
-  // It is an optimization since the privileges check and session only
-  // state determination are complex and share the code (comes hand in
-  // hand together).
-  static bool CanUseStorage(nsPIDOMWindowInner* aWindow,
-                            const Maybe<nsIPrincipal*>& aSubjectPrincipal,
-                            DOMStorage* aStorage = nullptr);
-
   bool IsPrivate() const;
   bool IsSessionOnly() const { return mIsSessionOnly; }
 
   bool IsForkOf(const DOMStorage* aOther) const
   {
     MOZ_ASSERT(aOther);
     return mCache == aOther->mCache;
   }
 
+protected:
+  // The method checks whether the caller can use a storage.
+  // CanUseStorage is called before any DOM initiated operation
+  // on a storage is about to happen and ensures that the storage's
+  // session-only flag is properly set according the current settings.
+  // It is an optimization since the privileges check and session only
+  // state determination are complex and share the code (comes hand in
+  // hand together).
+  bool CanUseStorage(nsIPrincipal& aSubjectPrincipal);
+
 private:
   ~DOMStorage();
 
   friend class DOMStorageManager;
   friend class DOMStorageCache;
 
   nsCOMPtr<nsPIDOMWindowInner> mWindow;
   RefPtr<DOMStorageManager> mManager;
--- a/dom/webidl/DataTransfer.webidl
+++ b/dom/webidl/DataTransfer.webidl
@@ -12,18 +12,18 @@ interface DataTransfer {
            attribute DOMString dropEffect;
            attribute DOMString effectAllowed;
 
   readonly attribute DataTransferItemList items;
 
   [Throws]
   void setDragImage(Element image, long x, long y);
 
-  [Throws]
-  readonly attribute DOMStringList types;
+  [Pure, Cached, Frozen, NeedsSubjectPrincipal]
+  readonly attribute sequence<DOMString> types;
   [Throws, NeedsSubjectPrincipal]
   DOMString getData(DOMString format);
   [Throws, NeedsSubjectPrincipal]
   void setData(DOMString format, DOMString data);
   [Throws, NeedsSubjectPrincipal]
   void clearData(optional DOMString format);
   [Throws, NeedsSubjectPrincipal]
   readonly attribute FileList? files;
--- a/dom/webidl/DataTransferItemList.webidl
+++ b/dom/webidl/DataTransferItemList.webidl
@@ -4,17 +4,16 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  *
  * The origin of this IDL file is:
  * https://html.spec.whatwg.org/multipage/interaction.html#the-datatransferitemlist-interface
  */
 
 interface DataTransferItemList {
   readonly attribute unsigned long length;
-  [Throws]
   getter DataTransferItem (unsigned long index);
   [Throws, NeedsSubjectPrincipal]
   DataTransferItem? add(DOMString data, DOMString type);
   [Throws, NeedsSubjectPrincipal]
   DataTransferItem? add(File data);
   [Throws, NeedsSubjectPrincipal]
   void remove(unsigned long index);
   [Throws, NeedsSubjectPrincipal]
--- a/dom/webidl/Window.webidl
+++ b/dom/webidl/Window.webidl
@@ -104,17 +104,17 @@ interface WindowSessionStorage {
   //[Throws] readonly attribute Storage sessionStorage;
   [Throws] readonly attribute Storage? sessionStorage;
 };
 Window implements WindowSessionStorage;
 
 // http://www.whatwg.org/specs/web-apps/current-work/
 [NoInterfaceObject]
 interface WindowLocalStorage {
-  [Throws, NeedsSubjectPrincipal] readonly attribute Storage? localStorage;
+  [Throws] readonly attribute Storage? localStorage;
 };
 Window implements WindowLocalStorage;
 
 // http://www.whatwg.org/specs/web-apps/current-work/
 partial interface Window {
   void captureEvents();
   void releaseEvents();
 };
--- a/editor/libeditor/EditorEventListener.cpp
+++ b/editor/libeditor/EditorEventListener.cpp
@@ -929,29 +929,26 @@ EditorEventListener::CanDrop(nsIDOMDragE
     return false;
   }
 
   nsCOMPtr<nsIDOMDataTransfer> domDataTransfer;
   aEvent->GetDataTransfer(getter_AddRefs(domDataTransfer));
   nsCOMPtr<DataTransfer> dataTransfer = do_QueryInterface(domDataTransfer);
   NS_ENSURE_TRUE(dataTransfer, false);
 
-  ErrorResult err;
-  RefPtr<DOMStringList> types = dataTransfer->GetTypes(err);
-  if (NS_WARN_IF(err.Failed())) {
-    return false;
-  }
+  nsTArray<nsString> types;
+  dataTransfer->GetTypes(types, *nsContentUtils::GetSystemPrincipal());
 
   // Plaintext editors only support dropping text. Otherwise, HTML and files
   // can be dropped as well.
-  if (!types->Contains(NS_LITERAL_STRING(kTextMime)) &&
-      !types->Contains(NS_LITERAL_STRING(kMozTextInternal)) &&
+  if (!types.Contains(NS_LITERAL_STRING(kTextMime)) &&
+      !types.Contains(NS_LITERAL_STRING(kMozTextInternal)) &&
       (mEditorBase->IsPlaintextEditor() ||
-       (!types->Contains(NS_LITERAL_STRING(kHTMLMime)) &&
-        !types->Contains(NS_LITERAL_STRING(kFileMime))))) {
+       (!types.Contains(NS_LITERAL_STRING(kHTMLMime)) &&
+        !types.Contains(NS_LITERAL_STRING(kFileMime))))) {
     return false;
   }
 
   // If there is no source node, this is probably an external drag and the
   // drop is allowed. The later checks rely on checking if the drag target
   // is the same as the drag source.
   nsCOMPtr<nsIDOMNode> sourceNode;
   dataTransfer->GetMozSourceNode(getter_AddRefs(sourceNode));
--- a/js/public/GCAPI.h
+++ b/js/public/GCAPI.h
@@ -479,39 +479,40 @@ IsGenerationalGCEnabled(JSRuntime* rt);
  * Returns the GC's "number". This does not correspond directly to the number
  * of GCs that have been run, but is guaranteed to be monotonically increasing
  * with GC activity.
  */
 extern JS_PUBLIC_API(size_t)
 GetGCNumber();
 
 /**
- * Assert if a GC occurs while this class is live. This class does not disable
- * the static rooting hazard analysis.
+ * Pass a subclass of this "abstract" class to callees to require that they
+ * never GC. Subclasses can use assertions or the hazard analysis to ensure no
+ * GC happens.
  */
-class JS_PUBLIC_API(AutoAssertOnGC)
+class JS_PUBLIC_API(AutoRequireNoGC)
 {
-#ifdef DEBUG
+  protected:
+    AutoRequireNoGC() {}
+    ~AutoRequireNoGC() {}
+};
+
+/**
+ * Release assert if a GC occurs while this class is live. This class does
+ * not disable the static rooting hazard analysis.
+ */
+class JS_PUBLIC_API(AutoAssertOnGC) : public AutoRequireNoGC
+{
     js::gc::GCRuntime* gc;
     size_t gcNumber;
 
   public:
     AutoAssertOnGC();
     explicit AutoAssertOnGC(JSContext* cx);
     ~AutoAssertOnGC();
-
-    static void VerifyIsSafeToGC(JSRuntime* rt);
-#else
-  public:
-    AutoAssertOnGC() {}
-    explicit AutoAssertOnGC(JSContext* cx) {}
-    ~AutoAssertOnGC() {}
-
-    static void VerifyIsSafeToGC(JSRuntime* rt) {}
-#endif
 };
 
 /**
  * Assert if an allocation of a GC thing occurs while this class is live. This
  * class does not disable the static rooting hazard analysis.
  */
 class JS_PUBLIC_API(AutoAssertNoAlloc)
 {
@@ -527,16 +528,30 @@ class JS_PUBLIC_API(AutoAssertNoAlloc)
   public:
     AutoAssertNoAlloc() {}
     explicit AutoAssertNoAlloc(JSContext* cx) {}
     void disallowAlloc(JSRuntime* rt) {}
 #endif
 };
 
 /**
+ * Assert if a GC barrier is invoked while this class is live. This class does
+ * not disable the static rooting hazard analysis.
+ */
+class JS_PUBLIC_API(AutoAssertOnBarrier)
+{
+    JSContext* context;
+    bool prev;
+
+  public:
+    explicit AutoAssertOnBarrier(JSContext* cx);
+    ~AutoAssertOnBarrier();
+};
+
+/**
  * Disable the static rooting hazard analysis in the live region and assert if
  * any allocation that could potentially trigger a GC occurs while this guard
  * object is live. This is most useful to help the exact rooting hazard analysis
  * in complex regions, since it cannot understand dataflow.
  *
  * Note: GC behavior is unpredictable even when deterministic and is generally
  *       non-deterministic in practice. The fact that this guard has not
  *       asserted is not a guarantee that a GC cannot happen in the guarded
@@ -570,23 +585,34 @@ class JS_PUBLIC_API(AutoAssertGCCallback
  * Place AutoCheckCannotGC in scopes that you believe can never GC. These
  * annotations will be verified both dynamically via AutoAssertOnGC, and
  * statically with the rooting hazard analysis (implemented by making the
  * analysis consider AutoCheckCannotGC to be a GC pointer, and therefore
  * complain if it is live across a GC call.) It is useful when dealing with
  * internal pointers to GC things where the GC thing itself may not be present
  * for the static analysis: e.g. acquiring inline chars from a JSString* on the
  * heap.
+ *
+ * We only do the assertion checking in DEBUG builds.
  */
+#ifdef DEBUG
 class JS_PUBLIC_API(AutoCheckCannotGC) : public AutoAssertOnGC
 {
   public:
     AutoCheckCannotGC() : AutoAssertOnGC() {}
     explicit AutoCheckCannotGC(JSContext* cx) : AutoAssertOnGC(cx) {}
 } JS_HAZ_GC_INVALIDATED;
+#else
+class JS_PUBLIC_API(AutoCheckCannotGC) : public AutoRequireNoGC
+{
+  public:
+    AutoCheckCannotGC() {}
+    explicit AutoCheckCannotGC(JSContext* cx) {}
+} JS_HAZ_GC_INVALIDATED;
+#endif
 
 /**
  * Unsets the gray bit for anything reachable from |thing|. |kind| should not be
  * JS::TraceKind::Shape. |thing| should be non-null. The return value indicates
  * if anything was unmarked.
  */
 extern JS_FRIEND_API(bool)
 UnmarkGrayGCThingRecursively(GCCellPtr thing);
@@ -604,26 +630,28 @@ ExposeGCThingToActiveJS(JS::GCCellPtr th
     /*
      * GC things residing in the nursery cannot be gray: they have no mark bits.
      * All live objects in the nursery are moved to tenured at the beginning of
      * each GC slice, so the gray marker never sees nursery things.
      */
     if (IsInsideNursery(thing.asCell()))
         return;
     JS::shadow::Runtime* rt = detail::GetGCThingRuntime(thing.unsafeAsUIntPtr());
+    MOZ_DIAGNOSTIC_ASSERT(rt->allowGCBarriers());
     if (IsIncrementalBarrierNeededOnTenuredGCThing(rt, thing))
         JS::IncrementalReferenceBarrier(thing);
     else if (JS::GCThingIsMarkedGray(thing))
         JS::UnmarkGrayGCThingRecursively(thing);
 }
 
 static MOZ_ALWAYS_INLINE void
 MarkGCThingAsLive(JSRuntime* aRt, JS::GCCellPtr thing)
 {
     JS::shadow::Runtime* rt = JS::shadow::Runtime::asShadowRuntime(aRt);
+    MOZ_DIAGNOSTIC_ASSERT(rt->allowGCBarriers());
     /*
      * Any object in the nursery will not be freed during any GC running at that time.
      */
     if (IsInsideNursery(thing.asCell()))
         return;
     if (IsIncrementalBarrierNeededOnTenuredGCThing(rt, thing))
         JS::IncrementalReferenceBarrier(thing);
 }
@@ -671,11 +699,23 @@ extern JS_FRIEND_API(void)
 PokeGC(JSContext* cx);
 
 /*
  * Internal to Firefox.
  */
 extern JS_FRIEND_API(void)
 NotifyDidPaint(JSContext* cx);
 
+// GC Interrupt callbacks are run during GC. You should not run JS code or use
+// the JS engine at all while the callback is running. Otherwise they resemble
+// normal JS interrupt callbacks.
+typedef bool
+(* GCInterruptCallback)(JSContext* cx);
+
+extern JS_FRIEND_API(bool)
+AddGCInterruptCallback(JSContext* cx, GCInterruptCallback callback);
+
+extern JS_FRIEND_API(void)
+RequestGCInterruptCallback(JSContext* cx);
+
 } /* namespace JS */
 
 #endif /* js_GCAPI_h */
--- a/js/public/SliceBudget.h
+++ b/js/public/SliceBudget.h
@@ -2,16 +2,18 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * 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/. */
 
 #ifndef js_SliceBudget_h
 #define js_SliceBudget_h
 
+#include "mozilla/Atomics.h"
+
 #include <stdint.h>
 
 namespace js {
 
 struct JS_PUBLIC_API(TimeBudget)
 {
     int64_t budget;
 
@@ -31,57 +33,82 @@ struct JS_PUBLIC_API(WorkBudget)
  * to run for unlimited time, and others are bounded. To reduce the number of
  * gettimeofday calls, we only check the time every 1000 operations.
  */
 class JS_PUBLIC_API(SliceBudget)
 {
     static const int64_t unlimitedDeadline = INT64_MAX;
     static const intptr_t unlimitedStartCounter = INTPTR_MAX;
 
-    bool checkOverBudget();
+    bool checkOverBudget(JSContext* maybeCx);
 
     SliceBudget();
 
   public:
     // Memory of the originally requested budget. If isUnlimited, neither of
     // these are in use. If deadline==0, then workBudget is valid. Otherwise
     // timeBudget is valid.
     TimeBudget timeBudget;
     WorkBudget workBudget;
 
     int64_t deadline; /* in microseconds */
-    intptr_t counter;
+    mozilla::Atomic<intptr_t, mozilla::Relaxed> counter;
 
     static const intptr_t CounterReset = 1000;
 
     static const int64_t UnlimitedTimeBudget = -1;
     static const int64_t UnlimitedWorkBudget = -1;
 
     /* Use to create an unlimited budget. */
     static SliceBudget unlimited() { return SliceBudget(); }
 
     /* Instantiate as SliceBudget(TimeBudget(n)). */
     explicit SliceBudget(TimeBudget time);
 
     /* Instantiate as SliceBudget(WorkBudget(n)). */
     explicit SliceBudget(WorkBudget work);
 
+    // Need an explicit copy constructor because Atomic fails to provide one.
+    SliceBudget(const SliceBudget& other)
+        : timeBudget(other.timeBudget),
+          workBudget(other.workBudget),
+          deadline(other.deadline),
+          counter(other.counter)
+    {}
+
+    // Need an explicit operator= because Atomic fails to provide one.
+    SliceBudget& operator=(const SliceBudget& other) {
+        timeBudget = other.timeBudget;
+        workBudget = other.workBudget;
+        deadline = other.deadline;
+        counter = intptr_t(other.counter);
+        return *this;
+    }
+
     void makeUnlimited() {
         deadline = unlimitedDeadline;
         counter = unlimitedStartCounter;
     }
 
+    // Request that checkOverBudget be called the next time isOverBudget is
+    // called.
+    void requestFullCheck() {
+        counter = 0;
+    }
+
     void step(intptr_t amt = 1) {
         counter -= amt;
     }
 
-    bool isOverBudget() {
+    // Only need to pass maybeCx if the GC interrupt callback should be checked
+    // (and possibly invoked).
+    bool isOverBudget(JSContext* maybeCx = nullptr) {
         if (counter > 0)
             return false;
-        return checkOverBudget();
+        return checkOverBudget(maybeCx);
     }
 
     bool isWorkBudget() const { return deadline == 0; }
     bool isTimeBudget() const { return deadline > 0 && !isUnlimited(); }
     bool isUnlimited() const { return deadline == unlimitedDeadline; }
 
     int describe(char* buffer, size_t maxlen) const;
 };
--- a/js/src/builtin/SIMD.cpp
+++ b/js/src/builtin/SIMD.cpp
@@ -185,17 +185,17 @@ template bool js::ToSimdConstant<Int16x8
 template bool js::ToSimdConstant<Int32x4>(JSContext* cx, HandleValue v, jit::SimdConstant* out);
 template bool js::ToSimdConstant<Float32x4>(JSContext* cx, HandleValue v, jit::SimdConstant* out);
 template bool js::ToSimdConstant<Bool8x16>(JSContext* cx, HandleValue v, jit::SimdConstant* out);
 template bool js::ToSimdConstant<Bool16x8>(JSContext* cx, HandleValue v, jit::SimdConstant* out);
 template bool js::ToSimdConstant<Bool32x4>(JSContext* cx, HandleValue v, jit::SimdConstant* out);
 
 template<typename Elem>
 static Elem
-TypedObjectMemory(HandleValue v, const JS::AutoAssertOnGC& nogc)
+TypedObjectMemory(HandleValue v, const JS::AutoRequireNoGC& nogc)
 {
     TypedObject& obj = v.toObject().as<TypedObject>();
     return reinterpret_cast<Elem>(obj.typedMem(nogc));
 }
 
 static const ClassOps SimdTypeDescrClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
--- a/js/src/builtin/TypedObject.h
+++ b/js/src/builtin/TypedObject.h
@@ -544,24 +544,24 @@ class TypedObject : public ShapedObject
     }
 
     TypeDescr& typeDescr() const {
         return group()->typeDescr();
     }
 
     int32_t offset() const;
     int32_t length() const;
-    uint8_t* typedMem(const JS::AutoAssertOnGC&) const { return typedMem(); }
+    uint8_t* typedMem(const JS::AutoRequireNoGC&) const { return typedMem(); }
     bool isAttached() const;
 
     int32_t size() const {
         return typeDescr().size();
     }
 
-    uint8_t* typedMem(size_t offset, const JS::AutoAssertOnGC& nogc) const {
+    uint8_t* typedMem(size_t offset, const JS::AutoRequireNoGC& nogc) const {
         // It seems a bit surprising that one might request an offset
         // == size(), but it can happen when taking the "address of" a
         // 0-sized value. (In other words, we maintain the invariant
         // that `offset + size <= size()` -- this is always checked in
         // the caller's side.)
         MOZ_ASSERT(offset <= (size_t) size());
         return typedMem(nogc) + offset;
     }
@@ -699,17 +699,17 @@ class InlineTypedObject : public TypedOb
 
     static gc::AllocKind allocKindForTypeDescriptor(TypeDescr* descr) {
         size_t nbytes = descr->size();
         MOZ_ASSERT(nbytes <= MaximumSize);
 
         return gc::GetGCObjectKindForBytes(nbytes + sizeof(TypedObject));
     }
 
-    uint8_t* inlineTypedMem(const JS::AutoAssertOnGC&) const {
+    uint8_t* inlineTypedMem(const JS::AutoRequireNoGC&) const {
         return inlineTypedMem();
     }
 
     uint8_t* inlineTypedMemForGC() const {
         return inlineTypedMem();
     }
 
     static void obj_trace(JSTracer* trace, JSObject* object);
--- a/js/src/gc/Allocator.cpp
+++ b/js/src/gc/Allocator.cpp
@@ -198,17 +198,17 @@ GCRuntime::checkAllocatorState(JSContext
                   kind == AllocKind::JITCODE ||
                   kind == AllocKind::SCOPE);
     MOZ_ASSERT(!rt->isHeapBusy());
     MOZ_ASSERT(isAllocAllowed());
 #endif
 
     // Crash if we perform a GC action when it is not safe.
     if (allowGC && !rt->mainThread.suppressGC)
-        JS::AutoAssertOnGC::VerifyIsSafeToGC(rt);
+        rt->gc.verifyIsSafeToGC();
 
     // For testing out of memory conditions
     if (js::oom::ShouldFailWithOOM()) {
         // If we are doing a fallible allocation, percolate up the OOM
         // instead of reporting it.
         if (allowGC)
             ReportOutOfMemory(cx);
         return false;
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -19,16 +19,17 @@
 #include "gc/StoreBuffer.h"
 #include "gc/Tracer.h"
 #include "js/GCAnnotations.h"
 
 namespace js {
 
 class AutoLockGC;
 class AutoLockHelperThreadState;
+class SliceBudget;
 class VerifyPreTracer;
 
 namespace gc {
 
 typedef Vector<JS::Zone*, 4, SystemAllocPolicy> ZoneVector;
 using BlackGrayEdgeVector = Vector<TenuredCell*, 0, SystemAllocPolicy>;
 
 class AutoMaybeStartBackgroundAllocation;
@@ -716,30 +717,35 @@ class GCRuntime
 
     bool isNurseryAllocAllowed() { return noNurseryAllocationCheck == 0; }
     void disallowNurseryAlloc() { ++noNurseryAllocationCheck; }
     void allowNurseryAlloc() {
         MOZ_ASSERT(!isNurseryAllocAllowed());
         --noNurseryAllocationCheck;
     }
 
+    bool isStrictProxyCheckingEnabled() { return disableStrictProxyCheckingCount == 0; }
+    void disableStrictProxyChecking() { ++disableStrictProxyCheckingCount; }
+    void enableStrictProxyChecking() {
+        MOZ_ASSERT(disableStrictProxyCheckingCount > 0);
+        --disableStrictProxyCheckingCount;
+    }
+#endif // DEBUG
+
     bool isInsideUnsafeRegion() { return inUnsafeRegion != 0; }
     void enterUnsafeRegion() { ++inUnsafeRegion; }
     void leaveUnsafeRegion() {
         MOZ_ASSERT(inUnsafeRegion > 0);
         --inUnsafeRegion;
     }
 
-    bool isStrictProxyCheckingEnabled() { return disableStrictProxyCheckingCount == 0; }
-    void disableStrictProxyChecking() { ++disableStrictProxyCheckingCount; }
-    void enableStrictProxyChecking() {
-        MOZ_ASSERT(disableStrictProxyCheckingCount > 0);
-        --disableStrictProxyCheckingCount;
+    void verifyIsSafeToGC() {
+        MOZ_DIAGNOSTIC_ASSERT(!isInsideUnsafeRegion(),
+                              "[AutoAssertOnGC] possible GC in GC-unsafe region");
     }
-#endif // DEBUG
 
     void setAlwaysPreserveCode() { alwaysPreserveCode = true; }
 
     bool isIncrementalGCAllowed() const { return incrementalAllowed; }
     void disallowIncrementalGC() { incrementalAllowed = false; }
 
     bool isIncrementalGCEnabled() const { return mode == JSGC_MODE_INCREMENTAL && incrementalAllowed; }
     bool isIncrementalGCInProgress() const { return state() != State::NotActive; }
@@ -853,16 +859,29 @@ class GCRuntime
     void startVerifyPreBarriers();
     void endVerifyPreBarriers();
     void finishVerifier();
     bool isVerifyPreBarriersEnabled() const { return !!verifyPreData; }
 #else
     bool isVerifyPreBarriersEnabled() const { return false; }
 #endif
 
+    // GC interrupt callbacks.
+    bool addInterruptCallback(JS::GCInterruptCallback callback);
+    void requestInterruptCallback();
+
+    bool checkInterruptCallback(JSContext* cx) {
+        if (interruptCallbackRequested) {
+            invokeInterruptCallback(cx);
+            return true;
+        }
+        return false;
+    }
+    void invokeInterruptCallback(JSContext* cx);
+
     // Free certain LifoAlloc blocks when it is safe to do so.
     void freeUnusedLifoBlocksAfterSweeping(LifoAlloc* lifo);
     void freeAllLifoBlocksAfterSweeping(LifoAlloc* lifo);
     void freeAllLifoBlocksAfterMinorGC(LifoAlloc* lifo);
 
     // Queue a thunk to run after the next minor GC.
     void callAfterMinorGC(void (*thunk)(void* data), void* data) {
         nursery.queueSweepAction(thunk, data);
@@ -1065,16 +1084,23 @@ class GCRuntime
     mozilla::Atomic<uint64_t, mozilla::ReleaseAcquire> nextCellUniqueId_;
 
     /*
      * Number of the committed arenas in all GC chunks including empty chunks.
      */
     mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> numArenasFreeCommitted;
     VerifyPreTracer* verifyPreData;
 
+    // GC interrupt callbacks.
+    using GCInterruptCallbackVector = js::Vector<JS::GCInterruptCallback, 2, js::SystemAllocPolicy>;
+    GCInterruptCallbackVector interruptCallbacks;
+
+    mozilla::Atomic<bool, mozilla::Relaxed> interruptCallbackRequested;
+    SliceBudget* currentBudget;
+
   private:
     bool chunkAllocationSinceLastGC;
     int64_t lastGCTime;
 
     JSGCMode mode;
 
     mozilla::Atomic<size_t, mozilla::ReleaseAcquire> numActiveZoneIters;
 
@@ -1339,25 +1365,25 @@ class GCRuntime
      * collector.
      */
     CallbackVector<JSTraceDataOp> blackRootTracers;
     Callback<JSTraceDataOp> grayRootTracer;
 
     /* Always preserve JIT code during GCs, for testing. */
     bool alwaysPreserveCode;
 
-#ifdef DEBUG
     /*
      * Some regions of code are hard for the static rooting hazard analysis to
      * understand. In those cases, we trade the static analysis for a dynamic
      * analysis. When this is non-zero, we should assert if we trigger, or
      * might trigger, a GC.
      */
     int inUnsafeRegion;
 
+#ifdef DEBUG
     size_t noGCOrAllocationCheck;
     size_t noNurseryAllocationCheck;
 
     bool arenasEmptyAtShutdown;
 #endif
 
     /* Synchronize GC heap access between main thread and GCHelperState. */
     friend class js::AutoLockGC;
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -1540,23 +1540,25 @@ bool
 GCMarker::drainMarkStack(SliceBudget& budget)
 {
 #ifdef DEBUG
     MOZ_ASSERT(!strictCompartmentChecking);
     strictCompartmentChecking = true;
     auto acc = mozilla::MakeScopeExit([&] {strictCompartmentChecking = false;});
 #endif
 
-    if (budget.isOverBudget())
+    JSContext* cx = runtime()->contextFromMainThread();
+
+    if (budget.isOverBudget(cx))
         return false;
 
     for (;;) {
         while (!stack.isEmpty()) {
             processMarkStackTop(budget);
-            if (budget.isOverBudget()) {
+            if (budget.isOverBudget(cx)) {
                 saveValueRanges();
                 return false;
             }
         }
 
         if (!hasDelayedChildren())
             break;
 
@@ -1621,16 +1623,18 @@ GCMarker::processMarkStackTop(SliceBudge
     HeapSlot* end;
     JSObject* obj;
 
     // Decode
     uintptr_t addr = stack.pop();
     uintptr_t tag = addr & StackTagMask;
     addr &= ~StackTagMask;
 
+    JSContext* cx = runtime()->contextFromMainThread();
+
     // Dispatch
     switch (tag) {
       case ValueArrayTag: {
         JS_STATIC_ASSERT(ValueArrayTag == 0);
         MOZ_ASSERT(!(addr & CellMask));
         obj = reinterpret_cast<JSObject*>(addr);
         uintptr_t addr2 = stack.pop();
         uintptr_t addr3 = stack.pop();
@@ -1674,17 +1678,17 @@ GCMarker::processMarkStackTop(SliceBudge
       default: MOZ_CRASH("Invalid tag in mark stack");
     }
     return;
 
   scan_value_array:
     MOZ_ASSERT(vp <= end);
     while (vp != end) {
         budget.step();
-        if (budget.isOverBudget()) {
+        if (budget.isOverBudget(cx)) {
             pushValueArray(obj, vp, end);
             return;
         }
 
         const Value& v = *vp++;
         if (v.isString()) {
             traverseEdge(obj, v.toString());
         } else if (v.isObject()) {
@@ -1704,17 +1708,17 @@ GCMarker::processMarkStackTop(SliceBudge
     }
     return;
 
   scan_obj:
     {
         AssertZoneIsMarking(obj);
 
         budget.step();
-        if (budget.isOverBudget()) {
+        if (budget.isOverBudget(cx)) {
             repush(obj);
             return;
         }
 
         markImplicitEdges(obj);
         ObjectGroup* group = obj->groupFromGC();
         traverseEdge(obj, group);
 
@@ -2152,17 +2156,17 @@ GCMarker::markDelayedChildren(SliceBudge
         unmarkedArenaStackTop = arena->getNextDelayedMarking();
         arena->unsetDelayedMarking();
 #ifdef DEBUG
         markLaterArenas--;
 #endif
         markDelayedChildren(arena);
 
         budget.step(150);
-        if (budget.isOverBudget())
+        if (budget.isOverBudget(runtime()->contextFromMainThread()))
             return false;
     } while (unmarkedArenaStackTop);
     MOZ_ASSERT(!markLaterArenas);
 
     return true;
 }
 
 template<typename T>
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -183,16 +183,17 @@
 
 #include "jsgcinlines.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/MacroForEach.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Move.h"
+#include "mozilla/ScopeExit.h"
 
 #include <ctype.h>
 #include <string.h>
 #ifndef XP_WIN
 # include <sys/mman.h>
 # include <unistd.h>
 #endif
 
@@ -501,30 +502,32 @@ FinalizeTypedArenas(FreeOp* fop,
 
     // During background sweeping free arenas are released later on in
     // sweepBackgroundThings().
     MOZ_ASSERT_IF(!fop->onMainThread(), keepArenas == ArenaLists::KEEP_ARENAS);
 
     size_t thingSize = Arena::thingSize(thingKind);
     size_t thingsPerArena = Arena::thingsPerArena(thingKind);
 
+    JSContext* cx = fop->onMainThread() ? fop->runtime()->contextFromMainThread() : nullptr;
+
     while (Arena* arena = *src) {
         *src = arena->next;
         size_t nmarked = arena->finalize<T>(fop, thingKind, thingSize);
         size_t nfree = thingsPerArena - nmarked;
 
         if (nmarked)
             dest.insertAt(arena, nfree);
         else if (keepArenas == ArenaLists::KEEP_ARENAS)
             arena->chunk()->recycleArena(arena, dest, thingsPerArena);
         else
             fop->runtime()->gc.releaseArena(arena, maybeLock.ref());
 
         budget.step(thingsPerArena);
-        if (budget.isOverBudget())
+        if (budget.isOverBudget(cx))
             return false;
     }
 
     return true;
 }
 
 /*
  * Finalize the list. On return, |al|'s cursor points to the first non-empty
@@ -803,16 +806,18 @@ GCRuntime::GCRuntime(JSRuntime* rt) :
     stats(rt),
     marker(rt),
     usage(nullptr),
     mMemProfiler(rt),
     maxMallocBytes(0),
     nextCellUniqueId_(LargestTaggedNullCellPointer + 1), // Ensure disjoint from null tagged pointers.
     numArenasFreeCommitted(0),
     verifyPreData(nullptr),
+    interruptCallbackRequested(false),
+    currentBudget(nullptr),
     chunkAllocationSinceLastGC(false),
     lastGCTime(PRMJ_Now()),
     mode(JSGC_MODE_INCREMENTAL),
     numActiveZoneIters(0),
     cleanUpEverything(false),
     grayBufferState(GCRuntime::GrayBufferState::Unused),
     grayBitsValid(false),
     majorGCTriggerReason(JS::gcreason::NO_REASON),
@@ -859,18 +864,18 @@ GCRuntime::GCRuntime(JSRuntime* rt) :
     nextScheduled(0),
     deterministicOnly(false),
     incrementalLimit(0),
 #endif
     fullCompartmentChecks(false),
     mallocBytesUntilGC(0),
     mallocGCTriggered(false),
     alwaysPreserveCode(false),
+    inUnsafeRegion(0),
 #ifdef DEBUG
-    inUnsafeRegion(0),
     noGCOrAllocationCheck(0),
     noNurseryAllocationCheck(0),
     arenasEmptyAtShutdown(true),
 #endif
     allocTask(rt, emptyChunks_),
     decommitTask(rt),
     helperState(rt)
 {
@@ -2928,18 +2933,21 @@ SliceBudget::describe(char* buffer, size
         return snprintf(buffer, maxlen, "unlimited");
     else if (isWorkBudget())
         return snprintf(buffer, maxlen, "work(%" PRId64 ")", workBudget.budget);
     else
         return snprintf(buffer, maxlen, "%" PRId64 "ms", timeBudget.budget);
 }
 
 bool
-SliceBudget::checkOverBudget()
-{
+SliceBudget::checkOverBudget(JSContext* cx)
+{
+    if (cx)
+        cx->gc.checkInterruptCallback(cx);
+
     bool over = PRMJ_Now() >= deadline;
     if (!over)
         counter = CounterReset;
     return over;
 }
 
 void
 js::MarkCompartmentActive(InterpreterFrame* fp)
@@ -5476,17 +5484,17 @@ GCRuntime::compactPhase(JS::gcreason::Re
 
         Zone* zone = zonesToMaybeCompact.front();
         MOZ_ASSERT(zone->isGCFinished());
         zone->setGCState(Zone::Compact);
         if (relocateArenas(zone, reason, relocatedArenas, sliceBudget))
             updatePointersToRelocatedCells(zone, lock);
         zone->setGCState(Zone::Finished);
         zonesToMaybeCompact.removeFront();
-        if (sliceBudget.isOverBudget())
+        if (sliceBudget.isOverBudget(rt->contextFromMainThread()))
             break;
     }
 
     if (ShouldProtectRelocatedArenas(reason))
         protectAndHoldArenas(relocatedArenas);
     else
         releaseRelocatedArenas(relocatedArenas);
 
@@ -5882,17 +5890,17 @@ GCRuntime::incrementalCollectSlice(Slice
 
         incrementalState = State::Sweep;
 
         /*
          * This runs to completion, but we don't continue if the budget is
          * now exhasted.
          */
         beginSweepPhase(destroyingRuntime, lock);
-        if (budget.isOverBudget())
+        if (budget.isOverBudget(rt->contextFromMainThread()))
             break;
 
         /*
          * Always yield here when running in incremental multi-slice zeal
          * mode, so RunDebugGC can reset the slice buget.
          */
         if (isIncremental && useZeal && hasZealMode(ZealMode::IncrementalMultipleSlices))
             break;
@@ -6128,17 +6136,17 @@ GCRuntime::gcCycle(bool nonincrementalBy
     if (!isIncrementalGCInProgress())
         incMajorGcNumber();
 
     // It's ok if threads other than the main thread have suppressGC set, as
     // they are operating on zones which will not be collected from here.
     MOZ_ASSERT(!rt->mainThread.suppressGC);
 
     // Assert if this is a GC unsafe region.
-    JS::AutoAssertOnGC::VerifyIsSafeToGC(rt);
+    verifyIsSafeToGC();
 
     {
         gcstats::AutoPhase ap(stats, gcstats::PHASE_WAIT_BACKGROUND_THREAD);
 
         // Background finalization and decommit are finished by defininition
         // before we can start a new GC session.
         if (!isIncrementalGCInProgress()) {
             assertBackgroundSweepingFinished();
@@ -6289,16 +6297,21 @@ GCRuntime::collect(bool nonincrementalBy
 {
     // Checks run for each request, even if we do not actually GC.
     checkCanCallAPI();
 
     // Check if we are allowed to GC at this time before proceeding.
     if (!checkIfGCAllowedInCurrentState(reason))
         return;
 
+    currentBudget = &budget;
+    auto guard = mozilla::MakeScopeExit([&] {
+        currentBudget = nullptr;
+    });
+
     AutoTraceLog logGC(TraceLoggerForMainThread(rt), TraceLogger_GC);
     AutoStopVerifyingBarriers av(rt, IsShutdownGC(reason));
     AutoEnqueuePendingParseTasksAfterGC aept(*this);
     AutoScheduleZonesForGC asz(rt);
 
     bool repeat = false;
     do {
         poked = false;
@@ -6969,17 +6982,16 @@ JS::GetGCNumber()
 {
     JSRuntime* rt = js::TlsPerThreadData.get()->runtimeFromMainThread();
     if (!rt)
         return 0;
     return rt->gc.gcNumber();
 }
 #endif
 
-#ifdef DEBUG
 JS::AutoAssertOnGC::AutoAssertOnGC()
   : gc(nullptr), gcNumber(0)
 {
     js::PerThreadData* data = js::TlsPerThreadData.get();
     if (data) {
         /*
          * GC's from off-thread will always assert, so off-thread is implicitly
          * AutoAssertOnGC. We still need to allow AutoAssertOnGC to be used in
@@ -7009,23 +7021,30 @@ JS::AutoAssertOnGC::~AutoAssertOnGC()
         /*
          * The following backstop assertion should never fire: if we bumped the
          * gcNumber, we should have asserted because inUnsafeRegion was true.
          */
         MOZ_ASSERT(gcNumber == gc->gcNumber(), "GC ran inside an AutoAssertOnGC scope.");
     }
 }
 
-/* static */ void
-JS::AutoAssertOnGC::VerifyIsSafeToGC(JSRuntime* rt)
-{
-    if (rt->gc.isInsideUnsafeRegion())
-        MOZ_CRASH("[AutoAssertOnGC] possible GC in GC-unsafe region");
-}
-
+JS::AutoAssertOnBarrier::AutoAssertOnBarrier(JSContext* cx)
+  : context(cx),
+    prev(cx->runtime()->allowGCBarriers())
+{
+    context->runtime()->allowGCBarriers_ = false;
+}
+
+JS::AutoAssertOnBarrier::~AutoAssertOnBarrier()
+{
+    MOZ_ASSERT(!context->runtime()->allowGCBarriers_);
+    context->runtime()->allowGCBarriers_ = prev;
+}
+
+#ifdef DEBUG
 JS::AutoAssertNoAlloc::AutoAssertNoAlloc(JSContext* cx)
   : gc(nullptr)
 {
     disallowAlloc(cx);
 }
 
 void JS::AutoAssertNoAlloc::disallowAlloc(JSRuntime* rt)
 {
@@ -7657,8 +7676,46 @@ js::gc::Cell::dump(FILE* fp) const
 
 // For use in a debugger.
 void
 js::gc::Cell::dump() const
 {
     dump(stderr);
 }
 #endif
+
+bool
+JS::AddGCInterruptCallback(JSContext* cx, GCInterruptCallback callback)
+{
+    return cx->runtime()->gc.addInterruptCallback(callback);
+}
+
+void
+JS::RequestGCInterruptCallback(JSContext* cx)
+{
+    cx->runtime()->gc.requestInterruptCallback();
+}
+
+bool
+GCRuntime::addInterruptCallback(JS::GCInterruptCallback callback)
+{
+    return interruptCallbacks.append(callback);
+}
+
+void
+GCRuntime::requestInterruptCallback()
+{
+    if (currentBudget) {
+        interruptCallbackRequested = true;
+        currentBudget->requestFullCheck();
+    }
+}
+
+void
+GCRuntime::invokeInterruptCallback(JSContext* cx)
+{
+    JS::AutoAssertOnGC nogc(cx);
+    JS::AutoAssertOnBarrier nobarrier(cx);
+    JS::AutoSuppressGCAnalysis suppress;
+    for (JS::GCInterruptCallback callback : interruptCallbacks) {
+        (*callback)(cx);
+    }
+}
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -126,16 +126,17 @@ namespace gc {
 class AutoTraceSession;
 class StoreBuffer;
 } // namespace gc
 } // namespace js
 
 namespace JS {
 
 class JS_PUBLIC_API(AutoEnterCycleCollection);
+class JS_PUBLIC_API(AutoAssertOnBarrier);
 struct PropertyDescriptor;
 
 typedef void (*OffThreadCompileCallback)(void* token, void* callbackData);
 
 enum class HeapState {
     Idle,             // doing nothing with the GC heap
     Tracing,          // tracing the GC heap without collecting, e.g. IterateCompartments()
     MajorCollecting,  // doing a GC of the major heap
@@ -148,32 +149,40 @@ namespace shadow {
 struct Runtime
 {
   protected:
     // Allow inlining of heapState checks.
     friend class js::gc::AutoTraceSession;
     friend class JS::AutoEnterCycleCollection;
     JS::HeapState heapState_;
 
+    // In some cases, invoking GC barriers (incremental or otherwise) will break
+    // things. These barriers assert if this flag is set.
+    bool allowGCBarriers_;
+    friend class JS::AutoAssertOnBarrier;
+
     js::gc::StoreBuffer* gcStoreBufferPtr_;
 
   public:
     Runtime()
       : heapState_(JS::HeapState::Idle)
+      , allowGCBarriers_(true)
       , gcStoreBufferPtr_(nullptr)
     {}
 
     bool isHeapBusy() const { return heapState_ != JS::HeapState::Idle; }
     bool isHeapMajorCollecting() const { return heapState_ == JS::HeapState::MajorCollecting; }
     bool isHeapMinorCollecting() const { return heapState_ == JS::HeapState::MinorCollecting; }
     bool isHeapCollecting() const { return isHeapMinorCollecting() || isHeapMajorCollecting(); }
     bool isCycleCollecting() const {
         return heapState_ == JS::HeapState::CycleCollecting;
     }
 
+    bool allowGCBarriers() const { return allowGCBarriers_; }
+
     js::gc::StoreBuffer* gcStoreBufferPtr() { return gcStoreBufferPtr_; }
 
     static JS::shadow::Runtime* asShadowRuntime(JSRuntime* rt) {
         return reinterpret_cast<JS::shadow::Runtime*>(rt);
     }
 
   protected:
     void setGCStoreBufferPtr(js::gc::StoreBuffer* storeBuffer) {
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -1493,17 +1493,17 @@ ArrayBufferViewObject::notifyBufferDetac
             return;
         as<TypedArrayObject>().notifyBufferDetached(cx, newData);
     } else {
         as<OutlineTypedObject>().notifyBufferDetached(newData);
     }
 }
 
 uint8_t*
-ArrayBufferViewObject::dataPointerUnshared(const JS::AutoAssertOnGC& nogc)
+ArrayBufferViewObject::dataPointerUnshared(const JS::AutoRequireNoGC& nogc)
 {
     if (is<DataViewObject>())
         return static_cast<uint8_t*>(as<DataViewObject>().dataPointer());
     if (is<TypedArrayObject>()) {
         MOZ_ASSERT(!as<TypedArrayObject>().isSharedMemory());
         return static_cast<uint8_t*>(as<TypedArrayObject>().viewDataUnshared());
     }
     return as<TypedObject>().typedMem(nogc);
--- a/js/src/vm/ArrayBufferObject.h
+++ b/js/src/vm/ArrayBufferObject.h
@@ -435,17 +435,17 @@ class ArrayBufferViewObject : public JSO
     void notifyBufferDetached(JSContext* cx, void* newData);
 
 #ifdef DEBUG
     bool isSharedMemory();
 #endif
 
     // By construction we only need unshared variants here.  See
     // comments in ArrayBufferObject.cpp.
-    uint8_t* dataPointerUnshared(const JS::AutoAssertOnGC&);
+    uint8_t* dataPointerUnshared(const JS::AutoRequireNoGC&);
     void setDataPointerUnshared(uint8_t* data);
 
     static void trace(JSTracer* trc, JSObject* obj);
 };
 
 bool
 ToClampedIndex(JSContext* cx, HandleValue v, uint32_t length, uint32_t* out);
 
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -358,17 +358,17 @@ ExecuteState::pushInterpreterFrame(JSCon
 # pragma optimize("g", off)
 #endif
 bool
 js::RunScript(JSContext* cx, RunState& state)
 {
     JS_CHECK_RECURSION(cx, return false);
 
     // Since any script can conceivably GC, make sure it's safe to do so.
-    JS::AutoAssertOnGC::VerifyIsSafeToGC(cx->runtime());
+    cx->runtime()->gc.verifyIsSafeToGC();
 
     if (!Debugger::checkNoExecute(cx, state.script()))
         return false;
 
 #if defined(MOZ_HAVE_RDTSC)
     js::AutoStopwatch stopwatch(cx);
 #endif // defined(MOZ_HAVE_RDTSC)
 
--- a/layout/forms/nsFileControlFrame.cpp
+++ b/layout/forms/nsFileControlFrame.cpp
@@ -374,24 +374,20 @@ nsFileControlFrame::DnDListener::GetBlob
 
 bool
 nsFileControlFrame::DnDListener::IsValidDropData(nsIDOMDataTransfer* aDOMDataTransfer)
 {
   nsCOMPtr<DataTransfer> dataTransfer = do_QueryInterface(aDOMDataTransfer);
   NS_ENSURE_TRUE(dataTransfer, false);
 
   // We only support dropping files onto a file upload control
-  ErrorResult rv;
-  RefPtr<DOMStringList> types = dataTransfer->GetTypes(rv);
-  if (NS_WARN_IF(rv.Failed())) {
-    rv.SuppressException();
-    return false;
-  }
+  nsTArray<nsString> types;
+  dataTransfer->GetTypes(types, *nsContentUtils::GetSystemPrincipal());
 
-  return types->Contains(NS_LITERAL_STRING("Files"));
+  return types.Contains(NS_LITERAL_STRING("Files"));
 }
 
 bool
 nsFileControlFrame::DnDListener::CanDropTheseFiles(nsIDOMDataTransfer* aDOMDataTransfer,
                                                    bool aSupportsMultiple)
 {
   nsCOMPtr<DataTransfer> dataTransfer = do_QueryInterface(aDOMDataTransfer);
   NS_ENSURE_TRUE(dataTransfer, false);
--- a/layout/style/CSSStyleSheet.cpp
+++ b/layout/style/CSSStyleSheet.cpp
@@ -1621,22 +1621,20 @@ void
 CSSStyleSheet::DidDirty()
 {
   MOZ_ASSERT(!mInner->mComplete || mDirty,
              "caller must have called WillDirty()");
   ClearRuleCascades();
 }
 
 void
-CSSStyleSheet::SubjectSubsumesInnerPrincipal(const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+CSSStyleSheet::SubjectSubsumesInnerPrincipal(nsIPrincipal& aSubjectPrincipal,
                                              ErrorResult& aRv)
 {
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
-
-  if (aSubjectPrincipal.value()->Subsumes(mInner->mPrincipal)) {
+  if (aSubjectPrincipal.Subsumes(mInner->mPrincipal)) {
     return;
   }
 
   // Allow access only if CORS mode is not NONE
   if (GetCORSMode() == CORS_NONE) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return;
   }
@@ -1654,17 +1652,17 @@ CSSStyleSheet::SubjectSubsumesInnerPrinc
   // anyway if not complete, so we can just do that here too.
   if (!mInner->mComplete) {
     aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
     return;
   }
 
   WillDirty();
 
-  mInner->mPrincipal = aSubjectPrincipal.value();
+  mInner->mPrincipal = &aSubjectPrincipal;
 
   DidDirty();
 }
 
 nsresult
 CSSStyleSheet::RegisterNamespaceRule(css::Rule* aRule)
 {
   if (!mInner->mNameSpaceMap) {
@@ -1777,23 +1775,23 @@ CSSStyleSheet::GetDOMOwnerRule() const
   return mOwnerRule ? mOwnerRule->GetDOMRule() : nullptr;
 }
 
 NS_IMETHODIMP    
 CSSStyleSheet::GetCssRules(nsIDOMCSSRuleList** aCssRules)
 {
   ErrorResult rv;
   nsCOMPtr<nsIDOMCSSRuleList> rules =
-    GetCssRules(Some(nsContentUtils::SubjectPrincipal()), rv);
+    GetCssRules(*nsContentUtils::SubjectPrincipal(), rv);
   rules.forget(aCssRules);
   return rv.StealNSResult();
 }
 
 CSSRuleList*
-CSSStyleSheet::GetCssRules(const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+CSSStyleSheet::GetCssRules(nsIPrincipal& aSubjectPrincipal,
                            ErrorResult& aRv)
 {
   // No doing this on incomplete sheets!
   if (!mInner->mComplete) {
     aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
     return nullptr;
   }
   
@@ -1814,23 +1812,23 @@ CSSStyleSheet::GetCssRules(const Maybe<n
 
 NS_IMETHODIMP    
 CSSStyleSheet::InsertRule(const nsAString& aRule,
                           uint32_t aIndex,
                           uint32_t* aReturn)
 {
   ErrorResult rv;
   *aReturn =
-    InsertRule(aRule, aIndex, Some(nsContentUtils::SubjectPrincipal()), rv);
+    InsertRule(aRule, aIndex, *nsContentUtils::SubjectPrincipal(), rv);
   return rv.StealNSResult();
 }
 
 uint32_t
 CSSStyleSheet::InsertRule(const nsAString& aRule, uint32_t aIndex,
-                          const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                          nsIPrincipal& aSubjectPrincipal,
                           ErrorResult& aRv)
 {
   //-- Security check: Only scripts whose principal subsumes that of the
   //   style sheet can modify rule collections.
   SubjectSubsumesInnerPrincipal(aSubjectPrincipal, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return 0;
   }
@@ -1972,23 +1970,23 @@ CSSStyleSheet::InsertRuleInternal(const 
 
   return aIndex;
 }
 
 NS_IMETHODIMP    
 CSSStyleSheet::DeleteRule(uint32_t aIndex)
 {
   ErrorResult rv;
-  DeleteRule(aIndex, Some(nsContentUtils::SubjectPrincipal()), rv);
+  DeleteRule(aIndex, *nsContentUtils::SubjectPrincipal(), rv);
   return rv.StealNSResult();
 }
 
 void
 CSSStyleSheet::DeleteRule(uint32_t aIndex,
-                          const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                          nsIPrincipal& aSubjectPrincipal,
                           ErrorResult& aRv)
 {
   // No doing this if the sheet is not complete!
   if (!mInner->mComplete) {
     aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
     return;
   }
 
--- a/layout/style/CSSStyleSheet.h
+++ b/layout/style/CSSStyleSheet.h
@@ -241,24 +241,24 @@ public:
   bool Disabled() const { return mDisabled; }
   // The XPCOM SetDisabled is fine for WebIDL
 
   // WebIDL CSSStyleSheet API
   // Can't be inline because we can't include ImportRule here.  And can't be
   // called GetOwnerRule because that would be ambiguous with the ImportRule
   // version.
   nsIDOMCSSRule* GetDOMOwnerRule() const;
-  dom::CSSRuleList* GetCssRules(const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+  dom::CSSRuleList* GetCssRules(nsIPrincipal& aSubjectPrincipal,
                                 ErrorResult& aRv);
   uint32_t InsertRule(const nsAString& aRule, uint32_t aIndex,
-                      const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                      nsIPrincipal& aSubjectPrincipal,
                       ErrorResult& aRv);
 
   void DeleteRule(uint32_t aIndex,
-                  const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                  nsIPrincipal& aSubjectPrincipal,
                   ErrorResult& aRv);
 
   // WebIDL miscellaneous bits
   dom::ParentObject GetParentObject() const {
     if (mOwningNode) {
       return dom::ParentObject(mOwningNode);
     }
 
@@ -286,17 +286,17 @@ protected:
   virtual ~CSSStyleSheet();
 
   void ClearRuleCascades();
 
   // Return success if the subject principal subsumes the principal of our
   // inner, error otherwise.  This will also succeed if the subject has
   // UniversalXPConnect or if access is allowed by CORS.  In the latter case,
   // it will set the principal of the inner to the subject principal.
-  void SubjectSubsumesInnerPrincipal(const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+  void SubjectSubsumesInnerPrincipal(nsIPrincipal& aSubjectPrincipal,
                                      ErrorResult& aRv);
 
   // Add the namespace mapping from this @namespace rule to our namespace map
   nsresult RegisterNamespaceRule(css::Rule* aRule);
 
   // Drop our reference to mRuleCollection
   void DropRuleCollection();
 
--- a/layout/style/ServoBindingList.h
+++ b/layout/style/ServoBindingList.h
@@ -33,23 +33,23 @@ SERVO_BINDING_FUNC(Servo_StyleSheet_AddR
                    RawServoStyleSheetBorrowed sheet)
 SERVO_BINDING_FUNC(Servo_StyleSheet_Release, void,
                    RawServoStyleSheetBorrowed sheet)
 SERVO_BINDING_FUNC(Servo_StyleSheet_HasRules, bool,
                    RawServoStyleSheetBorrowed sheet)
 SERVO_BINDING_FUNC(Servo_StyleSet_Init, RawServoStyleSetOwned)
 SERVO_BINDING_FUNC(Servo_StyleSet_Drop, void, RawServoStyleSetOwned set)
 SERVO_BINDING_FUNC(Servo_StyleSet_AppendStyleSheet, void,
-                   RawServoStyleSetBorrowedMut set, RawServoStyleSheetBorrowed sheet)
+                   RawServoStyleSetBorrowed set, RawServoStyleSheetBorrowed sheet)
 SERVO_BINDING_FUNC(Servo_StyleSet_PrependStyleSheet, void,
-                   RawServoStyleSetBorrowedMut set, RawServoStyleSheetBorrowed sheet)
+                   RawServoStyleSetBorrowed set, RawServoStyleSheetBorrowed sheet)
 SERVO_BINDING_FUNC(Servo_StyleSet_RemoveStyleSheet, void,
-                   RawServoStyleSetBorrowedMut set, RawServoStyleSheetBorrowed sheet)
+                   RawServoStyleSetBorrowed set, RawServoStyleSheetBorrowed sheet)
 SERVO_BINDING_FUNC(Servo_StyleSet_InsertStyleSheetBefore, void,
-                   RawServoStyleSetBorrowedMut set, RawServoStyleSheetBorrowed sheet,
+                   RawServoStyleSetBorrowed set, RawServoStyleSheetBorrowed sheet,
                    RawServoStyleSheetBorrowed reference)
 
 // Animations API
 SERVO_BINDING_FUNC(Servo_ParseProperty,
                    ServoDeclarationBlockStrong,
                    const uint8_t* property_bytes,
                    uint32_t property_length,
                    const uint8_t* value_bytes,
@@ -91,22 +91,22 @@ SERVO_BINDING_FUNC(Servo_CSSSupports, bo
                    const uint8_t* value, uint32_t value_length)
 
 // Computed style data
 SERVO_BINDING_FUNC(Servo_ComputedValues_Get, ServoComputedValuesStrong,
                    RawGeckoNodeBorrowed node)
 SERVO_BINDING_FUNC(Servo_ComputedValues_GetForAnonymousBox,
                    ServoComputedValuesStrong,
                    ServoComputedValuesBorrowedOrNull parent_style_or_null,
-                   nsIAtom* pseudoTag, RawServoStyleSetBorrowedMut set)
+                   nsIAtom* pseudoTag, RawServoStyleSetBorrowed set)
 SERVO_BINDING_FUNC(Servo_ComputedValues_GetForPseudoElement,
                    ServoComputedValuesStrong,
                    ServoComputedValuesBorrowed parent_style,
                    RawGeckoElementBorrowed match_element, nsIAtom* pseudo_tag,
-                   RawServoStyleSetBorrowedMut set, bool is_probe)
+                   RawServoStyleSetBorrowed set, bool is_probe)
 SERVO_BINDING_FUNC(Servo_ComputedValues_Inherit, ServoComputedValuesStrong,
                    ServoComputedValuesBorrowedOrNull parent_style)
 SERVO_BINDING_FUNC(Servo_ComputedValues_AddRef, void,
                    ServoComputedValuesBorrowed computed_values)
 SERVO_BINDING_FUNC(Servo_ComputedValues_Release, void,
                    ServoComputedValuesBorrowed computed_values)
 
 // Initialize Servo components. Should be called exactly once at startup.
@@ -116,17 +116,17 @@ SERVO_BINDING_FUNC(Servo_Shutdown, void)
 
 // Restyle hints
 SERVO_BINDING_FUNC(Servo_ComputeRestyleHint, nsRestyleHint,
                    RawGeckoElementBorrowed element, ServoElementSnapshot* snapshot,
                    RawServoStyleSetBorrowed set)
 
 // Restyle the given subtree.
 SERVO_BINDING_FUNC(Servo_RestyleSubtree, void,
-                   RawGeckoNodeBorrowed node, RawServoStyleSetBorrowedMut set)
+                   RawGeckoNodeBorrowed node, RawServoStyleSetBorrowed set)
 
 // Style-struct management.
 #define STYLE_STRUCT(name, checkdata_cb)                            \
   struct nsStyle##name;                                             \
   SERVO_BINDING_FUNC(Servo_GetStyle##name, const nsStyle##name*,  \
                      ServoComputedValuesBorrowedOrNull computed_values)
 #include "nsStyleStructList.h"
 #undef STYLE_STRUCT
--- a/netwerk/protocol/http/nsHttpHeaderArray.h
+++ b/netwerk/protocol/http/nsHttpHeaderArray.h
@@ -105,33 +105,45 @@ public:
 
     uint32_t Count() const { return mHeaders.Length(); }
 
     const char *PeekHeaderAt(uint32_t i, nsHttpAtom &header) const;
 
     void Clear();
 
     // Must be copy-constructable and assignable
+#if defined(XP_WIN) && (defined(_M_IX86) || defined(_M_X64))
+#pragma pack(1)
+#endif
     struct nsEntry
     {
         nsHttpAtom header;
+#if defined(XP_WIN) && (defined(_M_IX86) || defined(_M_X64))
+        char padding[4096 -
+                     sizeof(nsTArrayHeader) -
+                     sizeof(nsHttpAtom) -
+                     sizeof(void*)];
+#endif
         nsCString value;
         HeaderVariety variety = eVarietyUnknown;
 
         struct MatchHeader {
           bool Equals(const nsEntry &aEntry, const nsHttpAtom &aHeader) const {
             return aEntry.header == aHeader;
           }
         };
 
         bool operator==(const nsEntry& aOther) const
         {
             return header == aOther.header && value == aOther.value;
         }
     };
+#if defined(XP_WIN) && (defined(_M_IX86) || defined(_M_X64))
+#pragma pack()
+#endif
 
     bool operator==(const nsHttpHeaderArray& aOther) const
     {
         return mHeaders == aOther.mHeaders;
     }
 
 private:
     // LookupEntry function will never return eVarietyResponseNetOriginal.
--- a/netwerk/protocol/http/nsHttpRequestHead.cpp
+++ b/netwerk/protocol/http/nsHttpRequestHead.cpp
@@ -11,29 +11,57 @@
 
 //-----------------------------------------------------------------------------
 // nsHttpRequestHead
 //-----------------------------------------------------------------------------
 
 namespace mozilla {
 namespace net {
 
+#if defined(XP_WIN) && (defined(_M_IX86) || defined(_M_X64))
+void nsHttpRequestHead::DbgReentrantMonitorAutoEnter::Protect(bool aOn)
+{
+    if (XRE_GetProcessType() == GeckoProcessType_Content &&
+        mInst.mHeaders.Count()) {
+        DWORD oldProtect;
+        LPVOID hdr = reinterpret_cast<PUINT8>(mInst.mHeaders.mHeaders.Elements()) -
+                     sizeof(nsTArrayHeader);
+        if (aOn) {
+            VirtualProtect(hdr, 4096, PAGE_READONLY, &oldProtect);
+        } else {
+            VirtualProtect(hdr, 4096, PAGE_READWRITE, &oldProtect);
+        }
+    }
+}
+
+#define ReentrantMonitorAutoEnter DbgReentrantMonitorAutoEnter
+#define mon(x) mon(*this)
+#endif
+
 nsHttpRequestHead::nsHttpRequestHead()
     : mMethod(NS_LITERAL_CSTRING("GET"))
     , mVersion(NS_HTTP_VERSION_1_1)
     , mParsedMethod(kMethod_Get)
     , mHTTPS(false)
     , mReentrantMonitor("nsHttpRequestHead.mReentrantMonitor")
     , mInVisitHeaders(false)
 {
     MOZ_COUNT_CTOR(nsHttpRequestHead);
 }
 
 nsHttpRequestHead::~nsHttpRequestHead()
 {
+#if defined(XP_WIN) && (defined(_M_IX86) || defined(_M_X64))
+    if (XRE_GetProcessType() == GeckoProcessType_Content && mHeaders.Count()) {
+        DWORD oldProtect;
+        LPVOID hdr = reinterpret_cast<PUINT8>(mHeaders.mHeaders.Elements()) -
+                     sizeof(nsTArrayHeader);
+        VirtualProtect(hdr, 4096, PAGE_READWRITE, &oldProtect);
+    }
+#endif
     MOZ_COUNT_DTOR(nsHttpRequestHead);
 }
 
 // Don't use this function. It is only used by HttpChannelParent to avoid
 // copying of request headers!!!
 const nsHttpHeaderArray &
 nsHttpRequestHead::Headers() const
 {
@@ -360,10 +388,15 @@ nsHttpRequestHead::Flatten(nsACString &b
         buf.AppendLiteral("1.0");
     }
 
     buf.AppendLiteral("\r\n");
 
     mHeaders.Flatten(buf, pruneProxyHeaders, false);
 }
 
+#if defined(XP_WIN) && (defined(_M_IX86) || defined(_M_X64))
+#undef ReentrantMonitorAutoEnter
+#undef mon
+#endif
+
 } // namespace net
 } // namespace mozilla
--- a/netwerk/protocol/http/nsHttpRequestHead.h
+++ b/netwerk/protocol/http/nsHttpRequestHead.h
@@ -115,14 +115,36 @@ private:
     bool              mHTTPS;
 
     // We are using ReentrantMonitor instead of a Mutex because VisitHeader
     // function calls nsIHttpHeaderVisitor::VisitHeader while under lock.
     ReentrantMonitor  mReentrantMonitor;
 
     // During VisitHeader we sould not allow cal to SetHeader.
     bool mInVisitHeaders;
+
+#if defined(XP_WIN) && (defined(_M_IX86) || defined(_M_X64))
+    class DbgReentrantMonitorAutoEnter : ReentrantMonitorAutoEnter
+    {
+    public:
+        explicit DbgReentrantMonitorAutoEnter(nsHttpRequestHead& aInst)
+            : ReentrantMonitorAutoEnter(aInst.mReentrantMonitor),
+              mInst(aInst)
+        {
+            Protect(false);
+        }
+        ~DbgReentrantMonitorAutoEnter(void)
+        {
+            Protect(true);
+        }
+
+    private:
+        void Protect(bool aOn);
+
+        nsHttpRequestHead& mInst;
+    };
+#endif
 };
 
 } // namespace net
 } // namespace mozilla
 
 #endif // nsHttpRequestHead_h__
--- a/toolkit/components/viewsource/content/viewSource.js
+++ b/toolkit/components/viewsource/content/viewSource.js
@@ -510,17 +510,17 @@ ViewSourceChrome.prototype = {
   /**
    * Called when the user drags something over the content browser.
    */
   onDragOver(event) {
     // For drags that appear to be internal text (for example, tab drags),
     // set the dropEffect to 'none'. This prevents the drop even if some
     // other listener cancelled the event.
     let types = event.dataTransfer.types;
-    if (types.contains("text/x-moz-text-internal") && !types.contains("text/plain")) {
+    if (types.includes("text/x-moz-text-internal") && !types.includes("text/plain")) {
         event.dataTransfer.dropEffect = "none";
         event.stopPropagation();
         event.preventDefault();
     }
 
     let linkHandler = Cc["@mozilla.org/content/dropped-link-handler;1"]
                         .getService(Ci.nsIDroppedLinkHandler);
 
--- a/toolkit/content/customizeToolbar.js
+++ b/toolkit/content/customizeToolbar.js
@@ -650,17 +650,17 @@ function onToolbarDragStart(aEvent)
 
 function onToolbarDragOver(aEvent)
 {
   if (isUnwantedDragEvent(aEvent)) {
     return;
   }
 
   var documentId = gToolboxDocument.documentElement.id;
-  if (!aEvent.dataTransfer.types.contains("text/toolbarwrapper-id/" + documentId.toLowerCase()))
+  if (!aEvent.dataTransfer.types.includes("text/toolbarwrapper-id/" + documentId.toLowerCase()))
     return;
 
   var toolbar = aEvent.target;
   var dropTarget = aEvent.target;
   while (toolbar && toolbar.localName != "toolbar") {
     dropTarget = toolbar;
     toolbar = toolbar.parentNode;
   }
@@ -779,17 +779,17 @@ function onToolbarDrop(aEvent)
 }
 
 function onPaletteDragOver(aEvent)
 {
   if (isUnwantedDragEvent(aEvent)) {
     return;
   }
   var documentId = gToolboxDocument.documentElement.id;
-  if (aEvent.dataTransfer.types.contains("text/toolbarwrapper-id/" + documentId.toLowerCase()))
+  if (aEvent.dataTransfer.types.includes("text/toolbarwrapper-id/" + documentId.toLowerCase()))
     aEvent.preventDefault();
 }
 
 function onPaletteDrop(aEvent)
 {
   if (isUnwantedDragEvent(aEvent)) {
     return;
   }
--- a/toolkit/content/widgets/browser.xml
+++ b/toolkit/content/widgets/browser.xml
@@ -1440,17 +1440,17 @@
       <![CDATA[
         if (!this.droppedLinkHandler || event.defaultPrevented)
           return;
 
         // For drags that appear to be internal text (for example, tab drags),
         // set the dropEffect to 'none'. This prevents the drop even if some
         // other listener cancelled the event.
         var types = event.dataTransfer.types;
-        if (types.contains("text/x-moz-text-internal") && !types.contains("text/plain")) {
+        if (types.includes("text/x-moz-text-internal") && !types.includes("text/plain")) {
           event.dataTransfer.dropEffect = "none";
           event.stopPropagation();
           event.preventDefault();
         }
 
         // No need to handle "dragover" in e10s, since nsDocShellTreeOwner.cpp in the child process
         // handles that case using "@mozilla.org/content/dropped-link-handler;1" service.
         if (this.isRemoteBrowser)
--- a/toolkit/content/widgets/findbar.xml
+++ b/toolkit/content/widgets/findbar.xml
@@ -134,17 +134,17 @@
       <handler event="compositionend"><![CDATA[
         let findbar = this.findbar;
         findbar._isIMEComposing = false;
         if (findbar._findMode != findbar.FIND_NORMAL)
           findbar._setFindCloseTimeout();
       ]]></handler>
 
       <handler event="dragover"><![CDATA[
-        if (event.dataTransfer.types.contains("text/plain"))
+        if (event.dataTransfer.types.includes("text/plain"))
           event.preventDefault();
       ]]></handler>
 
       <handler event="drop"><![CDATA[
         let value = event.dataTransfer.getData("text/plain");
         this.value = value;
         this.findbar._find(value);
         event.stopPropagation();
--- a/toolkit/mozapps/downloads/content/downloads.js
+++ b/toolkit/mozapps/downloads/content/downloads.js
@@ -656,19 +656,19 @@ var gDownloadDNDObserver =
     dt.setData("text/plain", url);
     dt.effectAllowed = "copyMove";
     dt.addElement(dl);
   },
 
   onDragOver: function (aEvent)
   {
     var types = aEvent.dataTransfer.types;
-    if (types.contains("text/uri-list") ||
-        types.contains("text/x-moz-url") ||
-        types.contains("text/plain"))
+    if (types.includes("text/uri-list") ||
+        types.includes("text/x-moz-url") ||
+        types.includes("text/plain"))
       aEvent.preventDefault();
   },
 
   onDrop: function(aEvent)
   {
     var dt = aEvent.dataTransfer;
     // If dragged item is from our source, do not try to
     // redownload already downloaded file.
--- a/toolkit/mozapps/extensions/content/extensions.js
+++ b/toolkit/mozapps/extensions/content/extensions.js
@@ -3815,19 +3815,19 @@ var gUpdatesView = {
     if (aProperties.indexOf("applyBackgroundUpdates") != -1)
       this.updateAvailableCount();
   }
 };
 
 var gDragDrop = {
   onDragOver: function(aEvent) {
     var types = aEvent.dataTransfer.types;
-    if (types.contains("text/uri-list") ||
-        types.contains("text/x-moz-url") ||
-        types.contains("application/x-moz-file"))
+    if (types.includes("text/uri-list") ||
+        types.includes("text/x-moz-url") ||
+        types.includes("application/x-moz-file"))
       aEvent.preventDefault();
   },
 
   onDrop: function(aEvent) {
     var dataTransfer = aEvent.dataTransfer;
     var urls = [];
 
     // Convert every dropped item into a url
--- a/widget/gtk/nsGtkKeyUtils.cpp
+++ b/widget/gtk/nsGtkKeyUtils.cpp
@@ -14,16 +14,17 @@
 #include <gdk/gdk.h>
 #include <gdk/gdkx.h>
 #if (MOZ_WIDGET_GTK == 3)
 #include <gdk/gdkkeysyms-compat.h>
 #endif
 #include <X11/XKBlib.h>
 #include "WidgetUtils.h"
 #include "keysym2ucs.h"
+#include "nsContentUtils.h"
 #include "nsGtkUtils.h"
 #include "nsIBidiKeyboard.h"
 #include "nsServiceManagerUtils.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/TextEvents.h"
 
@@ -36,17 +37,16 @@ LazyLogModule gKeymapWrapperLog("KeymapW
     ((('a' <= key) && (key <= 'z')) || (('A' <= key) && (key <= 'Z')))
 
 #define MOZ_MODIFIER_KEYS "MozKeymapWrapper"
 
 KeymapWrapper* KeymapWrapper::sInstance = nullptr;
 guint KeymapWrapper::sLastRepeatableHardwareKeyCode = 0;
 KeymapWrapper::RepeatState KeymapWrapper::sRepeatState =
     KeymapWrapper::NOT_PRESSED;
-nsIBidiKeyboard* sBidiKeyboard = nullptr;
 
 static const char* GetBoolName(bool aBool)
 {
     return aBool ? "TRUE" : "FALSE";
 }
 
 /* static */ const char*
 KeymapWrapper::GetModifierName(Modifier aModifier)
@@ -165,16 +165,18 @@ KeymapWrapper::KeymapWrapper() :
 {
     MOZ_LOG(gKeymapWrapperLog, LogLevel::Info,
         ("%p Constructor, mGdkKeymap=%p",
          this, mGdkKeymap));
 
     g_object_ref(mGdkKeymap);
     g_signal_connect(mGdkKeymap, "keys-changed",
                      (GCallback)OnKeysChanged, this);
+    g_signal_connect(mGdkKeymap, "direction-changed",
+                     (GCallback)OnDirectionChanged, this);
 
     if (GDK_IS_X11_DISPLAY(gdk_display_get_default()))
         InitXKBExtension();
 
     Init();
 }
 
 void
@@ -437,18 +439,19 @@ KeymapWrapper::InitBySystemSettings()
     XFree(xkeymap);
 }
 
 KeymapWrapper::~KeymapWrapper()
 {
     gdk_window_remove_filter(nullptr, FilterEvents, this);
     g_signal_handlers_disconnect_by_func(mGdkKeymap,
                                          FuncToGpointer(OnKeysChanged), this);
+    g_signal_handlers_disconnect_by_func(mGdkKeymap,
+                                         FuncToGpointer(OnDirectionChanged), this);
     g_object_unref(mGdkKeymap);
-    NS_IF_RELEASE(sBidiKeyboard);
     MOZ_LOG(gKeymapWrapperLog, LogLevel::Info,
         ("%p Destructor", this));
 }
 
 /* static */ GdkFilterReturn
 KeymapWrapper::FilterEvents(GdkXEvent* aXEvent,
                             GdkEvent* aGdkEvent,
                             gpointer aData)
@@ -514,38 +517,62 @@ KeymapWrapper::FilterEvents(GdkXEvent* a
             }
             break;
         }
     }
 
     return GDK_FILTER_CONTINUE;
 }
 
+static void
+ResetBidiKeyboard()
+{
+    // Reset the bidi keyboard settings for the new GdkKeymap
+    nsCOMPtr<nsIBidiKeyboard> bidiKeyboard = nsContentUtils::GetBidiKeyboard();
+    if (bidiKeyboard) {
+        bidiKeyboard->Reset();
+    }
+    WidgetUtils::SendBidiKeyboardInfoToContent();
+}
+
 /* static */ void
 KeymapWrapper::OnKeysChanged(GdkKeymap *aGdkKeymap,
                              KeymapWrapper* aKeymapWrapper)
 {
     MOZ_LOG(gKeymapWrapperLog, LogLevel::Info,
         ("OnKeysChanged, aGdkKeymap=%p, aKeymapWrapper=%p",
          aGdkKeymap, aKeymapWrapper));
 
     MOZ_ASSERT(sInstance == aKeymapWrapper,
                "This instance must be the singleton instance");
 
     // We cannot reintialize here becasue we don't have GdkWindow which is using
     // the GdkKeymap.  We'll reinitialize it when next GetInstance() is called.
     sInstance->mInitialized = false;
+    ResetBidiKeyboard();
+}
 
-    // Reset the bidi keyboard settings for the new GdkKeymap
-    if (!sBidiKeyboard) {
-        CallGetService("@mozilla.org/widget/bidikeyboard;1", &sBidiKeyboard);
-    }
-    if (sBidiKeyboard) {
-        sBidiKeyboard->Reset();
-    }
+// static
+void
+KeymapWrapper::OnDirectionChanged(GdkKeymap *aGdkKeymap,
+                                  KeymapWrapper* aKeymapWrapper)
+{
+    // XXX
+    // A lot of diretion-changed signal might be fired on switching bidi
+    // keyboard when using both ibus (with arabic layout) and fcitx (with IME).
+    // See https://github.com/fcitx/fcitx/issues/257
+    //
+    // Also, when using ibus, switching to IM might not cause this signal.
+    // See https://github.com/ibus/ibus/issues/1848
+
+    MOZ_LOG(gKeymapWrapperLog, LogLevel::Info,
+        ("OnDirectionChanged, aGdkKeymap=%p, aKeymapWrapper=%p",
+         aGdkKeymap, aKeymapWrapper));
+
+    ResetBidiKeyboard();
 }
 
 /* static */ guint
 KeymapWrapper::GetCurrentModifierState()
 {
     GdkModifierType modifiers;
     gdk_display_get_pointer(gdk_display_get_default(),
                             nullptr, nullptr, nullptr, &modifiers);
--- a/widget/gtk/nsGtkKeyUtils.h
+++ b/widget/gtk/nsGtkKeyUtils.h
@@ -252,16 +252,18 @@ protected:
      * Otherwise, false.
      */
     bool IsAutoRepeatableKey(guint aHardwareKeyCode);
 
     /**
      * Signal handlers.
      */
     static void OnKeysChanged(GdkKeymap* aKeymap, KeymapWrapper* aKeymapWrapper);
+    static void OnDirectionChanged(GdkKeymap *aGdkKeymap,
+                                   KeymapWrapper* aKeymapWrapper);
 
     /**
      * GetCharCodeFor() Computes what character is inputted by the key event
      * with aModifierState and aGroup.
      *
      * @param aGdkKeyEvent      Native key event, must not be nullptr.
      * @param aModifierState    Combination of GdkModifierType which you
      *                          want to test with aGdkKeyEvent.