summaryrefslogtreecommitdiffstats
path: root/dom/xbl/test/file_bug821850.xhtml
diff options
context:
space:
mode:
Diffstat (limited to 'dom/xbl/test/file_bug821850.xhtml')
-rw-r--r--dom/xbl/test/file_bug821850.xhtml299
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 000000000..0d68a908f
--- /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>