Bug 1381408. Make cross-origin-exposed non-symbol properties enumerable. r=bholley
authorBoris Zbarsky <bzbarsky@mit.edu>
Wed, 19 Jul 2017 11:47:39 -0400
changeset 418416 be396a7cf55b4a3faebb5ac0f053cfca22199686
parent 418415 c91796e4b8bdb6f60c5ae5fc5559e1243d0e42cb
child 418417 c99d20ba408a4fd19bab0bc04ff926e73d838fbf
push id7566
push usermtabara@mozilla.com
push dateWed, 02 Aug 2017 08:25:16 +0000
treeherdermozilla-beta@86913f512c3c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbholley
bugs1381408
milestone56.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 1381408. Make cross-origin-exposed non-symbol properties enumerable. r=bholley The list of exposed properties can be gotten via Object.getOwnPropertyNames/Symbols already, so there's nothing to be won from the non-enumerability. The web platform test changes are backports of https://github.com/w3c/web-platform-tests/pull/6538 and https://github.com/w3c/web-platform-tests/pull/6571 and https://github.com/w3c/web-platform-tests/pull/6583 The js/xpconnect/tests/mochitest/*crossOriginObject* tests being removed are just older versions of the web platfrom test.
js/xpconnect/tests/mochitest/file_crossOriginObjects.html
js/xpconnect/tests/mochitest/file_crossOriginObjects_documentDomain.html
js/xpconnect/tests/mochitest/mochitest.ini
js/xpconnect/tests/mochitest/test_bug862380.html
js/xpconnect/tests/mochitest/test_crossOriginObjects.html
js/xpconnect/wrappers/FilteringWrapper.cpp
testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects.html
testing/web-platform/tests/html/browsers/origin/cross-origin-objects/frame.html
deleted file mode 100644
--- a/js/xpconnect/tests/mochitest/file_crossOriginObjects.html
+++ /dev/null
@@ -1,35 +0,0 @@
-<!doctype html>
-<html>
-<head>
-<script>
-  // Override the |frames| property to test that such overrides are
-  // properly ignored cross-origin.
-  window.frames = "override";
-
-  // If we get a postMessage, we grab references to everything and set
-  // document.domain to trim off our topmost subdomain.
-  window.onmessage = function(evt) {
-    window.windowReferences = [];
-    window.locationReferences = [];
-    for (var i = 0; i < parent.length; ++i) {
-      windowReferences.push(parent[i]);
-      locationReferences.push(parent[i].location);
-    }
-    document.domain = document.domain.substring(document.domain.indexOf('.') + 1);
-    evt.source.postMessage('', '*');
-  }
-
-  function checkWindowReferences() {
-    for (var i = 0; i < parent.length; ++i) {
-      if (windowReferences[i] != parent[i])
-        throw new Error("Window references don't match for " + i + " after document.domain");
-      if (locationReferences[i] != parent[i].location)
-        throw new Error("Location references don't match for " + i + " after document.domain");
-    }
-    return true;
-  }
-</script>
-</head>
-<body>
-</body>
-</html>
deleted file mode 100644
--- a/js/xpconnect/tests/mochitest/file_crossOriginObjects_documentDomain.html
+++ /dev/null
@@ -1,55 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-  <script>
-    function loadFrames() {
-      window.A = document.getElementById('A').contentWindow;
-      window.B = document.getElementById('B').contentWindow;
-      window.C = document.getElementById('C').contentWindow;
-      window.D = document.getElementById('D').contentWindow;
-
-      var path = location.pathname.substring(0, location.pathname.lastIndexOf('/')) + '/file_crossOriginObjects.html';
-      A.location = 'file_crossOriginObjects.html';
-      B.location = frameURI = 'http://test2.mochi.test:' + location.port + path;
-      C.location = frameURI = 'http://test1.mochi.test:' + location.port + path;
-      D.location = frameURI = 'http://test1.mochi.test:' + location.port + path;
-
-      var loadCount = 0;
-      function frameLoaded() {
-        if (++loadCount == 4)
-          go();
-      }
-      Array.forEach(document.getElementsByTagName('iframe'), function(ifr) { ifr.onload = frameLoaded; });
-    }
-
-
-    var results = [];
-    function assert(cond, msg) {
-      results.push({pass: !!cond, message: msg});
-    }
-
-    function go() {
-      window.onmessage = function() {
-        assert(B.checkWindowReferences(), "B's Window references are still self-consistent after document.domain");
-        for (var i = 0; i < window.length; ++i) {
-          assert(window[i] === B.windowReferences[i],
-                 "Window reference " + i + " consistent between globals after document.domain");
-          assert(window[i].location === B.locationReferences[i],
-                 "Location reference " + i + " consistent between globals after document.domain");
-        }
-        opener.postMessage(results, '*');
-      };
-      A.document.domain = A.document.domain;
-      document.domain = document.domain;
-      B.postMessage('', '*');
-    }
-
-  </script>
-</head>
-<body onload="loadFrames()">
-  <iframe id="A"></iframe>
-  <iframe id="B"></iframe>
-  <iframe id="C"></iframe>
-  <iframe id="D"></iframe>
-</body>
-</html>
--- a/js/xpconnect/tests/mochitest/mochitest.ini
+++ b/js/xpconnect/tests/mochitest/mochitest.ini
@@ -17,18 +17,16 @@ support-files =
   file_bug760131.html
   file_bug781476.html
   file_bug789713.html
   file_bug795275.html
   file_bug795275.xml
   file_bug799348.html
   file_bug802557.html
   file_bug860494.html
