Merged mozilla-inbound to mozilla-central a=merge
authordvarga <dvarga@mozilla.com>
Sun, 05 Aug 2018 00:49:28 +0300
changeset 485273 37b33c4f58b9
parent 485263 073983e062f7 (current diff)
parent 485272 9f3a26721364 (diff)
child 485274 542243f5f600
child 485289 259a2e4d857f
push id9719
push userffxbld-merge
push dateFri, 24 Aug 2018 17:49:46 +0000
treeherdermozilla-beta@719ec98fba77 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone63.0a1
first release with
nightly linux32
37b33c4f58b9 / 63.0a1 / 20180804220307 / files
nightly linux64
37b33c4f58b9 / 63.0a1 / 20180804220307 / files
nightly mac
37b33c4f58b9 / 63.0a1 / 20180804220307 / files
nightly win32
37b33c4f58b9 / 63.0a1 / 20180804220307 / files
nightly win64
37b33c4f58b9 / 63.0a1 / 20180804220307 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merged mozilla-inbound to mozilla-central a=merge
--- a/browser/base/content/test/tabs/browser_overflowScroll.js
+++ b/browser/base/content/test/tabs/browser_overflowScroll.js
@@ -21,16 +21,20 @@ add_task(async function() {
   let left = ele => rect(ele).left;
   let right = ele => rect(ele).right;
   let isLeft = (ele, msg) => is(left(ele), left(scrollbox), msg);
   let isRight = (ele, msg) => is(right(ele), right(scrollbox), msg);
   let elementFromPoint = x => arrowScrollbox._elementFromPoint(x);
   let nextLeftElement = () => elementFromPoint(left(scrollbox) - 1);
   let nextRightElement = () => elementFromPoint(right(scrollbox) + 1);
   let firstScrollable = () => tabs[gBrowser._numPinnedTabs];
+  let waitForNextFrame = async function() {
+    await window.promiseDocumentFlushed(() => {});
+    await new Promise(resolve => Services.tm.dispatchToMainThread(resolve));
+  };
 
   arrowScrollbox.smoothScroll = false;
   registerCleanupFunction(() => {
     arrowScrollbox.smoothScroll = originalSmoothScroll;
   });
 
   while (tabs.length < tabCountForOverflow) {
     BrowserTestUtils.addTab(gBrowser, "about:blank", { skipAnimation: true });
@@ -50,35 +54,41 @@ add_task(async function() {
   let element;
 
   gBrowser.selectedTab = firstScrollable();
   ok(left(scrollbox) <= left(firstScrollable()), "Selecting the first tab scrolls it into view " +
      "(" + left(scrollbox) + " <= " + left(firstScrollable()) + ")");
 
   element = nextRightElement();
   EventUtils.synthesizeMouseAtCenter(downButton, {});
+  await waitForNextFrame();
   isRight(element, "Scrolled one tab to the right with a single click");
 
   gBrowser.selectedTab = tabs[tabs.length - 1];
+  await waitForNextFrame();
   ok(right(gBrowser.selectedTab) <= right(scrollbox), "Selecting the last tab scrolls it into view " +
      "(" + right(gBrowser.selectedTab) + " <= " + right(scrollbox) + ")");
 
   element = nextLeftElement();
   EventUtils.synthesizeMouseAtCenter(upButton, {});
+  await waitForNextFrame();
   isLeft(element, "Scrolled one tab to the left with a single click");
 
   let elementPoint = left(scrollbox) - width(scrollbox);
   element = elementFromPoint(elementPoint);
-  if (elementPoint == right(element)) {
-    element = element.nextSibling;
-  }
+  element = element.nextSibling;
+
   EventUtils.synthesizeMouseAtCenter(upButton, {clickCount: 2});
+  await waitForNextFrame();
+  await BrowserTestUtils.waitForCondition(() =>
+    !gBrowser.tabContainer.arrowScrollbox._isScrolling);
   isLeft(element, "Scrolled one page of tabs with a double click");
 
   EventUtils.synthesizeMouseAtCenter(upButton, {clickCount: 3});
+  await waitForNextFrame();
   var firstScrollableLeft = left(firstScrollable());
   ok(left(scrollbox) <= firstScrollableLeft, "Scrolled to the start with a triple click " +
      "(" + left(scrollbox) + " <= " + firstScrollableLeft + ")");
 
   while (tabs.length > 1) {
     BrowserTestUtils.removeTab(gBrowser.tabs[0]);
   }
 });
--- a/dom/base/ChromeUtils.cpp
+++ b/dom/base/ChromeUtils.cpp
@@ -477,26 +477,21 @@ namespace module_getter {
     JS::Rooted<JSObject*> moduleExports(aCx);
     nsresult rv = moduleloader->Import(aCx, uri, &moduleGlobal, &moduleExports);
     if (NS_FAILED(rv)) {
       Throw(aCx, rv);
       return false;
     }
 
     JS::RootedValue value(aCx);
-    {
-      JSAutoRealmAllowCCW ar(aCx, moduleExports);
-
-      if (!JS_GetPropertyById(aCx, moduleExports, id, &value)) {
-        return false;
-      }
+    if (!JS_GetPropertyById(aCx, moduleExports, id, &value)) {
+      return false;
     }
 
-    if (!JS_WrapValue(aCx, &value) ||
-        !JS_DefinePropertyById(aCx, thisObj, id, value,
+    if (!JS_DefinePropertyById(aCx, thisObj, id, value,
                                JSPROP_ENUMERATE)) {
       return false;
     }
 
     args.rval().set(value);
     return true;
   }
 
--- a/dom/base/nsContentPermissionHelper.cpp
+++ b/dom/base/nsContentPermissionHelper.cpp
@@ -703,22 +703,27 @@ nsContentPermissionRequestProxy::Allow(J
   nsTArray<PermissionChoice> choices;
   if (aChoices.isNullOrUndefined()) {
     // No choice is specified.
   } else if (aChoices.isObject()) {
     // Iterate through all permission types.
     for (uint32_t i = 0; i < mPermissionRequests.Length(); ++i) {
       nsCString type = mPermissionRequests[i].type();
 
+      JS::Rooted<JSObject*> obj(RootingCx(), &aChoices.toObject());
+      obj = CheckedUnwrap(obj);
+      if (!obj) {
+        return NS_ERROR_FAILURE;
+      }
+
       AutoJSAPI jsapi;
       jsapi.Init();
 
       JSContext* cx = jsapi.cx();
-      JS::Rooted<JSObject*> obj(cx, &aChoices.toObject());
-      JSAutoRealmAllowCCW ar(cx, obj);
+      JSAutoRealm ar(cx, obj);
 
       JS::Rooted<JS::Value> val(cx);
 
       if (!JS_GetProperty(cx, obj, type.BeginReading(), &val) ||
           !val.isString()) {
         // no setting for the permission type, clear exception and skip it
         jsapi.ClearException();
       } else {
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -7866,25 +7866,28 @@ class CGPerSignatureCall(CGThing):
                           // expando object already preserved our wrapper.
                           $*{preserveWrapper}
                         }
                         """,
                         preserveWrapper=preserveWrapper)
                 slotStorageSteps += preserveWrapper
 
             if checkForXray:
-                conversionScope = "isXray ? obj : slotStorage"
+                # In the Xray case we use the current global as conversion
+                # scope, as explained in the big compartment/conversion comment
+                # above.
+                conversionScope = "isXray ? JS::CurrentGlobalOrNull(cx) : slotStorage"
             else:
                 conversionScope = "slotStorage"
 
             wrapCode = fill(
                 """
                 {
                   JS::Rooted<JSObject*> conversionScope(cx, ${conversionScope});
-                  JSAutoRealmAllowCCW ar(cx, conversionScope);
+                  JSAutoRealm ar(cx, conversionScope);
                   do { // block we break out of when done wrapping
                     $*{wrapCode}
                   } while (false);
                   $*{postConversionSteps}
                 }
                 { // And now store things in the realm of our slotStorage.
                   JSAutoRealm ar(cx, slotStorage);
                   $*{slotStorageSteps}
--- a/dom/bindings/nsIScriptError.idl
+++ b/dom/bindings/nsIScriptError.idl
@@ -77,16 +77,25 @@ interface nsIScriptError : nsIConsoleMes
        returned if init() was used instead of initWithWindowID(). */
     readonly attribute unsigned long long innerWindowID;
 
     readonly attribute boolean isFromPrivateWindow;
 
     attribute jsval stack;
 
     /**
+     * If |stack| is an object, then stackGlobal must be a global object that's
+     * same-compartment with |stack|. This can be used to enter the correct
+     * realm when working with the stack object. We can't use the object itself
+     * because it might be a cross-compartment wrapper and CCWs are not
+     * associated with a single realm/global.
+     */
+    [noscript] readonly attribute jsval stackGlobal;
+
+    /**
      * The name of a template string, as found in js.msg, associated with the
      * error message.
      */
     attribute AString errorMessageName;
 
     readonly attribute nsIArray notes;
 
     void init(in AString message,
--- a/dom/bindings/nsScriptError.cpp
+++ b/dom/bindings/nsScriptError.cpp
@@ -165,16 +165,23 @@ nsScriptErrorBase::GetStack(JS::MutableH
 }
 
 NS_IMETHODIMP
 nsScriptErrorBase::SetStack(JS::HandleValue aStack) {
     return NS_OK;
 }
 
 NS_IMETHODIMP
+nsScriptErrorBase::GetStackGlobal(JS::MutableHandleValue aStackGlobal)
+{
+    aStackGlobal.setUndefined();
+    return NS_OK;
+}
+
+NS_IMETHODIMP
 nsScriptErrorBase::GetErrorMessageName(nsAString& aErrorMessageName) {
     aErrorMessageName = mMessageName;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsScriptErrorBase::SetErrorMessageName(const nsAString& aErrorMessageName) {
     mMessageName = aErrorMessageName;
--- a/dom/bindings/nsScriptError.h
+++ b/dom/bindings/nsScriptError.h
@@ -92,16 +92,17 @@ private:
 class nsScriptErrorWithStack : public nsScriptErrorBase {
 public:
   nsScriptErrorWithStack(JS::HandleObject aStack, JS::HandleObject aStackGlobal);
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsScriptErrorWithStack)
 
   NS_IMETHOD GetStack(JS::MutableHandleValue) override;
+  NS_IMETHOD GetStackGlobal(JS::MutableHandleValue) override;
   NS_IMETHOD ToString(nsACString& aResult) override;
 
 private:
   virtual ~nsScriptErrorWithStack();
   // Complete stackframe where the error happened.
   // Must be a (possibly wrapped) SavedFrame object.
   JS::Heap<JSObject*> mStack;
   // Global object that must be same-compartment with mStack.
--- a/dom/bindings/nsScriptErrorWithStack.cpp
+++ b/dom/bindings/nsScriptErrorWithStack.cpp
@@ -83,16 +83,23 @@ nsScriptErrorWithStack::~nsScriptErrorWi
 
 NS_IMETHODIMP
 nsScriptErrorWithStack::GetStack(JS::MutableHandleValue aStack) {
     aStack.setObjectOrNull(mStack);
     return NS_OK;
 }
 
 NS_IMETHODIMP
+nsScriptErrorWithStack::GetStackGlobal(JS::MutableHandleValue aStackGlobal)
+{
+    aStackGlobal.setObjectOrNull(mStackGlobal);
+    return NS_OK;
+}
+
+NS_IMETHODIMP
 nsScriptErrorWithStack::ToString(nsACString& /*UTF8*/ aResult)
 {
     MOZ_ASSERT(NS_IsMainThread());
 
     nsCString message;
     nsresult rv = nsScriptErrorBase::ToString(message);
     NS_ENSURE_SUCCESS(rv, rv);
 
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -473,17 +473,23 @@ ConsoleListener::Observe(nsIConsoleMessa
       jsapi.Init();
       JSContext* cx = jsapi.cx();
 
       JS::RootedValue stack(cx);
       rv = scriptError->GetStack(&stack);
       NS_ENSURE_SUCCESS(rv, rv);
 
       if (stack.isObject()) {
-        JSAutoRealmAllowCCW ar(cx, &stack.toObject());
+        // Because |stack| might be a cross-compartment wrapper, we can't use it
+        // with JSAutoRealm. Use the stackGlobal for that.
+        JS::RootedValue stackGlobal(cx);
+        rv = scriptError->GetStackGlobal(&stackGlobal);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        JSAutoRealm ar(cx, &stackGlobal.toObject());
 
         StructuredCloneData data;
         ErrorResult err;
         data.Write(cx, stack, err);
         if (err.Failed()) {
           return err.StealNSResult();
         }
 
--- a/dom/plugins/base/nsJSNPRuntime.cpp
+++ b/dom/plugins/base/nsJSNPRuntime.cpp
@@ -575,23 +575,27 @@ JSValToNPVariant(NPP npp, JSContext *cx,
 
   // The reflected plugin object may be in another compartment if the plugin
   // element has since been adopted into a new document. We don't bother
   // transplanting the plugin objects, and just do a unwrap with security
   // checks if we encounter one of them as an argument. If the unwrap fails,
   // we run with the original wrapped object, since sometimes there are
   // legitimate cases where a security wrapper ends up here (for example,
   // Location objects, which are _always_ behind security wrappers).
-  JS::Rooted<JSObject*> obj(cx, val.toObjectOrNull());
+  JS::Rooted<JSObject*> obj(cx, &val.toObject());
+  JS::Rooted<JSObject*> global(cx);
   obj = js::CheckedUnwrap(obj);
-  if (!obj) {
-    obj = val.toObjectOrNull();
+  if (obj) {
+    global = JS::GetNonCCWObjectGlobal(obj);
+  } else {
+    obj = &val.toObject();
+    global = JS::CurrentGlobalOrNull(cx);
   }
 
-  NPObject* npobj = nsJSObjWrapper::GetNewOrUsed(npp, obj);
+  NPObject* npobj = nsJSObjWrapper::GetNewOrUsed(npp, obj, global);
   if (!npobj) {
     return false;
   }
 
   // Pass over ownership of npobj to *variant
   OBJECT_TO_NPVARIANT(npobj, *variant);
 
   return true;
@@ -640,17 +644,17 @@ ReportExceptionIfPending(JSContext *cx)
   }
 
   ThrowJSExceptionASCII(cx, nullptr);
 
   return false;
 }
 
 nsJSObjWrapper::nsJSObjWrapper(NPP npp)
-  : mJSObj(nullptr), mNpp(npp), mDestroyPending(false)
+  : mJSObj(nullptr), mJSObjGlobal(nullptr), mNpp(npp), mDestroyPending(false)
 {
   MOZ_COUNT_CTOR(nsJSObjWrapper);
   OnWrapperCreated();
 }
 
 nsJSObjWrapper::~nsJSObjWrapper()
 {
   MOZ_COUNT_DTOR(nsJSObjWrapper);
@@ -692,16 +696,17 @@ nsJSObjWrapper::NP_Invalidate(NPObject *
       nsJSObjWrapperKey key(jsnpobj->mJSObj, jsnpobj->mNpp);
       JSObjWrapperTable::Ptr ptr = sJSObjWrappers.lookup(key);
       MOZ_ASSERT(ptr.found());
       sJSObjWrappers.remove(ptr);
     }
 
     // Forget our reference to the JSObject.
     jsnpobj->mJSObj = nullptr;
+    jsnpobj->mJSObjGlobal = nullptr;
   }
 }
 
 static bool
 GetProperty(JSContext *cx, JSObject *objArg, NPIdentifier npid, JS::MutableHandle<JS::Value> rval)
 {
   NS_ASSERTION(NPIdentifierIsInt(npid) || NPIdentifierIsString(npid),
                "id must be either string or int!\n");
@@ -733,17 +738,17 @@ nsJSObjWrapper::NP_HasMethod(NPObject *n
     ThrowJSExceptionASCII(cx,
                           "Null npobj in nsJSObjWrapper::NP_HasMethod!");
 
     return false;
   }
 
   nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
 
-  JSAutoRealmAllowCCW ar(cx, npjsobj->mJSObj);
+  JSAutoRealm ar(cx, npjsobj->mJSObjGlobal);
   MarkCrossZoneNPIdentifier(cx, id);
 
   AutoJSExceptionSuppressor suppressor(aes, npjsobj);
 
   JS::Rooted<JS::Value> v(cx);
   bool ok = GetProperty(cx, npjsobj->mJSObj, id, &v);
 
   return ok && !v.isPrimitive() &&
@@ -773,17 +778,17 @@ doInvoke(NPObject *npobj, NPIdentifier m
   }
 
   // Initialize *result
   VOID_TO_NPVARIANT(*result);
 
   nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
 
   JS::Rooted<JSObject*> jsobj(cx, npjsobj->mJSObj);
-  JSAutoRealmAllowCCW ar(cx, jsobj);
+  JSAutoRealm ar(cx, npjsobj->mJSObjGlobal);
   MarkCrossZoneNPIdentifier(cx, method);
   JS::Rooted<JS::Value> fv(cx);
 
   AutoJSExceptionSuppressor suppressor(aes, npjsobj);
 
   if (method != NPIdentifier_VOID) {
     if (!GetProperty(cx, jsobj, method, &fv) ||
         ::JS_TypeOfValue(cx, fv) != JSTYPE_FUNCTION) {
@@ -866,17 +871,17 @@ nsJSObjWrapper::NP_HasProperty(NPObject 
     return false;
   }
 
   nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
   bool found, ok = false;
 
   AutoJSExceptionSuppressor suppressor(aes, npjsobj);
   JS::Rooted<JSObject*> jsobj(cx, npjsobj->mJSObj);
-  JSAutoRealmAllowCCW ar(cx, jsobj);
+  JSAutoRealm ar(cx, npjsobj->mJSObjGlobal);
   MarkCrossZoneNPIdentifier(cx, npid);
 
   NS_ASSERTION(NPIdentifierIsInt(npid) || NPIdentifierIsString(npid),
                "id must be either string or int!\n");
   JS::Rooted<jsid> id(cx, NPIdentifierToJSId(npid));
   ok = ::JS_HasPropertyById(cx, jsobj, id, &found);
   return ok && found;
 }
@@ -903,17 +908,17 @@ nsJSObjWrapper::NP_GetProperty(NPObject 
                           "Null npobj in nsJSObjWrapper::NP_GetProperty!");
 
     return false;
   }
 
   nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
 
   AutoJSExceptionSuppressor suppressor(aes, npjsobj);
-  JSAutoRealmAllowCCW ar(cx, npjsobj->mJSObj);
+  JSAutoRealm ar(cx, npjsobj->mJSObjGlobal);
   MarkCrossZoneNPIdentifier(cx, id);
 
   JS::Rooted<JS::Value> v(cx);
   return (GetProperty(cx, npjsobj->mJSObj, id, &v) &&
           JSValToNPVariant(npp, cx, v, result));
 }
 
 // static
@@ -940,17 +945,17 @@ nsJSObjWrapper::NP_SetProperty(NPObject 
     return false;
   }
 
   nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
   bool ok = false;
 
   AutoJSExceptionSuppressor suppressor(aes, npjsobj);
   JS::Rooted<JSObject*> jsObj(cx, npjsobj->mJSObj);
-  JSAutoRealmAllowCCW ar(cx, jsObj);
+  JSAutoRealm ar(cx, npjsobj->mJSObjGlobal);
   MarkCrossZoneNPIdentifier(cx, npid);
 
   JS::Rooted<JS::Value> v(cx, NPVariantToJSVal(npp, cx, value));
 
   NS_ASSERTION(NPIdentifierIsInt(npid) || NPIdentifierIsString(npid),
                "id must be either string or int!\n");
   JS::Rooted<jsid> id(cx, NPIdentifierToJSId(npid));
   ok = ::JS_SetPropertyById(cx, jsObj, id, v);
@@ -978,17 +983,17 @@ nsJSObjWrapper::NP_RemoveProperty(NPObje
     return false;
   }
 
   nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
 
   AutoJSExceptionSuppressor suppressor(aes, npjsobj);
   JS::ObjectOpResult result;
   JS::Rooted<JSObject*> obj(cx, npjsobj->mJSObj);
-  JSAutoRealmAllowCCW ar(cx, obj);
+  JSAutoRealm ar(cx, npjsobj->mJSObjGlobal);
   MarkCrossZoneNPIdentifier(cx, npid);
 
   NS_ASSERTION(NPIdentifierIsInt(npid) || NPIdentifierIsString(npid),
                "id must be either string or int!\n");
   JS::Rooted<jsid> id(cx, NPIdentifierToJSId(npid));
   if (!::JS_DeletePropertyById(cx, obj, id, result))
     return false;
 
@@ -1032,17 +1037,17 @@ nsJSObjWrapper::NP_Enumerate(NPObject *n
 
     return false;
   }
 
   nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
 
   AutoJSExceptionSuppressor suppressor(aes, npjsobj);
   JS::Rooted<JSObject*> jsobj(cx, npjsobj->mJSObj);
-  JSAutoRealmAllowCCW ar(cx, jsobj);
+  JSAutoRealm ar(cx, npjsobj->mJSObjGlobal);
 
   JS::Rooted<JS::IdVector> ida(cx, JS::IdVector(cx));
   if (!JS_Enumerate(cx, jsobj, &ida)) {
     return false;
   }
 
   *count = ida.length();
   *idarray = (NPIdentifier*) malloc(*count * sizeof(NPIdentifier));
@@ -1086,24 +1091,29 @@ nsJSObjWrapper::NP_Construct(NPObject *n
 {
   return doInvoke(npobj, NPIdentifier_VOID, args, argCount, true, result);
 }
 
 // Look up or create an NPObject that wraps the JSObject obj.
 
 // static
 NPObject *
-nsJSObjWrapper::GetNewOrUsed(NPP npp, JS::Handle<JSObject*> obj)
+nsJSObjWrapper::GetNewOrUsed(NPP npp, JS::Handle<JSObject*> obj,
+                             JS::Handle<JSObject*> objGlobal)
 {
   if (!npp) {
     NS_ERROR("Null NPP passed to nsJSObjWrapper::GetNewOrUsed()!");
 
     return nullptr;
   }
 
+  MOZ_ASSERT(JS_IsGlobalObject(objGlobal));
+  MOZ_RELEASE_ASSERT(js::GetObjectCompartment(obj) ==
+                     js::GetObjectCompartment(objGlobal));
+
   // No need to enter the right compartment here as we only get the
   // class and private from the JSObject, neither of which cares about
   // compartments.
 
   if (nsNPObjWrapper::IsWrapper(obj)) {
     // obj is one of our own, its private data is the NPObject we're
     // looking for.
 
@@ -1143,16 +1153,17 @@ nsJSObjWrapper::GetNewOrUsed(NPP npp, JS
     (nsJSObjWrapper *)_createobject(npp, &sJSObjWrapperNPClass);
 
   if (!wrapper) {
     // Out of memory, entry not yet added to table.
     return nullptr;
   }
 
   wrapper->mJSObj = obj;
+  wrapper->mJSObjGlobal = objGlobal;
 
   // Insert the new wrapper into the hashtable, rooting the JSObject. Its
   // lifetime is now tied to that of the NPObject.
   if (!sJSObjWrappers.putNew(nsJSObjWrapperKey(obj, npp), wrapper)) {
     // Out of memory, free the wrapper we created.
     _releaseobject(wrapper);
     return nullptr;
   }
--- a/dom/plugins/base/nsJSNPRuntime.h
+++ b/dom/plugins/base/nsJSNPRuntime.h
@@ -49,24 +49,30 @@ public:
 
   JS::Heap<JSObject*> mJSObj;
   NPP mNpp;
 };
 
 class nsJSObjWrapper : public NPObject
 {
 public:
-  JS::Heap<JSObject *> mJSObj;
+  JS::Heap<JSObject*> mJSObj;
+  // Because mJSObj might be a cross-compartment wrapper, we can't use it to
+  // enter the target realm. We use this global instead (it's always
+  // same-compartment with mJSObj).
+  JS::Heap<JSObject*> mJSObjGlobal;
   const NPP mNpp;
   bool mDestroyPending;
 
-  static NPObject* GetNewOrUsed(NPP npp, JS::Handle<JSObject*> obj);
+  static NPObject* GetNewOrUsed(NPP npp, JS::Handle<JSObject*> obj,
+                                JS::Handle<JSObject*> objGlobal);
 
   void trace(JSTracer* trc) {
-      JS::TraceEdge(trc, &mJSObj, "nsJSObjWrapper");
+      JS::TraceEdge(trc, &mJSObj, "nsJSObjWrapper::mJSObj");
+      JS::TraceEdge(trc, &mJSObjGlobal, "nsJSObjWrapper::mJSObjGlobal");
   }
 
 protected:
   explicit nsJSObjWrapper(NPP npp);
   ~nsJSObjWrapper();
 
   static NPObject * NP_Allocate(NPP npp, NPClass *aClass);
   static void NP_Deallocate(NPObject *obj);
--- a/dom/plugins/base/nsNPAPIPlugin.cpp
+++ b/dom/plugins/base/nsNPAPIPlugin.cpp
@@ -693,19 +693,21 @@ NPObject*
 
   // The window want to return here is the outer window, *not* the inner (since
   // we don't know what the plugin will do with it).
   nsIDocument* doc = GetDocumentFromNPP(npp);
   NS_ENSURE_TRUE(doc, nullptr);
   nsCOMPtr<nsPIDOMWindowOuter> outer = doc->GetWindow();
   NS_ENSURE_TRUE(outer, nullptr);
 
+  JS::Rooted<JSObject*> windowProxy(dom::RootingCx(),
+                                    nsGlobalWindowOuter::Cast(outer)->GetGlobalJSObject());
   JS::Rooted<JSObject*> global(dom::RootingCx(),
-                               nsGlobalWindowOuter::Cast(outer)->GetGlobalJSObject());
-  return nsJSObjWrapper::GetNewOrUsed(npp, global);
+                               JS::GetNonCCWObjectGlobal(windowProxy));
+  return nsJSObjWrapper::GetNewOrUsed(npp, windowProxy, global);
 }
 
 NPObject*
 _getpluginelement(NPP npp)
 {
   if (!NS_IsMainThread()) {
     NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_getpluginelement called from the wrong thread\n"));
     return nullptr;
@@ -740,18 +742,18 @@ NPObject*
     return nullptr;
   }
 
   if (NS_WARN_IF(!val.isObject())) {
     return nullptr;
   }
 
   JS::RootedObject obj(cx, &val.toObject());
-
-  return nsJSObjWrapper::GetNewOrUsed(npp, obj);
+  JS::RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
+  return nsJSObjWrapper::GetNewOrUsed(npp, obj, global);
 }
 
 NPIdentifier
 _getstringidentifier(const NPUTF8* name)
 {
   if (!name) {
     NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS, ("NPN_getstringidentifier: passed null name"));
     return nullptr;
--- a/dom/xbl/nsXBLBinding.cpp
+++ b/dom/xbl/nsXBLBinding.cpp
@@ -991,17 +991,17 @@ nsXBLBinding::DoInitJSClass(JSContext *c
   } else {
     JSAutoRealm innerAR(cx, xblScope);
     holder = GetOrCreateClassObjectMap(cx, xblScope, "__ContentClassObjectMap__");
   }
   if (NS_WARN_IF(!holder)) {
     return NS_ERROR_FAILURE;
   }
   js::AssertSameCompartment(holder, xblScope);
-  JSAutoRealmAllowCCW ar(cx, holder);
+  JSAutoRealm ar(cx, holder);
 
   // Look up the class on the property holder. The only properties on the
   // holder should be class objects. If we don't find the class object, we need
   // to create and define it.
   JS::Rooted<JSObject*> proto(cx);
   JS::Rooted<JS::PropertyDescriptor> desc(cx);
   if (!JS_GetOwnUCPropertyDescriptor(cx, holder, aClassName.get(), &desc)) {
     return NS_ERROR_OUT_OF_MEMORY;
@@ -1031,17 +1031,17 @@ nsXBLBinding::DoInitJSClass(JSContext *c
     nsXBLDocumentInfo* docInfo = aProtoBinding->XBLDocumentInfo();
     ::JS_SetPrivate(proto, docInfo);
     NS_ADDREF(docInfo);
     RecordReplayRegisterDeferredFinalize(docInfo);
     JS_SetReservedSlot(proto, 0, JS::PrivateValue(aProtoBinding));
 
     // Next, enter the realm of the property holder, wrap the proto, and
     // stick it on.
-    JSAutoRealmAllowCCW ar3(cx, holder);
+    JSAutoRealm ar3(cx, holder);
     if (!JS_WrapObject(cx, &proto) ||
         !JS_DefineUCProperty(cx, holder, aClassName.get(), -1, proto,
                              JSPROP_READONLY | JSPROP_PERMANENT))
     {
       return NS_ERROR_OUT_OF_MEMORY;
     }
   }
 
--- a/toolkit/content/widgets/scrollbox.xml
+++ b/toolkit/content/widgets/scrollbox.xml
@@ -151,18 +151,16 @@
         this.orient == "vertical" ? ["top", "bottom"] : ["left", "right"];
       ]]></field>
 
       <field name="_isRTLScrollbox"><![CDATA[
         this.orient != "vertical" &&
         document.defaultView.getComputedStyle(this._scrollbox).direction == "rtl";
       ]]></field>
 
-      <field name="_scrollTarget">null</field>
-
       <method name="_boundsWithoutFlushing">
         <parameter name="element"/>
         <body><![CDATA[
           if (!("_DOMWindowUtils" in this)) {
             this._DOMWindowUtils = window.windowUtils;
           }
 
           return this._DOMWindowUtils ?
@@ -182,44 +180,41 @@
           // If we get only zeros for the client rect, this means the element
           // is hidden. As a performance optimization, we don't flush layout
           // here which means that on the fly changes aren't fully supported.
           let rect = this._boundsWithoutFlushing(element);
           return !!(rect.top || rect.left || rect.width || rect.height);
         ]]></body>
       </method>
 
+      <field name="_ensureElementIsVisibleAnimationFrame">0</field>
       <method name="ensureElementIsVisible">
         <parameter name="element"/>
         <parameter name="aInstant"/>
         <body><![CDATA[
           if (!this._canScrollToElement(element))
             return;
 
-          element.scrollIntoView({ behavior: aInstant ? "instant" : "auto" });
+          if (this._ensureElementIsVisibleAnimationFrame) {
+            window.cancelAnimationFrame(this._ensureElementIsVisibleAnimationFrame);
+          }
+          this._ensureElementIsVisibleAnimationFrame = window.requestAnimationFrame(() => {
+            element.scrollIntoView({ behavior: aInstant ? "instant" : "auto" });
+            this._ensureElementIsVisibleAnimationFrame = 0;
+          });
         ]]></body>
       </method>
 
       <method name="scrollByIndex">
         <parameter name="index"/>
         <parameter name="aInstant"/>
         <body><![CDATA[
           if (index == 0)
             return;
 
-          // Each scrollByIndex call is expected to scroll the given number of
-          // items. If a previous call is still in progress because of smooth
-          // scrolling, we need to complete it before starting a new one.
-          if (this._scrollTarget) {
-            let elements = this._getScrollableElements();
-            if (this._scrollTarget != elements[0] &&
-                this._scrollTarget != elements[elements.length - 1])
-              this.ensureElementIsVisible(this._scrollTarget, true);
-          }
-
           var rect = this.scrollClientRect;
           var [start, end] = this._startEndProps;
           var x = index > 0 ? rect[end] + 1 : rect[start] - 1;
           var nextElement = this._elementFromPoint(x, index);
           if (!nextElement)
             return;
 
           var targetElement;