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