test_xray_instanceof.js (10055B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 add_task(function instanceof_xrays() { 6 let sandbox = Cu.Sandbox(null); 7 Cu.evalInSandbox(` 8 this.proxy = new Proxy([], { 9 getPrototypeOf() { 10 return Date.prototype; 11 }, 12 }); 13 14 this.inheritedProxy = Object.create(this.proxy); 15 16 this.FunctionProxy = new Proxy(function() {}, {}); 17 this.functionProxyInstance = new this.FunctionProxy(); 18 19 this.CustomClass = class {}; 20 this.customClassInstance = new this.CustomClass(); 21 `, sandbox); 22 23 { 24 // Sanity check that instanceof still works with standard constructors when xrays are present. 25 Assert.ok(Cu.evalInSandbox(`new Date()`, sandbox) instanceof sandbox.Date, 26 "async function result in sandbox instanceof sandbox.Date"); 27 Assert.ok(new sandbox.Date() instanceof sandbox.Date, 28 "sandbox.Date() instanceof sandbox.Date"); 29 30 Assert.ok(sandbox.CustomClass instanceof sandbox.Function, 31 "Class constructor instanceof sandbox.Function"); 32 Assert.ok(sandbox.CustomClass instanceof sandbox.Object, 33 "Class constructor instanceof sandbox.Object"); 34 35 // Both operands must have the same kind of Xray vision. 36 Assert.equal(Cu.waiveXrays(sandbox.CustomClass) instanceof sandbox.Function, false, 37 "Class constructor with waived xrays instanceof sandbox.Function"); 38 Assert.equal(Cu.waiveXrays(sandbox.CustomClass) instanceof sandbox.Object, false, 39 "Class constructor with waived xrays instanceof sandbox.Object"); 40 } 41 42 { 43 let {proxy} = sandbox; 44 Assert.equal(proxy instanceof sandbox.Date, false, 45 "instanceof should ignore the proxy trap"); 46 Assert.equal(proxy instanceof sandbox.Array, false, 47 "instanceof should ignore the proxy target"); 48 Assert.equal(Cu.waiveXrays(proxy) instanceof sandbox.Date, false, 49 "instanceof should ignore the proxy trap despite the waived xrays on the proxy"); 50 Assert.equal(Cu.waiveXrays(proxy) instanceof sandbox.Array, false, 51 "instanceof should ignore the proxy target despite the waived xrays on the proxy"); 52 53 Assert.ok(proxy instanceof Cu.waiveXrays(sandbox.Date), 54 "instanceof should trigger the proxy trap after waiving Xrays on the constructor"); 55 Assert.equal(proxy instanceof Cu.waiveXrays(sandbox.Array), false, 56 "instanceof should trigger the proxy trap after waiving Xrays on the constructor"); 57 58 Assert.ok(Cu.waiveXrays(proxy) instanceof Cu.waiveXrays(sandbox.Date), 59 "instanceof should trigger the proxy trap after waiving both Xrays"); 60 } 61 62 63 { 64 let {inheritedProxy} = sandbox; 65 Assert.equal(inheritedProxy instanceof sandbox.Date, false, 66 "instanceof should ignore the inherited proxy trap"); 67 Assert.equal(Cu.waiveXrays(inheritedProxy) instanceof sandbox.Date, false, 68 "instanceof should ignore the inherited proxy trap despite the waived xrays on the proxy"); 69 70 Assert.ok(inheritedProxy instanceof Cu.waiveXrays(sandbox.Date), 71 "instanceof should trigger the inherited proxy trap after waiving Xrays on the constructor"); 72 73 Assert.ok(Cu.waiveXrays(inheritedProxy) instanceof Cu.waiveXrays(sandbox.Date), 74 "instanceof should trigger the inherited proxy trap after waiving both Xrays"); 75 } 76 77 { 78 let {FunctionProxy, functionProxyInstance} = sandbox; 79 80 // Ideally, the next two test cases should both throw "... not a function". 81 // However, because the opaque XrayWrapper does not override isCallable, an 82 // opaque XrayWrapper is still considered callable if the proxy target is, 83 // and "instanceof" will try to look up the prototype of the wrapper (and 84 // fail because opaque XrayWrappers hide the properties). 85 Assert.throws( 86 () => functionProxyInstance instanceof FunctionProxy, 87 /'prototype' property of FunctionProxy is not an object/, 88 "Opaque constructor proxy should be hidden by Xrays"); 89 Assert.throws( 90 () => functionProxyInstance instanceof sandbox.proxy, 91 /sandbox.proxy is not a function/, 92 "Opaque non-constructor proxy should be hidden by Xrays"); 93 94 Assert.ok(functionProxyInstance instanceof Cu.waiveXrays(FunctionProxy), 95 "instanceof should get through the proxy after waiving Xrays on the constructor proxy"); 96 Assert.ok(Cu.waiveXrays(functionProxyInstance) instanceof Cu.waiveXrays(FunctionProxy), 97 "instanceof should get through the proxy after waiving both Xrays"); 98 } 99 100 { 101 let {CustomClass, customClassInstance} = sandbox; 102 // Under Xray vision, every JS object is either a plain object or array. 103 // Prototypical inheritance is invisible when the constructor is wrapped. 104 Assert.throws( 105 () => customClassInstance instanceof CustomClass, 106 /TypeError: 'prototype' property of CustomClass is not an object/, 107 "instanceof on a custom JS class with xrays should fail"); 108 Assert.ok(customClassInstance instanceof Cu.waiveXrays(CustomClass), 109 "instanceof should see the true prototype of CustomClass after waiving Xrays on the class"); 110 Assert.ok(Cu.waiveXrays(customClassInstance) instanceof Cu.waiveXrays(CustomClass), 111 "instanceof should see the true prototype of CustomClass after waiving Xrays"); 112 } 113 }); 114 115 add_task(function instanceof_dom_xrays_hasInstance() { 116 const principal = Services.scriptSecurityManager.createNullPrincipal({}); 117 const webnav = Services.appShell.createWindowlessBrowser(false); 118 webnav.docShell.createAboutBlankDocumentViewer(principal, principal); 119 let window = webnav.document.defaultView; 120 121 let sandbox = Cu.Sandbox(principal); 122 sandbox.DOMObjectWithHasInstance = window.document; 123 Cu.evalInSandbox(` 124 this.DOMObjectWithHasInstance[Symbol.hasInstance] = function() { 125 return true; 126 }; 127 this.ObjectWithHasInstance = { 128 [Symbol.hasInstance](v) { 129 v.throwsIfVCannotBeAccessed; 130 return true; 131 }, 132 }; 133 `, sandbox); 134 135 // Override the hasInstance handler in the window, so that we can detect when 136 // we end up triggering hasInstance in the window's compartment. 137 window.eval(` 138 document[Symbol.hasInstance] = function() { 139 throw "hasInstance_in_window"; 140 }; 141 `); 142 143 sandbox.domobj = window.document.body; 144 Assert.ok(sandbox.eval(`domobj.wrappedJSObject`), 145 "DOM object is a XrayWrapper"); 146 Assert.ok(sandbox.eval(`DOMObjectWithHasInstance.wrappedJSObject`), 147 "DOM object with Symbol.hasInstance is a XrayWrapper"); 148 149 for (let Obj of ["ObjectWithHasInstance", "DOMObjectWithHasInstance"]) { 150 // Tests Xray vision *inside* the sandbox. The Symbol.hasInstance member 151 // is a property / expando object in the sandbox's compartment, so the 152 // "instanceof" operator should always trigger the hasInstance function. 153 Assert.ok(sandbox.eval(`[] instanceof ${Obj}`), 154 `Should call ${Obj}[Symbol.hasInstance] when left operand has no Xrays`); 155 Assert.ok(sandbox.eval(`domobj instanceof ${Obj}`), 156 `Should call ${Obj}[Symbol.hasInstance] when left operand has Xrays`); 157 Assert.ok(sandbox.eval(`domobj.wrappedJSObject instanceof ${Obj}`), 158 `Should call ${Obj}[Symbol.hasInstance] when left operand has waived Xrays`); 159 160 // Tests Xray vision *outside* the sandbox. The Symbol.hasInstance member 161 // should be hidden by Xrays. 162 let sandboxObjWithHasInstance = sandbox[Obj]; 163 Assert.ok(Cu.isXrayWrapper(sandboxObjWithHasInstance), 164 `sandbox.${Obj} is a XrayWrapper`); 165 Assert.throws( 166 () => sandbox.Object() instanceof sandboxObjWithHasInstance, 167 /sandboxObjWithHasInstance is not a function/, 168 `sandbox.${Obj}[Symbol.hasInstance] should be hidden by Xrays`); 169 170 Assert.throws( 171 () => Cu.waiveXrays(sandbox.Object()) instanceof sandboxObjWithHasInstance, 172 /sandboxObjWithHasInstance is not a function/, 173 `sandbox.${Obj}[Symbol.hasInstance] should be hidden by Xrays, despite the waived Xrays at the left`); 174 175 // (Cases where the left operand has no Xrays are checked below.) 176 } 177 178 // hasInstance is expected to be called, but still trigger an error because 179 // properties of the object from the current context should not be readable 180 // by the hasInstance function in the sandbox with a different principal. 181 Assert.throws( 182 () => [] instanceof Cu.waiveXrays(sandbox.ObjectWithHasInstance), 183 /Permission denied to access property "throwsIfVCannotBeAccessed"/, 184 `Should call (waived) sandbox.ObjectWithHasInstance[Symbol.hasInstance] when the right operand has waived Xrays`); 185 186 // The Xray waiver on the right operand should be sufficient to call 187 // hasInstance even if the left operand still has Xrays. 188 Assert.ok(sandbox.Object() instanceof Cu.waiveXrays(sandbox.ObjectWithHasInstance), 189 `Should call (waived) sandbox.ObjectWithHasInstance[Symbol.hasInstance] when the right operand has waived Xrays`); 190 Assert.ok(Cu.waiveXrays(sandbox.Object()) instanceof Cu.waiveXrays(sandbox.ObjectWithHasInstance), 191 `Should call (waived) sandbox.ObjectWithHasInstance[Symbol.hasInstance] when both operands have waived Xrays`); 192 193 // When Xrays of the DOM object are waived, we end up in the owner document's 194 // compartment (instead of the sandbox). 195 Assert.throws( 196 () => [] instanceof Cu.waiveXrays(sandbox.DOMObjectWithHasInstance), 197 /hasInstance_in_window/, 198 "Should call (waived) sandbox.DOMObjectWithHasInstance[Symbol.hasInstance] when the right operand has waived Xrays"); 199 200 Assert.throws( 201 () => Cu.waiveXrays(sandbox.Object()) instanceof Cu.waiveXrays(sandbox.DOMObjectWithHasInstance), 202 /hasInstance_in_window/, 203 "Should call (waived) sandbox.DOMObjectWithHasInstance[Symbol.hasInstance] when both operands have waived Xrays"); 204 205 webnav.close(); 206 });