Bug 1573247: Add Cu.isRemoteProxy helper. r=mccr8
authorKris Maglione <maglione.k@gmail.com>
Tue, 13 Aug 2019 14:29:08 -0700
changeset 488078 f8e99bd2a193f00d159581d9dffeb5cd634ad624
parent 488077 4b826e607ca87939b656b62836721973c67dac71
child 488079 6692575191f32991dd8ceb2b108dd2adaba576f0
push id36435
push usercbrindusan@mozilla.com
push dateThu, 15 Aug 2019 09:46:49 +0000
treeherdermozilla-central@0db07ff50ab5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmccr8
bugs1573247
milestone70.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 1573247: Add Cu.isRemoteProxy helper. r=mccr8 MANUAL PUSH: I made a vain attempt to update everything at once into a single patch stack so I didn't have to do 6 manual pushes, then go to 6 separate landing pages, and go through multiple steps in each to land the patches for each bug... which not only did not combine everything into a single stack, but also reset all of the review flags. Differential Revision: https://phabricator.services.mozilla.com/D41846
js/xpconnect/idl/xpccomponents.idl
js/xpconnect/src/XPCComponents.cpp
js/xpconnect/tests/mochitest/mochitest.ini
js/xpconnect/tests/mochitest/test_isRemoteProxy.html
--- a/js/xpconnect/idl/xpccomponents.idl
+++ b/js/xpconnect/idl/xpccomponents.idl
@@ -411,16 +411,31 @@ interface nsIXPCComponents_Utils : nsISu
      *
      * Dead-wrapper objects hold no other objects alive (they have no outgoing
      * reference edges) and will throw if you touch them (e.g. by
      * reading/writing a property).
      */
     bool isDeadWrapper(in jsval obj);
 
     /**
+     * Determines whether this value is a remote object proxy, such as
+     * RemoteWindowProxy or RemoteLocationProxy, for an out-of-process frame.
+     *
+     * Remote object proxies do not grant chrome callers the same exemptions
+     * to the same-origin-policy that in-process wrappers typically do, so
+     * this can be used to determine whether access to cross-origin proxies is
+     * safe:
+     *
+     *   if (!Cu.isRemoteProxy(frame.contentWindow)) {
+     *     frame.contentWindow.doCrossOriginThing();
+     *   }
+     */
+    bool isRemoteProxy(in jsval val);
+
+    /**
      * Determines whether this object is a cross-process wrapper.
      */
     bool isCrossProcessWrapper(in jsval obj);
 
     /**
      * CPOWs can have user data attached to them. This data originates
      * in the local process via the
      * nsIRemoteTagService.getRemoteObjectTag method. It's sent along
--- a/js/xpconnect/src/XPCComponents.cpp
+++ b/js/xpconnect/src/XPCComponents.cpp
@@ -25,16 +25,17 @@
 #include "mozilla/Preferences.h"
 #include "nsJSEnvironment.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/ResultExtensions.h"
 #include "mozilla/URLPreloader.h"
 #include "mozilla/dom/DOMException.h"
 #include "mozilla/dom/DOMExceptionBinding.h"
 #include "mozilla/dom/BindingUtils.h"
+#include "mozilla/dom/RemoteObjectProxy.h"
 #include "mozilla/dom/StructuredCloneTags.h"
 #include "mozilla/dom/WindowBinding.h"
 #include "nsZipArchive.h"
 #include "nsWindowMemoryReporter.h"
 #include "nsICycleCollectorListener.h"
 #include "nsIException.h"
 #include "nsIScriptError.h"
 #include "nsISimpleEnumerator.h"
@@ -1909,16 +1910,27 @@ nsXPCComponents_Utils::IsDeadWrapper(Han
   MOZ_ASSERT_IF(js::IsCrossCompartmentWrapper(&obj.toObject()),
                 !JS_IsDeadWrapper(js::UncheckedUnwrap(&obj.toObject())));
 
   *out = JS_IsDeadWrapper(&obj.toObject());
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsXPCComponents_Utils::IsRemoteProxy(HandleValue val, bool* out) {
+  if (val.isObject()) {
+    *out = dom::IsRemoteObjectProxy(UncheckedUnwrap(&val.toObject()));
+    ;
+  } else {
+    *out = false;
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsXPCComponents_Utils::IsCrossProcessWrapper(HandleValue obj, bool* out) {
   *out = false;
   if (obj.isPrimitive()) {
     return NS_ERROR_INVALID_ARG;
   }
 
   *out = jsipc::IsWrappedCPOW(&obj.toObject());
   return NS_OK;
--- a/js/xpconnect/tests/mochitest/mochitest.ini
+++ b/js/xpconnect/tests/mochitest/mochitest.ini
@@ -110,16 +110,17 @@ skip-if = fission && webrender && debug 
 [test_bug1158558.html]
 [test_bug1448048.html]
 [test_crosscompartment_weakmap.html]
 [test_frameWrapping.html]
 # The JS test component we use below is only available in debug builds.
 [test_getWebIDLCaller.html]
 skip-if = (debug == false)
 [test_getweakmapkeys.html]
+[test_isRemoteProxy.html]
 [test_paris_weakmap_keys.html]
 skip-if = (debug == false)
 [test_nukeContentWindow.html]
 [test_sameOriginPolicy.html]
 [test_sandbox_fetch.html]
   support-files =
     ../../../../dom/tests/mochitest/fetch/test_fetch_basic.js
 [test_weakmaps.html]
new file mode 100644
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_isRemoteProxy.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Tests for Cu.isRemoteProxy</title>
+  <script src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<script>
+/* eslint-disable prettier/prettier */
+
+async function addFrame(url) {
+  let frame = document.createElement("iframe");
+  frame.src = url;
+  document.body.appendChild(frame);
+
+  await new Promise(resolve => {
+    frame.addEventListener("load", resolve, { once: true });
+  });
+
+  return frame;
+}
+
+add_task(async function() {
+  const { Cu } = SpecialPowers;
+
+  let localFrame = await addFrame("file_empty.html");
+  let remoteFrame = await addFrame(
+    SimpleTest.getTestFileURL("file_empty.html")
+              .replace("mochi.test:8888", "example.com"));
+
+  ok(frames[0] === localFrame.contentWindow, "frames[0] is localFrame");
+  ok(frames[1] === remoteFrame.contentWindow, "frames[1] is remoteFrame");
+
+  ok(!Cu.isRemoteProxy(window), "window is not a remote proxy");
+  ok(!Cu.isRemoteProxy(location), "location is not a remote proxy");
+
+  ok(!Cu.isRemoteProxy(frames[0]), "frames[0] is not a remote proxy");
+  ok(
+    !Cu.isRemoteProxy(frames[0].location),
+    "frames[0].location is not a remote proxy"
+  );
+
+  const { useRemoteSubframes } = SpecialPowers;
+  is(Cu.isRemoteProxy(frames[1]), useRemoteSubframes,
+     "frames[1] is a remote proxy if Fission is enabled");
+  is(Cu.isRemoteProxy(frames[1].location), useRemoteSubframes,
+    "frames[1].location is a remote proxy if Fission is enabled");
+});
+
+</script>
+</body>
+</html>