1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/xbl/test/file_bug821850.xhtml Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,298 @@ 1.4 +<html xmlns="http://www.w3.org/1999/xhtml"> 1.5 +<!-- 1.6 +https://bugzilla.mozilla.org/show_bug.cgi?id=821850 1.7 +--> 1.8 +<head> 1.9 + <bindings xmlns="http://www.mozilla.org/xbl"> 1.10 + <binding id="testBinding"> 1.11 + <implementation> 1.12 + <constructor> 1.13 + // Store a property as an expando on the bound element. 1.14 + this._prop = "propVal"; 1.15 + 1.16 + // Wait for both constructors to fire. 1.17 + window.constructorCount = (window.constructorCount + 1) || 1; 1.18 + if (window.constructorCount != 2) 1.19 + return; 1.20 + 1.21 + // Grab some basic infrastructure off the content window. 1.22 + var win = XPCNativeWrapper.unwrap(window); 1.23 + SpecialPowers = win.SpecialPowers; 1.24 + Cu = SpecialPowers.Cu; 1.25 + is = win.is; 1.26 + ok = win.ok; 1.27 + SimpleTest = win.SimpleTest; 1.28 + 1.29 + // Stick some expandos on the content window. 1.30 + window.xrayExpando = 3; 1.31 + win.primitiveExpando = 11; 1.32 + win.stringExpando = "stringExpando"; 1.33 + win.objectExpando = { foo: 12 }; 1.34 + win.globalExpando = SpecialPowers.unwrap(Cu.getGlobalForObject({})); 1.35 + win.functionExpando = function() { return "called" }; 1.36 + win.functionExpando.prop = 2; 1.37 + 1.38 + // Make sure we're Xraying. 1.39 + ok(Cu.isXrayWrapper(window), "Window is Xrayed"); 1.40 + ok(Cu.isXrayWrapper(document), "Document is Xrayed"); 1.41 + 1.42 + var bound = document.getElementById('bound'); 1.43 + ok(bound, "bound is non-null"); 1.44 + is(bound.method('baz'), "method:baz", "Xray methods work"); 1.45 + is(bound.prop, "propVal", "Property Xrays work"); 1.46 + is(bound.primitiveField, undefined, "Xrays don't show fields"); 1.47 + is(bound.wrappedJSObject.primitiveField, 2, "Waiving Xrays show fields"); 1.48 + 1.49 + // Check exposure behavior. 1.50 + is(typeof bound.unexposedMethod, 'function', 1.51 + "Unexposed method should be visible to XBL"); 1.52 + is(typeof bound.wrappedJSObject.unexposedMethod, 'undefined', 1.53 + "Unexposed method should not be defined in content"); 1.54 + is(typeof bound.unexposedProperty, 'number', 1.55 + "Unexposed property should be visible to XBL"); 1.56 + is(typeof bound.wrappedJSObject.unexposedProperty, 'undefined', 1.57 + "Unexposed property should not be defined in content"); 1.58 + 1.59 + // Check that here HTMLImageElement.QueryInterface works 1.60 + var img = document.querySelector("img"); 1.61 + ok("QueryInterface" in img, 1.62 + "Should have a img.QueryInterface here"); 1.63 + is(img.QueryInterface(Components.interfaces.nsIImageLoadingContent), 1.64 + img, "Should be able to QI the image"); 1.65 + 1.66 + // Make sure standard constructors work right in the presence of 1.67 + // sandboxPrototype and Xray-resolved constructors. 1.68 + is(window.Function, XPCNativeWrapper(window.wrappedJSObject.Function), 1.69 + "window.Function comes from the window, not the global"); 1.70 + ok(Function != window.Function, "Function constructors are distinct"); 1.71 + is(Object.getPrototypeOf(Function.prototype), Object.getPrototypeOf({foo: 42}), 1.72 + "Function constructor is local"); 1.73 + 1.74 + // This gets invoked by an event handler. 1.75 + window.finish = function() { 1.76 + // Content messed with stuff. Make sure we still see the right thing. 1.77 + is(bound.method('bay'), "method:bay", "Xray methods work"); 1.78 + is(bound.wrappedJSObject.method('bay'), "hah", "Xray waived methods work"); 1.79 + is(bound.prop, "set:someOtherVal", "Xray props work"); 1.80 + is(bound.wrappedJSObject.prop, "redefined", "Xray waived props work"); 1.81 + is(bound.wrappedJSObject.primitiveField, 321, "Can't do anything about redefined fields"); 1.82 + 1.83 + SimpleTest.finish(); 1.84 + } 1.85 + 1.86 + // Hand things off to content. Content will call us back. 1.87 + win.go(); 1.88 + </constructor> 1.89 + <field name="primitiveField">2</field> 1.90 + <method name="unexposedMethod"><body></body></method> 1.91 + <property name="unexposedProperty" onget="return 2;" readonly="true"></property> 1.92 + <method name="method" exposeToUntrustedContent="true"> 1.93 + <parameter name="arg" /> 1.94 + <body> 1.95 + return "method:" + arg; 1.96 + </body> 1.97 + </method> 1.98 + <method name="passMeAJSObject" exposeToUntrustedContent="true"> 1.99 + <parameter name="arg" /> 1.100 + <body> 1.101 + is(typeof arg.prop, 'undefined', "No properties"); 1.102 + is(Object.getOwnPropertyNames(arg).length, 0, "Should have no own properties"); 1.103 + try { 1.104 + arg.foo = 2; 1.105 + ok(true, "Stuff fails silently"); 1.106 + } catch (e) { 1.107 + ok(false, "Stuff should fail silently"); 1.108 + } 1.109 + is(typeof arg.foo, 'undefined', "Shouldn't place props"); 1.110 + </body> 1.111 + </method> 1.112 + <property name="prop" exposeToUntrustedContent="true"> 1.113 + <getter>return this._prop;</getter> 1.114 + <setter>this._prop = "set:" + val;</setter> 1.115 + </property> 1.116 + </implementation> 1.117 + <handlers> 1.118 + <handler event="testevent" action="ok(true, 'called event handler'); finish();" allowuntrusted="true"/> 1.119 + <handler event="testtrusted" action="ok(true, 'called trusted handler'); window.wrappedJSObject.triggeredTrustedHandler = true;"/> 1.120 + <handler event="keyup" action="ok(true, 'called untrusted key handler'); window.wrappedJSObject.triggeredUntrustedKeyHandler = true;" allowuntrusted="true"/> 1.121 + <handler event="keydown" action="ok(true, 'called trusted key handler'); window.wrappedJSObject.triggeredTrustedKeyHandler = true;"/> 1.122 + </handlers> 1.123 + </binding> 1.124 + </bindings> 1.125 + <script type="application/javascript"> 1.126 + <![CDATA[ 1.127 + 1.128 + ok = parent.ok; 1.129 + is = parent.is; 1.130 + SimpleTest = parent.SimpleTest; 1.131 + SpecialPowers = parent.SpecialPowers; 1.132 + 1.133 + // Test the Xray waiving behavior when accessing fields. We should be able to 1.134 + // see sequential JS-implemented properties, but should regain Xrays when we 1.135 + // hit a native property. 1.136 + window.contentVal = { foo: 10, rabbit: { hole: { bar: 100, win: window} } }; 1.137 + ok(true, "Set contentVal"); 1.138 + 1.139 + // Check that we're not exposing QueryInterface to non-XBL code 1.140 + ok(!("QueryInterface" in document), 1.141 + "Should not have a document.QueryInterface here"); 1.142 + 1.143 + function go() { 1.144 + "use strict"; 1.145 + 1.146 + // Test what we can and cannot access in the XBL scope. 1.147 + is(typeof window.xrayExpando, "undefined", "Xray expandos are private to the caller"); 1.148 + is(window.primitiveExpando, 11, "Can see waived expandos"); 1.149 + is(window.stringExpando, "stringExpando", "Can see waived expandos"); 1.150 + is(typeof window.objectExpando, "object", "object expando exists"); 1.151 + checkThrows(function() window.objectExpando.foo); 1.152 + is(SpecialPowers.wrap(window.objectExpando).foo, 12, "SpecialPowers sees the right thing"); 1.153 + is(typeof window.globalExpando, "object", "Can see global object"); 1.154 + checkThrows(function() window.globalExpando.win); 1.155 + is(window.functionExpando(), "called", "XBL functions are callable"); 1.156 + checkThrows(function() window.functionExpando.prop); 1.157 + 1.158 + // Inspect the bound element. 1.159 + var bound = document.getElementById('bound'); 1.160 + is(bound.primitiveField, 2, "Can see primitive fields"); 1.161 + is(bound.method("foo"), "method:foo", "Can invoke XBL method from content"); 1.162 + is(bound.prop, "propVal", "Can access properties from content"); 1.163 + bound.prop = "someOtherVal"; 1.164 + is(bound.prop, "set:someOtherVal", "Can set properties from content"); 1.165 + 1.166 + // Make sure we can't pass JS objects to the XBL scope. 1.167 + var proto = bound.__proto__; 1.168 + proto.passMeAJSObject({prop: 2}); 1.169 + 1.170 + // 1.171 + // Try sticking a bunch of stuff on the prototype object. 1.172 + // 1.173 + 1.174 + proto.someExpando = 201; 1.175 + is(bound.someExpando, 201, "Can stick non-XBL properties on the proto"); 1.176 + 1.177 + // Previously, this code checked that content couldn't tamper with its XBL 1.178 + // prototype. But we decided to allow this to reduce regression risk, so for 1.179 + // now just check that this works. 1.180 + function checkMayTamper(obj, propName, desc) { 1.181 + var accessor = !('value' in Object.getOwnPropertyDescriptor(obj, propName)); 1.182 + if (!accessor) 1.183 + checkAllowed(function() { obj[propName] = function() {} }, desc + ": assign"); 1.184 + checkAllowed(function() { Object.defineProperty(obj, propName, {configurable: true, value: 3}) }, desc + ": define with value"); 1.185 + checkAllowed(function() { Object.defineProperty(obj, propName, {configurable: true, writable: true}) }, desc + ": make writable"); 1.186 + checkAllowed(function() { Object.defineProperty(obj, propName, {configurable: true}) }, desc + ": make configurable"); 1.187 + checkAllowed(function() { Object.defineProperty(obj, propName, {configurable: true, get: function() {}}) }, desc + ": define with getter"); 1.188 + checkAllowed(function() { Object.defineProperty(obj, propName, {configurable: true, set: function() {}}) }, desc + ": define with setter"); 1.189 + 1.190 + // Windows are implemented as proxies, and Proxy::delete_ doesn't currently 1.191 + // pass strict around. Work around it in the window.binding case by just 1.192 + // checking if delete returns false. 1.193 + // manually. 1.194 + checkAllowed(function() { delete obj[propName]; }, desc + ": delete"); 1.195 + 1.196 + if (!accessor) 1.197 + checkAllowed(function() { obj[propName] = function() {} }, desc + ": assign (again)"); 1.198 + } 1.199 + 1.200 + // Make sure content can do whatever it wants with the prototype. 1.201 + checkMayTamper(proto, 'method', "XBL Proto Method"); 1.202 + checkMayTamper(proto, 'prop', "XBL Proto Prop"); 1.203 + checkMayTamper(proto, 'primitiveField', "XBL Field Accessor"); 1.204 + 1.205 + // Tamper with the derived object. This doesn't affect the XBL scope thanks 1.206 + // to Xrays. 1.207 + bound.method = function() { return "heh"; }; 1.208 + Object.defineProperty(bound, 'method', {value: function() { return "hah" }}); 1.209 + Object.defineProperty(bound, 'prop', {value: "redefined"}); 1.210 + bound.primitiveField = 321; 1.211 + 1.212 + // We need a chrome window to create trusted events. This isn't really doable 1.213 + // in child processes, so let's just skip if that's the case. 1.214 + if (SpecialPowers.isMainProcess()) { 1.215 + var Ci = SpecialPowers.Ci; 1.216 + var chromeWin = SpecialPowers.wrap(window.top) 1.217 + .QueryInterface(Ci.nsIInterfaceRequestor) 1.218 + .getInterface(Ci.nsIWebNavigation) 1.219 + .QueryInterface(Ci.nsIDocShell) 1.220 + .chromeEventHandler.ownerDocument.defaultView; 1.221 + 1.222 + // Untrusted events should not trigger event handlers without 1.223 + // exposeToUntrustedContent=true. 1.224 + window.triggeredTrustedHandler = false; 1.225 + var untrustedEvent = new CustomEvent('testtrusted'); 1.226 + ok(!untrustedEvent.isTrusted, "Created an untrusted event"); 1.227 + is(untrustedEvent.type, 'testtrusted', "Constructor should see type"); 1.228 + bound.dispatchEvent(untrustedEvent); 1.229 + ok(!window.triggeredTrustedHandler, "untrusted events should not trigger trusted handler"); 1.230 + var trustedEvent = new chromeWin.CustomEvent('testtrusted'); 1.231 + ok(trustedEvent.isTrusted, "Created a trusted event"); 1.232 + is(trustedEvent.type, 'testtrusted', "Wrapped constructor should see type"); 1.233 + SpecialPowers.wrap(bound).dispatchEvent(trustedEvent); 1.234 + ok(window.triggeredTrustedHandler, "trusted events should trigger trusted handler"); 1.235 + 1.236 + // 1.237 + // We check key events as well, since they're implemented differently. 1.238 + // 1.239 + // NB: We don't check isTrusted on the events we create here, because 1.240 + // according to smaug, old-style event initialization doesn't mark the 1.241 + // event as trusted until it's dispatched. 1.242 + // 1.243 + 1.244 + window.triggeredUntrustedKeyHandler = false; 1.245 + window.triggeredTrustedKeyHandler = false; 1.246 + 1.247 + // Untrusted event, permissive handler. 1.248 + var untrustedKeyEvent = document.createEvent('KeyboardEvent'); 1.249 + untrustedKeyEvent.initEvent('keyup', true, true); 1.250 + bound.dispatchEvent(untrustedKeyEvent); 1.251 + ok(window.triggeredUntrustedKeyHandler, "untrusted key events should trigger untrusted handler"); 1.252 + 1.253 + // Untrusted event, strict handler. 1.254 + var fakeTrustedKeyEvent = document.createEvent('KeyboardEvent'); 1.255 + fakeTrustedKeyEvent.initEvent('keydown', true, true); 1.256 + bound.dispatchEvent(fakeTrustedKeyEvent); 1.257 + ok(!window.triggeredTrustedKeyHandler, "untrusted key events should not trigger trusted handler"); 1.258 + 1.259 + // Trusted event, strict handler. 1.260 + var trustedKeyEvent = chromeWin.document.createEvent('KeyboardEvent'); 1.261 + trustedKeyEvent.initEvent('keydown', true, true); 1.262 + SpecialPowers.wrap(bound).dispatchEvent(trustedKeyEvent); 1.263 + ok(window.triggeredTrustedKeyHandler, "trusted key events should trigger trusted handler"); 1.264 + } 1.265 + 1.266 + // Hand control back to the XBL scope by dispatching an event on the bound element. 1.267 + bound.dispatchEvent(new CustomEvent('testevent')); 1.268 + } 1.269 + 1.270 + function checkThrows(fn) { 1.271 + try { fn(); ok(false, "Should have thrown"); } 1.272 + catch (e) { ok(!!/denied|insecure/.exec(e), "Should have thrown security exception: " + e); } 1.273 + } 1.274 + 1.275 + function checkAllowed(fn, desc) { 1.276 + try { fn(); ok(true, desc + ": Didn't throw"); } 1.277 + catch (e) { ok(false, desc + ": Threw: " + e); } 1.278 + } 1.279 + 1.280 + function setup() { 1.281 + // When the bindings are applied, the constructor will be invoked and the 1.282 + // test will continue. 1.283 + document.getElementById('bound').style.MozBinding = 'url(#testBinding)'; 1.284 + document.getElementById('bound2').style.MozBinding = 'url(#testBinding)'; 1.285 + } 1.286 + 1.287 + ]]> 1.288 +</script> 1.289 +</head> 1.290 +<body onload="setup()"> 1.291 +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=821850">Mozilla Bug 821850</a> 1.292 +<p id="display"></p> 1.293 +<div id="content"> 1.294 + <div id="bound">Bound element</div> 1.295 + <div id="bound2">Bound element</div> 1.296 + <img/> 1.297 +</div> 1.298 +<pre id="test"> 1.299 +</pre> 1.300 +</body> 1.301 +</html>