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