Bug 1353867 - Add cross-process proxies for WindowProxy. r=bzbarsky
☠☠ backed out by cc4bb8c7fa92 ☠ ☠
authorPeter Van der Beken <peterv@propagandism.org>
Mon, 31 Dec 2018 12:44:07 +0000
changeset 452193 aa9b106b15d93573e5125356667f3a1f474d2e5a
parent 452192 8c05f4d3f7ad300b5ee0ac6c78cc34b19107a4e9
child 452194 19828a8dd8ae5ce8d7aa37d57ed607ee3043e49b
push id35292
push userdvarga@mozilla.com
push dateMon, 31 Dec 2018 21:26:28 +0000
treeherdermozilla-central@d96e39ed615b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbzbarsky
bugs1353867
milestone66.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
Bug 1353867 - Add cross-process proxies for WindowProxy. r=bzbarsky Differential Revision: https://phabricator.services.mozilla.com/D12656
docshell/base/BrowsingContext.cpp
docshell/base/BrowsingContext.h
docshell/base/ChromeBrowsingContext.h
docshell/base/moz.build
docshell/base/nsDocShell.cpp
dom/base/Location.h
dom/base/PostMessageEvent.cpp
dom/base/PostMessageEvent.h
dom/base/RemoteOuterWindowProxy.cpp
dom/base/WindowProxyHolder.h
dom/base/moz.build
dom/base/nsDocument.cpp
dom/base/nsGlobalWindowInner.cpp
dom/base/nsGlobalWindowInner.h
dom/base/nsGlobalWindowOuter.cpp
dom/base/nsGlobalWindowOuter.h
dom/base/nsIDocument.h
dom/base/nsPIDOMWindow.h
dom/bindings/BindingUtils.cpp
dom/bindings/Codegen.py
dom/bindings/RemoteObjectProxy.cpp
dom/bindings/RemoteObjectProxy.h
dom/bindings/ToJSValue.cpp
dom/bindings/moz.build
dom/bindings/nsScriptError.cpp
dom/bindings/nsScriptError.h
dom/events/UIEvent.h
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PContent.ipdl
dom/smil/TimeEvent.h
dom/webidl/Window.webidl
js/xpconnect/src/XPCJSRuntime.cpp
js/xpconnect/src/xpcprivate.h
media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
xpfe/appshell/nsContentTreeOwner.cpp
--- a/docshell/base/BrowsingContext.cpp
+++ b/docshell/base/BrowsingContext.cpp
@@ -5,24 +5,29 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/BrowsingContext.h"
 
 #include "mozilla/dom/ChromeBrowsingContext.h"
 #include "mozilla/dom/BrowsingContextBinding.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/Location.h"
+#include "mozilla/dom/LocationBinding.h"
+#include "mozilla/dom/WindowBinding.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/HashTable.h"
 #include "mozilla/Logging.h"
 #include "mozilla/StaticPtr.h"
 
 #include "nsDocShell.h"
+#include "nsGlobalWindowOuter.h"
 #include "nsContentUtils.h"
+#include "nsScriptError.h"
 #include "nsThreadUtils.h"
 
 namespace mozilla {
 namespace dom {
 
 static LazyLogModule gBrowsingContextLog("BrowsingContext");
 
 static StaticAutoPtr<BrowsingContext::Children> sRootBrowsingContexts;
@@ -45,24 +50,22 @@ static void Register(BrowsingContext* aB
 
 static void Sync(BrowsingContext* aBrowsingContext) {
   if (!XRE_IsContentProcess()) {
     return;
   }
 
   auto cc = ContentChild::GetSingleton();
   MOZ_DIAGNOSTIC_ASSERT(cc);
-  nsAutoString name;
-  aBrowsingContext->GetName(name);
   RefPtr<BrowsingContext> parent = aBrowsingContext->GetParent();
   BrowsingContext* opener = aBrowsingContext->GetOpener();
   cc->SendAttachBrowsingContext(BrowsingContextId(parent ? parent->Id() : 0),
                                 BrowsingContextId(opener ? opener->Id() : 0),
                                 BrowsingContextId(aBrowsingContext->Id()),
-                                name);
+                                aBrowsingContext->Name());
 }
 
 /* static */ void BrowsingContext::Init() {
   if (!sRootBrowsingContexts) {
     sRootBrowsingContexts = new BrowsingContext::Children();
     ClearOnShutdown(&sRootBrowsingContexts);
   }
 
@@ -146,17 +149,18 @@ static void Sync(BrowsingContext* aBrows
 BrowsingContext::BrowsingContext(BrowsingContext* aParent,
                                  BrowsingContext* aOpener,
                                  const nsAString& aName,
                                  uint64_t aBrowsingContextId, Type aType)
     : mType(aType),
       mBrowsingContextId(aBrowsingContextId),
       mParent(aParent),
       mOpener(aOpener),
-      mName(aName) {
+      mName(aName),
+      mClosed(false) {
   if (mParent) {
     mBrowsingContextGroup = mParent->mBrowsingContextGroup;
   } else if (mOpener) {
     mBrowsingContextGroup = mOpener->mBrowsingContextGroup;
   } else {
     mBrowsingContextGroup = new BrowsingContextGroup();
   }
 
@@ -320,10 +324,142 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
   }
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(BrowsingContext)
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(BrowsingContext, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(BrowsingContext, Release)
 
+void BrowsingContext::Location(JSContext* aCx,
+                               JS::MutableHandle<JSObject*> aLocation,
+                               OOMReporter& aError) {}
+
+void BrowsingContext::Close(CallerType aCallerType, ErrorResult& aError) {
+  // FIXME We need to set mClosed, but only once we're sending the
+  //       DOMWindowClose event (which happens in the process where the
+  //       document for this browsing context is loaded).
+  //       See https://bugzilla.mozilla.org/show_bug.cgi?id=1516343.
+  ContentChild* cc = ContentChild::GetSingleton();
+  cc->SendWindowClose(BrowsingContextId(mBrowsingContextId),
+                      aCallerType == CallerType::System);
+}
+
+void BrowsingContext::Focus(ErrorResult& aError) {
+  ContentChild* cc = ContentChild::GetSingleton();
+  cc->SendWindowFocus(BrowsingContextId(mBrowsingContextId));
+}
+
+void BrowsingContext::Blur(ErrorResult& aError) {
+  ContentChild* cc = ContentChild::GetSingleton();
+  cc->SendWindowBlur(BrowsingContextId(mBrowsingContextId));
+}
+
+Nullable<WindowProxyHolder> BrowsingContext::GetTop(ErrorResult& aError) {
+  // We never return null or throw an error, but the implementation in
+  // nsGlobalWindow does and we need to use the same signature.
+  BrowsingContext* bc = this;
+  BrowsingContext* parent;
+  while ((parent = bc->mParent)) {
+    bc = parent;
+  }
+  return WindowProxyHolder(bc);
+}
+
+void BrowsingContext::GetOpener(JSContext* aCx,
+                                JS::MutableHandle<JS::Value> aOpener,
+                                ErrorResult& aError) const {
+  auto* opener = GetOpener();
+  if (!opener) {
+    aOpener.setNull();
+    return;
+  }
+
+  if (!ToJSValue(aCx, WindowProxyHolder(opener), aOpener)) {
+    aError.NoteJSContextException(aCx);
+  }
+}
+
+Nullable<WindowProxyHolder> BrowsingContext::GetParent(
+    ErrorResult& aError) const {
+  // We never throw an error, but the implementation in nsGlobalWindow does and
+  // we need to use the same signature.
+  if (!mParent) {
+    return nullptr;
+  }
+  return WindowProxyHolder(mParent.get());
+}
+
+void BrowsingContext::PostMessageMoz(JSContext* aCx,
+                                     JS::Handle<JS::Value> aMessage,
+                                     const nsAString& aTargetOrigin,
+                                     const Sequence<JSObject*>& aTransfer,
+                                     nsIPrincipal& aSubjectPrincipal,
+                                     ErrorResult& aError) {
+  RefPtr<BrowsingContext> sourceBc;
+  PostMessageData data;
+  data.targetOrigin() = aTargetOrigin;
+  data.subjectPrincipal() = &aSubjectPrincipal;
+  RefPtr<nsGlobalWindowInner> callerInnerWindow;
+  if (!nsGlobalWindowOuter::GatherPostMessageData(
+          aCx, aTargetOrigin, getter_AddRefs(sourceBc), data.origin(),
+          getter_AddRefs(data.targetOriginURI()),
+          getter_AddRefs(data.callerPrincipal()),
+          getter_AddRefs(callerInnerWindow),
+          getter_AddRefs(data.callerDocumentURI()), aError)) {
+    return;
+  }
+  data.source() = BrowsingContextId(sourceBc->Id());
+  data.isFromPrivateWindow() =
+      callerInnerWindow &&
+      nsScriptErrorBase::ComputeIsFromPrivateWindow(callerInnerWindow);
+
+  JS::Rooted<JS::Value> transferArray(aCx);
+  aError = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransfer,
+                                                             &transferArray);
+  if (NS_WARN_IF(aError.Failed())) {
+    return;
+  }
+
+  ipc::StructuredCloneData message;
+  message.Write(aCx, aMessage, transferArray, aError);
+  if (NS_WARN_IF(aError.Failed())) {
+    return;
+  }
+
+  ContentChild* cc = ContentChild::GetSingleton();
+  ClonedMessageData messageData;
+  if (!message.BuildClonedMessageDataForChild(cc, messageData)) {
+    aError.Throw(NS_ERROR_FAILURE);
+    return;
+  }
+
+  cc->SendWindowPostMessage(BrowsingContextId(mBrowsingContextId), messageData,
+                            data);
+}
+
+void BrowsingContext::PostMessageMoz(JSContext* aCx,
+                                     JS::Handle<JS::Value> aMessage,
+                                     const WindowPostMessageOptions& aOptions,
+                                     nsIPrincipal& aSubjectPrincipal,
+                                     ErrorResult& aError) {
+  PostMessageMoz(aCx, aMessage, aOptions.mTargetOrigin, aOptions.mTransfer,
+                 aSubjectPrincipal, aError);
+}
+
+already_AddRefed<BrowsingContext> BrowsingContext::FindChildWithName(
+    const nsAString& aName) {
+  // FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=1515646 will reimplement
+  //       this on top of the BC tree.
+  MOZ_ASSERT(mDocShell);
+  nsCOMPtr<nsIDocShellTreeItem> child;
+  mDocShell->FindChildWithName(aName, false, true, nullptr, nullptr,
+                               getter_AddRefs(child));
+  nsCOMPtr<nsIDocShell> childDS = do_QueryInterface(child);
+  RefPtr<BrowsingContext> bc;
+  if (childDS) {
+    childDS->GetBrowsingContext(getter_AddRefs(bc));
+  }
+  return bc.forget();
+}
+
 }  // namespace dom
 }  // namespace mozilla
--- a/docshell/base/BrowsingContext.h
+++ b/docshell/base/BrowsingContext.h
@@ -5,34 +5,43 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_BrowsingContext_h
 #define mozilla_dom_BrowsingContext_h
 
 #include "mozilla/LinkedList.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/WeakPtr.h"
+#include "mozilla/dom/BindingDeclarations.h"
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsIDocShell.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "nsWrapperCache.h"
 
 class nsGlobalWindowOuter;
 class nsOuterWindowProxy;
 
 namespace mozilla {
 
+class ErrorResult;
 class LogModule;
+class OOMReporter;
 
 namespace dom {
 
 class BrowsingContext;
 class ContentParent;
+template <typename>
+struct Nullable;
+template <typename T>
+class Sequence;
+struct WindowPostMessageOptions;
+class WindowProxyHolder;
 
 // List of top-level or auxiliary BrowsingContexts
 class BrowsingContextGroup : public nsTArray<WeakPtr<BrowsingContext>> {
  public:
   NS_INLINE_DECL_REFCOUNTING(BrowsingContextGroup)
  private:
   ~BrowsingContextGroup() {}
 };
@@ -104,28 +113,28 @@ class BrowsingContext : public nsWrapper
 
   // Determine if the current BrowsingContext was 'cached' by the logic in
   // CacheChildren.
   bool IsCached();
 
   // TODO(farre): We should sync changes from SetName to the parent
   // process. [Bug 1490303]
   void SetName(const nsAString& aName) { mName = aName; }
-  void GetName(nsAString& aName) { aName = mName; }
+  const nsString& Name() const { return mName; }
   bool NameEquals(const nsAString& aName) { return mName.Equals(aName); }
 
   bool IsContent() const { return mType == Type::Content; }
 
   uint64_t Id() const { return mBrowsingContextId; }
 
   BrowsingContext* GetParent() { return mParent; }
 
   void GetChildren(nsTArray<RefPtr<BrowsingContext>>& aChildren);
 
-  BrowsingContext* GetOpener() { return mOpener; }
+  BrowsingContext* GetOpener() const { return mOpener; }
 
   void SetOpener(BrowsingContext* aOpener);
 
   static void GetRootBrowsingContexts(
       nsTArray<RefPtr<BrowsingContext>>& aBrowsingContexts);
 
   nsISupports* GetParentObject() const;
   JSObject* WrapObject(JSContext* aCx,
@@ -138,16 +147,44 @@ class BrowsingContext : public nsWrapper
     mWindowProxy = aWindowProxy;
   }
 
   MOZ_DECLARE_WEAKREFERENCE_TYPENAME(BrowsingContext)
   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(BrowsingContext)
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(BrowsingContext)
 
   using Children = nsTArray<RefPtr<BrowsingContext>>;
+  const Children& GetChildren() { return mChildren; }
+
+  // Window APIs that are cross-origin-accessible (from the HTML spec).
+  BrowsingContext* Window() { return Self(); }
+  BrowsingContext* Self() { return this; }
+  void Location(JSContext* aCx, JS::MutableHandle<JSObject*> aLocation,
+                OOMReporter& aError);
+  void Close(CallerType aCallerType, ErrorResult& aError);
+  bool GetClosed(ErrorResult&) { return mClosed; }
+  void Focus(ErrorResult& aError);
+  void Blur(ErrorResult& aError);
+  BrowsingContext* GetFrames(ErrorResult& aError) { return Self(); }
+  int32_t Length() const { return mChildren.Length(); }
+  Nullable<WindowProxyHolder> GetTop(ErrorResult& aError);
+  void GetOpener(JSContext* aCx, JS::MutableHandle<JS::Value> aOpener,
+                 ErrorResult& aError) const;
+  Nullable<WindowProxyHolder> GetParent(ErrorResult& aError) const;
+  void PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
+                      const nsAString& aTargetOrigin,
+                      const Sequence<JSObject*>& aTransfer,
+                      nsIPrincipal& aSubjectPrincipal, ErrorResult& aError);
+  void PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
+                      const WindowPostMessageOptions& aOptions,
+                      nsIPrincipal& aSubjectPrincipal, ErrorResult& aError);
+
+  already_AddRefed<BrowsingContext> FindChildWithName(const nsAString& aName);
+
+  JSObject* WrapObject(JSContext* aCx);
 
  protected:
   virtual ~BrowsingContext();
   BrowsingContext(BrowsingContext* aParent, BrowsingContext* aOpener,
                   const nsAString& aName, uint64_t aBrowsingContextId,
                   Type aType);
 
  private:
@@ -179,14 +216,27 @@ class BrowsingContext : public nsWrapper
   WeakPtr<BrowsingContext> mOpener;
   nsCOMPtr<nsIDocShell> mDocShell;
   nsString mName;
   // This is not a strong reference, but using a JS::Heap for that should be
   // fine. The JSObject stored in here should be a proxy with a
   // nsOuterWindowProxy handler, which will update the pointer from its
   // objectMoved hook and clear it from its finalize hook.
   JS::Heap<JSObject*> mWindowProxy;
+  bool mClosed;
 };
 
+/**
+ * Gets a WindowProxy object for a BrowsingContext that lives in a different
+ * process (creating the object if it doesn't already exist). The WindowProxy
+ * object will be in the compartment that aCx is currently in. This should only
+ * be called if aContext doesn't hold a docshell, otherwise the BrowsingContext
+ * lives in this process, and a same-process WindowProxy should be used (see
+ * nsGlobalWindowOuter). This should only be called by bindings code, ToJSValue
+ * is the right API to get a WindowProxy for a BrowsingContext.
+ */
+extern bool GetRemoteOuterWindowProxy(JSContext* aCx, BrowsingContext* aContext,
+                                      JS::MutableHandle<JSObject*> aRetVal);
+
 }  // namespace dom
 }  // namespace mozilla
 
 #endif  // !defined(mozilla_dom_BrowsingContext_h)
--- a/docshell/base/ChromeBrowsingContext.h
+++ b/docshell/base/ChromeBrowsingContext.h
@@ -29,16 +29,17 @@ class ChromeBrowsingContext final : publ
   static void CleanupContexts(uint64_t aProcessId);
   static already_AddRefed<ChromeBrowsingContext> Get(uint64_t aId);
   static ChromeBrowsingContext* Cast(BrowsingContext* aContext);
   static const ChromeBrowsingContext* Cast(const BrowsingContext* aContext);
 
   bool IsOwnedByProcess(uint64_t aProcessId) const {
     return mProcessId == aProcessId;
   }
+  uint64_t OwnerProcessId() const { return mProcessId; }
 
   void GetWindowGlobals(nsTArray<RefPtr<WindowGlobalParent>>& aWindows);
 
   // Called by WindowGlobalParent to register and unregister window globals.
   void RegisterWindowGlobal(WindowGlobalParent* aGlobal);
   void UnregisterWindowGlobal(WindowGlobalParent* aGlobal);
 
   // The current active WindowGlobal.
