OLD | NEW |
(Empty) | |
| 1 <!-- Once most browsers pass this test it can replace cross-origin-objects.html.
It is meant to be |
| 2 identical (please verify), except for also checking that the exceptions are
correct. --> |
| 3 <!doctype html> |
| 4 <meta charset=utf-8> |
| 5 <meta name="timeout" content="long"> |
| 6 <title>Cross-origin behavior of Window and Location</title> |
| 7 <link rel="author" title="Bobby Holley (:bholley)" href="bobbyholley@gmail.com"> |
| 8 <link rel="help" href="https://html.spec.whatwg.org/multipage/#security-window"> |
| 9 <link rel="help" href="https://html.spec.whatwg.org/multipage/#security-location
"> |
| 10 <script src="/resources/testharness.js"></script> |
| 11 <script src="/resources/testharnessreport.js"></script> |
| 12 <script src="/common/get-host-info.sub.js"></script> |
| 13 <div id=log></div> |
| 14 <iframe id="B"></iframe> |
| 15 <iframe id="C"></iframe> |
| 16 <script> |
| 17 /* |
| 18 * Setup boilerplate. This gives us a same-origin window "B" and a cross-origin |
| 19 * window "C". |
| 20 */ |
| 21 var host_info = get_host_info(); |
| 22 |
| 23 setup({explicit_done: true}); |
| 24 path = location.pathname.substring(0, location.pathname.lastIndexOf('/')) + '/fr
ame.html'; |
| 25 var B = document.getElementById('B').contentWindow; |
| 26 var C = document.getElementById('C').contentWindow; |
| 27 B.frameElement.uriToLoad = path; |
| 28 C.frameElement.uriToLoad = get_host_info().HTTP_REMOTE_ORIGIN + path; |
| 29 |
| 30 function reloadSubframes(cb) { |
| 31 var iframes = document.getElementsByTagName('iframe'); |
| 32 iframes.forEach = Array.prototype.forEach; |
| 33 var count = 0; |
| 34 function frameLoaded() { |
| 35 this.onload = null; |
| 36 if (++count == iframes.length) |
| 37 cb(); |
| 38 } |
| 39 iframes.forEach(function(ifr) { ifr.onload = frameLoaded; ifr.setAttribute('sr
c', ifr.uriToLoad); }); |
| 40 } |
| 41 function isObject(x) { return Object(x) === x; } |
| 42 |
| 43 /* |
| 44 * Note: we eschew assert_equals in a lot of these tests, since the harness ends |
| 45 * up throwing when it tries to format a message involving a cross-origin object
. |
| 46 */ |
| 47 |
| 48 var testList = []; |
| 49 function addTest(fun, desc) { testList.push([fun, desc]); } |
| 50 |
| 51 |
| 52 /* |
| 53 * Basic sanity testing. |
| 54 */ |
| 55 |
| 56 addTest(function() { |
| 57 // Note: we do not check location.host as its default port semantics are hard
to reflect statically |
| 58 assert_equals(location.hostname, host_info.ORIGINAL_HOST, 'Need to run the top
-level test from domain ' + host_info.ORIGINAL_HOST); |
| 59 assert_equals(get_port(location), host_info.HTTP_PORT, 'Need to run the top-le
vel test from port ' + host_info.HTTP_PORT); |
| 60 assert_equals(B.parent, window, "window.parent works same-origin"); |
| 61 assert_equals(C.parent, window, "window.parent works cross-origin"); |
| 62 assert_equals(B.location.pathname, path, "location.href works same-origin"); |
| 63 assert_throws("SecurityError", function() { C.location.pathname; }, "location.
pathname throws cross-origin"); |
| 64 assert_equals(B.frames, 'override', "Overrides visible in the same-origin case
"); |
| 65 assert_equals(C.frames, C, "Overrides invisible in the cross-origin case"); |
| 66 }, "Basic sanity-checking"); |
| 67 |
| 68 /* |
| 69 * Whitelist behavior. |
| 70 * |
| 71 * Also tests for [[GetOwnProperty]] and [[HasOwnProperty]] behavior. |
| 72 */ |
| 73 |
| 74 var whitelistedWindowProps = ['location', 'postMessage', 'window', 'frames', 'se
lf', 'top', 'parent', |
| 75 'opener', 'closed', 'close', 'blur', 'focus', 'len
gth']; |
| 76 addTest(function() { |
| 77 for (var prop in window) { |
| 78 if (whitelistedWindowProps.indexOf(prop) != -1) { |
| 79 C[prop]; // Shouldn't throw. |
| 80 Object.getOwnPropertyDescriptor(C, prop); // Shouldn't throw. |
| 81 assert_true(Object.prototype.hasOwnProperty.call(C, prop), "hasOwnProperty
for " + prop); |
| 82 } else { |
| 83 assert_throws("SecurityError", function() { C[prop]; }, "Should throw when
accessing " + prop + " on Window"); |
| 84 assert_throws("SecurityError", function() { Object.getOwnPropertyDescripto
r(C, prop); }, |
| 85 "Should throw when accessing property descriptor for " + pro
p + " on Window"); |
| 86 assert_throws("SecurityError", function() { Object.prototype.hasOwnPropert
y.call(C, prop); }, |
| 87 "Should throw when invoking hasOwnProperty for " + prop + "
on Window"); |
| 88 } |
| 89 if (prop != 'location') |
| 90 assert_throws("SecurityError", function() { C[prop] = undefined; }, "Shoul
d throw when writing to " + prop + " on Window"); |
| 91 } |
| 92 for (var prop in location) { |
| 93 if (prop == 'replace') { |
| 94 C.location[prop]; // Shouldn't throw. |
| 95 Object.getOwnPropertyDescriptor(C.location, prop); // Shouldn't throw. |
| 96 assert_true(Object.prototype.hasOwnProperty.call(C.location, prop), "hasOw
nProperty for " + prop); |
| 97 } |
| 98 else { |
| 99 assert_throws("SecurityError", function() { C[prop]; }, "Should throw when
accessing " + prop + " on Location"); |
| 100 assert_throws("SecurityError", function() { Object.getOwnPropertyDescripto
r(C, prop); }, |
| 101 "Should throw when accessing property descriptor for " + pro
p + " on Location"); |
| 102 assert_throws("SecurityError", function() { Object.prototype.hasOwnPropert
y.call(C, prop); }, |
| 103 "Should throw when invoking hasOwnProperty for " + prop + "
on Location"); |
| 104 } |
| 105 if (prop != 'href') |
| 106 assert_throws("SecurityError", function() { C[prop] = undefined; }, "Shoul
d throw when writing to " + prop + " on Location"); |
| 107 } |
| 108 }, "Only whitelisted properties are accessible cross-origin"); |
| 109 |
| 110 /* |
| 111 * ES Internal Methods. |
| 112 */ |
| 113 |
| 114 /* |
| 115 * [[GetPrototypeOf]] |
| 116 */ |
| 117 addTest(function() { |
| 118 assert_true(Object.getPrototypeOf(C) === null, "cross-origin Window proto is n
ull"); |
| 119 assert_true(Object.getPrototypeOf(C.location) === null, "cross-origin Location
proto is null (__proto__)"); |
| 120 var protoGetter = Object.getOwnPropertyDescriptor(Object.prototype, '__proto__
').get; |
| 121 assert_true(protoGetter.call(C) === null, "cross-origin Window proto is null")
; |
| 122 assert_true(protoGetter.call(C.location) === null, "cross-origin Location prot
o is null (__proto__)"); |
| 123 assert_throws("SecurityError", function() { C.__proto__; }, "__proto__ propert
y not available cross-origin"); |
| 124 assert_throws("SecurityError", function() { C.location.__proto__; }, "__proto_
_ property not available cross-origin"); |
| 125 |
| 126 }, "[[GetPrototypeOf]] should return null"); |
| 127 |
| 128 /* |
| 129 * [[SetPrototypeOf]] |
| 130 */ |
| 131 addTest(function() { |
| 132 assert_throws("SecurityError", function() { C.__proto__ = new Object(); }, "pr
oto set on cross-origin Window"); |
| 133 assert_throws("SecurityError", function() { C.location.__proto__ = new Object(
); }, "proto set on cross-origin Location"); |
| 134 var setters = [Object.getOwnPropertyDescriptor(Object.prototype, '__proto__').
set]; |
| 135 if (Object.setPrototypeOf) |
| 136 setters.push(function(p) { Object.setPrototypeOf(this, p); }); |
| 137 setters.forEach(function(protoSetter) { |
| 138 assert_throws(new TypeError, function() { protoSetter.call(C, new Object());
}, "proto setter |call| on cross-origin Window"); |
| 139 assert_throws(new TypeError, function() { protoSetter.call(C.location, new O
bject()); }, "proto setter |call| on cross-origin Location"); |
| 140 }); |
| 141 }, "[[SetPrototypeOf]] should throw"); |
| 142 |
| 143 /* |
| 144 * [[IsExtensible]] |
| 145 */ |
| 146 addTest(function() { |
| 147 assert_true(Object.isExtensible(C), "cross-origin Window should be extensible"
); |
| 148 assert_true(Object.isExtensible(C.location), "cross-origin Location should be
extensible"); |
| 149 }, "[[IsExtensible]] should return true for cross-origin objects"); |
| 150 |
| 151 /* |
| 152 * [[PreventExtensions]] |
| 153 */ |
| 154 addTest(function() { |
| 155 assert_throws(new TypeError, function() { Object.preventExtensions(C) }, |
| 156 "preventExtensions on cross-origin Window should throw"); |
| 157 assert_throws(new TypeError, function() { Object.preventExtensions(C.location)
}, |
| 158 "preventExtensions on cross-origin Location should throw"); |
| 159 }, "[[PreventExtensions]] should throw for cross-origin objects"); |
| 160 |
| 161 /* |
| 162 * [[GetOwnProperty]] |
| 163 */ |
| 164 |
| 165 addTest(function() { |
| 166 assert_true(isObject(Object.getOwnPropertyDescriptor(C, 'close')), "C.close is
|own|"); |
| 167 assert_true(isObject(Object.getOwnPropertyDescriptor(C, 'top')), "C.top is |ow
n|"); |
| 168 assert_true(isObject(Object.getOwnPropertyDescriptor(C.location, 'href')), "C.
location.href is |own|"); |
| 169 assert_true(isObject(Object.getOwnPropertyDescriptor(C.location, 'replace')),
"C.location.replace is |own|"); |
| 170 }, "[[GetOwnProperty]] - Properties on cross-origin objects should be reported |
own|"); |
| 171 |
| 172 function checkPropertyDescriptor(desc, propName, expectWritable) { |
| 173 assert_true(isObject(desc), "property descriptor for " + propName + " should e
xist"); |
| 174 assert_equals(desc.enumerable, false, "property descriptor for " + propName +
" should be non-enumerable"); |
| 175 assert_equals(desc.configurable, true, "property descriptor for " + propName +
" should be configurable"); |
| 176 if ('value' in desc) |
| 177 assert_equals(desc.writable, expectWritable, "property descriptor for " + pr
opName + " should have writable: " + expectWritable); |
| 178 else |
| 179 assert_equals(typeof desc.set != 'undefined', expectWritable, |
| 180 "property descriptor for " + propName + " should " + (expectWr
itable ? "" : "not ") + "have setter"); |
| 181 } |
| 182 |
| 183 addTest(function() { |
| 184 whitelistedWindowProps.forEach(function(prop) { |
| 185 var desc = Object.getOwnPropertyDescriptor(C, prop); |
| 186 checkPropertyDescriptor(desc, prop, prop == 'location'); |
| 187 }); |
| 188 checkPropertyDescriptor(Object.getOwnPropertyDescriptor(C.location, 'replace')
, 'replace', false); |
| 189 checkPropertyDescriptor(Object.getOwnPropertyDescriptor(C.location, 'href'), '
href', true); |
| 190 assert_equals(typeof Object.getOwnPropertyDescriptor(C.location, 'href').get,
'undefined', "Cross-origin location should have no href getter"); |
| 191 }, "[[GetOwnProperty]] - Property descriptors for cross-origin properties should
be set up correctly"); |
| 192 |
| 193 /* |
| 194 * [[Delete]] |
| 195 */ |
| 196 addTest(function() { |
| 197 assert_throws("SecurityError", function() { delete C[0]; }, "Can't delete cros
s-origin indexed property"); |
| 198 assert_throws("SecurityError", function() { delete C[100]; }, "Can't delete cr
oss-origin indexed property"); |
| 199 assert_throws("SecurityError", function() { delete C.location; }, "Can't delet
e cross-origin property"); |
| 200 assert_throws("SecurityError", function() { delete C.parent; }, "Can't delete
cross-origin property"); |
| 201 assert_throws("SecurityError", function() { delete C.length; }, "Can't delete
cross-origin property"); |
| 202 assert_throws("SecurityError", function() { delete C.document; }, "Can't delet
e cross-origin property"); |
| 203 assert_throws("SecurityError", function() { delete C.foopy; }, "Can't delete c
ross-origin property"); |
| 204 assert_throws("SecurityError", function() { delete C.location.href; }, "Can't
delete cross-origin property"); |
| 205 assert_throws("SecurityError", function() { delete C.location.replace; }, "Can
't delete cross-origin property"); |
| 206 assert_throws("SecurityError", function() { delete C.location.port; }, "Can't
delete cross-origin property"); |
| 207 assert_throws("SecurityError", function() { delete C.location.foopy; }, "Can't
delete cross-origin property"); |
| 208 }, "[[Delete]] Should throw on cross-origin objects"); |
| 209 |
| 210 /* |
| 211 * [[DefineOwnProperty]] |
| 212 */ |
| 213 function checkDefine(obj, prop) { |
| 214 var valueDesc = { configurable: true, enumerable: false, writable: false, valu
e: 2 }; |
| 215 var accessorDesc = { configurable: true, enumerable: false, get: function() {}
}; |
| 216 assert_throws("SecurityError", function() { Object.defineProperty(obj, prop, v
alueDesc); }, "Can't define cross-origin value property " + prop); |
| 217 assert_throws("SecurityError", function() { Object.defineProperty(obj, prop, a
ccessorDesc); }, "Can't define cross-origin accessor property " + prop); |
| 218 } |
| 219 addTest(function() { |
| 220 checkDefine(C, 'length'); |
| 221 checkDefine(C, 'parent'); |
| 222 checkDefine(C, 'location'); |
| 223 checkDefine(C, 'document'); |
| 224 checkDefine(C, 'foopy'); |
| 225 checkDefine(C.location, 'href'); |
| 226 checkDefine(C.location, 'replace'); |
| 227 checkDefine(C.location, 'port'); |
| 228 checkDefine(C.location, 'foopy'); |
| 229 }, "[[DefineOwnProperty]] Should throw for cross-origin objects"); |
| 230 |
| 231 /* |
| 232 * [[Enumerate]] |
| 233 */ |
| 234 |
| 235 addTest(function() { |
| 236 for (var prop in C) |
| 237 assert_unreached("Shouldn't have been able to enumerate " + prop + " on cros
s-origin Window"); |
| 238 for (var prop in C.location) |
| 239 assert_unreached("Shouldn't have been able to enumerate " + prop + " on cros
s-origin Location"); |
| 240 }, "[[Enumerate]] should return an empty iterator"); |
| 241 |
| 242 /* |
| 243 * [[OwnPropertyKeys]] |
| 244 */ |
| 245 |
| 246 addTest(function() { |
| 247 assert_array_equals(whitelistedWindowProps.sort(), Object.getOwnPropertyNames(
C).sort(), |
| 248 "Object.getOwnPropertyNames() gives the right answer for c
ross-origin Window"); |
| 249 assert_array_equals(Object.getOwnPropertyNames(C.location).sort(), ['href', 'r
eplace'], |
| 250 "Object.getOwnPropertyNames() gives the right answer for c
ross-origin Location"); |
| 251 }, "[[OwnPropertyKeys]] should return all properties from cross-origin objects")
; |
| 252 |
| 253 addTest(function() { |
| 254 assert_true(B.eval('parent.C') === C, "A and B observe the same identity for C
's Window"); |
| 255 assert_true(B.eval('parent.C.location') === C.location, "A and B observe the s
ame identity for C's Location"); |
| 256 }, "A and B jointly observe the same identity for cross-origin Window and Locati
on"); |
| 257 |
| 258 function checkFunction(f, proto) { |
| 259 var name = f.name || '<missing name>'; |
| 260 assert_equals(typeof f, 'function', name + " is a function"); |
| 261 assert_equals(Object.getPrototypeOf(f), proto, f.name + " has the right protot
ype"); |
| 262 } |
| 263 |
| 264 addTest(function() { |
| 265 checkFunction(C.close, Function.prototype); |
| 266 checkFunction(C.location.replace, Function.prototype); |
| 267 }, "Cross-origin functions get local Function.prototype"); |
| 268 |
| 269 addTest(function() { |
| 270 assert_true(isObject(Object.getOwnPropertyDescriptor(C, 'parent')), |
| 271 "Need to be able to use Object.getOwnPropertyDescriptor do this te
st"); |
| 272 checkFunction(Object.getOwnPropertyDescriptor(C, 'parent').get, Function.proto
type); |
| 273 checkFunction(Object.getOwnPropertyDescriptor(C.location, 'href').set, Functio
n.prototype); |
| 274 }, "Cross-origin Window accessors get local Function.prototype"); |
| 275 |
| 276 addTest(function() { |
| 277 checkFunction(close, Function.prototype); |
| 278 assert_true(close != B.close, 'same-origin Window functions get their own obje
ct'); |
| 279 assert_true(close != C.close, 'cross-origin Window functions get their own obj
ect'); |
| 280 var close_B = B.eval('parent.C.close'); |
| 281 assert_true(close != close_B, 'close_B is unique when viewed by the parent'); |
| 282 assert_true(close_B != C.close, 'different Window functions per-incumbent scri
pt settings object'); |
| 283 checkFunction(close_B, B.Function.prototype); |
| 284 |
| 285 checkFunction(location.replace, Function.prototype); |
| 286 assert_true(location.replace != C.location.replace, "cross-origin Location fun
ctions get their own object"); |
| 287 var replace_B = B.eval('parent.C.location.replace'); |
| 288 assert_true(replace_B != C.location.replace, 'different Location functions per
-incumbent script settings object'); |
| 289 checkFunction(replace_B, B.Function.prototype); |
| 290 }, "Same-origin observers get different functions for cross-origin objects"); |
| 291 |
| 292 addTest(function() { |
| 293 assert_true(isObject(Object.getOwnPropertyDescriptor(C, 'parent')), |
| 294 "Need to be able to use Object.getOwnPropertyDescriptor do this te
st"); |
| 295 var get_self_parent = Object.getOwnPropertyDescriptor(window, 'parent').get; |
| 296 var get_parent_A = Object.getOwnPropertyDescriptor(C, 'parent').get; |
| 297 var get_parent_B = B.eval('Object.getOwnPropertyDescriptor(parent.C, "parent")
.get'); |
| 298 assert_true(get_self_parent != get_parent_A, 'different Window accessors per-i
ncumbent script settings object'); |
| 299 assert_true(get_parent_A != get_parent_B, 'different Window accessors per-incu
mbent script settings object'); |
| 300 checkFunction(get_self_parent, Function.prototype); |
| 301 checkFunction(get_parent_A, Function.prototype); |
| 302 checkFunction(get_parent_B, B.Function.prototype); |
| 303 }, "Same-origin observers get different accessors for cross-origin Window"); |
| 304 |
| 305 addTest(function() { |
| 306 var set_self_href = Object.getOwnPropertyDescriptor(window.location, 'href').s
et; |
| 307 var set_href_A = Object.getOwnPropertyDescriptor(C.location, 'href').set; |
| 308 var set_href_B = B.eval('Object.getOwnPropertyDescriptor(parent.C.location, "h
ref").set'); |
| 309 assert_true(set_self_href != set_href_A, 'different Location accessors per-inc
umbent script settings object'); |
| 310 assert_true(set_href_A != set_href_B, 'different Location accessors per-incumb
ent script settings object'); |
| 311 checkFunction(set_self_href, Function.prototype); |
| 312 checkFunction(set_href_A, Function.prototype); |
| 313 checkFunction(set_href_B, B.Function.prototype); |
| 314 }, "Same-origin observers get different accessors for cross-origin Location"); |
| 315 |
| 316 // We do a fresh load of the subframes for each test to minimize side-effects. |
| 317 // It would be nice to reload ourselves as well, but we can't do that without |
| 318 // disrupting the test harness. |
| 319 function runNextTest() { |
| 320 var entry = testList.shift(); |
| 321 test(entry[0], entry[1]); |
| 322 if (testList.length != 0) |
| 323 reloadSubframes(runNextTest); |
| 324 else |
| 325 done(); |
| 326 } |
| 327 reloadSubframes(runNextTest); |
| 328 |
| 329 </script> |
OLD | NEW |