|
1 <html xmlns="http://www.w3.org/1999/xhtml"> |
|
2 <!-- |
|
3 https://bugzilla.mozilla.org/show_bug.cgi?id=821850 |
|
4 --> |
|
5 <head> |
|
6 <bindings xmlns="http://www.mozilla.org/xbl"> |
|
7 <binding id="testBinding"> |
|
8 <implementation> |
|
9 <constructor> |
|
10 // Store a property as an expando on the bound element. |
|
11 this._prop = "propVal"; |
|
12 |
|
13 // Wait for both constructors to fire. |
|
14 window.constructorCount = (window.constructorCount + 1) || 1; |
|
15 if (window.constructorCount != 2) |
|
16 return; |
|
17 |
|
18 // Grab some basic infrastructure off the content window. |
|
19 var win = XPCNativeWrapper.unwrap(window); |
|
20 SpecialPowers = win.SpecialPowers; |
|
21 Cu = SpecialPowers.Cu; |
|
22 is = win.is; |
|
23 ok = win.ok; |
|
24 SimpleTest = win.SimpleTest; |
|
25 |
|
26 // Stick some expandos on the content window. |
|
27 window.xrayExpando = 3; |
|
28 win.primitiveExpando = 11; |
|
29 win.stringExpando = "stringExpando"; |
|
30 win.objectExpando = { foo: 12 }; |
|
31 win.globalExpando = SpecialPowers.unwrap(Cu.getGlobalForObject({})); |
|
32 win.functionExpando = function() { return "called" }; |
|
33 win.functionExpando.prop = 2; |
|
34 |
|
35 // Make sure we're Xraying. |
|
36 ok(Cu.isXrayWrapper(window), "Window is Xrayed"); |
|
37 ok(Cu.isXrayWrapper(document), "Document is Xrayed"); |
|
38 |
|
39 var bound = document.getElementById('bound'); |
|
40 ok(bound, "bound is non-null"); |
|
41 is(bound.method('baz'), "method:baz", "Xray methods work"); |
|
42 is(bound.prop, "propVal", "Property Xrays work"); |
|
43 is(bound.primitiveField, undefined, "Xrays don't show fields"); |
|
44 is(bound.wrappedJSObject.primitiveField, 2, "Waiving Xrays show fields"); |
|
45 |
|
46 // Check exposure behavior. |
|
47 is(typeof bound.unexposedMethod, 'function', |
|
48 "Unexposed method should be visible to XBL"); |
|
49 is(typeof bound.wrappedJSObject.unexposedMethod, 'undefined', |
|
50 "Unexposed method should not be defined in content"); |
|
51 is(typeof bound.unexposedProperty, 'number', |
|
52 "Unexposed property should be visible to XBL"); |
|
53 is(typeof bound.wrappedJSObject.unexposedProperty, 'undefined', |
|
54 "Unexposed property should not be defined in content"); |
|
55 |
|
56 // Check that here HTMLImageElement.QueryInterface works |
|
57 var img = document.querySelector("img"); |
|
58 ok("QueryInterface" in img, |
|
59 "Should have a img.QueryInterface here"); |
|
60 is(img.QueryInterface(Components.interfaces.nsIImageLoadingContent), |
|
61 img, "Should be able to QI the image"); |
|
62 |
|
63 // Make sure standard constructors work right in the presence of |
|
64 // sandboxPrototype and Xray-resolved constructors. |
|
65 is(window.Function, XPCNativeWrapper(window.wrappedJSObject.Function), |
|
66 "window.Function comes from the window, not the global"); |
|
67 ok(Function != window.Function, "Function constructors are distinct"); |
|
68 is(Object.getPrototypeOf(Function.prototype), Object.getPrototypeOf({foo: 42}), |
|
69 "Function constructor is local"); |
|
70 |
|
71 // This gets invoked by an event handler. |
|
72 window.finish = function() { |
|
73 // Content messed with stuff. Make sure we still see the right thing. |
|
74 is(bound.method('bay'), "method:bay", "Xray methods work"); |
|
75 is(bound.wrappedJSObject.method('bay'), "hah", "Xray waived methods work"); |
|
76 is(bound.prop, "set:someOtherVal", "Xray props work"); |
|
77 is(bound.wrappedJSObject.prop, "redefined", "Xray waived props work"); |
|
78 is(bound.wrappedJSObject.primitiveField, 321, "Can't do anything about redefined fields"); |
|
79 |
|
80 SimpleTest.finish(); |
|
81 } |
|
82 |
|
83 // Hand things off to content. Content will call us back. |
|
84 win.go(); |
|
85 </constructor> |
|
86 <field name="primitiveField">2</field> |
|
87 <method name="unexposedMethod"><body></body></method> |
|
88 <property name="unexposedProperty" onget="return 2;" readonly="true"></property> |
|
89 <method name="method" exposeToUntrustedContent="true"> |
|
90 <parameter name="arg" /> |
|
91 <body> |
|
92 return "method:" + arg; |
|
93 </body> |
|
94 </method> |
|
95 <method name="passMeAJSObject" exposeToUntrustedContent="true"> |
|
96 <parameter name="arg" /> |
|
97 <body> |
|
98 is(typeof arg.prop, 'undefined', "No properties"); |
|
99 is(Object.getOwnPropertyNames(arg).length, 0, "Should have no own properties"); |
|
100 try { |
|
101 arg.foo = 2; |
|
102 ok(true, "Stuff fails silently"); |
|
103 } catch (e) { |
|
104 ok(false, "Stuff should fail silently"); |
|
105 } |
|
106 is(typeof arg.foo, 'undefined', "Shouldn't place props"); |
|
107 </body> |
|
108 </method> |
|
109 <property name="prop" exposeToUntrustedContent="true"> |
|
110 <getter>return this._prop;</getter> |
|
111 <setter>this._prop = "set:" + val;</setter> |
|
112 </property> |
|
113 </implementation> |
|
114 <handlers> |
|
115 <handler event="testevent" action="ok(true, 'called event handler'); finish();" allowuntrusted="true"/> |
|
116 <handler event="testtrusted" action="ok(true, 'called trusted handler'); window.wrappedJSObject.triggeredTrustedHandler = true;"/> |
|
117 <handler event="keyup" action="ok(true, 'called untrusted key handler'); window.wrappedJSObject.triggeredUntrustedKeyHandler = true;" allowuntrusted="true"/> |
|
118 <handler event="keydown" action="ok(true, 'called trusted key handler'); window.wrappedJSObject.triggeredTrustedKeyHandler = true;"/> |
|
119 </handlers> |
|
120 </binding> |
|
121 </bindings> |
|
122 <script type="application/javascript"> |
|
123 <![CDATA[ |
|
124 |
|
125 ok = parent.ok; |
|
126 is = parent.is; |
|
127 SimpleTest = parent.SimpleTest; |
|
128 SpecialPowers = parent.SpecialPowers; |
|
129 |
|
130 // Test the Xray waiving behavior when accessing fields. We should be able to |
|
131 // see sequential JS-implemented properties, but should regain Xrays when we |
|
132 // hit a native property. |
|
133 window.contentVal = { foo: 10, rabbit: { hole: { bar: 100, win: window} } }; |
|
134 ok(true, "Set contentVal"); |
|
135 |
|
136 // Check that we're not exposing QueryInterface to non-XBL code |
|
137 ok(!("QueryInterface" in document), |
|
138 "Should not have a document.QueryInterface here"); |
|
139 |
|
140 function go() { |
|
141 "use strict"; |
|
142 |
|
143 // Test what we can and cannot access in the XBL scope. |
|
144 is(typeof window.xrayExpando, "undefined", "Xray expandos are private to the caller"); |
|
145 is(window.primitiveExpando, 11, "Can see waived expandos"); |
|
146 is(window.stringExpando, "stringExpando", "Can see waived expandos"); |
|
147 is(typeof window.objectExpando, "object", "object expando exists"); |
|
148 checkThrows(function() window.objectExpando.foo); |
|
149 is(SpecialPowers.wrap(window.objectExpando).foo, 12, "SpecialPowers sees the right thing"); |
|
150 is(typeof window.globalExpando, "object", "Can see global object"); |
|
151 checkThrows(function() window.globalExpando.win); |
|
152 is(window.functionExpando(), "called", "XBL functions are callable"); |
|
153 checkThrows(function() window.functionExpando.prop); |
|
154 |
|
155 // Inspect the bound element. |
|
156 var bound = document.getElementById('bound'); |
|
157 is(bound.primitiveField, 2, "Can see primitive fields"); |
|
158 is(bound.method("foo"), "method:foo", "Can invoke XBL method from content"); |
|
159 is(bound.prop, "propVal", "Can access properties from content"); |
|
160 bound.prop = "someOtherVal"; |
|
161 is(bound.prop, "set:someOtherVal", "Can set properties from content"); |
|
162 |
|
163 // Make sure we can't pass JS objects to the XBL scope. |
|
164 var proto = bound.__proto__; |
|
165 proto.passMeAJSObject({prop: 2}); |
|
166 |
|
167 // |
|
168 // Try sticking a bunch of stuff on the prototype object. |
|
169 // |
|
170 |
|
171 proto.someExpando = 201; |
|
172 is(bound.someExpando, 201, "Can stick non-XBL properties on the proto"); |
|
173 |
|
174 // Previously, this code checked that content couldn't tamper with its XBL |
|
175 // prototype. But we decided to allow this to reduce regression risk, so for |
|
176 // now just check that this works. |
|
177 function checkMayTamper(obj, propName, desc) { |
|
178 var accessor = !('value' in Object.getOwnPropertyDescriptor(obj, propName)); |
|
179 if (!accessor) |
|
180 checkAllowed(function() { obj[propName] = function() {} }, desc + ": assign"); |
|
181 checkAllowed(function() { Object.defineProperty(obj, propName, {configurable: true, value: 3}) }, desc + ": define with value"); |
|
182 checkAllowed(function() { Object.defineProperty(obj, propName, {configurable: true, writable: true}) }, desc + ": make writable"); |
|
183 checkAllowed(function() { Object.defineProperty(obj, propName, {configurable: true}) }, desc + ": make configurable"); |
|
184 checkAllowed(function() { Object.defineProperty(obj, propName, {configurable: true, get: function() {}}) }, desc + ": define with getter"); |
|
185 checkAllowed(function() { Object.defineProperty(obj, propName, {configurable: true, set: function() {}}) }, desc + ": define with setter"); |
|
186 |
|
187 // Windows are implemented as proxies, and Proxy::delete_ doesn't currently |
|
188 // pass strict around. Work around it in the window.binding case by just |
|
189 // checking if delete returns false. |
|
190 // manually. |
|
191 checkAllowed(function() { delete obj[propName]; }, desc + ": delete"); |
|
192 |
|
193 if (!accessor) |
|
194 checkAllowed(function() { obj[propName] = function() {} }, desc + ": assign (again)"); |
|
195 } |
|
196 |
|
197 // Make sure content can do whatever it wants with the prototype. |
|
198 checkMayTamper(proto, 'method', "XBL Proto Method"); |
|
199 checkMayTamper(proto, 'prop', "XBL Proto Prop"); |
|
200 checkMayTamper(proto, 'primitiveField', "XBL Field Accessor"); |
|
201 |
|
202 // Tamper with the derived object. This doesn't affect the XBL scope thanks |
|
203 // to Xrays. |
|
204 bound.method = function() { return "heh"; }; |
|
205 Object.defineProperty(bound, 'method', {value: function() { return "hah" }}); |
|
206 Object.defineProperty(bound, 'prop', {value: "redefined"}); |
|
207 bound.primitiveField = 321; |
|
208 |
|
209 // We need a chrome window to create trusted events. This isn't really doable |
|
210 // in child processes, so let's just skip if that's the case. |
|
211 if (SpecialPowers.isMainProcess()) { |
|
212 var Ci = SpecialPowers.Ci; |
|
213 var chromeWin = SpecialPowers.wrap(window.top) |
|
214 .QueryInterface(Ci.nsIInterfaceRequestor) |
|
215 .getInterface(Ci.nsIWebNavigation) |
|
216 .QueryInterface(Ci.nsIDocShell) |
|
217 .chromeEventHandler.ownerDocument.defaultView; |
|
218 |
|
219 // Untrusted events should not trigger event handlers without |
|
220 // exposeToUntrustedContent=true. |
|
221 window.triggeredTrustedHandler = false; |
|
222 var untrustedEvent = new CustomEvent('testtrusted'); |
|
223 ok(!untrustedEvent.isTrusted, "Created an untrusted event"); |
|
224 is(untrustedEvent.type, 'testtrusted', "Constructor should see type"); |
|
225 bound.dispatchEvent(untrustedEvent); |
|
226 ok(!window.triggeredTrustedHandler, "untrusted events should not trigger trusted handler"); |
|
227 var trustedEvent = new chromeWin.CustomEvent('testtrusted'); |
|
228 ok(trustedEvent.isTrusted, "Created a trusted event"); |
|
229 is(trustedEvent.type, 'testtrusted', "Wrapped constructor should see type"); |
|
230 SpecialPowers.wrap(bound).dispatchEvent(trustedEvent); |
|
231 ok(window.triggeredTrustedHandler, "trusted events should trigger trusted handler"); |
|
232 |
|
233 // |
|
234 // We check key events as well, since they're implemented differently. |
|
235 // |
|
236 // NB: We don't check isTrusted on the events we create here, because |
|
237 // according to smaug, old-style event initialization doesn't mark the |
|
238 // event as trusted until it's dispatched. |
|
239 // |
|
240 |
|
241 window.triggeredUntrustedKeyHandler = false; |
|
242 window.triggeredTrustedKeyHandler = false; |
|
243 |
|
244 // Untrusted event, permissive handler. |
|
245 var untrustedKeyEvent = document.createEvent('KeyboardEvent'); |
|
246 untrustedKeyEvent.initEvent('keyup', true, true); |
|
247 bound.dispatchEvent(untrustedKeyEvent); |
|
248 ok(window.triggeredUntrustedKeyHandler, "untrusted key events should trigger untrusted handler"); |
|
249 |
|
250 // Untrusted event, strict handler. |
|
251 var fakeTrustedKeyEvent = document.createEvent('KeyboardEvent'); |
|
252 fakeTrustedKeyEvent.initEvent('keydown', true, true); |
|
253 bound.dispatchEvent(fakeTrustedKeyEvent); |
|
254 ok(!window.triggeredTrustedKeyHandler, "untrusted key events should not trigger trusted handler"); |
|
255 |
|
256 // Trusted event, strict handler. |
|
257 var trustedKeyEvent = chromeWin.document.createEvent('KeyboardEvent'); |
|
258 trustedKeyEvent.initEvent('keydown', true, true); |
|
259 SpecialPowers.wrap(bound).dispatchEvent(trustedKeyEvent); |
|
260 ok(window.triggeredTrustedKeyHandler, "trusted key events should trigger trusted handler"); |
|
261 } |
|
262 |
|
263 // Hand control back to the XBL scope by dispatching an event on the bound element. |
|
264 bound.dispatchEvent(new CustomEvent('testevent')); |
|
265 } |
|
266 |
|
267 function checkThrows(fn) { |
|
268 try { fn(); ok(false, "Should have thrown"); } |
|
269 catch (e) { ok(!!/denied|insecure/.exec(e), "Should have thrown security exception: " + e); } |
|
270 } |
|
271 |
|
272 function checkAllowed(fn, desc) { |
|
273 try { fn(); ok(true, desc + ": Didn't throw"); } |
|
274 catch (e) { ok(false, desc + ": Threw: " + e); } |
|
275 } |
|
276 |
|
277 function setup() { |
|
278 // When the bindings are applied, the constructor will be invoked and the |
|
279 // test will continue. |
|
280 document.getElementById('bound').style.MozBinding = 'url(#testBinding)'; |
|
281 document.getElementById('bound2').style.MozBinding = 'url(#testBinding)'; |
|
282 } |
|
283 |
|
284 ]]> |
|
285 </script> |
|
286 </head> |
|
287 <body onload="setup()"> |
|
288 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=821850">Mozilla Bug 821850</a> |
|
289 <p id="display"></p> |
|
290 <div id="content"> |
|
291 <div id="bound">Bound element</div> |
|
292 <div id="bound2">Bound element</div> |
|
293 <img/> |
|
294 </div> |
|
295 <pre id="test"> |
|
296 </pre> |
|
297 </body> |
|
298 </html> |