--- a/docshell/base/moz.build
+++ b/docshell/base/moz.build
@@ -98,16 +98,17 @@ UNIFIED_SOURCES += [
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 LOCAL_INCLUDES += [
     '/docshell/shistory',
     '/dom/base',
+    '/dom/bindings',
     '/layout/base',
     '/layout/generic',
     '/layout/style',
     '/layout/xul',
     '/netwerk/base',
     '/netwerk/protocol/viewsource',
     '/toolkit/components/browser',
     '/toolkit/components/find',
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -2304,17 +2304,17 @@ nsDocShell::NotifyScrollObservers() {
 }
 
 //*****************************************************************************
 // nsDocShell::nsIDocShellTreeItem
 //*****************************************************************************
 
 NS_IMETHODIMP
 nsDocShell::GetName(nsAString& aName) {
-  mBrowsingContext->GetName(aName);
+  aName = mBrowsingContext->Name();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocShell::SetName(const nsAString& aName) {
   mBrowsingContext->SetName(aName);
   return NS_OK;
 }
--- a/dom/base/Location.h
+++ b/dom/base/Location.h
@@ -4,16 +4,17 @@
  * 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 mozilla_dom_Location_h
 #define mozilla_dom_Location_h
 
 #include "js/TypeDecls.h"
 #include "mozilla/ErrorResult.h"
+#include "mozilla/dom/BrowsingContext.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsIWeakReferenceUtils.h"
 #include "nsPIDOMWindow.h"
 #include "nsString.h"
 #include "nsWrapperCache.h"
 
 class nsIDocShell;
 class nsIURI;
@@ -23,16 +24,18 @@ namespace mozilla {
 namespace dom {
 
 //*****************************************************************************
 // Location: Script "location" object
 //*****************************************************************************
 
 class Location final : public nsISupports, public nsWrapperCache {
  public:
+  typedef Location RemoteProxy;
+
   Location(nsPIDOMWindowInner* aWindow, nsIDocShell* aDocShell);
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Location)
 
   // WebIDL API:
   void Assign(const nsAString& aUrl, nsIPrincipal& aSubjectPrincipal,
               ErrorResult& aError);
--- a/dom/base/PostMessageEvent.cpp
+++ b/dom/base/PostMessageEvent.cpp
@@ -16,41 +16,42 @@
 #include "mozilla/dom/MessagePortBinding.h"
 #include "mozilla/dom/PMessagePort.h"
 #include "mozilla/dom/StructuredCloneTags.h"
 #include "mozilla/dom/UnionConversions.h"
 #include "mozilla/EventDispatcher.h"
 #include "nsContentUtils.h"
 #include "nsDocShell.h"
 #include "nsGlobalWindow.h"
+#include "nsIConsoleService.h"
 #include "nsIPresShell.h"
 #include "nsIPrincipal.h"
 #include "nsIScriptError.h"
+#include "nsNetUtil.h"
 #include "nsPresContext.h"
 #include "nsQueryObject.h"
 
 namespace mozilla {
 namespace dom {
 
 PostMessageEvent::PostMessageEvent(BrowsingContext* aSource,
                                    const nsAString& aCallerOrigin,
                                    nsGlobalWindowOuter* aTargetWindow,
                                    nsIPrincipal* aProvidedPrincipal,
-                                   uint64_t aCallerWindowID,
-                                   nsIURI* aCallerDocumentURI)
+                                   const Maybe<uint64_t>& aCallerWindowID,
+                                   nsIURI* aCallerDocumentURI,
+                                   bool aIsFromPrivateWindow)
     : Runnable("dom::PostMessageEvent"),
       mSource(aSource),
       mCallerOrigin(aCallerOrigin),
       mTargetWindow(aTargetWindow),
       mProvidedPrincipal(aProvidedPrincipal),
-      mHolder(StructuredCloneHolder::CloningSupported,
-              StructuredCloneHolder::TransferringSupported,
-              JS::StructuredCloneScope::SameProcessSameThread),
       mCallerWindowID(aCallerWindowID),
-      mCallerDocumentURI(aCallerDocumentURI) {}
+      mCallerDocumentURI(aCallerDocumentURI),
+      mIsFromPrivateWindow(aIsFromPrivateWindow) {}
 
 PostMessageEvent::~PostMessageEvent() {}
 
 NS_IMETHODIMP
 PostMessageEvent::Run() {
   // Note: We don't init this AutoJSAPI with targetWindow, because we do not
   // want exceptions during message deserialization to trigger error events on
   // targetWindow.
@@ -117,45 +118,74 @@ PostMessageEvent::Run() {
       NS_ENSURE_SUCCESS(rv, rv);
 
       const char16_t* params[] = {providedOrigin.get(), targetOrigin.get()};
 
       nsAutoString errorText;
       nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES,
                                             "TargetPrincipalDoesNotMatch",
                                             params, errorText);
-      nsContentUtils::ReportToConsoleByWindowID(
-          errorText, nsIScriptError::errorFlag,
-          NS_LITERAL_CSTRING("DOM Window"), mCallerWindowID, callerDocumentURI);
+
+      nsCOMPtr<nsIScriptError> errorObject =
+          do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
+      NS_ENSURE_SUCCESS(rv, rv);
 
-      return NS_OK;
+      if (mCallerWindowID.isSome()) {
+        rv = errorObject->InitWithSourceURI(
+            errorText, callerDocumentURI, EmptyString(), 0, 0,
+            nsIScriptError::errorFlag, "DOM Window", mCallerWindowID.value());
+      } else {
+        nsString uriSpec;
+        rv = NS_GetSanitizedURIStringFromURI(callerDocumentURI, uriSpec);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        rv = errorObject->Init(errorText, uriSpec, EmptyString(), 0, 0,
+                               nsIScriptError::errorFlag, "DOM Window",
+                               mIsFromPrivateWindow);
+      }
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      nsCOMPtr<nsIConsoleService> consoleService =
+          do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      return consoleService->LogMessage(errorObject);
     }
   }
 
   IgnoredErrorResult rv;
   JS::Rooted<JS::Value> messageData(cx);
   nsCOMPtr<mozilla::dom::EventTarget> eventTarget =
       do_QueryObject(targetWindow);
 
-  mHolder.Read(targetWindow->AsInner(), cx, &messageData, rv);
+  StructuredCloneHolder* holder;
+  if (mHolder.constructed<StructuredCloneHolder>()) {
+    mHolder.ref<StructuredCloneHolder>().Read(targetWindow->AsInner(), cx,
+                                              &messageData, rv);
+    holder = &mHolder.ref<StructuredCloneHolder>();
+  } else {
+    MOZ_ASSERT(mHolder.constructed<ipc::StructuredCloneData>());
+    mHolder.ref<ipc::StructuredCloneData>().Read(cx, &messageData, rv);
+    holder = &mHolder.ref<ipc::StructuredCloneData>();
+  }
   if (NS_WARN_IF(rv.Failed())) {
     DispatchError(cx, targetWindow, eventTarget);
     return NS_OK;
   }
 
   // Create the event
   RefPtr<MessageEvent> event = new MessageEvent(eventTarget, nullptr, nullptr);
 
   Nullable<WindowProxyOrMessagePortOrServiceWorker> source;
   if (mSource) {
     source.SetValue().SetAsWindowProxy() = mSource;
   }
 
   Sequence<OwningNonNull<MessagePort>> ports;
-  if (!mHolder.TakeTransferredPortsAsSequence(ports)) {
+  if (!holder->TakeTransferredPortsAsSequence(ports)) {
     DispatchError(cx, targetWindow, eventTarget);
     return NS_OK;
   }
 
   event->InitMessageEvent(nullptr, NS_LITERAL_STRING("message"), CanBubble::eNo,
                           Cancelable::eNo, messageData, mCallerOrigin,
                           EmptyString(), source, ports);
 
--- a/dom/base/PostMessageEvent.h
+++ b/dom/base/PostMessageEvent.h
@@ -3,18 +3,20 @@
 /* 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 mozilla_dom_PostMessageEvent_h
 #define mozilla_dom_PostMessageEvent_h
 
 #include "mozilla/dom/Event.h"
+#include "mozilla/dom/ipc/StructuredCloneData.h"
 #include "mozilla/dom/StructuredCloneHolder.h"
 #include "nsCOMPtr.h"
+#include "mozilla/MaybeOneOf.h"
 #include "mozilla/RefPtr.h"
 #include "nsTArray.h"
 #include "nsThreadUtils.h"
 
 class nsGlobalWindowOuter;
 class nsGlobalWindowInner;
 class nsIDocument;
 class nsIPrincipal;
@@ -27,39 +29,76 @@ class BrowsingContext;
 /**
  * Class used to represent events generated by calls to Window.postMessage,
  * which asynchronously creates and dispatches events.
  */
 class PostMessageEvent final : public Runnable {
  public:
   NS_DECL_NSIRUNNABLE
 
+  // aCallerWindowID should not be 0.
   PostMessageEvent(BrowsingContext* aSource, const nsAString& aCallerOrigin,
                    nsGlobalWindowOuter* aTargetWindow,
                    nsIPrincipal* aProvidedPrincipal, uint64_t aCallerWindowID,
-                   nsIURI* aCallerDocumentURI);
+                   nsIURI* aCallerDocumentURI)
+      : PostMessageEvent(aSource, aCallerOrigin, aTargetWindow,
+                         aProvidedPrincipal, Some(aCallerWindowID),
+                         aCallerDocumentURI, false) {}
+
+  // To be used if there is no WindowID for the PostMessage caller's window (for
+  // example because it lives in a different process).
+  PostMessageEvent(BrowsingContext* aSource, const nsAString& aCallerOrigin,
+                   nsGlobalWindowOuter* aTargetWindow,
+                   nsIPrincipal* aProvidedPrincipal, nsIURI* aCallerDocumentURI,
+                   bool aIsFromPrivateWindow)
+      : PostMessageEvent(aSource, aCallerOrigin, aTargetWindow,
+                         aProvidedPrincipal, Nothing(), aCallerDocumentURI,
+                         aIsFromPrivateWindow) {}
 
   void Write(JSContext* aCx, JS::Handle<JS::Value> aMessage,
              JS::Handle<JS::Value> aTransfer, ErrorResult& aError) {
-    mHolder.Write(aCx, aMessage, aTransfer, JS::CloneDataPolicy(), aError);
+    mHolder.construct<StructuredCloneHolder>(
+        StructuredCloneHolder::CloningSupported,
+        StructuredCloneHolder::TransferringSupported,
+        JS::StructuredCloneScope::SameProcessSameThread);
+    mHolder.ref<StructuredCloneHolder>().Write(aCx, aMessage, aTransfer,
+                                               JS::CloneDataPolicy(), aError);
+  }
+  void UnpackFrom(const ClonedMessageData& aMessageData) {
+    mHolder.construct<ipc::StructuredCloneData>();
+    // FIXME Want to steal!
+    //       See https://bugzilla.mozilla.org/show_bug.cgi?id=1516349.
+    mHolder.ref<ipc::StructuredCloneData>().CopyFromClonedMessageDataForChild(
+        aMessageData);
   }
 
  private:
+  PostMessageEvent(BrowsingContext* aSource, const nsAString& aCallerOrigin,
+                   nsGlobalWindowOuter* aTargetWindow,
+                   nsIPrincipal* aProvidedPrincipal,
+                   const Maybe<uint64_t>& aCallerWindowID,
+                   nsIURI* aCallerDocumentURI, bool aIsFromPrivateWindow);
   ~PostMessageEvent();
 
   void Dispatch(nsGlobalWindowInner* aTargetWindow, Event* aEvent);
 
   void DispatchError(JSContext* aCx, nsGlobalWindowInner* aTargetWindow,
                      mozilla::dom::EventTarget* aEventTarget);
 
   RefPtr<BrowsingContext> mSource;
   nsString mCallerOrigin;
   RefPtr<nsGlobalWindowOuter> mTargetWindow;
   nsCOMPtr<nsIPrincipal> mProvidedPrincipal;
-  StructuredCloneHolder mHolder;
-  uint64_t mCallerWindowID;
+  // If the postMessage call was made on a WindowProxy whose Window lives in a
+  // separate process then mHolder will contain a StructuredCloneData, else
+  // it'll contain a StructuredCloneHolder.
+  MaybeOneOf<StructuredCloneHolder, ipc::StructuredCloneData> mHolder;
+  Maybe<uint64_t> mCallerWindowID;
   nsCOMPtr<nsIURI> mCallerDocumentURI;
+  // This is only set to a relevant value if mCallerWindowID doesn't contain a
+  // value.
+  bool mIsFromPrivateWindow;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 #endif  // mozilla_dom_PostMessageEvent_h
new file mode 100644
--- /dev/null
+++ b/dom/base/RemoteOuterWindowProxy.cpp
@@ -0,0 +1,194 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "AccessCheck.h"
+#include "js/Proxy.h"
+#include "mozilla/dom/BrowsingContext.h"
+#include "mozilla/dom/RemoteObjectProxy.h"
+#include "mozilla/dom/WindowBinding.h"
+#include "xpcprivate.h"
+
+namespace mozilla {
+namespace dom {
+
+/**
+ * RemoteOuterWindowProxy is the proxy handler for the WindowProxy objects for
+ * Window objects that live in a different process.
+ *
+ * RemoteOuterWindowProxy holds a BrowsingContext, which is cycle collected.
+ * However, RemoteOuterWindowProxy only holds BrowsingContexts that don't have a
+ * reference to a docshell, so there's no need to declare the edge from
+ * RemoteOuterWindowProxy to its BrowsingContext to the cycle collector.
+ *
+ * FIXME Verify that this is correct:
+ *       https://bugzilla.mozilla.org/show_bug.cgi?id=1516350.
+ */
+
+class RemoteOuterWindowProxy
+    : public RemoteObjectProxy<BrowsingContext,
+                               Window_Binding::sCrossOriginAttributes,
+                               Window_Binding::sCrossOriginMethods> {
+ public:
+  constexpr RemoteOuterWindowProxy()
+      : RemoteObjectProxy(prototypes::id::Window) {}
+
+  // Standard internal methods
+  bool getOwnPropertyDescriptor(
+      JSContext* aCx, JS::Handle<JSObject*> aProxy, JS::Handle<jsid> aId,
+      JS::MutableHandle<JS::PropertyDescriptor> aDesc) const final;
+  bool ownPropertyKeys(JSContext* aCx, JS::Handle<JSObject*> aProxy,
+                       JS::AutoIdVector& aProps) const final;
+
+  // SpiderMonkey extensions
+  bool getOwnEnumerablePropertyKeys(JSContext* cx, JS::Handle<JSObject*> proxy,
+                                    JS::AutoIdVector& props) const final;
+  void finalize(JSFreeOp* aFop, JSObject* aProxy) const final;
+  const char* className(JSContext* aCx,
+                        JS::Handle<JSObject*> aProxy) const final;
+};
+
+static const RemoteOuterWindowProxy sSingleton;
+
+// Give RemoteOuterWindowProxyClass 2 reserved slots, like the other wrappers,
+// so JSObject::swap can swap it with CrossCompartmentWrappers without requiring
+// malloc.
+const js::Class RemoteOuterWindowProxyClass =
+    PROXY_CLASS_DEF("Proxy", JSCLASS_HAS_RESERVED_SLOTS(2));
+
+bool GetRemoteOuterWindowProxy(JSContext* aCx, BrowsingContext* aContext,
+                               JS::MutableHandle<JSObject*> aRetVal) {
+  MOZ_ASSERT(!aContext->GetDocShell(),
+             "Why are we creating a RemoteOuterWindowProxy?");
+
+  xpc::CompartmentPrivate* priv =
+      xpc::CompartmentPrivate::Get(JS::CurrentGlobalOrNull(aCx));
+  xpc::CompartmentPrivate::RemoteProxyMap& map = priv->GetRemoteProxyMap();
+  auto result = map.lookupForAdd(aContext);
+  if (result) {
+    aRetVal.set(result->value());
+    return true;
+  }
+
+  JS::Rooted<JSObject*> obj(
+      aCx, sSingleton.CreateProxyObject(aCx, aContext,
+                                        &RemoteOuterWindowProxyClass));
+  if (!obj) {
+    return false;
+  }
+  NS_ADDREF(aContext);
+
+  if (!map.add(result, aContext, obj)) {
+    JS_ReportOutOfMemory(aCx);
+    return false;
+  }
+
+  aRetVal.set(obj);
+  return true;
+}
+
+static BrowsingContext* GetBrowsingContext(JSObject* aProxy) {
+  MOZ_ASSERT(IsRemoteObjectProxy(aProxy, prototypes::id::Window));
+  return static_cast<BrowsingContext*>(
+      RemoteObjectProxyBase::GetNative(aProxy));
+}
+
+bool WrapResult(JSContext* aCx, JS::Handle<JSObject*> aProxy,
+                BrowsingContext* aResult, unsigned attrs,
+                JS::MutableHandle<JS::PropertyDescriptor> aDesc) {
+  JS::Rooted<JS::Value> v(aCx);
+  if (!ToJSValue(aCx, WindowProxyHolder(aResult), &v)) {
+    return false;
+  }
+  aDesc.object().set(aProxy);
+  aDesc.setDataDescriptor(v, attrs);
+  return true;
+}
+
+bool RemoteOuterWindowProxy::getOwnPropertyDescriptor(
+    JSContext* aCx, JS::Handle<JSObject*> aProxy, JS::Handle<jsid> aId,
+    JS::MutableHandle<JS::PropertyDescriptor> aDesc) const {
+  BrowsingContext* bc = GetBrowsingContext(aProxy);
+  uint32_t index = GetArrayIndexFromId(aId);
+  if (IsArrayIndex(index)) {
+    const BrowsingContext::Children& children = bc->GetChildren();
+    if (index < children.Length()) {
+      return WrapResult(aCx, aProxy, children[index],
+                        JSPROP_READONLY | JSPROP_ENUMERATE, aDesc);
+    }
+    return ReportCrossOriginDenial(aCx, aId, NS_LITERAL_CSTRING("access"));
+  }
+
+  bool ok = RemoteObjectProxy::getOwnPropertyDescriptorInternal(aCx, aProxy,
+                                                                aId, aDesc);
+  if (!ok || aDesc.object()) {
+    return ok;
+  }
+
+  if (JSID_IS_STRING(aId)) {
+    nsAutoJSString str;
+    if (!str.init(aCx, JSID_TO_STRING(aId))) {
+      return false;
+    }
+
+    for (BrowsingContext* child : bc->GetChildren()) {
+      if (child->NameEquals(str)) {
+        return WrapResult(aCx, aProxy, child, JSPROP_READONLY, aDesc);
+      }
+    }
+  }
+
+  return getOwnPropertyDescriptorTail(aCx, aProxy, aId, aDesc);
+}
+
+bool AppendIndexedPropertyNames(JSContext* aCx, BrowsingContext* aContext,
+                                JS::AutoIdVector& aIndexedProps) {
+  int32_t length = aContext->GetChildren().Length();
+  if (!aIndexedProps.reserve(aIndexedProps.length() + length)) {
+    return false;
+  }
+
+  for (int32_t i = 0; i < length; ++i) {
+    aIndexedProps.infallibleAppend(INT_TO_JSID(i));
+  }
+  return true;
+}
+
+bool RemoteOuterWindowProxy::ownPropertyKeys(JSContext* aCx,
+                                             JS::Handle<JSObject*> aProxy,
+                                             JS::AutoIdVector& aProps) const {
+  BrowsingContext* bc = GetBrowsingContext(aProxy);
+
+  // https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-ownpropertykeys:crossoriginownpropertykeys-(-o-)
+  // step 3 to 5
+  if (!AppendIndexedPropertyNames(aCx, bc, aProps)) {
+    return false;
+  }
+
+  // https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-ownpropertykeys:crossoriginownpropertykeys-(-o-)
+  // step 7
+  return RemoteObjectProxy::ownPropertyKeys(aCx, aProxy, aProps);
+}
+
+bool RemoteOuterWindowProxy::getOwnEnumerablePropertyKeys(
+    JSContext* aCx, JS::Handle<JSObject*> aProxy,
+    JS::AutoIdVector& aProps) const {
+  return AppendIndexedPropertyNames(aCx, GetBrowsingContext(aProxy), aProps);
+}
+
+void RemoteOuterWindowProxy::finalize(JSFreeOp* aFop, JSObject* aProxy) const {
+  BrowsingContext* bc = GetBrowsingContext(aProxy);
+  RefPtr<BrowsingContext> self(dont_AddRef(bc));
+}
+
+const char* RemoteOuterWindowProxy::className(
+    JSContext* aCx, JS::Handle<JSObject*> aProxy) const {
+  MOZ_ASSERT(js::IsProxy(aProxy));
+
+  return "Object";
+}
+
+}  // namespace dom
+}  // namespace mozilla
--- a/dom/base/WindowProxyHolder.h
+++ b/dom/base/WindowProxyHolder.h
@@ -4,16 +4,24 @@
  * 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 mozilla_dom_WindowProxyHolder_h__
 #define mozilla_dom_WindowProxyHolder_h__
 
 #include "mozilla/dom/BrowsingContext.h"
 
+struct JSContext;
+class JSObject;
+
+namespace JS {
+template <typename T>
+class MutableHandle;
+}  // namespace JS
+
 namespace mozilla {
 namespace dom {
 
 /**
  * This class is used for passing arguments and the return value for WebIDL
  * binding code that takes/returns a WindowProxy object and for WebIDL
  * unions/dictionaries that contain a WindowProxy member. It should never
  * contain null; if the value in WebIDL is nullable the binding code will use a
@@ -56,12 +64,15 @@ inline void ImplCycleCollectionTraverse(
     const char* aName, uint32_t aFlags = 0) {
   CycleCollectionNoteChild(aCallback, aProxy.get(), "mBrowsingContext", aFlags);
 }
 
 inline void ImplCycleCollectionUnlink(WindowProxyHolder& aProxy) {
   aProxy.mBrowsingContext = nullptr;
 }
 
+extern bool GetRemoteOuterWindowProxy(JSContext* aCx, BrowsingContext* aContext,
+                                      JS::MutableHandle<JSObject*> aValue);
+
 }  // namespace dom
 }  // namespace mozilla
 
 #endif /* mozilla_dom_WindowProxyHolder_h__ */
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -206,16 +206,17 @@ EXPORTS.mozilla.dom += [
     'PlacesBookmark.h',
     'PlacesBookmarkAddition.h',
     'PlacesEvent.h',
     'PlacesObservers.h',
     'PlacesVisit.h',
     'PlacesWeakCallbackWrapper.h',
     'PopupBlocker.h',
     'Pose.h',
+    'PostMessageEvent.h',
     'ProcessMessageManager.h',
     'ResponsiveImageSelector.h',
     'SameProcessMessageQueue.h',
     'ScreenLuminance.h',
     'ScreenOrientation.h',
     'Selection.h',
     'ShadowRoot.h',
     'StructuredCloneBlob.h',
@@ -364,16 +365,17 @@ UNIFIED_SOURCES += [
     'nsWrapperCache.cpp',
     'nsXHTMLContentSerializer.cpp',
     'nsXMLContentSerializer.cpp',
     'ParentProcessMessageManager.cpp',
     'PopupBlocker.cpp',
     'Pose.cpp',
     'PostMessageEvent.cpp',
     'ProcessMessageManager.cpp',
+    'RemoteOuterWindowProxy.cpp',
     'ResponsiveImageSelector.cpp',
     'SameProcessMessageQueue.cpp',
     'ScreenLuminance.cpp',
     'ScreenOrientation.cpp',
     'Selection.cpp',
     'SelectionChangeEventDispatcher.cpp',
     'ShadowRoot.cpp',
     'StorageAccessPermissionRequest.cpp',
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -5704,19 +5704,17 @@ already_AddRefed<TreeWalker> nsIDocument
 
 already_AddRefed<Location> nsIDocument::GetLocation() const {
   nsCOMPtr<nsPIDOMWindowInner> w = do_QueryInterface(mScriptGlobalObject);
 
   if (!w) {
     return nullptr;
   }
 
-  nsGlobalWindowInner* window = nsGlobalWindowInner::Cast(w);
-  RefPtr<Location> loc = window->GetLocation();
-  return loc.forget();
+  return do_AddRef(w->Location());
 }
 
 Element* nsIDocument::GetHtmlElement() const {
   Element* rootElement = GetRootElement();
   if (rootElement && rootElement->IsHTMLElement(nsGkAtoms::html))
     return rootElement;
   return nullptr;
 }
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -294,29 +294,38 @@ using mozilla::dom::cache::CacheStorage;
   if (!HasActiveDocument()) {                                        \
     NS_WARNING(outer ? "Inner window does not have active document." \
                      : "No outer window available!");                \
     return err_rval;                                                 \
   }                                                                  \
   return outer->method args;                                         \
   PR_END_MACRO
 
-#define FORWARD_TO_OUTER_OR_THROW(method, args, errorresult, err_rval) \
-  PR_BEGIN_MACRO                                                       \
-  nsGlobalWindowOuter* outer = GetOuterWindowInternal();               \
-  if (MOZ_LIKELY(HasActiveDocument())) {                               \
-    return outer->method args;                                         \
-  }                                                                    \
-  if (!outer) {                                                        \
-    NS_WARNING("No outer window available!");                          \
-    errorresult.Throw(NS_ERROR_NOT_INITIALIZED);                       \
-  } else {                                                             \
-    errorresult.Throw(NS_ERROR_XPC_SECURITY_MANAGER_VETO);             \
-  }                                                                    \
-  return err_rval;                                                     \
+static nsGlobalWindowOuter* GetOuterWindowForForwarding(
+    nsGlobalWindowInner* aInner, ErrorResult& aError) {
+  nsGlobalWindowOuter* outer = aInner->GetOuterWindowInternal();
+  if (MOZ_LIKELY(aInner->HasActiveDocument())) {
+    return outer;
+  }
+  if (!outer) {
+    NS_WARNING("No outer window available!");
+    aError.Throw(NS_ERROR_NOT_INITIALIZED);
+  } else {
+    aError.Throw(NS_ERROR_XPC_SECURITY_MANAGER_VETO);
+  }
+  return nullptr;
+}
+
+#define FORWARD_TO_OUTER_OR_THROW(method, args, errorresult, err_rval)         \
+  PR_BEGIN_MACRO                                                               \
+  nsGlobalWindowOuter* outer = GetOuterWindowForForwarding(this, errorresult); \
+  if (MOZ_LIKELY(outer)) {                                                     \
+    return outer->method args;                                                 \
+  }                                                                            \
+  return err_rval;                                                             \
   PR_END_MACRO
 
 #define FORWARD_TO_OUTER_VOID(method, args)                          \
   PR_BEGIN_MACRO                                                     \
   nsGlobalWindowOuter* outer = GetOuterWindowInternal();             \
   if (!HasActiveDocument()) {                                        \
     NS_WARNING(outer ? "Inner window does not have active document." \
                      : "No outer window available!");                \
@@ -2107,19 +2116,19 @@ void nsPIDOMWindowInner::MuteAudioContex
 void nsPIDOMWindowInner::UnmuteAudioContexts() {
   for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
     if (!mAudioContexts[i]->IsOffline()) {
       mAudioContexts[i]->Unmute();
     }
   }
 }
 
-nsGlobalWindowInner* nsGlobalWindowInner::Window() { return this; }
-
-nsGlobalWindowInner* nsGlobalWindowInner::Self() { return this; }
+BrowsingContext* nsGlobalWindowInner::Window() {
+  return mOuterWindow ? mOuterWindow->GetBrowsingContext() : nullptr;
+}
 
 Navigator* nsPIDOMWindowInner::Navigator() {
   if (!mNavigator) {
     mNavigator = new mozilla::dom::Navigator(this);
   }
 
   return mNavigator;
 }
@@ -3414,17 +3423,17 @@ void nsGlobalWindowInner::Prompt(const n
                                  nsIPrincipal& aSubjectPrincipal,
                                  ErrorResult& aError) {
   FORWARD_TO_OUTER_OR_THROW(
       PromptOuter, (aMessage, aInitial, aReturn, aSubjectPrincipal, aError),
       aError, );
 }
 
 void nsGlobalWindowInner::Focus(ErrorResult& aError) {
-  FORWARD_TO_OUTER_OR_THROW(FocusOuter, (aError), aError, );
+  FORWARD_TO_OUTER_OR_THROW(FocusOuter, (), aError, );
 }
 
 nsresult nsGlobalWindowInner::Focus() {
   ErrorResult rv;
   Focus(rv);
 
   return rv.StealNSResult();
 }
@@ -3746,18 +3755,18 @@ void nsGlobalWindowInner::PostMessageMoz
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
 
   PostMessageMoz(aCx, aMessage, aOptions.mTargetOrigin, transferArray,
                  aSubjectPrincipal, aRv);
 }
 
-void nsGlobalWindowInner::Close(ErrorResult& aError) {
-  FORWARD_TO_OUTER_OR_THROW(CloseOuter, (nsContentUtils::IsCallerChrome()),
+void nsGlobalWindowInner::Close(CallerType aCallerType, ErrorResult& aError) {
+  FORWARD_TO_OUTER_OR_THROW(CloseOuter, (aCallerType == CallerType::System),
                             aError, );
 }
 
 nsresult nsGlobalWindowInner::Close() {
   FORWARD_TO_OUTER(Close, (), NS_ERROR_UNEXPECTED);
 }
 
 bool nsGlobalWindowInner::IsInModalState() {
@@ -3929,17 +3938,17 @@ EventListenerManager* nsGlobalWindowInne
 EventListenerManager* nsGlobalWindowInner::GetExistingListenerManager() const {
   return mListenerManager;
 }
 
 //*****************************************************************************
 // nsGlobalWindowInner::nsPIDOMWindow
 //*****************************************************************************
 
-Location* nsGlobalWindowInner::GetLocation() {
+Location* nsGlobalWindowInner::Location() {
   if (!mLocation) {
     mLocation = new dom::Location(this, GetDocShell());
   }
 
   return mLocation;
 }
 
 void nsGlobalWindowInner::MaybeUpdateTouchState() {
--- a/dom/base/nsGlobalWindowInner.h
+++ b/dom/base/nsGlobalWindowInner.h
@@ -209,16 +209,18 @@ class nsGlobalWindowInner final : public
                                   public nsIScriptGlobalObject,
                                   public nsIScriptObjectPrincipal,
                                   public nsSupportsWeakReference,
                                   public nsIInterfaceRequestor,
                                   public PRCListStr,
                                   public nsAPostRefreshObserver,
                                   public mozilla::webgpu::InstanceProvider {
  public:
+  typedef mozilla::dom::BrowsingContext RemoteProxy;
+
   typedef mozilla::TimeStamp TimeStamp;
   typedef mozilla::TimeDuration TimeDuration;
 
   typedef nsDataHashtable<nsUint64HashKey, nsGlobalWindowInner*>
       InnerWindowByIdTable;
 
   static void AssertIsOnMainThread()
 #ifdef DEBUG
@@ -594,33 +596,34 @@ class nsGlobalWindowInner final : public
 #undef ERROR_EVENT
 #undef EVENT
 
   nsISupports* GetParentObject() { return nullptr; }
 
   static JSObject* CreateNamedPropertiesObject(JSContext* aCx,
                                                JS::Handle<JSObject*> aProto);
 
-  nsGlobalWindowInner* Window();
-  nsGlobalWindowInner* Self();
+  mozilla::dom::BrowsingContext* Window();
+  mozilla::dom::BrowsingContext* Self() { return Window(); }
   nsIDocument* GetDocument() { return GetDoc(); }
   void GetName(nsAString& aName, mozilla::ErrorResult& aError);
   void SetName(const nsAString& aName, mozilla::ErrorResult& aError);
-  mozilla::dom::Location* GetLocation() override;
+  mozilla::dom::Location* Location() override;
   nsHistory* GetHistory(mozilla::ErrorResult& aError);
   mozilla::dom::CustomElementRegistry* CustomElements() override;
   mozilla::dom::BarProp* GetLocationbar(mozilla::ErrorResult& aError);
   mozilla::dom::BarProp* GetMenubar(mozilla::ErrorResult& aError);
   mozilla::dom::BarProp* GetPersonalbar(mozilla::ErrorResult& aError);
   mozilla::dom::BarProp* GetScrollbars(mozilla::ErrorResult& aError);
   mozilla::dom::BarProp* GetStatusbar(mozilla::ErrorResult& aError);
   mozilla::dom::BarProp* GetToolbar(mozilla::ErrorResult& aError);
   void GetStatus(nsAString& aStatus, mozilla::ErrorResult& aError);
   void SetStatus(const nsAString& aStatus, mozilla::ErrorResult& aError);
-  void Close(mozilla::ErrorResult& aError);
+  void Close(mozilla::dom::CallerType aCallerType,
+             mozilla::ErrorResult& aError);
   nsresult Close() override;
   bool GetClosed(mozilla::ErrorResult& aError);
   void Stop(mozilla::ErrorResult& aError);
   void Focus(mozilla::ErrorResult& aError);
   nsresult Focus() override;
   void Blur(mozilla::ErrorResult& aError);
   nsDOMWindowList* GetFrames() final;
   mozilla::dom::BrowsingContext* GetFrames(mozilla::ErrorResult& aError);
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -3577,26 +3577,19 @@ Nullable<WindowProxyHolder> nsGlobalWind
   if (!top || !(topBC = top->GetBrowsingContext())) {
     return nullptr;
   }
   return WindowProxyHolder(topBC);
 }
 
 already_AddRefed<BrowsingContext> nsGlobalWindowOuter::GetChildWindow(
     const nsAString& aName) {
-  nsCOMPtr<nsIDocShell> docShell(GetDocShell());
-  NS_ENSURE_TRUE(docShell, nullptr);
-
-  nsCOMPtr<nsIDocShellTreeItem> child;
-  docShell->FindChildWithName(aName, false, true, nullptr, nullptr,
-                              getter_AddRefs(child));
-
-  return child && child->GetWindow()
-             ? do_AddRef(child->GetWindow()->GetBrowsingContext())
-             : nullptr;
+  NS_ENSURE_TRUE(mBrowsingContext, nullptr);
+
+  return mBrowsingContext->FindChildWithName(aName);
 }
 
 bool nsGlobalWindowOuter::DispatchCustomEvent(const nsAString& aEventName) {
   bool defaultActionEnabled = true;
   nsContentUtils::DispatchTrustedEvent(mDoc, ToSupports(this), aEventName,
                                        CanBubble::eYes, Cancelable::eYes,
                                        &defaultActionEnabled);
 
@@ -4482,18 +4475,18 @@ void nsGlobalWindowOuter::PromptOuter(co
   nsString outValue;
   outValue.Adopt(inoutValue);
 
   if (ok && inoutValue) {
     aReturn.Assign(outValue);
   }
 }
 
-void nsGlobalWindowOuter::FocusOuter(ErrorResult& aError) {
-  nsIFocusManager* fm = nsFocusManager::GetFocusManager();
+void nsGlobalWindowOuter::FocusOuter() {
+  nsFocusManager* fm = nsFocusManager::GetFocusManager();
   if (!fm) {
     return;
   }
 
   nsCOMPtr<nsIBaseWindow> baseWin = do_QueryInterface(mDocShell);
 
   bool isVisible = false;
   if (baseWin) {
@@ -4573,26 +4566,32 @@ void nsGlobalWindowOuter::FocusOuter(Err
     if (!parentdoc) {
       return;
     }
 
     RefPtr<Element> frame = parentdoc->FindContentForSubDocument(mDoc);
     if (frame) {
       uint32_t flags = nsIFocusManager::FLAG_NOSCROLL;
       if (canFocus) flags |= nsIFocusManager::FLAG_RAISE;
-      aError = fm->SetFocus(frame, flags);
+      DebugOnly<nsresult> rv = fm->SetFocus(frame, flags);
+      MOZ_ASSERT(NS_SUCCEEDED(rv),
+                 "SetFocus only fails if the first argument is null, "
+                 "but we pass an element");
     }
     return;
   }
 
   if (canFocus) {
     // if there is no parent, this must be a toplevel window, so raise the
     // window if canFocus is true. If this is a child process, the raise
     // window request will get forwarded to the parent by the puppet widget.
-    aError = fm->SetActiveWindow(this);
+    DebugOnly<nsresult> rv = fm->SetActiveWindow(this);
+    MOZ_ASSERT(NS_SUCCEEDED(rv),
+               "SetActiveWindow only fails if passed null or a non-toplevel "
+               "window, which is not the case here.");
   }
 }
 
 nsresult nsGlobalWindowOuter::Focus() {
   FORWARD_TO_INNER(Focus, (), NS_ERROR_UNEXPECTED);
 }
 
 void nsGlobalWindowOuter::BlurOuter() {
@@ -5364,16 +5363,17 @@ Nullable<WindowProxyHolder> nsGlobalWind
 }
 
 BrowsingContext* nsGlobalWindowOuter::GetFramesOuter() {
   RefPtr<nsPIDOMWindowOuter> frames(this);
   FlushPendingNotifications(FlushType::ContentAndNotify);
   return mBrowsingContext;
 }
 
+/* static */
 nsGlobalWindowInner* nsGlobalWindowOuter::CallerInnerWindow(JSContext* aCx) {
   nsIGlobalObject* global = GetIncumbentGlobal();
   NS_ENSURE_TRUE(global, nullptr);
   JS::Rooted<JSObject*> scope(aCx, global->GetGlobalJSObject());
   NS_ENSURE_TRUE(scope, nullptr);
 
   // When Jetpack runs content scripts inside a sandbox, it uses
   // sandboxPrototype to make them appear as though they're running in the
@@ -5399,17 +5399,17 @@ nsGlobalWindowInner* nsGlobalWindowOuter
   nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(global);
   return nsGlobalWindowInner::Cast(win);
 }
 
 /* static */
 bool nsGlobalWindowOuter::GatherPostMessageData(
     JSContext* aCx, const nsAString& aTargetOrigin, BrowsingContext** aSource,
     nsAString& aOrigin, nsIURI** aTargetOriginURI,
-    nsIPrincipal** aCallerPrincipal, uint64_t* aCallerInnerWindowID,
+    nsIPrincipal** aCallerPrincipal, nsGlobalWindowInner** aCallerInnerWindow,
     nsIURI** aCallerDocumentURI, ErrorResult& aError) {
   //
   // 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
   //
@@ -5417,29 +5417,26 @@ bool nsGlobalWindowOuter::GatherPostMess
   // First, get the caller's window
   RefPtr<nsGlobalWindowInner> callerInnerWin = CallerInnerWindow(aCx);
   nsIPrincipal* callerPrin;
   if (callerInnerWin) {
     nsCOMPtr<nsIDocument> doc = callerInnerWin->GetExtantDoc();
     if (!doc) {
       return false;
     }
-    *aCallerInnerWindowID = doc->InnerWindowID();
     NS_IF_ADDREF(*aCallerDocumentURI = doc->GetDocumentURI());
 
     // Compute the caller's origin either from its principal or, in the case the
     // principal doesn't carry a URI (e.g. the system principal), the caller's
     // document.  We must get this now instead of when the event is created and
     // dispatched, because ultimately it is the identity of the calling window
     // *now* that determines who sent the message (and not an identity which
     // might have changed due to intervening navigations).
     callerPrin = callerInnerWin->GetPrincipal();
   } else {
-    *aCallerInnerWindowID = 0;
-
     // In case the global is not a window, it can be a sandbox, and the
     // sandbox's principal can be used for the security check.
     nsIGlobalObject* global = GetIncumbentGlobal();
     NS_ASSERTION(global, "Why is there no global object?");
     callerPrin = global->PrincipalOrNull();
   }
   if (!callerPrin) {
     return false;
@@ -5488,16 +5485,18 @@ bool nsGlobalWindowOuter::GatherPostMess
   if (!nsContentUtils::IsCallerChrome() && callerInnerWin &&
       callerInnerWin->GetOuterWindowInternal()) {
     NS_ADDREF(*aSource = callerInnerWin->GetOuterWindowInternal()
                              ->GetBrowsingContext());
   } else {
     *aSource = nullptr;
   }
 
+  callerInnerWin.forget(aCallerInnerWindow);
+
   return true;
 }
 
 bool nsGlobalWindowOuter::GetPrincipalForPostMessage(
     const nsAString& aTargetOrigin, nsIURI* aTargetOriginURI,
     nsIPrincipal* aCallerPrincipal, nsIPrincipal& aSubjectPrincipal,
     nsIPrincipal** aProvidedPrincipal) {
   //
@@ -5590,37 +5589,38 @@ void nsGlobalWindowOuter::PostMessageMoz
                                               const nsAString& aTargetOrigin,
                                               JS::Handle<JS::Value> aTransfer,
                                               nsIPrincipal& aSubjectPrincipal,
                                               ErrorResult& aError) {
   RefPtr<BrowsingContext> sourceBc;
   nsAutoString origin;
   nsCOMPtr<nsIURI> targetOriginURI;
   nsCOMPtr<nsIPrincipal> callerPrincipal;
-  uint64_t callerInnerWindowID;
+  RefPtr<nsGlobalWindowInner> callerInnerWindow;
   nsCOMPtr<nsIURI> callerDocumentURI;
-  if (!GatherPostMessageData(
-          aCx, aTargetOrigin, getter_AddRefs(sourceBc), origin,
-          getter_AddRefs(targetOriginURI), getter_AddRefs(callerPrincipal),
-          &callerInnerWindowID, getter_AddRefs(callerDocumentURI), aError)) {
+  if (!GatherPostMessageData(aCx, aTargetOrigin, getter_AddRefs(sourceBc),
+                             origin, getter_AddRefs(targetOriginURI),
+                             getter_AddRefs(callerPrincipal),
+                             getter_AddRefs(callerInnerWindow),
+                             getter_AddRefs(callerDocumentURI), aError)) {
     return;
   }
 
   nsCOMPtr<nsIPrincipal> providedPrincipal;
   if (!GetPrincipalForPostMessage(aTargetOrigin, targetOriginURI,
                                   callerPrincipal, aSubjectPrincipal,
                                   getter_AddRefs(providedPrincipal))) {
     return;
   }
 
   // Create and asynchronously dispatch a runnable which will handle actual DOM
   // event creation and dispatch.
-  RefPtr<PostMessageEvent> event =
-      new PostMessageEvent(sourceBc, origin, this, providedPrincipal,
-                           callerInnerWindowID, callerDocumentURI);
+  RefPtr<PostMessageEvent> event = new PostMessageEvent(
+      sourceBc, origin, this, providedPrincipal,
+      callerInnerWindow ? callerInnerWindow->WindowID() : 0, callerDocumentURI);
 
   event->Write(aCx, aMessage, aTransfer, aError);
   if (NS_WARN_IF(aError.Failed())) {
     return;
   }
 
   aError = Dispatch(TaskCategory::Other, event.forget());
 }
@@ -6242,17 +6242,17 @@ nsPIDOMWindowOuter* nsGlobalWindowOuter:
   }
 
   return top;
 }
 
 // This has a caller in Windows-only code (nsNativeAppSupportWin).
 Location* nsGlobalWindowOuter::GetLocation() {
   // This method can be called on the outer window as well.
-  FORWARD_TO_INNER(GetLocation, (), nullptr);
+  FORWARD_TO_INNER(Location, (), nullptr);
 }
 
 void nsGlobalWindowOuter::ActivateOrDeactivate(bool aActivate) {
   if (!mDoc) {
     return;
   }
 
   // Set / unset mIsActive on the top level window, which is used for the
--- a/dom/base/nsGlobalWindowOuter.h
+++ b/dom/base/nsGlobalWindowOuter.h
@@ -522,17 +522,17 @@ class nsGlobalWindowOuter final : public
   mozilla::dom::Location* GetLocation() override;
   void GetStatusOuter(nsAString& aStatus);
   void SetStatusOuter(const nsAString& aStatus);
   void CloseOuter(bool aTrustedCaller);
   nsresult Close() override;
   bool GetClosedOuter();
   bool Closed() override;
   void StopOuter(mozilla::ErrorResult& aError);
-  void FocusOuter(mozilla::ErrorResult& aError);
+  void FocusOuter();
   nsresult Focus() override;
   void BlurOuter();
   mozilla::dom::BrowsingContext* GetFramesOuter();
   nsDOMWindowList* GetFrames() final;
   uint32_t Length();
   mozilla::dom::Nullable<mozilla::dom::WindowProxyHolder> GetTopOuter();
 
   nsresult GetPrompter(nsIPrompt** aPrompt) override;
@@ -963,52 +963,17 @@ class nsGlobalWindowOuter final : public
                                    mozilla::ErrorResult& aError);
 
   void PostMessageMozOuter(JSContext* aCx, JS::Handle<JS::Value> aMessage,
                            const nsAString& aTargetOrigin,
                            JS::Handle<JS::Value> aTransfer,
                            nsIPrincipal& aSubjectPrincipal,
                            mozilla::ErrorResult& aError);
 
- private:
-  /**
-   * Gather the necessary data from the caller for a postMessage call.
-   *
-   * @param aCx The JSContext.
-   *
-   * @param aTargetOrigin The value passed as the targetOrigin argument to the
-   * postMessage call.
-   *
-   * @param aSource [out] The browsing context for the incumbent global.
-   *
-   * @param aOrigin [out] The value to use for the origin property of the
-   * MessageEvent object.
-   *
-   * @param aTargetOriginURI [out] The origin of the URI contained in
-   * aTargetOrigin, null if aTargetOrigin is "/" or "*".
-   *
-   * @param aCallerPrincipal [out] The principal of the incumbent global of the
-   *                               postMessage call.
-   *
-   * @param aCallerInnerWindowID [out] Inner window ID of the caller of
-   * postMessage, or 0 if the incumbent global is not a Window.
-   *
-   * @param aCallerDocumentURI [out] The URI of the document of the incumbent
-   * global if it's a Window, null otherwise.
-   *
-   * @param aError [out] The error, if any.
-   *
-   * @return Whether the postMessage call should continue or return now.
-   */
-  static bool GatherPostMessageData(
-      JSContext* aCx, const nsAString& aTargetOrigin,
-      mozilla::dom::BrowsingContext** aSource, nsAString& aOrigin,
-      nsIURI** aTargetOriginURI, nsIPrincipal** aCallerPrincipal,
-      uint64_t* aCallerInnerWindowID, nsIURI** aCallerDocumentURI,
-      mozilla::ErrorResult& aError);
+ public:
   /**
    * Compute the principal to use for checking against the target principal in a
    * postMessage call.
    *
    * @param aTargetOrigin The value passed as the targetOrigin argument to the
    * postMessage call.
    *
    * @param aTargetOriginURI The origin of the URI contained in aTargetOrigin
@@ -1025,16 +990,53 @@ class nsGlobalWindowOuter final : public
    * @return Whether the postMessage call should continue or return now.
    */
   bool GetPrincipalForPostMessage(const nsAString& aTargetOrigin,
                                   nsIURI* aTargetOriginURI,
                                   nsIPrincipal* aCallerPrincipal,
                                   nsIPrincipal& aSubjectPrincipal,
                                   nsIPrincipal** aProvidedPrincipal);
 
+ private:
+  /**
+   * Gather the necessary data from the caller for a postMessage call.
+   *
+   * @param aCx The JSContext.
+   *
+   * @param aTargetOrigin The value passed as the targetOrigin argument to the
+   * postMessage call.
+   *
+   * @param aSource [out] The browsing context for the incumbent global.
+   *
+   * @param aOrigin [out] The value to use for the origin property of the
+   * MessageEvent object.
+   *
+   * @param aTargetOriginURI [out] The origin of the URI contained in
+   * aTargetOrigin, null if aTargetOrigin is "/" or "*".
+   *
+   * @param aCallerPrincipal [out] The principal of the incumbent global of the
+   *                               postMessage call.
+   *
+   * @param aCallerInnerWindow [out] Inner window of the caller of
+   * postMessage, or null if the incumbent global is not a Window.
+   *
+   * @param aCallerDocumentURI [out] The URI of the document of the incumbent
+   * global if it's a Window, null otherwise.
+   *
+   * @param aError [out] The error, if any.
+   *
+   * @return Whether the postMessage call should continue or return now.
+   */
+  static bool GatherPostMessageData(
+      JSContext* aCx, const nsAString& aTargetOrigin,
+      mozilla::dom::BrowsingContext** aSource, nsAString& aOrigin,
+      nsIURI** aTargetOriginURI, nsIPrincipal** aCallerPrincipal,
+      nsGlobalWindowInner** aCallerInnerWindow, nsIURI** aCallerDocumentURI,
+      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
   // if we have caused a resolution change by moving across monitors.
   void CheckForDPIChange();
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -43,16 +43,17 @@
 #include "mozilla/net/ReferrerPolicy.h"  // for member
 #include "mozilla/UseCounter.h"
 #include "mozilla/WeakPtr.h"
 #include "Units.h"
 #include "nsContentListDeclarations.h"
 #include "nsExpirationTracker.h"
 #include "nsClassHashtable.h"
 #include "mozilla/CORSMode.h"
+#include "mozilla/dom/BrowsingContext.h"
 #include "mozilla/dom/ContentBlockingLog.h"
 #include "mozilla/dom/DispatcherTrait.h"
 #include "mozilla/dom/DocumentOrShadowRoot.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/NotNull.h"
 #include "mozilla/SegmentedVector.h"
 #include "mozilla/ServoBindingTypes.h"
 #include "mozilla/StyleSheet.h"
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -534,17 +534,17 @@ class nsPIDOMWindowInner : public mozIDO
   // WebIDL-ish APIs
   void MarkUncollectableForCCGeneration(uint32_t aGeneration) {
     mMarkedCCGeneration = aGeneration;
   }
 
   uint32_t GetMarkedCCGeneration() { return mMarkedCCGeneration; }
 
   mozilla::dom::Navigator* Navigator();
-  virtual mozilla::dom::Location* GetLocation() = 0;
+  virtual mozilla::dom::Location* Location() = 0;
 
   virtual nsresult GetControllers(nsIControllers** aControllers) = 0;
 
   virtual nsDOMWindowList* GetFrames() = 0;
 
   virtual nsresult GetInnerWidth(int32_t* aWidth) = 0;
   virtual nsresult GetInnerHeight(int32_t* aHeight) = 0;
 
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -2798,16 +2798,28 @@ namespace binding_detail {
  * ExtractThisObject: Takes a CallArgs for which HasValidThisValue was true and
  *                    returns the JSObject* to use for getting |this|.
  *
  * MaybeUnwrapThisObject: If our |this| is a JSObject* that this policy wants to
  *                        allow unchecked access to for this
  *                        getter/setter/method, unwrap it.  Otherwise just
  *                        return the given object.
  *
+ * UnwrapThisObject: Takes a MutableHandle for a JSObject which contains the
+ *                   this object (which the caller probably got from
+ *                   MaybeUnwrapThisObject). It will try to get the right native
+ *                   out of aObj. In some cases there are 2 possible types for
+ *                   the native (which is why aSelf is a reference to a void*).
+ *                   The ThisPolicy user should use the this JSObject* to
+ *                   determine what C++ class aSelf contains. aObj is used to
+ *                   keep the reflector object alive while self is being used,
+ *                   so its value before and after the UnwrapThisObject call
+ *                   could be different (if aObj was wrapped). The return value
+ *                   is an nsresult, which will signal if an error occurred.
+ *
  * HandleInvalidThis: If the |this| is not valid (wrong type of value, wrong
  *                    object, etc), decide what to do about it.  Returns a
  *                    boolean to return from the JSNative (false for failure,
  *                    true for succcess).
  */
 struct NormalThisPolicy {
   // This needs to be inlined because it's called on no-exceptions fast-paths.
   static MOZ_ALWAYS_INLINE bool HasValidThisValue(const JS::CallArgs& aArgs) {
@@ -2825,16 +2837,24 @@ struct NormalThisPolicy {
       const JS::CallArgs& aArgs) {
     return &aArgs.thisv().toObject();
   }
 
   static MOZ_ALWAYS_INLINE JSObject* MaybeUnwrapThisObject(JSObject* aObj) {
     return aObj;
   }
 
+  static MOZ_ALWAYS_INLINE nsresult
+  UnwrapThisObject(JS::MutableHandle<JSObject*> aObj, void*& aSelf,
+                   prototypes::ID aProtoID, uint32_t aProtoDepth) {
+    binding_detail::MutableObjectHandleWrapper wrapper(aObj);
+    return binding_detail::UnwrapObjectInternal<void, true>(
+        wrapper, aSelf, aProtoID, aProtoDepth);
+  }
+
   static bool HandleInvalidThis(JSContext* aCx, JS::CallArgs& aArgs,
                                 bool aSecurityError, prototypes::ID aProtoId) {
     return ThrowInvalidThis(aCx, aArgs, aSecurityError, aProtoId);
   }
 };
 
 struct MaybeGlobalThisPolicy : public NormalThisPolicy {
   static MOZ_ALWAYS_INLINE bool HasValidThisValue(const JS::CallArgs& aArgs) {
@@ -2881,21 +2901,60 @@ struct CrossOriginThisPolicy : public Ma
 
   // We want the ExtractThisObject of MaybeGlobalThisPolicy.
 
   static MOZ_ALWAYS_INLINE JSObject* MaybeUnwrapThisObject(JSObject* aObj) {
     if (xpc::WrapperFactory::IsXrayWrapper(aObj)) {
       return js::UncheckedUnwrap(aObj);
     }
 
-    // Else just return aObj; our UnwrapObjectInternal call will try to
-    // CheckedUnwrap it, and eitehr succeed or get a security error as needed.
+    // Else just return aObj; our UnwrapThisObject call will try to
+    // CheckedUnwrap it, and either succeed or get a security error as needed.
     return aObj;
   }
 
+  // After calling UnwrapThisObject aSelf can contain one of 2 types, depending
+  // on whether aObj is a proxy with a RemoteObjectProxy handler or a (maybe
+  // wrapped) normal WebIDL reflector. The generated binding code relies on this
+  // and uses IsRemoteObjectProxy to determine what type aSelf points to.
+  static MOZ_ALWAYS_INLINE nsresult
+  UnwrapThisObject(JS::MutableHandle<JSObject*> aObj, void*& aSelf,
+                   prototypes::ID aProtoID, uint32_t aProtoDepth) {
+    binding_detail::MutableObjectHandleWrapper wrapper(aObj);
+    // We need to pass false here, because if aObj doesn't have a DOMJSClass
+    // it might be a remote proxy object, and we don't want to throw in that
+    // case (even though unwrapping would fail).
+    nsresult rv = binding_detail::UnwrapObjectInternal<void, false>(
+        wrapper, aSelf, aProtoID, aProtoDepth);
+    if (NS_SUCCEEDED(rv)) {
+      return rv;
+    }
+
+    if (js::IsWrapper(wrapper)) {
+      JSObject* unwrappedObj =
+          js::CheckedUnwrap(wrapper, /* stopAtWindowProxy = */ false);
+      if (!unwrappedObj) {
+        return NS_ERROR_XPC_SECURITY_MANAGER_VETO;
+      }
+
+      // At this point we want to keep "unwrappedObj" alive, because we don't
+      // hold a strong reference in "aSelf".
+      wrapper = unwrappedObj;
+
+      return binding_detail::UnwrapObjectInternal<void, false>(
+          wrapper, aSelf, aProtoID, aProtoDepth);
+    }
+
+    if (!IsRemoteObjectProxy(wrapper, aProtoID)) {
+      return NS_ERROR_XPC_BAD_CONVERT_JS;
+    }
+    aSelf = RemoteObjectProxyBase::GetNative(wrapper);
+    return NS_OK;
+  }
+
   // We want the HandleInvalidThis of MaybeGlobalThisPolicy.
 };
 
 /**
  * An ExceptionPolicy struct provides a single HandleException method which is
  * used to handle an exception, if any.  The method is given the current
  * success/failure boolean so it can decide whether there is in fact an
  * exception involved.
@@ -2941,19 +3000,18 @@ bool GenericGetter(JSContext* cx, unsign
   JS::Rooted<JSObject*> obj(cx, ThisPolicy::ExtractThisObject(args));
 
   // NOTE: we want to leave obj in its initial compartment, so don't want to
   // pass it to UnwrapObjectInternal.  Also, the thing we pass to
   // UnwrapObjectInternal may be affected by our ThisPolicy.
   JS::Rooted<JSObject*> rootSelf(cx, ThisPolicy::MaybeUnwrapThisObject(obj));
   void* self;
   {
-    binding_detail::MutableObjectHandleWrapper wrapper(&rootSelf);
-    nsresult rv = binding_detail::UnwrapObjectInternal<void, true>(
-        wrapper, self, protoID, info->depth);
+    nsresult rv =
+        ThisPolicy::UnwrapThisObject(&rootSelf, self, protoID, info->depth);
     if (NS_FAILED(rv)) {
       bool ok = ThisPolicy::HandleInvalidThis(
           cx, args, rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO, protoID);
       return ExceptionPolicy::HandleException(cx, args, info, ok);
     }
   }
 
   MOZ_ASSERT(info->type() == JSJitInfo::Getter);
@@ -2998,19 +3056,18 @@ bool GenericSetter(JSContext* cx, unsign
   JS::Rooted<JSObject*> obj(cx, ThisPolicy::ExtractThisObject(args));
 
   // NOTE: we want to leave obj in its initial compartment, so don't want to
   // pass it to UnwrapObject.  Also the thing we pass to UnwrapObjectInternal
   // may be affected by our ThisPolicy.
   JS::Rooted<JSObject*> rootSelf(cx, ThisPolicy::MaybeUnwrapThisObject(obj));
   void* self;
   {
-    binding_detail::MutableObjectHandleWrapper wrapper(&rootSelf);
-    nsresult rv = binding_detail::UnwrapObjectInternal<void, true>(
-        wrapper, self, protoID, info->depth);
+    nsresult rv =
+        ThisPolicy::UnwrapThisObject(&rootSelf, self, protoID, info->depth);
     if (NS_FAILED(rv)) {
       return ThisPolicy::HandleInvalidThis(
           cx, args, rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO, protoID);
     }
   }
   if (args.length() == 0) {
     return ThrowNoSetterArg(cx, args, protoID);
   }
@@ -3048,19 +3105,18 @@ bool GenericMethod(JSContext* cx, unsign
   JS::Rooted<JSObject*> obj(cx, ThisPolicy::ExtractThisObject(args));
 
   // NOTE: we want to leave obj in its initial compartment, so don't want to
   // pass it to UnwrapObjectInternal.  Also, the thing we pass to
   // UnwrapObjectInternal may be affected by our ThisPolicy.
   JS::Rooted<JSObject*> rootSelf(cx, ThisPolicy::MaybeUnwrapThisObject(obj));
   void* self;
   {
-    binding_detail::MutableObjectHandleWrapper wrapper(&rootSelf);
-    nsresult rv = binding_detail::UnwrapObjectInternal<void, true>(
-        wrapper, self, protoID, info->depth);
+    nsresult rv =
+        ThisPolicy::UnwrapThisObject(&rootSelf, self, protoID, info->depth);
     if (NS_FAILED(rv)) {
       bool ok = ThisPolicy::HandleInvalidThis(
           cx, args, rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO, protoID);
       return ExceptionPolicy::HandleException(cx, args, info, ok);
     }
   }
   MOZ_ASSERT(info->type() == JSJitInfo::Method);
   JSJitMethodOp method = info->method;
@@ -3225,16 +3281,22 @@ nsresult UnwrapArgImpl(JSContext* cx, JS
   // the right thing for the various 'special' interfaces; e.g.
   // nsIPropertyBag. We must use AggregatedQueryInterface in cases where
   // there is an outer to avoid nasty recursion.
   return wrappedJS->QueryInterface(iid, ppArg);
 }
 
 nsresult UnwrapWindowProxyArg(JSContext* cx, JS::Handle<JSObject*> src,
                               WindowProxyHolder& ppArg) {
+  if (IsRemoteObjectProxy(src, prototypes::id::Window)) {
+    ppArg =
+        static_cast<BrowsingContext*>(RemoteObjectProxyBase::GetNative(src));
+    return NS_OK;
+  }
+
   nsCOMPtr<nsPIDOMWindowInner> inner;
   nsresult rv = UnwrapArg<nsPIDOMWindowInner>(cx, src, getter_AddRefs(inner));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsPIDOMWindowOuter> outer = inner->GetOuterWindow();
   RefPtr<BrowsingContext> bc = outer ? outer->GetBrowsingContext() : nullptr;
   ppArg = bc.forget();
   return NS_OK;
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -7689,41 +7689,49 @@ class CGPerSignatureCall(CGThing):
        the method is called (|self->nativeMethodName(...)| vs
        |nativeMethodName(...)|).
 
     We also need to know whether this is a method or a getter/setter
     to do error reporting correctly.
 
     The idlNode parameter can be either a method or an attr. We can query
     |idlNode.identifier| in both cases, so we can be agnostic between the two.
+
+    dontSetSlot should be set to True if the value should not be cached in a
+    slot (even if the attribute is marked as StoreInSlot or Cached in the
+    WebIDL).
     """
     # XXXbz For now each entry in the argument list is either an
     # IDLArgument or a FakeArgument, but longer-term we may want to
     # have ways of flagging things like JSContext* or optional_argc in
     # there.
 
     def __init__(self, returnType, arguments, nativeMethodName, static,
                  descriptor, idlNode, argConversionStartsAt=0, getter=False,
                  setter=False, isConstructor=False, useCounterName=None,
-                 resultVar=None, objectName="obj"):
+                 resultVar=None, objectName="obj", dontSetSlot=False,
+                 extendedAttributes=None):
         assert idlNode.isMethod() == (not getter and not setter)
         assert idlNode.isAttr() == (getter or setter)
         # Constructors are always static
         assert not isConstructor or static
 
         CGThing.__init__(self)
         self.returnType = returnType
         self.descriptor = descriptor
         self.idlNode = idlNode
-        self.extendedAttributes = descriptor.getExtendedAttributes(idlNode,
-                                                                   getter=getter,
-                                                                   setter=setter)
+        if extendedAttributes is None:
+            extendedAttributes = descriptor.getExtendedAttributes(idlNode,
+                                                                  getter=getter,
+                                                                  setter=setter)
+        self.extendedAttributes = extendedAttributes
         self.arguments = arguments
         self.argCount = len(arguments)
         self.isConstructor = isConstructor
+        self.setSlot = not dontSetSlot and idlNode.isAttr() and idlNode.slotIndices is not None
         cgThings = []
 
         deprecated = (idlNode.getExtendedAttribute("Deprecated") or
                       (idlNode.isStatic() and descriptor.interface.getExtendedAttribute("Deprecated")))
         if deprecated:
             cgThings.append(CGGeneric(dedent(
                 """
                 DeprecationWarning(cx, obj, nsIDocument::e%s);
@@ -7956,45 +7964,44 @@ class CGPerSignatureCall(CGThing):
             (self.returnType.isGeckoInterface() or
              self.returnType.isPromise())):
             wrapCode += dedent(
                 """
                 static_assert(!IsPointer<decltype(result)>::value,
                               "NewObject implies that we need to keep the object alive with a strong reference.");
                 """)
 
-        setSlot = self.idlNode.isAttr() and self.idlNode.slotIndices is not None
-        if setSlot:
+        if self.setSlot:
             # For attributes in slots, we want to do some
             # post-processing once we've wrapped them.
             successCode = "break;\n"
         else:
             successCode = None
 
         resultTemplateValues = {
             'jsvalRef': 'args.rval()',
             'jsvalHandle': 'args.rval()',
             'returnsNewObject': returnsNewObject,
             'isConstructorRetval': self.isConstructor,
             'successCode': successCode,
             # 'obj' in this dictionary is the thing whose compartment we are
             # trying to do the to-JS conversion in.  We're going to put that
             # thing in a variable named "conversionScope" if setSlot is true.
             # Otherwise, just use "obj" for lack of anything better.
-            'obj': "conversionScope" if setSlot else "obj"
+            'obj': "conversionScope" if self.setSlot else "obj"
         }
         try:
             wrapCode += wrapForType(self.returnType, self.descriptor, resultTemplateValues)
         except MethodNotNewObjectError, err:
             assert not returnsNewObject
             raise TypeError("%s being returned from non-NewObject method or property %s.%s" %
                             (err.typename,
                              self.descriptor.interface.identifier.name,
                              self.idlNode.identifier.name))
-        if setSlot:
+        if self.setSlot:
             if self.idlNode.isStatic():
                 raise TypeError(
                     "Attribute %s.%s is static, so we don't have a useful slot "
                     "to cache it in, because we don't have support for that on "
                     "interface objects.  See "
                     "https://bugzilla.mozilla.org/show_bug.cgi?id=1363870" %
                     (self.descriptor.interface.identifier.name,
                      self.idlNode.identifier.name))
@@ -8585,38 +8592,43 @@ class CGMethodCall(CGThing):
         return self.cgRoot.define()
 
 
 class CGGetterCall(CGPerSignatureCall):
     """
     A class to generate a native object getter call for a particular IDL
     getter.
     """
-    def __init__(self, returnType, nativeMethodName, descriptor, attr):
+    def __init__(self, returnType, nativeMethodName, descriptor, attr, dontSetSlot=False,
+                 extendedAttributes=None):
         if attr.getExtendedAttribute("UseCounter"):
             useCounterName = "%s_%s_getter" % (descriptor.interface.identifier.name,
                                                attr.identifier.name)
         else:
             useCounterName = None
         if attr.isStatic():
             nativeMethodName = "%s::%s" % (descriptor.nativeType, nativeMethodName)
         CGPerSignatureCall.__init__(self, returnType, [], nativeMethodName,
                                     attr.isStatic(), descriptor, attr,
-                                    getter=True, useCounterName=useCounterName)
+                                    getter=True, useCounterName=useCounterName,
+                                    dontSetSlot=dontSetSlot,
+                                    extendedAttributes=extendedAttributes)
 
 
 class CGNavigatorGetterCall(CGPerSignatureCall):
     """
     A class to generate a native object getter call for an IDL getter for a
     property generated by NavigatorProperty.
     """
-    def __init__(self, returnType, _, descriptor, attr):
+    def __init__(self, returnType, _, descriptor, attr,
+                 dontSetSlot=False):
         nativeMethodName = "%s::ConstructNavigatorObject" % (toBindingNamespace(returnType.inner.identifier.name))
         CGPerSignatureCall.__init__(self, returnType, [], nativeMethodName,
-                                    True, descriptor, attr, getter=True)
+                                    True, descriptor, attr, getter=True,
+                                    dontSetSlot=dontSetSlot)
 
     def getArguments(self):
         # The navigator object should be associated with the global of
         # the navigator it's coming from, which will be the global of
         # the object whose slot it gets cached in.  That's stored in
         # "slotStorage".
         return [(FakeArgument(BuiltinTypes[IDLBuiltinType.Types.object],
                               self.idlNode),
@@ -8786,28 +8798,58 @@ def MakeNativeName(name):
 class CGSpecializedMethod(CGAbstractStaticMethod):
     """
     A class for generating the C++ code for a specialized method that the JIT
     can call with lower overhead.
     """
     def __init__(self, descriptor, method):
         self.method = method
         name = CppKeywords.checkMethodName(IDLToCIdentifier(method.identifier.name))
+        if method.getExtendedAttribute("CrossOriginCallable"):
+            selfArg = Argument('void*', 'void_self')
+        else:
+            selfArg = Argument('%s*' % descriptor.nativeType, 'self')
         args = [Argument('JSContext*', 'cx'),
                 Argument('JS::Handle<JSObject*>', 'obj'),
-                Argument('%s*' % descriptor.nativeType, 'self'),
+                selfArg,
                 Argument('const JSJitMethodCallArgs&', 'args')]
         CGAbstractStaticMethod.__init__(self, descriptor, name, 'bool', args,
                                         canRunScript=True)
 
     def definition_body(self):
         nativeName = CGSpecializedMethod.makeNativeName(self.descriptor,
                                                         self.method)
-        return CGMethodCall(nativeName, self.method.isStatic(), self.descriptor,
+        call = CGMethodCall(nativeName, self.method.isStatic(), self.descriptor,
                             self.method).define()
+        if self.method.getExtendedAttribute("CrossOriginCallable"):
+            for signature in self.method.signatures():
+                # non-void signatures would require us to deal with remote proxies for the
+                # return value here.
+                if not signature[0].isVoid():
+                    raise TypeError("We don't support a method marked as CrossOriginCallable "
+                                    "with non-void return type")
+            prototypeID, _ = PrototypeIDAndDepth(self.descriptor)
+            return fill("""
+                // CrossOriginThisPolicy::UnwrapThisObject stores a ${nativeType}::RemoteProxy in void_self
+                // if obj is a proxy with a RemoteObjectProxy handler for the right type, or else it stores
+                // a ${nativeType}. If we get here from the JIT (without going through UnwrapThisObject) we
+                // know void_self contains a ${nativeType}; we don't have special cases in the JIT to deal
+                // with remote object proxies.
+                if (IsRemoteObjectProxy(obj, ${prototypeID})) {
+                    ${nativeType}::RemoteProxy* self = static_cast<${nativeType}::RemoteProxy*>(void_self);
+                    $*{call}
+                }
+                ${nativeType}* self = static_cast<${nativeType}*>(void_self);
+                $*{call}
+                """,
+                prototypeID=prototypeID,
+                ifaceName=self.descriptor.name,
+                nativeType=self.descriptor.nativeType,
+                call=call)
+        return call
 
     def auto_profiler_label(self):
         interface_name = self.descriptor.interface.identifier.name
         method_name = self.method.identifier.name
         return fill(
             """
             AUTO_PROFILER_LABEL_DYNAMIC_FAST(
               "${interface_name}", "${method_name}", DOM, cx,
@@ -9089,20 +9131,24 @@ class CGStaticMethod(CGAbstractStaticBin
 class CGSpecializedGetter(CGAbstractStaticMethod):
     """
     A class for generating the code for a specialized attribute getter
     that the JIT can call with lower overhead.
     """
     def __init__(self, descriptor, attr):
         self.attr = attr
         name = 'get_' + IDLToCIdentifier(attr.identifier.name)
+        if attr.getExtendedAttribute("CrossOriginReadable"):
+            selfArg = Argument('void*', 'void_self')
+        else:
+            selfArg = Argument('%s*' % descriptor.nativeType, 'self')
         args = [
             Argument('JSContext*', 'cx'),
             Argument('JS::Handle<JSObject*>', 'obj'),
-            Argument('%s*' % descriptor.nativeType, 'self'),
+            selfArg,
             Argument('JSJitGetterCallArgs', 'args')
         ]
         # StoreInSlot attributes have their getters called from Wrap().  We
         # really hope they can't run script, and don't want to annotate Wrap()
         # methods as doing that anyway, so let's not annotate them as
         # MOZ_CAN_RUN_SCRIPT.
         CGAbstractStaticMethod.__init__(
             self, descriptor, name, "bool", args,
@@ -9112,16 +9158,40 @@ class CGSpecializedGetter(CGAbstractStat
         if self.attr.isMaplikeOrSetlikeAttr():
             # If the interface is maplike/setlike, there will be one getter
             # method for the size property of the backing object. Due to having
             # to unpack the backing object from the slot, this requires its own
             # generator.
             return getMaplikeOrSetlikeSizeGetterBody(self.descriptor, self.attr)
         nativeName = CGSpecializedGetter.makeNativeName(self.descriptor,
                                                         self.attr)
+        prefix = ""
+        type = self.attr.type
+        if self.attr.getExtendedAttribute("CrossOriginReadable"):
+            remoteType = type
+            extendedAttributes = self.descriptor.getExtendedAttributes(self.attr, getter=True)
+            if remoteType.isGeckoInterface() and not remoteType.unroll().inner.isExternal():
+                # We'll use a JSObject. It might make more sense to use remoteType's
+                # RemoteProxy, but it's not easy to construct a type for that from here.
+                remoteType = BuiltinTypes[IDLBuiltinType.Types.object]
+                extendedAttributes.append('canOOM')
+                extendedAttributes.remove('infallible')
+            prototypeID, _ = PrototypeIDAndDepth(self.descriptor)
+            prefix = fill("""
+                if (IsRemoteObjectProxy(obj, ${prototypeID})) {
+                    ${nativeType}::RemoteProxy* self = static_cast<${nativeType}::RemoteProxy*>(void_self);
+                    $*{call}
+                }
+                ${nativeType}* self = static_cast<${nativeType}*>(void_self);
+            """,
+            prototypeID=prototypeID,
+            ifaceName=self.descriptor.name,
+            nativeType=self.descriptor.nativeType,
+            call=CGGetterCall(remoteType, nativeName, self.descriptor, self.attr, dontSetSlot=True,
+                              extendedAttributes=extendedAttributes).define())
         if self.attr.slotIndices is not None:
             # We're going to store this return value in a slot on some object,
             # to cache it.  The question is, which object?  For dictionary and
             # sequence return values, we want to use a slot on the Xray expando
             # if we're called via Xrays, and a slot on our reflector otherwise.
             # On the other hand, when dealing with some interfacce types
             # (navigator properties, window.document) we want to avoid calling
             # the getter more than once.  In the case of navigator properties
@@ -9132,31 +9202,31 @@ class CGSpecializedGetter(CGAbstractStat
             # around.
             #
             # The upshot is that we use the reflector slot for any getter whose
             # type is a gecko interface, whether we're called via Xrays or not.
             # Since [Cached] and [StoreInSlot] cannot be used with "NewObject",
             # we know that in the interface type case the returned object is
             # wrappercached.  So creating Xrays to it is reasonable.
             if mayUseXrayExpandoSlots(self.descriptor, self.attr):
-                prefix = fill(
+                prefix += fill(
                     """
                     // Have to either root across the getter call or reget after.
                     bool isXray;
                     JS::Rooted<JSObject*> slotStorage(cx, GetCachedSlotStorageObject(cx, obj, &isXray));
                     if (!slotStorage) {
                       return false;
                     }
                     const size_t slotIndex = isXray ? ${xraySlotIndex} : ${slotIndex};
                     """,
                     xraySlotIndex=memberXrayExpandoReservedSlot(self.attr,
                                                                 self.descriptor),
                     slotIndex=memberReservedSlot(self.attr, self.descriptor))
             else:
-                prefix = fill(
+                prefix += fill(
                     """
                     // Have to either root across the getter call or reget after.
                     JS::Rooted<JSObject*> slotStorage(cx, js::UncheckedUnwrap(obj, /* stopAtWindowProxy = */ false));
                     MOZ_ASSERT(IsDOMObject(slotStorage));
                     const size_t slotIndex = ${slotIndex};
                     """,
                     slotIndex=memberReservedSlot(self.attr, self.descriptor))
 
@@ -9171,25 +9241,23 @@ class CGSpecializedGetter(CGAbstractStat
                     // The cached value is in the compartment of slotStorage,
                     // so wrap into the caller compartment as needed.
                     return ${maybeWrap}(cx, args.rval());
                   }
                 }
 
                 """,
                 maybeWrap=getMaybeWrapValueFuncForType(self.attr.type))
-        else:
-            prefix = ""
 
         if self.attr.navigatorObjectGetter:
             cgGetterCall = CGNavigatorGetterCall
         else:
             cgGetterCall = CGGetterCall
         return (prefix +
-                cgGetterCall(self.attr.type, nativeName,
+                cgGetterCall(type, nativeName,
                              self.descriptor, self.attr).define())
 
     def auto_profiler_label(self):
         interface_name = self.descriptor.interface.identifier.name
         attr_name = self.attr.identifier.name
         return fill(
             """
             AUTO_PROFILER_LABEL_DYNAMIC_FAST(
@@ -9275,28 +9343,53 @@ class CGStaticGetter(CGAbstractStaticBin
 class CGSpecializedSetter(CGAbstractStaticMethod):
     """
     A class for generating the code for a specialized attribute setter
     that the JIT can call with lower overhead.
     """
     def __init__(self, descriptor, attr):
         self.attr = attr
         name = 'set_' + IDLToCIdentifier(attr.identifier.name)
+        if attr.getExtendedAttribute("CrossOriginWritable"):
+            selfArg = Argument('void*', 'void_self')
+        else:
+            selfArg = Argument('%s*' % descriptor.nativeType, 'self')
         args = [Argument('JSContext*', 'cx'),
                 Argument('JS::Handle<JSObject*>', 'obj'),
-                Argument('%s*' % descriptor.nativeType, 'self'),
+                selfArg,
                 Argument('JSJitSetterCallArgs', 'args')]
         CGAbstractStaticMethod.__init__(self, descriptor, name, "bool", args,
                                         canRunScript=True)
 
     def definition_body(self):
         nativeName = CGSpecializedSetter.makeNativeName(self.descriptor,
                                                         self.attr)
-        return CGSetterCall(self.attr.type, nativeName, self.descriptor,
-                            self.attr).define()
+        type = self.attr.type
+        call = CGSetterCall(type, nativeName, self.descriptor, self.attr).define()
+        if self.attr.getExtendedAttribute("CrossOriginWritable"):
+            if type.isGeckoInterface() and not type.unroll().inner.isExternal():
+                # a setter taking a Gecko interface would require us to deal with remote
+                # proxies for the value here.
+                raise TypeError("We don't support the setter of %s marked as "
+                                "CrossOriginWritable because it takes a Gecko interface "
+                                "as the value", attr.identifier.name)
+            prototypeID, _ = PrototypeIDAndDepth(self.descriptor)
+            return fill("""
+                if (IsRemoteObjectProxy(obj, ${prototypeID})) {
+                    ${nativeType}::RemoteProxy* self = static_cast<${nativeType}::RemoteProxy*>(void_self);
+                    $*{call}
+                }
+                ${nativeType}* self = static_cast<${nativeType}*>(void_self);
+                $*{call}
+                """,
+                prototypeID=prototypeID,
+                ifaceName=self.descriptor.name,
+                nativeType=self.descriptor.nativeType,
+                call=call)
+        return call
 
     def auto_profiler_label(self):
         interface_name = self.descriptor.interface.identifier.name
         attr_name = self.attr.identifier.name
         return fill(
             """
             AUTO_PROFILER_LABEL_DYNAMIC_FAST(
               "${interface_name}", "${attr_name}", DOM, cx,
@@ -14085,16 +14178,17 @@ class CGBindingRoot(CGThing):
             def hasCrossOriginProperty(m):
                 props = memberProperties(m, desc)
                 return (props.isCrossOriginMethod or
                         props.isCrossOriginGetter or
                         props.isCrossOriginSetter)
 
             return any(hasCrossOriginProperty(m) for m in desc.interface.members)
 
+        bindingDeclareHeaders["mozilla/dom/RemoteObjectProxy.h"] = any(descriptorHasCrossOriginProperties(d) for d in descriptors)
         bindingDeclareHeaders["jsapi.h"] = any(descriptorHasCrossOriginProperties(d) for d in descriptors)
         bindingDeclareHeaders["jspubtd.h"] = not bindingDeclareHeaders["jsapi.h"]
         bindingDeclareHeaders["js/RootingAPI.h"] = not bindingDeclareHeaders["jsapi.h"]
 
         def descriptorHasIteratorAlias(desc):
             def hasIteratorAlias(m):
                 return m.isMethod() and "@@iterator" in m.aliases
             return any(hasIteratorAlias(m) for m in desc.interface.members)
new file mode 100644
--- /dev/null
+++ b/dom/bindings/RemoteObjectProxy.cpp
@@ -0,0 +1,240 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "RemoteObjectProxy.h"
+#include "AccessCheck.h"
+#include "jsfriendapi.h"
+
+namespace mozilla {
+namespace dom {
+
+// Give RemoteObjectProxy 2 reserved slots, like the other wrappers, so
+// JSObject::swap can swap it with CrossCompartmentWrappers without requiring
+// malloc.
+const js::Class RemoteObjectProxyClass =
+    PROXY_CLASS_DEF("Proxy", JSCLASS_HAS_RESERVED_SLOTS(2));
+
+bool RemoteObjectProxyBase::getOwnPropertyDescriptor(
+    JSContext* aCx, JS::Handle<JSObject*> aProxy, JS::Handle<jsid> aId,
+    JS::MutableHandle<JS::PropertyDescriptor> aDesc) const {
+  bool ok = getOwnPropertyDescriptorInternal(aCx, aProxy, aId, aDesc);
+  if (!ok || aDesc.object()) {
+    return ok;
+  }
+
+  return getOwnPropertyDescriptorTail(aCx, aProxy, aId, aDesc);
+}
+
+bool RemoteObjectProxyBase::defineProperty(
+    JSContext* aCx, JS::Handle<JSObject*> aProxy, JS::Handle<jsid> aId,
+    JS::Handle<JS::PropertyDescriptor> aDesc,
+    JS::ObjectOpResult& aResult) const {
+  // https://html.spec.whatwg.org/multipage/browsers.html#windowproxy-defineownproperty
+  // step 3 and
+  // https://html.spec.whatwg.org/multipage/browsers.html#location-defineownproperty
+  // step 2
+  return ReportCrossOriginDenial(aCx, aId, NS_LITERAL_CSTRING("define"));
+}
+
+bool RemoteObjectProxyBase::ownPropertyKeys(JSContext* aCx,
+                                            JS::Handle<JSObject*> aProxy,
+                                            JS::AutoIdVector& aProps) const {
+  // https://html.spec.whatwg.org/multipage/browsers.html#crossoriginownpropertykeys-(-o-)
+  // step 2 and
+  // https://html.spec.whatwg.org/multipage/browsers.html#crossoriginproperties-(-o-)
+  JS::Rooted<JSObject*> holder(aCx);
+  if (!EnsureHolder(aCx, aProxy, &holder) ||
+      !js::GetPropertyKeys(aCx, holder,
+                           JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS,
+                           &aProps)) {
+    return false;
+  }
+
+  // https://html.spec.whatwg.org/multipage/browsers.html#crossoriginownpropertykeys-(-o-)
+  // step 3 and 4
+  return xpc::AppendCrossOriginWhitelistedPropNames(aCx, aProps);
+}
+
+bool RemoteObjectProxyBase::delete_(JSContext* aCx,
+                                    JS::Handle<JSObject*> aProxy,
+                                    JS::Handle<jsid> aId,
+                                    JS::ObjectOpResult& aResult) const {
+  // https://html.spec.whatwg.org/multipage/browsers.html#windowproxy-delete
+  // step 3 and
+  // https://html.spec.whatwg.org/multipage/browsers.html#location-delete step 2
+  return ReportCrossOriginDenial(aCx, aId, NS_LITERAL_CSTRING("delete"));
+}
+
+bool RemoteObjectProxyBase::getPrototype(
+    JSContext* aCx, JS::Handle<JSObject*> aProxy,
+    JS::MutableHandle<JSObject*> aProtop) const {
+  // https://html.spec.whatwg.org/multipage/browsers.html#windowproxy-getprototypeof
+  // step 3 and
+  // https://html.spec.whatwg.org/multipage/browsers.html#location-getprototypeof
+  // step 2
+  aProtop.set(nullptr);
+  return true;
+}
+
+bool RemoteObjectProxyBase::setPrototype(JSContext* aCx,
+                                         JS::Handle<JSObject*> aProxy,
+                                         JS::Handle<JSObject*> aProto,
+                                         JS::ObjectOpResult& aResult) const {
+  // https://html.spec.whatwg.org/multipage/browsers.html#windowproxy-setprototypeof
+  // and
+  // https://html.spec.whatwg.org/multipage/browsers.html#location-setprototypeof
+  // say to call SetImmutablePrototype, which does nothing and just returns
+  // whether the passed-in value equals the current prototype. Our current
+  // prototype is always null, so this just comes down to returning whether null
+  // was passed in.
+  //
+  // In terms of ObjectOpResult that means calling one of the fail*() things on
+  // it if non-null was passed, and it's got one that does just what we want.
+  if (!aProto) {
+    return aResult.succeed();
+  }
+  return aResult.failCantSetProto();
+}
+
+bool RemoteObjectProxyBase::getPrototypeIfOrdinary(
+    JSContext* aCx, JS::Handle<JSObject*> aProxy, bool* aIsOrdinary,
+    JS::MutableHandle<JSObject*> aProtop) const {
+  // WindowProxy's and Location's [[GetPrototypeOf]] traps aren't the ordinary
+  // definition:
+  //
+  //   https://html.spec.whatwg.org/multipage/browsers.html#windowproxy-getprototypeof
+  //   https://html.spec.whatwg.org/multipage/browsers.html#location-getprototypeof
+  //
+  // We nonetheless can implement it with a static [[Prototype]], because the
+  // [[GetPrototypeOf]] trap should always return null.
+  *aIsOrdinary = true;
+  return true;
+}
+
+bool RemoteObjectProxyBase::preventExtensions(
+    JSContext* aCx, JS::Handle<JSObject*> aProxy,
+    JS::ObjectOpResult& aResult) const {
+  // https://html.spec.whatwg.org/multipage/browsers.html#windowproxy-preventextensions
+  // and
+  // https://html.spec.whatwg.org/multipage/browsers.html#location-preventextensions
+  return aResult.failCantPreventExtensions();
+}
+
+bool RemoteObjectProxyBase::isExtensible(JSContext* aCx,
+                                         JS::Handle<JSObject*> aProxy,
+                                         bool* aExtensible) const {
+  // https://html.spec.whatwg.org/multipage/browsers.html#windowproxy-isextensible
+  // and
+  // https://html.spec.whatwg.org/multipage/browsers.html#location-isextensible
+  *aExtensible = true;
+  return true;
+}
+
+bool RemoteObjectProxyBase::get(JSContext* aCx, JS::Handle<JSObject*> aProxy,
+                                JS::Handle<JS::Value> aReceiver,
+                                JS::Handle<jsid> aId,
+                                JS::MutableHandle<JS::Value> aVp) const {
+  Rooted<PropertyDescriptor> desc(aCx);
+  if (!getOwnPropertyDescriptor(aCx, aProxy, aId, &desc)) {
+    return false;
+  }
+
+  MOZ_ASSERT(desc.object());
+
+  if (desc.isDataDescriptor()) {
+    aVp.set(desc.value());
+    return true;
+  }
+
+  if (!desc.hasGetterObject()) {
+    return ReportCrossOriginDenial(aCx, aId, NS_LITERAL_CSTRING("get"));
+  }
+
+  JS::Rooted<JSObject*> getter(aCx, desc.getterObject());
+  return JS::Call(aCx, aReceiver, getter, JS::HandleValueArray::empty(), aVp);
+}
+
+bool RemoteObjectProxyBase::set(JSContext* aCx, JS::Handle<JSObject*> aProxy,
+                                JS::Handle<jsid> aId,
+                                JS::Handle<JS::Value> aValue,
+                                JS::Handle<JS::Value> aReceiver,
+                                JS::ObjectOpResult& aResult) const {
+  Rooted<PropertyDescriptor> desc(aCx);
+  if (!getOwnPropertyDescriptor(aCx, aProxy, aId, &desc)) {
+    return false;
+  }
+
+  MOZ_ASSERT(desc.object());
+
+  if (!desc.hasSetterObject()) {
+    return ReportCrossOriginDenial(aCx, aId, NS_LITERAL_CSTRING("set"));
+  }
+
+  JS::Rooted<JSObject*> setter(aCx, desc.setterObject());
+  JS::Rooted<JS::Value> rv(aCx);
+  return JS::Call(aCx, aReceiver, setter, JS::HandleValueArray(aValue), &rv) &&
+         aResult.succeed();
+}
+
+bool RemoteObjectProxyBase::hasOwn(JSContext* aCx, JS::Handle<JSObject*> aProxy,
+                                   JS::Handle<jsid> aId, bool* aBp) const {
+  JS::Rooted<JSObject*> holder(aCx);
+  if (!EnsureHolder(aCx, aProxy, &holder) ||
+      !JS_AlreadyHasOwnPropertyById(aCx, holder, aId, aBp)) {
+    return false;
+  }
+
+  if (!*aBp) {
+    *aBp = xpc::IsCrossOriginWhitelistedProp(aCx, aId);
+  }
+
+  return true;
+}
+
+bool RemoteObjectProxyBase::getOwnEnumerablePropertyKeys(
+    JSContext* aCx, JS::Handle<JSObject*> aProxy,
+    JS::AutoIdVector& aProps) const {
+  return true;
+}
+
+JSObject* RemoteObjectProxyBase::CreateProxyObject(
+    JSContext* aCx, void* aNative, const js::Class* aClasp) const {
+  js::ProxyOptions options;
+  options.setClass(aClasp);
+  JS::Rooted<JS::Value> native(aCx, JS::PrivateValue(aNative));
+  return js::NewProxyObject(aCx, this, native, nullptr, options);
+}
+
+/* static */
+bool RemoteObjectProxyBase::getOwnPropertyDescriptorTail(
+    JSContext* aCx, JS::Handle<JSObject*> aProxy, JS::Handle<jsid> aId,
+    JS::MutableHandle<JS::PropertyDescriptor> aDesc) {
+  if (xpc::IsCrossOriginWhitelistedProp(aCx, aId)) {
+    // https://html.spec.whatwg.org/multipage/browsers.html#crossorigingetownpropertyhelper-(-o,-p-)
+    // step 3 says to return PropertyDescriptor {
+    //   [[Value]]: undefined, [[Writable]]: false, [[Enumerable]]: false,
+    //   [[Configurable]]: true
+    // }.
+    //
+    aDesc.setDataDescriptor(JS::UndefinedHandleValue, JSPROP_READONLY);
+    aDesc.object().set(aProxy);
+    return true;
+  }
+
+  return ReportCrossOriginDenial(aCx, aId, NS_LITERAL_CSTRING("access"));
+}
+
+/* static */
+bool RemoteObjectProxyBase::ReportCrossOriginDenial(
+    JSContext* aCx, JS::Handle<jsid> aId, const nsACString& aAccessType) {
+  xpc::AccessCheck::reportCrossOriginDenial(aCx, aId, aAccessType);
+  return false;
+}
+
+const char RemoteObjectProxyBase::sCrossOriginProxyFamily = 0;
+
+}  // namespace dom
+}  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/bindings/RemoteObjectProxy.h
@@ -0,0 +1,200 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_RemoteObjectProxy_h
+#define mozilla_dom_RemoteObjectProxy_h
+
+#include "js/Proxy.h"
+#include "mozilla/dom/PrototypeList.h"
+#include "xpcpublic.h"
+
+namespace mozilla {
+namespace dom {
+
+/**
+ * Base class for RemoteObjectProxy. Implements the pieces of the handler that
+ * don't depend on properties/methods of the specific WebIDL interface that this
+ * proxy implements.
+ */
+class RemoteObjectProxyBase : public js::BaseProxyHandler {
+ protected:
+  explicit constexpr RemoteObjectProxyBase(prototypes::ID aPrototypeID)
+      : BaseProxyHandler(&sCrossOriginProxyFamily, false),
+        mPrototypeID(aPrototypeID) {}
+
+ public:
+  bool finalizeInBackground(const JS::Value& priv) const final { return false; }
+
+  // Standard internal methods
+  bool getOwnPropertyDescriptor(
+      JSContext* aCx, JS::Handle<JSObject*> aProxy, JS::Handle<jsid> aId,
+      JS::MutableHandle<JS::PropertyDescriptor> aDesc) const override;
+  bool ownPropertyKeys(JSContext* aCx, JS::Handle<JSObject*> aProxy,
+                       JS::AutoIdVector& aProps) const override;
+  bool defineProperty(JSContext* aCx, JS::Handle<JSObject*> aProxy,
+                      JS::Handle<jsid> aId,
+                      JS::Handle<JS::PropertyDescriptor> aDesc,
+                      JS::ObjectOpResult& result) const final;
+  bool delete_(JSContext* aCx, JS::Handle<JSObject*> aProxy,
+               JS::Handle<jsid> aId, JS::ObjectOpResult& aResult) const final;
+
+  bool getPrototype(JSContext* aCx, JS::Handle<JSObject*> aProxy,
+                    JS::MutableHandle<JSObject*> aProtop) const final;
+  bool setPrototype(JSContext* aCx, JS::Handle<JSObject*> aProxy,
+                    JS::Handle<JSObject*> aProto,
+                    JS::ObjectOpResult& aResult) const final;
+  bool getPrototypeIfOrdinary(JSContext* aCx, JS::Handle<JSObject*> aProxy,
+                              bool* aIsOrdinary,
+                              JS::MutableHandle<JSObject*> aProtop) const final;
+
+  bool preventExtensions(JSContext* aCx, JS::Handle<JSObject*> aProxy,
+                         JS::ObjectOpResult& aResult) const final;
+  bool isExtensible(JSContext* aCx, JS::Handle<JSObject*> aProxy,
+                    bool* aExtensible) const final;
+
+  bool get(JSContext* cx, JS::Handle<JSObject*> aProxy,
+           JS::Handle<JS::Value> aReceiver, JS::Handle<jsid> aId,
+           JS::MutableHandle<JS::Value> aVp) const final;
+  bool set(JSContext* cx, JS::Handle<JSObject*> aProxy, JS::Handle<jsid> aId,
+           JS::Handle<JS::Value> aValue, JS::Handle<JS::Value> aReceiver,
+           JS::ObjectOpResult& aResult) const final;
+
+  // SpiderMonkey extensions
+  bool hasOwn(JSContext* aCx, JS::Handle<JSObject*> aProxy,
+              JS::Handle<jsid> aId, bool* aBp) const override;
+  bool getOwnEnumerablePropertyKeys(JSContext* aCx,
+                                    JS::Handle<JSObject*> aProxy,
+                                    JS::AutoIdVector& aProps) const override;
+
+  bool isCallable(JSObject* aObj) const final { return false; }
+  bool isConstructor(JSObject* aObj) const final { return false; }
+
+  static void* GetNative(JSObject* aProxy) {
+    return js::GetProxyPrivate(aProxy).toPrivate();
+  }
+
+  /**
+   * Returns true if aProxy represents an object implementing the WebIDL
+   * interface for aProtoID. aProxy should be a proxy object.
+   */
+  static inline bool IsRemoteObjectProxy(JSObject* aProxy,
+                                         prototypes::ID aProtoID) {
+    const js::BaseProxyHandler* handler = js::GetProxyHandler(aProxy);
+    return handler->family() == &sCrossOriginProxyFamily &&
+           static_cast<const RemoteObjectProxyBase*>(handler)->mPrototypeID ==
+               aProtoID;
+  }
+
+ protected:
+  bool getOwnPropertyDescriptorInternal(
+      JSContext* aCx, JS::Handle<JSObject*> aProxy, JS::Handle<jsid> aId,
+      JS::MutableHandle<JS::PropertyDescriptor> aDesc) const {
+    JS::Rooted<JSObject*> holder(aCx);
+    if (!EnsureHolder(aCx, aProxy, &holder) ||
+        !JS_GetOwnPropertyDescriptorById(aCx, holder, aId, aDesc)) {
+      return false;
+    }
+
+    if (aDesc.object()) {
+      aDesc.object().set(aProxy);
+    }
+
+    return true;
+  }
+
+  JSObject* CreateProxyObject(JSContext* aCx, void* aNative,
+                              const js::Class* aClasp) const;
+
+  /**
+   * Implements the tail of getOwnPropertyDescriptor, dealing in particular with
+   * properties that are whitelisted by xpc::IsCrossOriginWhitelistedProp.
+   */
+  static bool getOwnPropertyDescriptorTail(
+      JSContext* aCx, JS::Handle<JSObject*> aProxy, JS::Handle<jsid> aId,
+      JS::MutableHandle<JS::PropertyDescriptor> aDesc);
+  static bool ReportCrossOriginDenial(JSContext* aCx, JS::Handle<jsid> aId,
+                                      const nsACString& aAccessType);
+
+  /**
+   * This gets a cached, or creates and caches, a holder object that contains
+   * the WebIDL properties for this proxy.
+   */
+  bool EnsureHolder(JSContext* aCx, JS::Handle<JSObject*> aProxy,
+                    JS::MutableHandle<JSObject*> aHolder) const {
+    // FIXME Need to have a holder per realm, should store a weakmap in the
+    //       reserved slot.
+    JS::Value v = js::GetProxyReservedSlot(aProxy, 0);
+    if (v.isObject()) {
+      aHolder.set(&v.toObject());
+      return true;
+    }
+
+    aHolder.set(JS_NewObjectWithGivenProto(aCx, nullptr, nullptr));
+    if (!aHolder || !DefinePropertiesAndFunctions(aCx, aHolder)) {
+      return false;
+    }
+
+    js::SetProxyReservedSlot(aProxy, 0, JS::ObjectValue(*aHolder));
+    return true;
+  }
+
+  virtual bool DefinePropertiesAndFunctions(
+      JSContext* aCx, JS::Handle<JSObject*> aHolder) const = 0;
+
+  const prototypes::ID mPrototypeID;
+
+  static const char sCrossOriginProxyFamily;
+};
+
+/**
+ * Proxy handler for proxy objects that represent an object implementing a
+ * WebIDL interface that has cross-origin accessible properties/methods, and
+ * which lives in a different process. The WebIDL code generator will create
+ * arrays of cross-origin accessible properties/methods that can be used as
+ * arguments to this template.
+ *
+ * The properties and methods can be cached on a holder JSObject, stored in a
+ * reserved slot on the proxy object.
+ *
+ * The proxy objects that use a handler derived from this one are stored in a
+ * hash map in the JS compartment's private (@see
+ * xpc::CompartmentPrivate::GetRemoteProxyMap).
+ */
+template <class Native, JSPropertySpec* P, JSFunctionSpec* F>
+class RemoteObjectProxy : public RemoteObjectProxyBase {
+ public:
+  JSObject* CreateProxyObject(JSContext* aCx, Native* aNative,
+                              const js::Class* aClasp) const {
+    return RemoteObjectProxyBase::CreateProxyObject(aCx, aNative, aClasp);
+  }
+
+ protected:
+  using RemoteObjectProxyBase::RemoteObjectProxyBase;
+
+ private:
+  virtual bool DefinePropertiesAndFunctions(
+      JSContext* aCx, JS::Handle<JSObject*> aHolder) const final {
+    return JS_DefineProperties(aCx, aHolder, P) &&
+           JS_DefineFunctions(aCx, aHolder, F);
+  }
+};
+
+/**
+ * Returns true if aObj is a proxy object that represents an object implementing
+ * the WebIDL interface for aProtoID.
+ */
+static inline bool IsRemoteObjectProxy(JSObject* aObj,
+                                       prototypes::ID aProtoID) {
+  if (!js::IsProxy(aObj)) {
+    return false;
+  }
+  return RemoteObjectProxyBase::IsRemoteObjectProxy(aObj, aProtoID);
+}
+
+}  // namespace dom
+}  // namespace mozilla
+
+#endif /* mozilla_dom_RemoteObjectProxy_h */
--- a/dom/bindings/ToJSValue.cpp
+++ b/dom/bindings/ToJSValue.cpp
@@ -63,21 +63,30 @@ bool ToJSValue(JSContext* aCx, Promise& 
 
 bool ToJSValue(JSContext* aCx, const WindowProxyHolder& aArgument,
                JS::MutableHandle<JS::Value> aValue) {
   BrowsingContext* bc = aArgument.get();
   if (!bc) {
     aValue.setNull();
     return true;
   }
-  JS::Rooted<JSObject*> windowProxy(aCx, bc->GetWindowProxy());
-  if (!windowProxy) {
-    nsPIDOMWindowOuter* window = bc->GetDOMWindow();
-    if (!window->EnsureInnerWindow()) {
-      return Throw(aCx, NS_ERROR_UNEXPECTED);
+  JS::Rooted<JSObject*> windowProxy(aCx);
+  if (bc->GetDocShell()) {
+    windowProxy = bc->GetWindowProxy();
+    if (!windowProxy) {
+      nsPIDOMWindowOuter* window = bc->GetDOMWindow();
+      if (!window->EnsureInnerWindow()) {
+        return Throw(aCx, NS_ERROR_UNEXPECTED);
+      }
+      windowProxy = bc->GetWindowProxy();
     }
-    windowProxy = bc->GetWindowProxy();
+    return ToJSValue(aCx, windowProxy, aValue);
   }
-  return ToJSValue(aCx, windowProxy, aValue);
+
+  if (!GetRemoteOuterWindowProxy(aCx, bc, &windowProxy)) {
+    return false;
+  }
+  aValue.setObjectOrNull(windowProxy);
+  return true;
 }
 
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/bindings/moz.build
+++ b/dom/bindings/moz.build
@@ -44,16 +44,17 @@ EXPORTS.mozilla.dom += [
     'FakeString.h',
     'IterableIterator.h',
     'JSSlots.h',
     'NonRefcountedDOMObject.h',
     'Nullable.h',
     'PrimitiveConversions.h',
     'ReadableStream.h',
     'Record.h',
+    'RemoteObjectProxy.h',
     'RootedDictionary.h',
     'SimpleGlobalObject.h',
     'SpiderMonkeyInterface.h',
     'StructuredClone.h',
     'ToJSValue.h',
     'TypedArray.h',
     'UnionMember.h',
     'WebIDLGlobalNameHash.h',
@@ -108,16 +109,17 @@ UNIFIED_SOURCES += [
     'CallbackInterface.cpp',
     'CallbackObject.cpp',
     'Date.cpp',
     'DOMJSProxyHandler.cpp',
     'Exceptions.cpp',
     'IterableIterator.cpp',
     'nsScriptError.cpp',
     'nsScriptErrorWithStack.cpp',
+    'RemoteObjectProxy.cpp',
     'SimpleGlobalObject.cpp',
     'ToJSValue.cpp',
     'WebIDLGlobalNameHash.cpp',
 ]
 
 SOURCES += [
     'StructuredClone.cpp',
 ]
--- a/dom/bindings/nsScriptError.cpp
+++ b/dom/bindings/nsScriptError.cpp
@@ -57,26 +57,17 @@ void nsScriptErrorBase::InitializeOnMain
 
   if (mInnerWindowID) {
     nsGlobalWindowInner* window =
         nsGlobalWindowInner::GetInnerWindowWithId(mInnerWindowID);
     if (window) {
       nsPIDOMWindowOuter* outer = window->GetOuterWindow();
       if (outer) mOuterWindowID = outer->WindowID();
 
-      nsIDocShell* docShell = window->GetDocShell();
-      nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell);
-
-      if (loadContext) {
-        // Never mark exceptions from chrome windows as having come from
-        // private windows, since we always want them to be reported.
-        nsIPrincipal* winPrincipal = window->GetPrincipal();
-        mIsFromPrivateWindow = loadContext->UsePrivateBrowsing() &&
-                               !nsContentUtils::IsSystemPrincipal(winPrincipal);
-      }
+      mIsFromPrivateWindow = ComputeIsFromPrivateWindow(window);
     }
   }
 
   mInitializedOnMainThread = true;
 }
 
 // nsIConsoleMessage methods
 NS_IMETHODIMP
@@ -384,16 +375,25 @@ nsScriptErrorBase::GetNotes(nsIArray** a
 
   uint32_t len = mNotes.Length();
   for (uint32_t i = 0; i < len; i++) array->AppendElement(mNotes[i]);
   array.forget(aNotes);
 
   return NS_OK;
 }
 
+/* static */ bool nsScriptErrorBase::ComputeIsFromPrivateWindow(
+    nsGlobalWindowInner* aWindow) {
+  // Never mark exceptions from chrome windows as having come from private
+  // windows, since we always want them to be reported.
+  nsIPrincipal* winPrincipal = aWindow->GetPrincipal();
+  return aWindow->IsPrivateBrowsing() &&
+         !nsContentUtils::IsSystemPrincipal(winPrincipal);
+}
+
 NS_IMPL_ISUPPORTS(nsScriptError, nsIConsoleMessage, nsIScriptError)
 
 nsScriptErrorNote::nsScriptErrorNote()
     : mMessage(), mSourceName(), mLineNumber(0), mColumnNumber(0) {}
 
 nsScriptErrorNote::~nsScriptErrorNote() {}
 
 void nsScriptErrorNote::Init(const nsAString& message,
--- a/dom/bindings/nsScriptError.h
+++ b/dom/bindings/nsScriptError.h
@@ -14,16 +14,18 @@
 #include "jsapi.h"
 #include "js/RootingAPI.h"
 
 #include "nsCOMArray.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsIScriptError.h"
 #include "nsString.h"
 
+class nsGlobalWindowInner;
+
 class nsScriptErrorNote final : public nsIScriptErrorNote {
  public:
   nsScriptErrorNote();
 
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSISCRIPTERRORNOTE
 
   void Init(const nsAString& message, const nsAString& sourceName,
@@ -44,16 +46,18 @@ class nsScriptErrorBase : public nsIScri
  public:
   nsScriptErrorBase();
 
   NS_DECL_NSICONSOLEMESSAGE
   NS_DECL_NSISCRIPTERROR
 
   void AddNote(nsIScriptErrorNote* note);
 
+  static bool ComputeIsFromPrivateWindow(nsGlobalWindowInner* aWindow);
+
  protected:
   virtual ~nsScriptErrorBase();
 
   void InitializeOnMainThread();
 
   void InitializationHelper(const nsAString& message,
                             const nsAString& sourceLine, uint32_t lineNumber,
                             uint32_t columnNumber, uint32_t flags,
--- a/dom/events/UIEvent.h
+++ b/dom/events/UIEvent.h
@@ -8,16 +8,17 @@
 #define mozilla_dom_UIEvent_h_
 
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/Event.h"
 #include "mozilla/dom/Nullable.h"
 #include "mozilla/dom/UIEventBinding.h"
 #include "mozilla/dom/WindowProxyHolder.h"
 #include "nsDeviceContext.h"
+#include "nsDocShell.h"
 #include "nsLayoutUtils.h"
 #include "nsPresContext.h"
 
 class nsINode;
 
 namespace mozilla {
 namespace dom {
 
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -40,16 +40,17 @@
 #include "mozilla/dom/DocGroup.h"
 #include "mozilla/dom/ExternalHelperAppChild.h"
 #include "mozilla/dom/FileCreatorHelper.h"
 #include "mozilla/dom/GetFilesHelper.h"
 #include "mozilla/dom/IPCBlobUtils.h"
 #include "mozilla/dom/LSObject.h"
 #include "mozilla/dom/MemoryReportRequest.h"
 #include "mozilla/dom/PLoginReputationChild.h"
+#include "mozilla/dom/PostMessageEvent.h"
 #include "mozilla/dom/PushNotifier.h"
 #include "mozilla/dom/RemoteWorkerService.h"
 #include "mozilla/dom/ServiceWorkerManager.h"
 #include "mozilla/dom/TabGroup.h"
 #include "mozilla/dom/nsIContentChild.h"
 #include "mozilla/dom/URLClassifierChild.h"
 #include "mozilla/dom/WorkerDebugger.h"
 #include "mozilla/dom/WorkerDebuggerManager.h"
@@ -3478,16 +3479,105 @@ PContentChild::Result ContentChild::OnMe
     MOZ_ASSERT(!aMsg.is_reply());
 
     LSObject::OnSyncMessageHandled();
   }
 
   return result;
 }
 
+mozilla::ipc::IPCResult ContentChild::RecvWindowClose(
+    const BrowsingContextId& aContextId, const bool& aTrustedCaller) {
+  RefPtr<BrowsingContext> bc = BrowsingContext::Get(aContextId);
+  if (!bc) {
+    MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
+            ("ChildIPC: Trying to send a message to dead or detached context "
+             "0x%08" PRIx64,
+             (uint64_t)aContextId));
+    return IPC_OK();
+  }
+
+  nsCOMPtr<nsPIDOMWindowOuter> window = bc->GetDOMWindow();
+  nsGlobalWindowOuter::Cast(window)->CloseOuter(aTrustedCaller);
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvWindowFocus(
+    const BrowsingContextId& aContextId) {
+  RefPtr<BrowsingContext> bc = BrowsingContext::Get(aContextId);
+  if (!bc) {
+    MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
+            ("ChildIPC: Trying to send a message to dead or detached context "
+             "0x%08" PRIx64,
+             (uint64_t)aContextId));
+    return IPC_OK();
+  }
+
+  nsCOMPtr<nsPIDOMWindowOuter> window = bc->GetDOMWindow();
+  nsGlobalWindowOuter::Cast(window)->FocusOuter();
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvWindowBlur(
+    const BrowsingContextId& aContextId) {
+  RefPtr<BrowsingContext> bc = BrowsingContext::Get(aContextId);
+  if (!bc) {
+    MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
+            ("ChildIPC: Trying to send a message to dead or detached context "
+             "0x%08" PRIx64,
+             (uint64_t)aContextId));
+    return IPC_OK();
+  }
+
+  nsCOMPtr<nsPIDOMWindowOuter> window = bc->GetDOMWindow();
+  nsGlobalWindowOuter::Cast(window)->BlurOuter();
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvWindowPostMessage(
+    const BrowsingContextId& aContextId, const ClonedMessageData& aMessage,
+    const PostMessageData& aData) {
+  RefPtr<BrowsingContext> bc = BrowsingContext::Get(aContextId);
+  if (!bc) {
+    MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
+            ("ChildIPC: Trying to send a message to dead or detached context "
+             "0x%08" PRIx64,
+             (uint64_t)aContextId));
+    return IPC_OK();
+  }
+
+  RefPtr<nsGlobalWindowOuter> window =
+      nsGlobalWindowOuter::Cast(bc->GetDOMWindow());
+  nsCOMPtr<nsIPrincipal> providedPrincipal;
+  if (!window->GetPrincipalForPostMessage(
+          aData.targetOrigin(), aData.targetOriginURI(),
+          aData.callerPrincipal(), *aData.subjectPrincipal(),
+          getter_AddRefs(providedPrincipal))) {
+    return IPC_OK();
+  }
+
+  RefPtr<BrowsingContext> sourceBc = BrowsingContext::Get(aData.source());
+  if (!sourceBc) {
+    MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
+            ("ChildIPC: Trying to use a dead or detached context 0x%08" PRIx64,
+             (uint64_t)aData.source()));
+    return IPC_OK();
+  }
+
+  // Create and asynchronously dispatch a runnable which will handle actual DOM
+  // event creation and dispatch.
+  RefPtr<PostMessageEvent> event = new PostMessageEvent(
+      sourceBc, aData.origin(), window, providedPrincipal,
+      aData.callerDocumentURI(), aData.isFromPrivateWindow());
+  event->UnpackFrom(aMessage);
+
+  window->Dispatch(TaskCategory::Other, event.forget());
+  return IPC_OK();
+}
+
 }  // namespace dom
 
 #if defined(__OpenBSD__) && defined(MOZ_CONTENT_SANDBOX)
 #include <unistd.h>
 
 static LazyLogModule sPledgeLog("SandboxPledge");
 
 bool StartOpenBSDSandbox(GeckoProcessType type) {
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -721,16 +721,26 @@ class ContentChild final : public PConte
   virtual already_AddRefed<nsIEventTarget> GetConstructedEventTarget(
       const Message& aMsg) override;
 
   virtual already_AddRefed<nsIEventTarget> GetSpecificMessageEventTarget(
       const Message& aMsg) override;
 
   virtual void OnChannelReceivedMessage(const Message& aMsg) override;
 
+  virtual mozilla::ipc::IPCResult RecvWindowClose(
+      const BrowsingContextId& aContextId, const bool& aTrustedCaller) override;
+  virtual mozilla::ipc::IPCResult RecvWindowFocus(
+      const BrowsingContextId& aContextId) override;
+  virtual mozilla::ipc::IPCResult RecvWindowBlur(
+      const BrowsingContextId& aContextId) override;
+  virtual mozilla::ipc::IPCResult RecvWindowPostMessage(
+      const BrowsingContextId& aContextId, const ClonedMessageData& aMessage,
+      const PostMessageData& aData) override;
+
 #ifdef NIGHTLY_BUILD
   virtual PContentChild::Result OnMessageReceived(const Message& aMsg) override;
 #else
   using PContentChild::OnMessageReceived;
 #endif
 
   virtual PContentChild::Result OnMessageReceived(const Message& aMsg,
                                                   Message*& aReply) override;
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -5122,29 +5122,16 @@ mozilla::ipc::IPCResult ContentParent::R
   RefPtr<IHandlerControl> proxy(aHandlerControl.Get());
   a11y::AccessibleWrap::SetHandlerControl(aPid, std::move(proxy));
   return IPC_OK();
 #else
   return IPC_FAIL_NO_REASON(this);
 #endif
 }
 
-}  // namespace dom
-}  // namespace mozilla
-
-NS_IMPL_ISUPPORTS(ParentIdleListener, nsIObserver)
-
-NS_IMETHODIMP
-ParentIdleListener::Observe(nsISupports*, const char* aTopic,
-                            const char16_t* aData) {
-  mozilla::Unused << mParent->SendNotifyIdleObserver(
-      mObserver, nsDependentCString(aTopic), nsDependentString(aData));
-  return NS_OK;
-}
-
 bool ContentParent::HandleWindowsMessages(const Message& aMsg) const {
   MOZ_ASSERT(aMsg.is_sync());
 
   // a11y messages can be triggered by windows messages, which means if we
   // allow handling windows messages while we wait for the response to a sync
   // a11y message we can reenter the ipc message sending code.
   if (a11y::PDocAccessible::PDocAccessibleStart < aMsg.type() &&
       a11y::PDocAccessible::PDocAccessibleEnd > aMsg.type()) {
@@ -5772,8 +5759,105 @@ void ContentParent::UnregisterRemoveWork
       !ShouldKeepProcessAlive() && !TryToRecycle()) {
     // In the case of normal shutdown, send a shutdown message to child to
     // allow it to perform shutdown tasks.
     MessageLoop::current()->PostTask(NewRunnableMethod<ShutDownMethod>(
         "dom::ContentParent::ShutDownProcess", this,
         &ContentParent::ShutDownProcess, SEND_SHUTDOWN_MESSAGE));
   }
 }
+
+mozilla::ipc::IPCResult ContentParent::RecvWindowClose(
+    const BrowsingContextId& aContextId, const bool& aTrustedCaller) {
+  RefPtr<ChromeBrowsingContext> bc = ChromeBrowsingContext::Get(aContextId);
+  if (!bc) {
+    MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
+            ("ParentIPC: Trying to send a message to dead or detached context "
+             "0x%08" PRIx64,
+             (uint64_t)aContextId));
+    return IPC_OK();
+  }
+
+  // FIXME Need to check that the sending process has access to the unit of
+  // related
+  //       browsing contexts of bc.
+
+  ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+  ContentParent* cp =
+      cpm->GetContentProcessById(ContentParentId(bc->OwnerProcessId()));
+  Unused << cp->SendWindowClose(aContextId, aTrustedCaller);
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvWindowFocus(
+    const BrowsingContextId& aContextId) {
+  RefPtr<ChromeBrowsingContext> bc = ChromeBrowsingContext::Get(aContextId);
+  if (!bc) {
+    MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
+            ("ParentIPC: Trying to send a message to dead or detached context "
+             "0x%08" PRIx64,
+             (uint64_t)aContextId));
+    return IPC_OK();
+  }
+
+  ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+  ContentParent* cp =
+      cpm->GetContentProcessById(ContentParentId(bc->OwnerProcessId()));
+  Unused << cp->SendWindowFocus(aContextId);
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvWindowBlur(
+    const BrowsingContextId& aContextId) {
+  RefPtr<ChromeBrowsingContext> bc = ChromeBrowsingContext::Get(aContextId);
+  if (!bc) {
+    MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
+            ("ParentIPC: Trying to send a message to dead or detached context "
+             "0x%08" PRIx64,
+             (uint64_t)aContextId));
+    return IPC_OK();
+  }
+
+  ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+  ContentParent* cp =
+      cpm->GetContentProcessById(ContentParentId(bc->OwnerProcessId()));
+  Unused << cp->SendWindowBlur(aContextId);
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvWindowPostMessage(
+    const BrowsingContextId& aContextId, const ClonedMessageData& aMessage,
+    const PostMessageData& aData) {
+  RefPtr<ChromeBrowsingContext> bc = ChromeBrowsingContext::Get(aContextId);
+  if (!bc) {
+    MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
+            ("ParentIPC: Trying to send a message to dead or detached context "
+             "0x%08" PRIx64,
+             (uint64_t)aContextId));
+    return IPC_OK();
+  }
+
+  ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+  ContentParent* cp =
+      cpm->GetContentProcessById(ContentParentId(bc->OwnerProcessId()));
+  StructuredCloneData messageFromChild;
+  UnpackClonedMessageDataForParent(aMessage, messageFromChild);
+  ClonedMessageData message;
+  if (!BuildClonedMessageDataForParent(cp, messageFromChild, message)) {
+    // FIXME Logging?
+    return IPC_OK();
+  }
+  Unused << cp->SendWindowPostMessage(aContextId, message, aData);
+  return IPC_OK();
+}
+
+}  // namespace dom
+}  // namespace mozilla
+
+NS_IMPL_ISUPPORTS(ParentIdleListener, nsIObserver)
+
+NS_IMETHODIMP
+ParentIdleListener::Observe(nsISupports*, const char* aTopic,
+                            const char16_t* aData) {
+  mozilla::Unused << mParent->SendNotifyIdleObserver(
+      mObserver, nsDependentCString(aTopic), nsDependentString(aData));
+  return NS_OK;
+}
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -632,16 +632,26 @@ class ContentParent final : public PCont
 
   virtual mozilla::ipc::IPCResult RecvDetachBrowsingContext(
       const BrowsingContextId& aContextId, const bool& aMoveToBFCache) override;
 
   virtual mozilla::ipc::IPCResult RecvSetOpenerBrowsingContext(
       const BrowsingContextId& aContextId,
       const BrowsingContextId& aOpenerContextId) override;
 
+  virtual mozilla::ipc::IPCResult RecvWindowClose(
+      const BrowsingContextId& aContextId, const bool& aTrustedCaller) override;
+  virtual mozilla::ipc::IPCResult RecvWindowFocus(
+      const BrowsingContextId& aContextId) override;
+  virtual mozilla::ipc::IPCResult RecvWindowBlur(
+      const BrowsingContextId& aContextId) override;
+  virtual mozilla::ipc::IPCResult RecvWindowPostMessage(
+      const BrowsingContextId& aContextId, const ClonedMessageData& aMessage,
+      const PostMessageData& aData) override;
+
  protected:
   void OnChannelConnected(int32_t pid) override;
 
   virtual void ActorDestroy(ActorDestroyReason why) override;
 
   bool ShouldContinueFromReplyTimeout() override;
 
   void OnVarChanged(const GfxVarUpdate& aVar) override;
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -317,16 +317,28 @@ struct NotificationEventData
     nsString lang;
     nsString body;
     nsString tag;
     nsString icon;
     nsString data;
     nsString behavior;
 };
 
+struct PostMessageData
+{
+    BrowsingContextId source;
+    nsString origin;
+    nsString targetOrigin;
+    nsIURI targetOriginURI;
+    nsIPrincipal callerPrincipal;
+    nsIPrincipal subjectPrincipal;
+    nsIURI callerDocumentURI;
+    bool isFromPrivateWindow;
+};
+
 /**
  * The PContent protocol is a top-level protocol between the UI process
  * and a content process. There is exactly one PContentParent/PContentChild pair
  * for each content process.
  */
 nested(upto inside_cpow) sync protocol PContent
 {
     manages PBrowser;
@@ -1252,12 +1264,19 @@ both:
      async AsyncMessage(nsString aMessage, CpowEntry[] aCpows,
                         Principal aPrincipal, ClonedMessageData aData);
 
     /**
      * Notify `push-subscription-modified` observers in the parent and child.
      */
     async NotifyPushSubscriptionModifiedObservers(nsCString scope,
                                                   Principal principal);
+
+    async WindowClose(BrowsingContextId aContextId,
+                      bool aTrustedCaller);
+    async WindowFocus(BrowsingContextId aContextId);
+    async WindowBlur(BrowsingContextId aContextId);
+    async WindowPostMessage(BrowsingContextId aContextId, ClonedMessageData aMessage,
+                            PostMessageData aData);
 };
 
 }
 }
--- a/dom/smil/TimeEvent.h
+++ b/dom/smil/TimeEvent.h
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_TimeEvent_h_
 #define mozilla_dom_TimeEvent_h_
 
+#include "nsDocShell.h"
 #include "mozilla/dom/Event.h"
 #include "mozilla/dom/TimeEventBinding.h"
 #include "mozilla/dom/Nullable.h"
 #include "mozilla/dom/WindowProxyHolder.h"
 
 class nsGlobalWindowInner;
 
 namespace mozilla {
--- a/dom/webidl/Window.webidl
+++ b/dom/webidl/Window.webidl
@@ -25,33 +25,33 @@ interface nsIDOMWindowUtils;
 
 typedef OfflineResourceList ApplicationCache;
 
 // http://www.whatwg.org/specs/web-apps/current-work/
 [PrimaryGlobal, LegacyUnenumerableNamedProperties, NeedResolve]
 /*sealed*/ interface Window : EventTarget {
   // the current browsing context
   [Unforgeable, Constant, StoreInSlot,
-   CrossOriginReadable] readonly attribute Window window;
+   CrossOriginReadable] readonly attribute WindowProxy window;
   [Replaceable, Constant, StoreInSlot,
-   CrossOriginReadable] readonly attribute Window self;
+   CrossOriginReadable] readonly attribute WindowProxy self;
   [Unforgeable, StoreInSlot, Pure] readonly attribute Document? document;
   [Throws] attribute DOMString name;
-  [PutForwards=href, Unforgeable, BinaryName="getLocation",
-   CrossOriginReadable, CrossOriginWritable] readonly attribute Location location;
+  [PutForwards=href, Unforgeable, CrossOriginReadable,
+   CrossOriginWritable] readonly attribute Location location;
   [Throws] readonly attribute History history;
   readonly attribute CustomElementRegistry customElements;
   [Replaceable, Throws] readonly attribute BarProp locationbar;
   [Replaceable, Throws] readonly attribute BarProp menubar;
   [Replaceable, Throws] readonly attribute BarProp personalbar;
   [Replaceable, Throws] readonly attribute BarProp scrollbars;
   [Replaceable, Throws] readonly attribute BarProp statusbar;
   [Replaceable, Throws] readonly attribute BarProp toolbar;
   [Throws] attribute DOMString status;
-  [Throws, CrossOriginCallable] void close();
+  [Throws, CrossOriginCallable, NeedsCallerType] void close();
   [Throws, CrossOriginReadable] readonly attribute boolean closed;
   [Throws] void stop();
   [Throws, CrossOriginCallable] void focus();
   [Throws, CrossOriginCallable] void blur();
   [Replaceable, Pref="dom.window.event.enabled"] readonly attribute any event;
 
   // other browsing contexts
   [Replaceable, Throws, CrossOriginReadable] readonly attribute WindowProxy frames;
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -964,16 +964,17 @@ void XPCJSRuntime::CustomGCCallback(JSGC
   // once for each compartment that is being swept.
   CompartmentPrivate* xpcComp = CompartmentPrivate::Get(comp);
   if (xpcComp) {
     xpcComp->UpdateWeakPointersAfterGC();
   }
 }
 
 void CompartmentPrivate::UpdateWeakPointersAfterGC() {
+  mRemoteProxies.sweep();
   mWrappedJSMap->UpdateWeakPointersAfterGC();
 }
 
 void XPCJSRuntime::CustomOutOfMemoryCallback() {
   if (!Preferences::GetBool("memory.dump_reports_on_oom")) {
     return;
   }
 
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -2753,18 +2753,35 @@ class CompartmentPrivate {
 
   JSObject2WrappedJSMap* GetWrappedJSMap() const { return mWrappedJSMap; }
   void UpdateWeakPointersAfterGC();
 
   void SystemIsBeingShutDown();
 
   size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
 
+  struct SweepPolicy {
+    static bool needsSweep(const void* /* unused */,
+                           JS::Heap<JSObject*>* value) {
+      return JS::GCPolicy<JS::Heap<JSObject*>>::needsSweep(value);
+    }
+  };
+
+  typedef JS::GCHashMap<const void*, JS::Heap<JSObject*>,
+                        mozilla::PointerHasher<const void*>,
+                        js::SystemAllocPolicy, SweepPolicy>
+      RemoteProxyMap;
+  RemoteProxyMap& GetRemoteProxyMap() { return mRemoteProxies; }
+
  private:
   JSObject2WrappedJSMap* mWrappedJSMap;
+
+  // Cache holding proxy objects for Window objects (and their Location oject)
+  // that are loaded in a different process.
+  RemoteProxyMap mRemoteProxies;
 };
 
 bool IsUniversalXPConnectEnabled(JS::Compartment* compartment);
 bool IsUniversalXPConnectEnabled(JSContext* cx);
 bool EnableUniversalXPConnect(JSContext* cx);
 
 inline void CrashIfNotInAutomation() { MOZ_RELEASE_ASSERT(IsInAutomation()); }
 
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
@@ -418,23 +418,22 @@ nsresult PeerConnectionImpl::Initialize(
   NS_ENSURE_STATE(mWindow);
 
   PRTime timestamp = PR_Now();
   // Ok if we truncate this.
   char temp[128];
 
   nsAutoCString locationCStr;
 
-  if (RefPtr<Location> location = mWindow->GetLocation()) {
-    nsAutoString locationAStr;
-    res = location->ToString(locationAStr);
-    NS_ENSURE_SUCCESS(res, res);
-
-    CopyUTF16toUTF8(locationAStr, locationCStr);
-  }
+  RefPtr<Location> location = mWindow->Location();
+  nsAutoString locationAStr;
+  res = location->ToString(locationAStr);
+  NS_ENSURE_SUCCESS(res, res);
+
+  CopyUTF16toUTF8(locationAStr, locationCStr);
 
   SprintfLiteral(temp, "%" PRIu64 " (id=%" PRIu64 " url=%s)",
                  static_cast<uint64_t>(timestamp),
                  static_cast<uint64_t>(mWindow ? mWindow->WindowID() : 0),
                  locationCStr.get() ? locationCStr.get() : "NULL");
 
   mName = temp;
 
--- a/xpfe/appshell/nsContentTreeOwner.cpp
+++ b/xpfe/appshell/nsContentTreeOwner.cpp
@@ -28,16 +28,17 @@
 #include "nsIWebNavigation.h"
 #include "nsDocShellCID.h"
 #include "nsIExternalURLHandlerService.h"
 #include "nsIMIMEInfo.h"
 #include "nsIWidget.h"
 #include "nsWindowWatcher.h"
 #include "mozilla/BrowserElementParent.h"
 #include "mozilla/NullPrincipal.h"
+#include "nsDocShell.h"
 #include "nsDocShellLoadState.h"
 
 #include "nsIScriptObjectPrincipal.h"
 #include "nsIURI.h"
 #include "nsIDocument.h"
 #if defined(XP_MACOSX)
 #include "nsThreadUtils.h"
 #endif