-  file_crossOriginObjects.html
-  file_crossOriginObjects_documentDomain.html
   file_crosscompartment_weakmap.html
   file_documentdomain.html
   file_doublewrappedcompartments.html
   file_empty.html
   file_evalInSandbox.html
   file_exnstack.html
   file_expandosharing.html
   file_matches.html
@@ -89,17 +87,16 @@ support-files =
 [test_bug940783.html]
 [test_bug965082.html]
 [test_bug960820.html]
 [test_bug986542.html]
 [test_bug993423.html]
 [test_bug1005806.html]
 [test_bug1094930.html]
 [test_bug1158558.html]
-[test_crossOriginObjects.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 || os == "android")
 [test_nac.xhtml]
 [test_nukeContentWindow.html]
 [test_sameOriginPolicy.html]
--- a/js/xpconnect/tests/mochitest/test_bug862380.html
+++ b/js/xpconnect/tests/mochitest/test_bug862380.html
@@ -8,26 +8,39 @@ https://bugzilla.mozilla.org/show_bug.cg
   <title>Test for Bug 862380</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="application/javascript">
 
   /** Test for Bug 862380 **/
   SimpleTest.waitForExplicitFinish();
   function go() {
-    checkNotEnumerable($('ifr').contentWindow);
-    checkNotEnumerable($('ifr').contentWindow.location);
+    checkNotEnumerable($('ifr').contentWindow, true);
+    checkNotEnumerable($('ifr').contentWindow.location, false);
     SimpleTest.finish();
   }
 
