diff options
Diffstat (limited to 'dom/xbl/test/file_bug821850.xhtml')
-rw-r--r-- | dom/xbl/test/file_bug821850.xhtml | 299 |
1 files changed, 299 insertions, 0 deletions
diff --git a/dom/xbl/test/file_bug821850.xhtml b/dom/xbl/test/file_bug821850.xhtml new file mode 100644 index 0000000000..0d68a908f8 --- /dev/null +++ b/dom/xbl/test/file_bug821850.xhtml @@ -0,0 +1,299 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=821850 +--> +<head> + <bindings xmlns="http://www.mozilla.org/xbl"> + <binding id="testBinding"> + <implementation> + <constructor> + // Store a property as an expando on the bound element. + this._prop = "propVal"; + + // Wait for both constructors to fire. + window.constructorCount = (window.constructorCount + 1) || 1; + if (window.constructorCount != 3) + return; + + // Grab some basic infrastructure off the content window. + var win = XPCNativeWrapper.unwrap(window); + SpecialPowers = win.SpecialPowers; + Cu = SpecialPowers.Cu; + is = win.is; + ok = win.ok; + SimpleTest = win.SimpleTest; + + // Stick some expandos on the content window. + window.xrayExpando = 3; + win.primitiveExpando = 11; + win.stringExpando = "stringExpando"; + win.objectExpando = { foo: 12 }; + win.globalExpando = SpecialPowers.unwrap(Cu.getGlobalForObject({})); + win.functionExpando = function() { return "called" }; + win.functionExpando.prop = 2; + + // Make sure we're Xraying. + ok(Cu.isXrayWrapper(window), "Window is Xrayed"); + ok(Cu.isXrayWrapper(document), "Document is Xrayed"); + + var bound = document.getElementById('bound'); + var bound2 = document.getElementById('bound2'); + var bound3 = document.getElementById('bound3'); + ok(bound, "bound is non-null"); + is(bound.method('baz'), "method:baz", "Xray methods work"); + is(bound.prop, "propVal", "Property Xrays work"); + is(bound.primitiveField, undefined, "Xrays don't show fields"); + is(bound.wrappedJSObject.primitiveField, 2, "Waiving Xrays show fields"); + + // Check exposure behavior. + is(typeof bound.unexposedMethod, 'function', + "Unexposed method should be visible to XBL"); + is(typeof bound.wrappedJSObject.unexposedMethod, 'undefined', + "Unexposed method should not be defined in content"); + is(typeof bound.unexposedProperty, 'number', + "Unexposed property should be visible to XBL"); + is(typeof bound.wrappedJSObject.unexposedProperty, 'undefined', + "Unexposed property should not be defined in content"); + + // Check that here HTMLImageElement.QueryInterface works + var img = document.querySelector("img"); + ok("QueryInterface" in img, + "Should have a img.QueryInterface here"); + is(img.QueryInterface(Components.interfaces.nsIImageLoadingContent), + img, "Should be able to QI the image"); + + // Make sure standard constructors work right in the presence of + // sandboxPrototype and Xray-resolved constructors. + is(window.Function, XPCNativeWrapper(window.wrappedJSObject.Function), + "window.Function comes from the window, not the global"); + ok(Function != window.Function, "Function constructors are distinct"); + is(Object.getPrototypeOf(Function.prototype), Object.getPrototypeOf({foo: 42}), + "Function constructor is local"); + + ok(Object.getPrototypeOf(bound.wrappedJSObject) == Object.getPrototypeOf(bound2.wrappedJSObject), + "Div and div should have the same content-side prototype"); + ok(Object.getPrototypeOf(bound.wrappedJSObject) != Object.getPrototypeOf(bound3.wrappedJSObject), + "Div and span should have different content-side prototypes"); + ok(bound.wrappedJSObject.method == bound3.wrappedJSObject.method, + "Methods should be shared"); + + // This gets invoked by an event handler. + window.finish = function() { + // Content messed with stuff. Make sure we still see the right thing. + is(bound.method('bay'), "method:bay", "Xray methods work"); + is(bound.wrappedJSObject.method('bay'), "hah", "Xray waived methods work"); + is(bound.prop, "set:someOtherVal", "Xray props work"); + is(bound.wrappedJSObject.prop, "redefined", "Xray waived props work"); + is(bound.wrappedJSObject.primitiveField, 321, "Can't do anything about redefined fields"); + + SimpleTest.finish(); + } + + // Hand things off to content. Content will call us back. + win.go(); + </constructor> + <field name="primitiveField">2</field> + <method name="unexposedMethod"><body></body></method> + <property name="unexposedProperty" onget="return 2;" readonly="true"></property> + <method name="method" exposeToUntrustedContent="true"> + <parameter name="arg" /> + <body> + return "method:" + arg; + </body> + </method> + <method name="passMeAJSObject" exposeToUntrustedContent="true"> + <parameter name="arg" /> + <body> + is(typeof arg.prop, 'undefined', "No properties"); + is(arg.wrappedJSObject.prop, 2, "Underlying object has properties"); + is(Object.getOwnPropertyNames(arg).length, 0, "Should have no own properties"); + is(Object.getOwnPropertyNames(arg.wrappedJSObject).length, 1, "Underlying object has properties"); + arg.foo = 2; + is(arg.foo, 2, "Should place expandos"); + is(typeof arg.wrappedJSObject.foo, 'undefined', "Expandos should be invisible to content"); + </body> + </method> + <property name="prop" exposeToUntrustedContent="true"> + <getter>return this._prop;</getter> + <setter>this._prop = "set:" + val;</setter> + </property> + </implementation> + <handlers> + <handler event="testevent" action="ok(true, 'called event handler'); finish();" allowuntrusted="true"/> + <handler event="testtrusted" action="ok(true, 'called trusted handler'); window.wrappedJSObject.triggeredTrustedHandler = true;"/> + <handler event="keyup" action="ok(true, 'called untrusted key handler'); window.wrappedJSObject.triggeredUntrustedKeyHandler = true;" allowuntrusted="true"/> + <handler event="keydown" action="ok(true, 'called trusted key handler'); window.wrappedJSObject.triggeredTrustedKeyHandler = true;"/> + </handlers> + </binding> + </bindings> + <script type="application/javascript"> + <![CDATA[ + + ok = parent.ok; + is = parent.is; + SimpleTest = parent.SimpleTest; + SpecialPowers = parent.SpecialPowers; + + // Test the Xray waiving behavior when accessing fields. We should be able to + // see sequential JS-implemented properties, but should regain Xrays when we + // hit a native property. + window.contentVal = { foo: 10, rabbit: { hole: { bar: 100, win: window} } }; + ok(true, "Set contentVal"); + + // Check that we're not exposing QueryInterface to non-XBL code + ok(!("QueryInterface" in document), + "Should not have a document.QueryInterface here"); + + function go() { + "use strict"; + + // Test what we can and cannot access in the XBL scope. + is(typeof window.xrayExpando, "undefined", "Xray expandos are private to the caller"); + is(window.primitiveExpando, 11, "Can see waived expandos"); + is(window.stringExpando, "stringExpando", "Can see waived expandos"); + is(typeof window.objectExpando, "object", "object expando exists"); + checkThrows(() => window.objectExpando.foo); + is(SpecialPowers.wrap(window.objectExpando).foo, 12, "SpecialPowers sees the right thing"); + is(typeof window.globalExpando, "object", "Can see global object"); + checkThrows(() => window.globalExpando.win); + is(window.functionExpando(), "called", "XBL functions are callable"); + checkThrows(() => window.functionExpando.prop); + + // Inspect the bound element. + var bound = document.getElementById('bound'); + is(bound.primitiveField, 2, "Can see primitive fields"); + is(bound.method("foo"), "method:foo", "Can invoke XBL method from content"); + is(bound.prop, "propVal", "Can access properties from content"); + bound.prop = "someOtherVal"; + is(bound.prop, "set:someOtherVal", "Can set properties from content"); + + // Make sure that XBL scopes get opaque wrappers. + // + // Note: Now that we have object Xrays, we have to pass in a more obscure + // ES type that we don't Xray to test this. + var proto = bound.__proto__; + var nonXrayableObject = new WeakMap(); + nonXrayableObject.prop = 2; + proto.passMeAJSObject(nonXrayableObject); + + // + // Try sticking a bunch of stuff on the prototype object. + // + + proto.someExpando = 201; + is(bound.someExpando, 201, "Can stick non-XBL properties on the proto"); + + // Previously, this code checked that content couldn't tamper with its XBL + // prototype. But we decided to allow this to reduce regression risk, so for + // now just check that this works. + function checkMayTamper(obj, propName, desc) { + var accessor = !('value' in Object.getOwnPropertyDescriptor(obj, propName)); + if (!accessor) + checkAllowed(function() { obj[propName] = function() {} }, desc + ": assign"); + checkAllowed(function() { Object.defineProperty(obj, propName, {configurable: true, value: 3}) }, desc + ": define with value"); + checkAllowed(function() { Object.defineProperty(obj, propName, {configurable: true, writable: true}) }, desc + ": make writable"); + checkAllowed(function() { Object.defineProperty(obj, propName, {configurable: true}) }, desc + ": make configurable"); + checkAllowed(function() { Object.defineProperty(obj, propName, {configurable: true, get: function() {}}) }, desc + ": define with getter"); + checkAllowed(function() { Object.defineProperty(obj, propName, {configurable: true, set: function() {}}) }, desc + ": define with setter"); + + // Windows are implemented as proxies, and Proxy::delete_ doesn't currently + // pass strict around. Work around it in the window.binding case by just + // checking if delete returns false. + // manually. + checkAllowed(function() { delete obj[propName]; }, desc + ": delete"); + + if (!accessor) + checkAllowed(function() { obj[propName] = function() {} }, desc + ": assign (again)"); + } + + // Make sure content can do whatever it wants with the prototype. + checkMayTamper(proto, 'method', "XBL Proto Method"); + checkMayTamper(proto, 'prop', "XBL Proto Prop"); + checkMayTamper(proto, 'primitiveField', "XBL Field Accessor"); + + // Tamper with the derived object. This doesn't affect the XBL scope thanks + // to Xrays. + bound.method = function() { return "heh"; }; + Object.defineProperty(bound, 'method', {value: function() { return "hah" }}); + Object.defineProperty(bound, 'prop', {value: "redefined"}); + bound.primitiveField = 321; + + // Untrusted events should not trigger event handlers without + // exposeToUntrustedContent=true. + window.triggeredTrustedHandler = false; + var untrustedEvent = new CustomEvent('testtrusted'); + is(untrustedEvent.type, 'testtrusted', "Constructor should see type"); + bound.dispatchEvent(untrustedEvent); + ok(!window.triggeredTrustedHandler, "untrusted events should not trigger trusted handler"); + var trustedEvent = new CustomEvent('testtrusted'); + is(trustedEvent.type, 'testtrusted', "Wrapped constructor should see type"); + SpecialPowers.wrap(bound).dispatchEvent(trustedEvent); // Dispatch over SpecialPowers to make the event trusted. + ok(window.triggeredTrustedHandler, "trusted events should trigger trusted handler"); + + // + // We check key events as well, since they're implemented differently. + // + // NB: We don't check isTrusted on the events we create here, because + // according to smaug, old-style event initialization doesn't mark the + // event as trusted until it's dispatched. + // + + window.triggeredUntrustedKeyHandler = false; + window.triggeredTrustedKeyHandler = false; + + // Untrusted event, permissive handler. + var untrustedKeyEvent = document.createEvent('KeyboardEvent'); + untrustedKeyEvent.initEvent('keyup', true, true); + bound.dispatchEvent(untrustedKeyEvent); + ok(window.triggeredUntrustedKeyHandler, "untrusted key events should trigger untrusted handler"); + + // Untrusted event, strict handler. + var fakeTrustedKeyEvent = document.createEvent('KeyboardEvent'); + fakeTrustedKeyEvent.initEvent('keydown', true, true); + bound.dispatchEvent(fakeTrustedKeyEvent); + ok(!window.triggeredTrustedKeyHandler, "untrusted key events should not trigger trusted handler"); + + // Trusted event, strict handler. Dispatch using SpecialPowers to make the event trusted. + var trustedKeyEvent = document.createEvent('KeyboardEvent'); + trustedKeyEvent.initEvent('keydown', true, true); + SpecialPowers.wrap(bound).dispatchEvent(trustedKeyEvent); + ok(window.triggeredTrustedKeyHandler, "trusted key events should trigger trusted handler"); + + // Hand control back to the XBL scope by dispatching an event on the bound element. + bound.dispatchEvent(new CustomEvent('testevent')); + } + + function checkThrows(fn) { + try { fn(); ok(false, "Should have thrown"); } + catch (e) { ok(!!/denied|insecure/.exec(e), "Should have thrown security exception: " + e); } + } + + function checkAllowed(fn, desc) { + try { fn(); ok(true, desc + ": Didn't throw"); } + catch (e) { ok(false, desc + ": Threw: " + e); } + } + + function setup() { + // When the bindings are applied, the constructor will be invoked and the + // test will continue. + document.getElementById('bound').style.MozBinding = 'url(#testBinding)'; + document.getElementById('bound2').style.MozBinding = 'url(#testBinding)'; + document.getElementById('bound3').style.MozBinding = 'url(#testBinding)'; + } + + ]]> +</script> +</head> +<body onload="setup()"> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=821850">Mozilla Bug 821850</a> +<p id="display"></p> +<div id="content"> + <div id="bound">Bound element</div> + <div id="bound2">Bound element</div> + <span id="bound3">Bound element</span> + <img/> +</div> +<pre id="test"> +</pre> +</body> +</html> |