|
1 <?xml version="1.0"?> |
|
2 <?xml-stylesheet href="chrome://global/skin" type="text/css"?> |
|
3 <?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" |
|
4 type="text/css"?> |
|
5 <!-- |
|
6 https://bugzilla.mozilla.org/show_bug.cgi?id=500931 |
|
7 --> |
|
8 <window title="Mozilla Bug 522764" |
|
9 xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> |
|
10 <script type="application/javascript" |
|
11 src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> |
|
12 |
|
13 <!-- test results are displayed in the html:body --> |
|
14 <body xmlns="http://www.w3.org/1999/xhtml"> |
|
15 <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=522764 " |
|
16 target="_blank">Mozilla Bug 522764 </a> |
|
17 </body> |
|
18 |
|
19 <!-- test code goes here --> |
|
20 <script type="application/javascript"><![CDATA[ |
|
21 const Ci = Components.interfaces; |
|
22 const Cu = Components.utils; |
|
23 |
|
24 var sandbox = new Cu.Sandbox("about:blank"); |
|
25 |
|
26 var test_utils = window.QueryInterface(Ci.nsIInterfaceRequestor). |
|
27 getInterface(Ci.nsIDOMWindowUtils); |
|
28 |
|
29 function getCOW(x) { |
|
30 if (typeof x != 'object' && typeof x != 'function') |
|
31 return x; |
|
32 var rval = {}; |
|
33 if (typeof x == "function") |
|
34 rval = eval(uneval(x)); |
|
35 for (var i in x) { |
|
36 if (x.__lookupGetter__(i)) |
|
37 rval.__defineGetter__(i, eval(uneval(x.__lookupGetter__(i)))) |
|
38 else |
|
39 rval[i] = getCOW(x[i]); |
|
40 } |
|
41 return rval; |
|
42 } |
|
43 |
|
44 // Give the sandbox a way to create ChromeObjectWrapped objects. |
|
45 sandbox.getCOW = getCOW; |
|
46 |
|
47 // Define test API functions in the sandbox. |
|
48 const TEST_API = ['is', 'isnot', 'ok', 'todo_is', 'todo_isnot', 'todo']; |
|
49 TEST_API.forEach(function(name) { sandbox[name] = window[name]; }); |
|
50 |
|
51 sandbox.alienObject = { |
|
52 __exposedProps__: {funProp: 'r'}, |
|
53 funProp: function foo(x) { |
|
54 return x + 1; |
|
55 } |
|
56 }; |
|
57 |
|
58 sandbox.chromeGet = function (obj, prop) { return obj[prop]; }; |
|
59 |
|
60 function COWTests() { |
|
61 // This function is actually decompiled and run inside a |
|
62 // sandbox with content privileges. |
|
63 |
|
64 // TODO: This could use some refactoring; creating helper |
|
65 // functions like assertIsWritable(myObj, 'someproperty') might |
|
66 // be useful. |
|
67 |
|
68 function isProp(obj, propName, value, desc) { |
|
69 try { |
|
70 is(obj[propName], value, "getting " + propName + " on " + desc); |
|
71 ok(propName in obj, |
|
72 propName + " on " + desc + " should exist"); |
|
73 ok(Object.hasOwnProperty.call(obj, propName), |
|
74 propName + " on " + desc + " should exist"); |
|
75 } catch (e) { |
|
76 ok(false, "getting " + propName + " on " + desc + " threw " + e); |
|
77 } |
|
78 } |
|
79 function isPropHidden(obj, propName, desc) { |
|
80 try { |
|
81 is(obj[propName], undefined, |
|
82 "getting " + propName + " on " + desc + " should return undefined"); |
|
83 ok(!(propName in obj), |
|
84 propName + " on " + desc + " should act as if it doesn't exist"); |
|
85 ok(!Object.hasOwnProperty.call(obj, propName), |
|
86 propName + " on " + desc + " should act as if it doesn't exist"); |
|
87 } catch (e) { |
|
88 ok(false, "getting " + propName + " on " + desc + " threw " + e); |
|
89 } |
|
90 } |
|
91 |
|
92 const PROPS_TO_TEST = ['foo', 'bar', 'prototype']; |
|
93 |
|
94 var empty = {}; |
|
95 var nonempty = {foo: 42, bar: 33}; |
|
96 is(getCOW(empty).foo, undefined, |
|
97 "shouldn't throw when accessing exposed properties that doesn't exist"); |
|
98 |
|
99 PROPS_TO_TEST.forEach(function(name) { |
|
100 isPropHidden(getCOW(nonempty), name, "object without exposedProps"); |
|
101 }); |
|
102 |
|
103 // Test function objects without __exposedProps__ |
|
104 var func = function(x) { return 42; }; |
|
105 func.foo = "foo property"; |
|
106 var funcCOW = getCOW(func); |
|
107 PROPS_TO_TEST.forEach(function(name) { |
|
108 isPropHidden(funcCOW, name, "function without exposedProps"); |
|
109 }); |
|
110 is([name for (name in funcCOW)].length, 0, |
|
111 "function without exposedProps shouldn't have any properties"); |
|
112 is(funcCOW(), 42, "COW without exposedProps should still be callable"); |
|
113 |
|
114 // Test function objects with __exposedProps__ |
|
115 var func = function(x) { return 42; }; |
|
116 func.foo = "foo property"; |
|
117 func.__exposedProps__ = { foo: "r" }; |
|
118 var funcCOWr = getCOW(func); |
|
119 PROPS_TO_TEST.forEach(function(name) { |
|
120 if (name == "foo") { |
|
121 isProp(funcCOWr, name, "foo property", |
|
122 "function with exposedProps"); |
|
123 } |
|
124 else { |
|
125 isPropHidden(funcCOWr, name, "function with exposedProps"); |
|
126 } |
|
127 }); |
|
128 is([name for (name in funcCOWr)].length, 1, |
|
129 "function with exposedProps should only enumerate exposed props"); |
|
130 is([name for (name in funcCOWr)][0], "foo", |
|
131 "function with exposedProps should only enumerate exposed props"); |
|
132 is(funcCOWr(), 42, "COW with exposedProps should be callable"); |
|
133 |
|
134 // Test function objects with __exposedProps__ |
|
135 var func = function(x) { return 42; }; |
|
136 func.foo = "foo property"; |
|
137 func.__exposedProps__ = { foo: "r", bar: "r" }; |
|
138 var funcCOWr2 = getCOW(func); |
|
139 PROPS_TO_TEST.forEach(function(name) { |
|
140 if (name == "foo") { |
|
141 isProp(funcCOWr2, name, "foo property", |
|
142 "function with more exposedProps"); |
|
143 } |
|
144 else { |
|
145 isPropHidden(funcCOWr2, name, "function with more exposedProps"); |
|
146 } |
|
147 }); |
|
148 is([name for (name in funcCOWr2)].length, 1, |
|
149 "function with exposedProps should only enumerate exposed props"); |
|
150 is([name for (name in funcCOWr2)][0], "foo", |
|
151 "function with exposedProps should only enumerate exposed props"); |
|
152 |
|
153 // Test object with empty exposedProps |
|
154 var strict = { __exposedProps__: {}, foo: "foo property" }; |
|
155 var strictCOW = getCOW(strict); |
|
156 PROPS_TO_TEST.forEach(function(name) { |
|
157 isPropHidden(strictCOW, name, "object with empty exposedProps"); |
|
158 }); |
|
159 is([name for (name in strictCOW)].length, 0, |
|
160 "object with empty exposedProps shouldn't have any properties"); |
|
161 |
|
162 // Test object with one exposed property |
|
163 var strict = { __exposedProps__: { foo: "r" }, foo: "foo property" }; |
|
164 var strictCOWr = getCOW(strict); |
|
165 PROPS_TO_TEST.forEach(function(name) { |
|
166 if (name == "foo") { |
|
167 isProp(strictCOWr, name, "foo property", |
|
168 "object with exposed 'foo'"); |
|
169 } |
|
170 else { |
|
171 isPropHidden(strictCOW, name, "object with exposed 'foo'"); |
|
172 } |
|
173 }); |
|
174 is([name for (name in strictCOWr)].length, 1, |
|
175 "object with exposedProps only enumerate exposed props"); |
|
176 is([name for (name in strictCOWr)][0], "foo", |
|
177 "object with exposedProps only enumerate exposed props"); |
|
178 |
|
179 // Test writable property |
|
180 var writable = getCOW({ __exposedProps__: {foo: 'w'}}); |
|
181 try { |
|
182 ok(!("foo" in writable), |
|
183 "non-existing write-only property shouldn't exist"); |
|
184 writable.foo = 5; |
|
185 is(chromeGet(writable, "foo"), 5, "writing to a write-only exposed prop works"); |
|
186 todo("foo" in writable, |
|
187 "existing write-only property should exist"); |
|
188 } catch (e) { |
|
189 ok(false, "writing to a write-only exposed prop shouldn't throw " + e); |
|
190 } |
|
191 try { |
|
192 writable.foo; |
|
193 todo(false, "reading from a write-only exposed prop should throw"); |
|
194 } catch (e) { |
|
195 todo(/Permission denied/.test(e), |
|
196 "reading from a write-only exposed prop should throw"); |
|
197 } |
|
198 try { |
|
199 delete writable.foo; |
|
200 is(chromeGet(writable, "foo"), undefined, |
|
201 "deleting a write-only exposed prop works"); |
|
202 } catch (e) { |
|
203 ok(false, "deleting a write-only exposed prop shouldn't throw " + e); |
|
204 } |
|
205 |
|
206 // Test readable property |
|
207 var readable = { __exposedProps__: {foo: 'r'}, |
|
208 foo: 5, |
|
209 bar: 6 }; |
|
210 try { |
|
211 isProp(getCOW(readable), "foo", 5, |
|
212 "reading from a readable exposed prop works"); |
|
213 } catch (e) { |
|
214 ok(false, "reading from a readable exposed prop shouldn't throw " + e); |
|
215 } |
|
216 try { |
|
217 getCOW(readable).foo = 1; |
|
218 ok(false, "writing to a read-only exposed prop should fail"); |
|
219 } catch (e) { |
|
220 ok(/Permission denied/.test(e), |
|
221 "writing to a read-only exposed prop should fail"); |
|
222 } |
|
223 try { |
|
224 delete getCOW(readable).foo; |
|
225 ok(false, "deleting a read-only exposed prop shouldn't work"); |
|
226 } catch (e) { |
|
227 ok(/Permission denied/.test(e), |
|
228 "deleting a read-only exposed prop should throw error"); |
|
229 } |
|
230 |
|
231 try { |
|
232 var props = [name for (name in getCOW(readable))]; |
|
233 is(props.length, 1, "COW w/ one exposed prop should enumerate once"); |
|
234 is(props[0], 'foo', "COW w/ one exposed prop should enumerate it"); |
|
235 } catch (e) { |
|
236 ok(false, "COW w/ a readable prop should not raise exc " + |
|
237 "on enumeration: " + e); |
|
238 } |
|
239 |
|
240 // Test read/write property |
|
241 var readwrite = getCOW({ __exposedProps__: {foo: 'rw'}}); |
|
242 try { |
|
243 ok(!("foo" in readwrite), |
|
244 "non-existing readwrite property shouldn't exist"); |
|
245 readwrite.foo = 5; |
|
246 is(readwrite.foo, 5, "writing to a readwrite exposed prop looks like it worked"); |
|
247 is(chromeGet(readwrite, "foo"), 5, "writing to a readwrite exposed prop works"); |
|
248 ok("foo" in readwrite, |
|
249 "existing readwrite property should exist"); |
|
250 } catch (e) { |
|
251 ok(false, "writing to a readwrite exposed prop shouldn't throw " + e); |
|
252 } |
|
253 try { |
|
254 delete readwrite.foo; |
|
255 is(readwrite.foo, undefined, "deleting readwrite prop looks like it worked"); |
|
256 ok(!("foo" in readwrite), "deleting readwrite prop looks like it really worked"); |
|
257 is(chromeGet(readwrite, "foo"), undefined, |
|
258 "deleting a readwrite exposed prop works"); |
|
259 } catch (e) { |
|
260 ok(false, "deleting a readwrite exposed prop shouldn't throw " + e); |
|
261 } |
|
262 |
|
263 // Readables and functions |
|
264 try { |
|
265 var COWFunc = getCOW((function() { return 5; })); |
|
266 is(COWFunc(), 5, "COWed functions should be callable"); |
|
267 } catch (e) { |
|
268 todo(false, "COWed functions should not raise " + e); |
|
269 } |
|
270 try { |
|
271 var objWithFunc = {__exposedProps__: {foo: 'r'}, |
|
272 foo: function foo() { return 5; }}; |
|
273 is(getCOW((objWithFunc)).foo(), 5, |
|
274 "Readable function exposed props should be callable"); |
|
275 } catch (e) { |
|
276 ok(false, "Readable function exposed props should be callable" + e); |
|
277 } |
|
278 |
|
279 // Readables with getters |
|
280 var obj = { |
|
281 get prop() { return { __exposedProps__: {}, test: "FAIL" } }, |
|
282 __exposedProps__: {prop: 'r'} |
|
283 }; |
|
284 is(getCOW(obj).prop.test, undefined, "getting prop.test shouldn't return anything"); |
|
285 ok(!("test" in getCOW(obj).prop), "getting prop.test shouldn't return anything"); |
|
286 |
|
287 // Alien objects |
|
288 try { |
|
289 is(alienObject.funProp(1), 2, |
|
290 "COWs wrapping objects from different principals should work"); |
|
291 } catch (e) { |
|
292 ok(false, "COWs wrapping objs from different principals " + |
|
293 "shouldn't throw " + e); |
|
294 } |
|
295 |
|
296 try { |
|
297 is(alienObject.funProp(1), 2, |
|
298 "COWs wrapping objs from different principals should work twice"); |
|
299 } catch (e) { |
|
300 ok(false, "COWs wrapping objs from different principals " + |
|
301 "shouldn't throw on second access but not first: " + e); |
|
302 } |
|
303 } |
|
304 |
|
305 // Decompile the COW test suite, re-evaluate it in the sandbox and execute it. |
|
306 Cu.evalInSandbox('(' + uneval(COWTests) + ')()', sandbox); |
|
307 |
|
308 // Test that COWed objects passing from content to chrome get unwrapped. |
|
309 function returnCOW() { |
|
310 return getCOW({__exposedProps__: {}, |
|
311 bar: 6}); |
|
312 } |
|
313 |
|
314 var unwrapped = Cu.evalInSandbox( |
|
315 '(' + uneval(returnCOW) + ')()', |
|
316 sandbox |
|
317 ); |
|
318 |
|
319 try { |
|
320 is(unwrapped.bar, 6, |
|
321 "COWs should be unwrapped when entering chrome space"); |
|
322 } catch (e) { |
|
323 todo(false, "COWs should be unwrapped when entering chrome space, " + |
|
324 "not raise " + e); |
|
325 } |
|
326 ]]></script> |
|
327 </window> |