-  function checkNotEnumerable(obj) {
+function checkNotEnumerable(obj, isWindow) {
     try {
-      is(Object.keys(obj).length, 0, "Object.keys gives empty array");
+      const expectedWindow = ["0", "window", "location", "top", "close",
+                              "focus", "blur", "postMessage", "self", "closed",
+                              "frames", "length", "opener", "parent"];
+      const expectedLocation = ["replace", "href"];
+      const expected = isWindow ? expectedWindow : expectedLocation;
+      is(Object.keys(obj).length, expected.length,
+         "Object.keys gives right array length");
+      var actual = [];
       for (var i in obj)
-        ok(false, "Enumerated something: " + i);
+        actual.push(i);
+      is(actual.length, expected.length,
+         "Enumeration sees the right number of props");
+      actual.sort();
+      expected.sort();
+      for (var i = 0; i < actual.length; ++i)
+        is(actual[i], expected[i], "Arrays should be the same " + i);
     } catch (e) {
       ok(false, "threw: " + e);
     }
   }
 
   </script>
 </head>
 <body>
deleted file mode 100644
--- a/js/xpconnect/tests/mochitest/test_crossOriginObjects.html
+++ /dev/null
@@ -1,335 +0,0 @@
-<!doctype html>
-<meta charset=utf-8>
-<meta name="timeout" content="long">
-<title>Cross-origin behavior of Window and Location</title>
-<link rel="author" title="Bobby Holley (:bholley)" href="bobbyholley@gmail.com">
-<link rel="help" href="http://www.whatwg.org/specs/web-apps/current-work/#security-window">
-<link rel="help" href="http://www.whatwg.org/specs/web-apps/current-work/#security-location">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<div id=log></div>
-<iframe id="B"></iframe>
-<iframe id="C"></iframe>
-<script>
-
-/*
- * Setup boilerplate. This gives us a same-origin window "B" and a cross-origin
- * window "C".
- */
-
-setup({explicit_done: true});
-path = location.pathname.substring(0, location.pathname.lastIndexOf('/')) + '/file_crossOriginObjects.html';
-var B = document.getElementById('B').contentWindow;
-var C = document.getElementById('C').contentWindow;
-B.frameElement.uriToLoad = path;
-C.frameElement.uriToLoad = 'http://test1.mochi.test:' + location.port + path;
-
-function reloadSubframes(cb) {
-  var iframes = document.getElementsByTagName('iframe');
-  iframes.forEach = Array.prototype.forEach;
-  var count = 0;
-  function frameLoaded() {
-    this.onload = null;
-    if (++count == iframes.length)
-      cb();
-  }
-  iframes.forEach(function(ifr) { ifr.onload = frameLoaded; ifr.setAttribute('src', ifr.uriToLoad); });
-}
-function isObject(x) { return Object(x) === x; }
-
-/*
- * Note: we eschew assert_equals in a lot of these tests, since the harness ends
- * up throwing when it tries to format a message involving a cross-origin object.
- */
-
-var testList = [];
-function addTest(fun, desc) { testList.push([fun, desc]); }
-
-
-/*
- * Basic sanity testing.
- */
-
-addTest(function() {
-  assert_equals(location.host, 'mochi.test:8888', 'Need to run the top-level test from mochi.test:8888');
-  assert_equals(B.parent, window, "window.parent works same-origin");
-  assert_equals(C.parent, window, "window.parent works cross-origin");
-  assert_equals(B.location.pathname, path, "location.href works same-origin");
-  assert_throws(null, function() { C.location.pathname; }, "location.pathname throws cross-origin");
-  assert_equals(B.frames, 'override', "Overrides visible in the same-origin case");
-  assert_equals(C.frames, C, "Overrides invisible in the cross-origin case");
-}, "Basic sanity-checking");
-
-/*
- * Whitelist behavior.
- *
- * Also tests for [[GetOwnProperty]] and [[HasOwnProperty]] behavior.
- */
-
-var whitelistedWindowProps = ['location', 'postMessage', 'window', 'frames', 'self', 'top', 'parent',
-                              'opener', 'closed', 'close', 'blur', 'focus', 'length'];
-addTest(function() {
-  for (var prop in window) {
-    if (whitelistedWindowProps.indexOf(prop) != -1) {
-      C[prop]; // Shouldn't throw.
-      Object.getOwnPropertyDescriptor(C, prop); // Shouldn't throw.
-      assert_true(Object.prototype.hasOwnProperty.call(C, prop), "hasOwnProperty for " + prop);
-    } else {
-      assert_throws(null, function() { C[prop]; }, "Should throw when accessing " + prop + " on Window");
-      assert_throws(null, function() { Object.getOwnPropertyDescriptor(C, prop); },
-                    "Should throw when accessing property descriptor for " + prop + " on Window");
-      assert_throws(null, function() { Object.prototype.hasOwnProperty.call(C, prop); },
-                    "Should throw when invoking hasOwnProperty for " + prop + " on Window");
-    }
-    if (prop != 'location')
-      assert_throws(null, function() { C[prop] = undefined; }, "Should throw when writing to " + prop + " on Window");
-  }
-  for (var prop in location) {
-    if (prop == 'replace') {
-      C.location[prop]; // Shouldn't throw.
-      Object.getOwnPropertyDescriptor(C.location, prop); // Shouldn't throw.
-      assert_true(Object.prototype.hasOwnProperty.call(C.location, prop), "hasOwnProperty for " + prop);
-    }
-    else {
-      assert_throws(null, function() { C[prop]; }, "Should throw when accessing " + prop + " on Location");
-      assert_throws(null, function() { Object.getOwnPropertyDescriptor(C, prop); },
-                    "Should throw when accessing property descriptor for " + prop + " on Location");
-      assert_throws(null, function() { Object.prototype.hasOwnProperty.call(C, prop); },
-                    "Should throw when invoking hasOwnProperty for " + prop + " on Location");
-    }
-    if (prop != 'href')
-      assert_throws(null, function() { C[prop] = undefined; }, "Should throw when writing to " + prop + " on Location");
-  }
-}, "Only whitelisted properties are accessible cross-origin");
-
-/*
- * ES Internal Methods.
- */
-
-/*
- * [[GetPrototypeOf]]
- */
-addTest(function() {
-  assert_true(Object.getPrototypeOf(C) === null, "cross-origin Window proto is null");
-  assert_true(Object.getPrototypeOf(C.location) === null, "cross-origin Location proto is null (__proto__)");
-  var protoGetter = Object.getOwnPropertyDescriptor(Object.prototype, '__proto__').get;
-  assert_true(protoGetter.call(C) === null, "cross-origin Window proto is null");
-  assert_true(protoGetter.call(C.location) === null, "cross-origin Location proto is null (__proto__)");
-  assert_throws(null, function() { C.__proto__; }, "__proto__ property not available cross-origin");
-  assert_throws(null, function() { C.location.__proto__; }, "__proto__ property not available cross-origin");
-
-}, "[[GetPrototypeOf]] should return null");
-
-/*
- * [[SetPrototypeOf]]
- */
-addTest(function() {
-  assert_throws(null, function() { C.__proto__ = new Object(); }, "proto set on cross-origin Window");
-  assert_throws(null, function() { C.location.__proto__ = new Object(); }, "proto set on cross-origin Location");
-  var setters = [Object.getOwnPropertyDescriptor(Object.prototype, '__proto__').set];
-  if (Object.setPrototypeOf)
-    setters.push(function(p) { Object.setPrototypeOf(this, p); });
-  setters.forEach(function(protoSetter) {
-    assert_throws(null, function() { protoSetter.call(C, new Object()); }, "proto setter |call| on cross-origin Window");
-    assert_throws(null, function() { protoSetter.call(C.location, new Object()); }, "proto setter |call| on cross-origin Location");
-  });
-}, "[[SetPrototypeOf]] should throw");
-
-/*
- * [[IsExtensible]]
- */
-addTest(function() {
-  assert_true(Object.isExtensible(C), "cross-origin Window should be extensible");
-  assert_true(Object.isExtensible(C.location), "cross-origin Location should be extensible");
-}, "[[IsExtensible]] should return true for cross-origin objects");
-
-/*
- * [[PreventExtensions]]
- */
-addTest(function() {
-  assert_throws(null, function() { Object.preventExtensions(C) },
-                "preventExtensions on cross-origin Window should throw");
-  assert_throws(null, function() { Object.preventExtensions(C.location) },
-                "preventExtensions on cross-origin Location should throw");
-}, "[[PreventExtensions]] should throw for cross-origin objects");
-
-/*
- * [[GetOwnProperty]]
- */
-
-addTest(function() {
-  assert_true(isObject(Object.getOwnPropertyDescriptor(C, 'close')), "C.close is |own|");
-  assert_true(isObject(Object.getOwnPropertyDescriptor(C, 'top')), "C.top is |own|");
-  assert_true(isObject(Object.getOwnPropertyDescriptor(C.location, 'href')), "C.location.href is |own|");
-  assert_true(isObject(Object.getOwnPropertyDescriptor(C.location, 'replace')), "C.location.replace is |own|");
-}, "[[GetOwnProperty]] - Properties on cross-origin objects should be reported |own|");
-
-function checkPropertyDescriptor(desc, propName, expectWritable) {
-  assert_true(isObject(desc), "property descriptor for " + propName + " should exist");
-  assert_equals(desc.enumerable, false, "property descriptor for " + propName + " should be non-enumerable");
-  assert_equals(desc.configurable, true, "property descriptor for " + propName + " should be configurable");
-  if ('value' in desc)
-    assert_equals(desc.writable, expectWritable, "property descriptor for " + propName + " should have writable: " + expectWritable);
-  else
-    assert_equals(typeof desc.set != 'undefined', expectWritable,
-                  "property descriptor for " + propName + " should " + (expectWritable ? "" : "not ") + "have setter");
-}
-
-addTest(function() {
-  whitelistedWindowProps.forEach(function(prop) {
-    var desc = Object.getOwnPropertyDescriptor(C, prop);
-    checkPropertyDescriptor(desc, prop, prop == 'location');
-  });
-  checkPropertyDescriptor(Object.getOwnPropertyDescriptor(C.location, 'replace'), 'replace', false);
-  checkPropertyDescriptor(Object.getOwnPropertyDescriptor(C.location, 'href'), 'href', true);
-  assert_equals(typeof Object.getOwnPropertyDescriptor(C.location, 'href').get, 'undefined', "Cross-origin location should have no href getter");
-}, "[[GetOwnProperty]] - Property descriptors for cross-origin properties should be set up correctly");
-
-/*
- * [[Delete]]
- */
-addTest(function() {
-  assert_throws(null, function() { delete C.location; }, "Can't delete cross-origin property");
-  assert_throws(null, function() { delete C.parent; }, "Can't delete cross-origin property");
-  assert_throws(null, function() { delete C.length; }, "Can't delete cross-origin property");
-  assert_throws(null, function() { delete C.document; }, "Can't delete cross-origin property");
-  assert_throws(null, function() { delete C.foopy; }, "Can't delete cross-origin property");
-  assert_throws(null, function() { delete C.location.href; }, "Can't delete cross-origin property");
-  assert_throws(null, function() { delete C.location.replace; }, "Can't delete cross-origin property");
-  assert_throws(null, function() { delete C.location.port; }, "Can't delete cross-origin property");
-  assert_throws(null, function() { delete C.location.foopy; }, "Can't delete cross-origin property");
-}, "[[Delete]] Should throw on cross-origin objects");
-
-/*
- * [[DefineOwnProperty]]
- */
-function checkDefine(obj, prop) {
-  var valueDesc = { configurable: true, enumerable: false, writable: false, value: 2 };
-  var accessorDesc = { configurable: true, enumerable: false, get: function() {} };
-  assert_throws(null, function() { Object.defineProperty(obj, prop, valueDesc); }, "Can't define cross-origin value property " + prop);
-  assert_throws(null, function() { Object.defineProperty(obj, prop, accessorDesc); }, "Can't define cross-origin accessor property " + prop);
-}
-addTest(function() {
-  checkDefine(C, 'length');
-  checkDefine(C, 'parent');
-  checkDefine(C, 'location');
-  checkDefine(C, 'document');
-  checkDefine(C, 'foopy');
-  checkDefine(C.location, 'href');
-  checkDefine(C.location, 'replace');
-  checkDefine(C.location, 'port');
-  checkDefine(C.location, 'foopy');
-}, "[[DefineOwnProperty]] Should throw for cross-origin objects");
-
-/*
- * [[Enumerate]]
- */
-
-addTest(function() {
-  for (var prop in C)
-    assert_unreached("Shouldn't have been able to enumerate " + prop + " on cross-origin Window");
-  for (var prop in C.location)
-    assert_unreached("Shouldn't have been able to enumerate " + prop + " on cross-origin Location");
-}, "[[Enumerate]] should return an empty iterator");
-
-/*
- * [[OwnPropertyKeys]]
- */
-
-addTest(function() {
-  assert_array_equals(whitelistedWindowProps.sort(), Object.getOwnPropertyNames(C).sort(),
-                      "Object.getOwnPropertyNames() gives the right answer for cross-origin Window");
-  assert_array_equals(Object.getOwnPropertyNames(C.location).sort(), ['href', 'replace'],
-                      "Object.getOwnPropertyNames() gives the right answer for cross-origin Location");
-}, "[[OwnPropertyKeys]] should return all properties from cross-origin objects");
-
-addTest(function() {
-  assert_true(B.eval('parent.C') === C, "A and B observe the same identity for C's Window");
-  assert_true(B.eval('parent.C.location') === C.location, "A and B observe the same identity for C's Location");
-}, "A and B jointly observe the same identity for cross-origin Window and Location");
-
-function checkFunction(f, proto) {
-  var name = f.name || '<missing name>';
-  assert_equals(typeof f, 'function', name + " is a function");
-  assert_equals(Object.getPrototypeOf(f), proto, f.name + " has the right prototype");
-}
-
-addTest(function() {
-  checkFunction(C.close, Function.prototype);
-  checkFunction(C.location.replace, Function.prototype);
-}, "Cross-origin functions get local Function.prototype");
-
-addTest(function() {
-  assert_true(isObject(Object.getOwnPropertyDescriptor(C, 'parent')),
-              "Need to be able to use Object.getOwnPropertyDescriptor do this test");
-  checkFunction(Object.getOwnPropertyDescriptor(C, 'parent').get, Function.prototype);
-  checkFunction(Object.getOwnPropertyDescriptor(C.location, 'href').set, Function.prototype);
-}, "Cross-origin Window accessors get local Function.prototype");
-
-addTest(function() {
-  checkFunction(close, Function.prototype);
-  assert_true(close != B.close, 'same-origin Window functions get their own object');
-  assert_true(close != C.close, 'cross-origin Window functions get their own object');
-  var close_B = B.eval('parent.C.close');
-  assert_true(close != close_B, 'close_B is unique when viewed by the parent');
-  assert_true(close_B != C.close, 'different Window functions per-incumbent script settings object');
-  checkFunction(close_B, B.Function.prototype);
-
-  checkFunction(location.replace, Function.prototype);
-  assert_true(location.replace != C.location.replace, "cross-origin Location functions get their own object");
-  var replace_B = B.eval('parent.C.location.replace');
-  assert_true(replace_B != C.location.replace, 'different Location functions per-incumbent script settings object');
-  checkFunction(replace_B, B.Function.prototype);
-}, "Same-origin observers get different functions for cross-origin objects");
-
-addTest(function() {
-  assert_true(isObject(Object.getOwnPropertyDescriptor(C, 'parent')),
-              "Need to be able to use Object.getOwnPropertyDescriptor do this test");
-  var get_self_parent = Object.getOwnPropertyDescriptor(window, 'parent').get;
-  var get_parent_A = Object.getOwnPropertyDescriptor(C, 'parent').get;
-  var get_parent_B = B.eval('Object.getOwnPropertyDescriptor(parent.C, "parent").get');
-  assert_true(get_self_parent != get_parent_A, 'different Window accessors per-incumbent script settings object');
-  assert_true(get_parent_A != get_parent_B, 'different Window accessors per-incumbent script settings object');
-  checkFunction(get_self_parent, Function.prototype);
-  checkFunction(get_parent_A, Function.prototype);
-  checkFunction(get_parent_B, B.Function.prototype);
-}, "Same-origin obsevers get different accessors for cross-origin Window");
-
-addTest(function() {
-  var set_self_href = Object.getOwnPropertyDescriptor(window.location, 'href').set;
-  var set_href_A = Object.getOwnPropertyDescriptor(C.location, 'href').set;
-  var set_href_B = B.eval('Object.getOwnPropertyDescriptor(parent.C.location, "href").set');
-  assert_true(set_self_href != set_href_A, 'different Location accessors per-incumbent script settings object');
-  assert_true(set_href_A != set_href_B, 'different Location accessors per-incumbent script settings object');
-  checkFunction(set_self_href, Function.prototype);
-  checkFunction(set_href_A, Function.prototype);
-  checkFunction(set_href_B, B.Function.prototype);
-}, "Same-origin observers get different accessors for cross-origin Location");
-
-function doDocumentDomainTest(cb) {
-  window.addEventListener('message', function(evt) {
-    test(function() {
-      var results = evt.data;
-      assert_true(results.length > 0, 'Need results');
-      results.forEach(function(r) { assert_true(r.pass, r.message); });
-    }, "Cross-origin object identity preserved across document.domain");
-    win.close();
-    cb();
-  }, {once: true});
-  var win = window.open('file_crossOriginObjects_documentDomain.html');
-}
-
-// We do a fresh load of the subframes for each test to minimize side-effects.
-// It would be nice to reload ourselves as well, but we can't do that without
-// disrupting the test harness.
-function runNextTest() {
-  var entry = testList.shift();
-  test(entry[0], entry[1]);
-  if (testList.length != 0)
-    reloadSubframes(runNextTest);
-  else
-    doDocumentDomainTest(done); // Asynchronous.
-}
-reloadSubframes(runNextTest);
-
-</script>
--- a/js/xpconnect/wrappers/FilteringWrapper.cpp
+++ b/js/xpconnect/wrappers/FilteringWrapper.cpp
@@ -218,19 +218,18 @@ CrossOriginXrayWrapper::getPropertyDescr
         // from the ones we add ourselves here.
         MOZ_ASSERT(!JSID_IS_SYMBOL(id),
                    "What's this symbol-named property that appeared on a "
                    "Window or Location instance?");
 
         // All properties on cross-origin DOM objects are |own|.
         desc.object().set(wrapper);
 
-        // All properties on cross-origin DOM objects are non-enumerable and
-        // "configurable". Any value attributes are read-only.
-        desc.attributesRef() &= ~JSPROP_ENUMERATE;
+        // All properties on cross-origin DOM objects are "configurable". Any
+        // value attributes are read-only.
         desc.attributesRef() &= ~JSPROP_PERMANENT;
         if (!desc.getter() && !desc.setter())
             desc.attributesRef() |= JSPROP_READONLY;
     } else if (IsCrossOriginWhitelistedSymbol(cx, id)) {
         // Spec says to return PropertyDescriptor {
         //   [[Value]]: undefined, [[Writable]]: false, [[Enumerable]]: false,
         //   [[Configurable]]: true
         // }.
--- a/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects.html
+++ b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects.html
@@ -182,24 +182,26 @@ addTest(function() {
   assert_true(isObject(Object.getOwnPropertyDescriptor(C.location, 'href')), "C.location.href is |own|");
   assert_true(isObject(Object.getOwnPropertyDescriptor(C.location, 'replace')), "C.location.replace is |own|");
 }, "[[GetOwnProperty]] - Properties on cross-origin objects should be reported |own|");
 
 function checkPropertyDescriptor(desc, propName, expectWritable) {
   var isSymbol = (typeof(propName) == "symbol");
   propName = String(propName);
   assert_true(isObject(desc), "property descriptor for " + propName + " should exist");
-  assert_equals(desc.enumerable, false, "property descriptor for " + propName + " should be non-enumerable");
   assert_equals(desc.configurable, true, "property descriptor for " + propName + " should be configurable");
   if (isSymbol) {
+    assert_equals(desc.enumerable, false, "symbol-property descriptor for " + propName + " should not be enumerable");
     assert_true("value" in desc,
                 "property descriptor for " + propName + " should be a value descriptor");
     assert_equals(desc.value, undefined,
                   "symbol-named cross-origin visible prop " + propName +
                   " should come back as undefined");
+  } else {
+    assert_equals(desc.enumerable, true, "property descriptor for " + propName + " should be enumerable");
   }
   if ('value' in desc)
     assert_equals(desc.writable, expectWritable, "property descriptor for " + propName + " should have writable: " + expectWritable);
   else
     assert_equals(typeof desc.set != 'undefined', expectWritable,
                   "property descriptor for " + propName + " should " + (expectWritable ? "" : "not ") + "have setter");
 }
 
@@ -251,37 +253,51 @@ addTest(function() {
   checkDefine(C, 'foopy');
   checkDefine(C.location, 'href');
   checkDefine(C.location, 'replace');
   checkDefine(C.location, 'port');
   checkDefine(C.location, 'foopy');
 }, "[[DefineOwnProperty]] Should throw for cross-origin objects");
 
 /*
- * [[Enumerate]]
+ * EnumerateObjectProperties (backed by [[OwnPropertyKeys]])
  */
 
 addTest(function() {
-  for (var prop in C)
-    assert_unreached("Shouldn't have been able to enumerate " + prop + " on cross-origin Window");
-  for (var prop in C.location)
-    assert_unreached("Shouldn't have been able to enumerate " + prop + " on cross-origin Location");
-}, "[[Enumerate]] should return an empty iterator");
+  let i = 0;
+  for (var prop in C) {
+    i++;
+    assert_true(whitelistedWindowPropNames.includes(prop), prop + " is not safelisted for a cross-origin Window");
+  }
+  assert_equals(i, whitelistedWindowPropNames.length, "Enumerate all safelisted cross-origin Window properties");
+  i = 0;
+  for (var prop in C.location) {
+    i++;
+    assert_true(whitelistedLocationPropNames.includes(prop), prop + " is not safelisted for a cross-origin Location");
+  }
+  assert_equals(i, whitelistedLocationPropNames.length, "Enumerate all safelisted cross-origin Location properties");
+}, "Can only enumerate safelisted properties");
 
 /*
  * [[OwnPropertyKeys]]
  */
 
 addTest(function() {
   assert_array_equals(Object.getOwnPropertyNames(C).sort(),
                       whitelistedWindowPropNames,
                       "Object.getOwnPropertyNames() gives the right answer for cross-origin Window");
+  assert_array_equals(Object.keys(C).sort(),
+                      whitelistedWindowPropNames,
+                      "Object.keys() gives the right answer for cross-origin Window");
   assert_array_equals(Object.getOwnPropertyNames(C.location).sort(),
                       whitelistedLocationPropNames,
                       "Object.getOwnPropertyNames() gives the right answer for cross-origin Location");
+  assert_array_equals(Object.keys(C.location).sort(),
+                      whitelistedLocationPropNames,
+                      "Object.keys() gives the right answer for cross-origin Location");
 }, "[[OwnPropertyKeys]] should return all properties from cross-origin objects");
 
 addTest(function() {
   assert_array_equals(Object.getOwnPropertySymbols(C), whitelistedSymbols,
     "Object.getOwnPropertySymbols() should return the three symbol-named properties that are exposed on a cross-origin Window");
   assert_array_equals(Object.getOwnPropertySymbols(C.location),
                       whitelistedSymbols,
     "Object.getOwnPropertySymbols() should return the three symbol-named properties that are exposed on a cross-origin Location");
--- a/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/frame.html
+++ b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/frame.html
@@ -32,11 +32,11 @@
     }
     return true;
   }
 </script>
 </head>
 <body>
   <!-- Two subframes to give us some indexed properties -->
   <iframe></iframe>
-  <iframe></iframe>
+  <iframe name=donotleakme></iframe><!-- "donotleakme" is excluded as cross-origin named property due to [[HideFromKeys]] -->
 </body>
 </html>