test_crossOriginObjects.html 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  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="http://www.whatwg.org/specs/web-apps/current-work/#security-window">
  7. <link rel="help" href="http://www.whatwg.org/specs/web-apps/current-work/#security-location">
  8. <script src="/resources/testharness.js"></script>
  9. <script src="/resources/testharnessreport.js"></script>
  10. <div id=log></div>
  11. <iframe id="B"></iframe>
  12. <iframe id="C"></iframe>
  13. <script>
  14. /*
  15. * Setup boilerplate. This gives us a same-origin window "B" and a cross-origin
  16. * window "C".
  17. */
  18. setup({explicit_done: true});
  19. path = location.pathname.substring(0, location.pathname.lastIndexOf('/')) + '/file_crossOriginObjects.html';
  20. var B = document.getElementById('B').contentWindow;
  21. var C = document.getElementById('C').contentWindow;
  22. B.frameElement.uriToLoad = path;
  23. C.frameElement.uriToLoad = 'http://test1.mochi.test:' + location.port + path;
  24. function reloadSubframes(cb) {
  25. var iframes = document.getElementsByTagName('iframe');
  26. iframes.forEach = Array.prototype.forEach;
  27. var count = 0;
  28. function frameLoaded() {
  29. this.onload = null;
  30. if (++count == iframes.length)
  31. cb();
  32. }
  33. iframes.forEach(function(ifr) { ifr.onload = frameLoaded; ifr.setAttribute('src', ifr.uriToLoad); });
  34. }
  35. function isObject(x) { return Object(x) === x; }
  36. /*
  37. * Note: we eschew assert_equals in a lot of these tests, since the harness ends
  38. * up throwing when it tries to format a message involving a cross-origin object.
  39. */
  40. var testList = [];
  41. function addTest(fun, desc) { testList.push([fun, desc]); }
  42. /*
  43. * Basic sanity testing.
  44. */
  45. addTest(function() {
  46. assert_equals(location.host, 'mochi.test:8888', 'Need to run the top-level test from mochi.test:8888');
  47. assert_equals(B.parent, window, "window.parent works same-origin");
  48. assert_equals(C.parent, window, "window.parent works cross-origin");
  49. assert_equals(B.location.pathname, path, "location.href works same-origin");
  50. assert_throws(null, function() { C.location.pathname; }, "location.pathname throws cross-origin");
  51. assert_equals(B.frames, 'override', "Overrides visible in the same-origin case");
  52. assert_equals(C.frames, C, "Overrides invisible in the cross-origin case");
  53. }, "Basic sanity-checking");
  54. /*
  55. * Whitelist behavior.
  56. *
  57. * Also tests for [[GetOwnProperty]] and [[HasOwnProperty]] behavior.
  58. */
  59. var whitelistedWindowProps = ['location', 'postMessage', 'window', 'frames', 'self', 'top', 'parent',
  60. 'opener', 'closed', 'close', 'blur', 'focus', 'length'];
  61. addTest(function() {
  62. for (var prop in window) {
  63. if (whitelistedWindowProps.indexOf(prop) != -1) {
  64. C[prop]; // Shouldn't throw.
  65. Object.getOwnPropertyDescriptor(C, prop); // Shouldn't throw.
  66. assert_true(Object.prototype.hasOwnProperty.call(C, prop), "hasOwnProperty for " + prop);
  67. } else {
  68. assert_throws(null, function() { C[prop]; }, "Should throw when accessing " + prop + " on Window");
  69. assert_throws(null, function() { Object.getOwnPropertyDescriptor(C, prop); },
  70. "Should throw when accessing property descriptor for " + prop + " on Window");
  71. assert_throws(null, function() { Object.prototype.hasOwnProperty.call(C, prop); },
  72. "Should throw when invoking hasOwnProperty for " + prop + " on Window");
  73. }
  74. if (prop != 'location')
  75. assert_throws(null, function() { C[prop] = undefined; }, "Should throw when writing to " + prop + " on Window");
  76. }
  77. for (var prop in location) {
  78. if (prop == 'replace') {
  79. C.location[prop]; // Shouldn't throw.
  80. Object.getOwnPropertyDescriptor(C.location, prop); // Shouldn't throw.
  81. assert_true(Object.prototype.hasOwnProperty.call(C.location, prop), "hasOwnProperty for " + prop);
  82. }
  83. else {
  84. assert_throws(null, function() { C[prop]; }, "Should throw when accessing " + prop + " on Location");
  85. assert_throws(null, function() { Object.getOwnPropertyDescriptor(C, prop); },
  86. "Should throw when accessing property descriptor for " + prop + " on Location");
  87. assert_throws(null, function() { Object.prototype.hasOwnProperty.call(C, prop); },
  88. "Should throw when invoking hasOwnProperty for " + prop + " on Location");
  89. }
  90. if (prop != 'href')
  91. assert_throws(null, function() { C[prop] = undefined; }, "Should throw when writing to " + prop + " on Location");
  92. }
  93. }, "Only whitelisted properties are accessible cross-origin");
  94. /*
  95. * ES Internal Methods.
  96. */
  97. /*
  98. * [[GetPrototypeOf]]
  99. */
  100. addTest(function() {
  101. assert_true(Object.getPrototypeOf(C) === null, "cross-origin Window proto is null");
  102. assert_true(Object.getPrototypeOf(C.location) === null, "cross-origin Location proto is null (__proto__)");
  103. var protoGetter = Object.getOwnPropertyDescriptor(Object.prototype, '__proto__').get;
  104. assert_true(protoGetter.call(C) === null, "cross-origin Window proto is null");
  105. assert_true(protoGetter.call(C.location) === null, "cross-origin Location proto is null (__proto__)");
  106. assert_throws(null, function() { C.__proto__; }, "__proto__ property not available cross-origin");
  107. assert_throws(null, function() { C.location.__proto__; }, "__proto__ property not available cross-origin");
  108. }, "[[GetPrototypeOf]] should return null");
  109. /*
  110. * [[SetPrototypeOf]]
  111. */
  112. addTest(function() {
  113. assert_throws(null, function() { C.__proto__ = new Object(); }, "proto set on cross-origin Window");
  114. assert_throws(null, function() { C.location.__proto__ = new Object(); }, "proto set on cross-origin Location");
  115. var setters = [Object.getOwnPropertyDescriptor(Object.prototype, '__proto__').set];
  116. if (Object.setPrototypeOf)
  117. setters.push(function(p) { Object.setPrototypeOf(this, p); });
  118. setters.forEach(function(protoSetter) {
  119. assert_throws(null, function() { protoSetter.call(C, new Object()); }, "proto setter |call| on cross-origin Window");
  120. assert_throws(null, function() { protoSetter.call(C.location, new Object()); }, "proto setter |call| on cross-origin Location");
  121. });
  122. }, "[[SetPrototypeOf]] should throw");
  123. /*
  124. * [[IsExtensible]]
  125. */
  126. addTest(function() {
  127. assert_true(Object.isExtensible(C), "cross-origin Window should be extensible");
  128. assert_true(Object.isExtensible(C.location), "cross-origin Location should be extensible");
  129. }, "[[IsExtensible]] should return true for cross-origin objects");
  130. /*
  131. * [[PreventExtensions]]
  132. */
  133. addTest(function() {
  134. assert_throws(null, function() { Object.preventExtensions(C) },
  135. "preventExtensions on cross-origin Window should throw");
  136. assert_throws(null, function() { Object.preventExtensions(C.location) },
  137. "preventExtensions on cross-origin Location should throw");
  138. }, "[[PreventExtensions]] should throw for cross-origin objects");
  139. /*
  140. * [[GetOwnProperty]]
  141. */
  142. addTest(function() {
  143. assert_true(isObject(Object.getOwnPropertyDescriptor(C, 'close')), "C.close is |own|");
  144. assert_true(isObject(Object.getOwnPropertyDescriptor(C, 'top')), "C.top is |own|");
  145. assert_true(isObject(Object.getOwnPropertyDescriptor(C.location, 'href')), "C.location.href is |own|");
  146. assert_true(isObject(Object.getOwnPropertyDescriptor(C.location, 'replace')), "C.location.replace is |own|");
  147. }, "[[GetOwnProperty]] - Properties on cross-origin objects should be reported |own|");
  148. function checkPropertyDescriptor(desc, propName, expectWritable) {
  149. assert_true(isObject(desc), "property descriptor for " + propName + " should exist");
  150. assert_equals(desc.enumerable, false, "property descriptor for " + propName + " should be non-enumerable");
  151. assert_equals(desc.configurable, true, "property descriptor for " + propName + " should be configurable");
  152. if ('value' in desc)
  153. assert_equals(desc.writable, expectWritable, "property descriptor for " + propName + " should have writable: " + expectWritable);
  154. else
  155. assert_equals(typeof desc.set != 'undefined', expectWritable,
  156. "property descriptor for " + propName + " should " + (expectWritable ? "" : "not ") + "have setter");
  157. }
  158. addTest(function() {
  159. whitelistedWindowProps.forEach(function(prop) {
  160. var desc = Object.getOwnPropertyDescriptor(C, prop);
  161. checkPropertyDescriptor(desc, prop, prop == 'location');
  162. });
  163. checkPropertyDescriptor(Object.getOwnPropertyDescriptor(C.location, 'replace'), 'replace', false);
  164. checkPropertyDescriptor(Object.getOwnPropertyDescriptor(C.location, 'href'), 'href', true);
  165. assert_equals(typeof Object.getOwnPropertyDescriptor(C.location, 'href').get, 'undefined', "Cross-origin location should have no href getter");
  166. }, "[[GetOwnProperty]] - Property descriptors for cross-origin properties should be set up correctly");
  167. /*
  168. * [[Delete]]
  169. */
  170. addTest(function() {
  171. assert_throws(null, function() { delete C.location; }, "Can't delete cross-origin property");
  172. assert_throws(null, function() { delete C.parent; }, "Can't delete cross-origin property");
  173. assert_throws(null, function() { delete C.length; }, "Can't delete cross-origin property");
  174. assert_throws(null, function() { delete C.document; }, "Can't delete cross-origin property");
  175. assert_throws(null, function() { delete C.foopy; }, "Can't delete cross-origin property");
  176. assert_throws(null, function() { delete C.location.href; }, "Can't delete cross-origin property");
  177. assert_throws(null, function() { delete C.location.replace; }, "Can't delete cross-origin property");
  178. assert_throws(null, function() { delete C.location.port; }, "Can't delete cross-origin property");
  179. assert_throws(null, function() { delete C.location.foopy; }, "Can't delete cross-origin property");
  180. }, "[[Delete]] Should throw on cross-origin objects");
  181. /*
  182. * [[DefineOwnProperty]]
  183. */
  184. function checkDefine(obj, prop) {
  185. var valueDesc = { configurable: true, enumerable: false, writable: false, value: 2 };
  186. var accessorDesc = { configurable: true, enumerable: false, get: function() {} };
  187. assert_throws(null, function() { Object.defineProperty(obj, prop, valueDesc); }, "Can't define cross-origin value property " + prop);
  188. assert_throws(null, function() { Object.defineProperty(obj, prop, accessorDesc); }, "Can't define cross-origin accessor property " + prop);
  189. }
  190. addTest(function() {
  191. checkDefine(C, 'length');
  192. checkDefine(C, 'parent');
  193. checkDefine(C, 'location');
  194. checkDefine(C, 'document');
  195. checkDefine(C, 'foopy');
  196. checkDefine(C.location, 'href');
  197. checkDefine(C.location, 'replace');
  198. checkDefine(C.location, 'port');
  199. checkDefine(C.location, 'foopy');
  200. }, "[[DefineOwnProperty]] Should throw for cross-origin objects");
  201. /*
  202. * [[Enumerate]]
  203. */
  204. addTest(function() {
  205. for (var prop in C)
  206. assert_unreached("Shouldn't have been able to enumerate " + prop + " on cross-origin Window");
  207. for (var prop in C.location)
  208. assert_unreached("Shouldn't have been able to enumerate " + prop + " on cross-origin Location");
  209. }, "[[Enumerate]] should return an empty iterator");
  210. /*
  211. * [[OwnPropertyKeys]]
  212. */
  213. addTest(function() {
  214. assert_array_equals(whitelistedWindowProps.sort(), Object.getOwnPropertyNames(C).sort(),
  215. "Object.getOwnPropertyNames() gives the right answer for cross-origin Window");
  216. assert_array_equals(Object.getOwnPropertyNames(C.location).sort(), ['href', 'replace'],
  217. "Object.getOwnPropertyNames() gives the right answer for cross-origin Location");
  218. }, "[[OwnPropertyKeys]] should return all properties from cross-origin objects");
  219. addTest(function() {
  220. assert_true(B.eval('parent.C') === C, "A and B observe the same identity for C's Window");
  221. assert_true(B.eval('parent.C.location') === C.location, "A and B observe the same identity for C's Location");
  222. }, "A and B jointly observe the same identity for cross-origin Window and Location");
  223. function checkFunction(f, proto) {
  224. var name = f.name || '<missing name>';
  225. assert_equals(typeof f, 'function', name + " is a function");
  226. assert_equals(Object.getPrototypeOf(f), proto, f.name + " has the right prototype");
  227. }
  228. addTest(function() {
  229. checkFunction(C.close, Function.prototype);
  230. checkFunction(C.location.replace, Function.prototype);
  231. }, "Cross-origin functions get local Function.prototype");
  232. addTest(function() {
  233. assert_true(isObject(Object.getOwnPropertyDescriptor(C, 'parent')),
  234. "Need to be able to use Object.getOwnPropertyDescriptor do this test");
  235. checkFunction(Object.getOwnPropertyDescriptor(C, 'parent').get, Function.prototype);
  236. checkFunction(Object.getOwnPropertyDescriptor(C.location, 'href').set, Function.prototype);
  237. }, "Cross-origin Window accessors get local Function.prototype");
  238. addTest(function() {
  239. checkFunction(close, Function.prototype);
  240. assert_true(close != B.close, 'same-origin Window functions get their own object');
  241. assert_true(close != C.close, 'cross-origin Window functions get their own object');
  242. var close_B = B.eval('parent.C.close');
  243. assert_true(close != close_B, 'close_B is unique when viewed by the parent');
  244. assert_true(close_B != C.close, 'different Window functions per-incumbent script settings object');
  245. checkFunction(close_B, B.Function.prototype);
  246. checkFunction(location.replace, Function.prototype);
  247. assert_true(location.replace != C.location.replace, "cross-origin Location functions get their own object");
  248. var replace_B = B.eval('parent.C.location.replace');
  249. assert_true(replace_B != C.location.replace, 'different Location functions per-incumbent script settings object');
  250. checkFunction(replace_B, B.Function.prototype);
  251. }, "Same-origin observers get different functions for cross-origin objects");
  252. addTest(function() {
  253. assert_true(isObject(Object.getOwnPropertyDescriptor(C, 'parent')),
  254. "Need to be able to use Object.getOwnPropertyDescriptor do this test");
  255. var get_self_parent = Object.getOwnPropertyDescriptor(window, 'parent').get;
  256. var get_parent_A = Object.getOwnPropertyDescriptor(C, 'parent').get;
  257. var get_parent_B = B.eval('Object.getOwnPropertyDescriptor(parent.C, "parent").get');
  258. assert_true(get_self_parent != get_parent_A, 'different Window accessors per-incumbent script settings object');
  259. assert_true(get_parent_A != get_parent_B, 'different Window accessors per-incumbent script settings object');
  260. checkFunction(get_self_parent, Function.prototype);
  261. checkFunction(get_parent_A, Function.prototype);
  262. checkFunction(get_parent_B, B.Function.prototype);
  263. }, "Same-origin obsevers get different accessors for cross-origin Window");
  264. addTest(function() {
  265. var set_self_href = Object.getOwnPropertyDescriptor(window.location, 'href').set;
  266. var set_href_A = Object.getOwnPropertyDescriptor(C.location, 'href').set;
  267. var set_href_B = B.eval('Object.getOwnPropertyDescriptor(parent.C.location, "href").set');
  268. assert_true(set_self_href != set_href_A, 'different Location accessors per-incumbent script settings object');
  269. assert_true(set_href_A != set_href_B, 'different Location accessors per-incumbent script settings object');
  270. checkFunction(set_self_href, Function.prototype);
  271. checkFunction(set_href_A, Function.prototype);
  272. checkFunction(set_href_B, B.Function.prototype);
  273. }, "Same-origin observers get different accessors for cross-origin Location");
  274. function doDocumentDomainTest(cb) {
  275. window.addEventListener('message', function onmessage(evt) {
  276. window.removeEventListener('message', onmessage);
  277. test(function() {
  278. var results = evt.data;
  279. assert_true(results.length > 0, 'Need results');
  280. results.forEach(function(r) { assert_true(r.pass, r.message); });
  281. }, "Cross-origin object identity preserved across document.domain");
  282. win.close();
  283. cb();
  284. });
  285. var win = window.open('file_crossOriginObjects_documentDomain.html');
  286. }
  287. // We do a fresh load of the subframes for each test to minimize side-effects.
  288. // It would be nice to reload ourselves as well, but we can't do that without
  289. // disrupting the test harness.
  290. function runNextTest() {
  291. var entry = testList.shift();
  292. test(entry[0], entry[1]);
  293. if (testList.length != 0)
  294. reloadSubframes(runNextTest);
  295. else
  296. doDocumentDomainTest(done); // Asynchronous.
  297. }
  298. reloadSubframes(runNextTest);
  299. </script>