diff options
Diffstat (limited to 'js/xpconnect/tests/unit/test_interposition.js')
-rw-r--r-- | js/xpconnect/tests/unit/test_interposition.js | 165 |
1 files changed, 165 insertions, 0 deletions
diff --git a/js/xpconnect/tests/unit/test_interposition.js b/js/xpconnect/tests/unit/test_interposition.js new file mode 100644 index 000000000..f755bbd7d --- /dev/null +++ b/js/xpconnect/tests/unit/test_interposition.js @@ -0,0 +1,165 @@ +const Cu = Components.utils; +const Ci = Components.interfaces; +const Cc = Components.classes; + +Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); + +const ADDONID = "bogus-addon@mozilla.org"; + +let gExpectedProp; +function expectAccess(prop, f) +{ + gExpectedProp = prop; + f(); + do_check_eq(gExpectedProp, undefined); +} + +let getter_run = false; +function test_getter() +{ + do_check_eq(getter_run, false); + getter_run = true; + return 200; +} + +let setter_run = false; +function test_setter(v) +{ + do_check_eq(setter_run, false); + do_check_eq(v, 300); + setter_run = true; +} + +let TestInterposition = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIAddonInterposition, + Ci.nsISupportsWeakReference]), + + getWhitelist: function() { + return ["abcxyz", "utils", "dataprop", "getterprop", "setterprop", + "objprop", "defineprop", "configurableprop"]; + }, + + interposeProperty: function(addonId, target, iid, prop) { + do_check_eq(addonId, ADDONID); + do_check_eq(gExpectedProp, prop); + gExpectedProp = undefined; + + if (prop == "dataprop") { + return { configurable: false, enumerable: true, writable: false, value: 100 }; + } else if (prop == "getterprop") { + return { configurable: false, enumerable: true, get: test_getter }; + } else if (prop == "setterprop") { + return { configurable: false, enumerable: true, get: test_getter, set: test_setter }; + } else if (prop == "utils") { + do_check_eq(iid, Ci.nsIXPCComponents.number); + return null; + } else if (prop == "objprop") { + gExpectedProp = "objprop"; // allow recursive access here + return { configurable: false, enumerable: true, writable: false, value: { objprop: 1 } }; + } else if (prop == "configurableprop") { + return { configurable: true, enumerable: true, writable: false, value: 10 }; + } + + return null; + }, + + interposeCall: function(addonId, originalFunc, originalThis, args) { + do_check_eq(addonId, ADDONID); + args.splice(0, 0, addonId); + return originalFunc.apply(originalThis, args); + } +}; + +function run_test() +{ + Cu.setAddonInterposition(ADDONID, TestInterposition); + + Cu.importGlobalProperties(["XMLHttpRequest"]); + + let sandbox = Cu.Sandbox(this, {addonId: ADDONID}); + sandbox.outerObj = new XMLHttpRequest(); + + expectAccess("abcxyz", () => { + Cu.evalInSandbox("outerObj.abcxyz = 12;", sandbox); + }); + + expectAccess("utils", () => { + Cu.evalInSandbox("Components.utils;", sandbox); + }); + + expectAccess("dataprop", () => { + do_check_eq(Cu.evalInSandbox("outerObj.dataprop;", sandbox), 100); + }); + + expectAccess("dataprop", () => { + try { + Cu.evalInSandbox("'use strict'; outerObj.dataprop = 400;", sandbox); + do_check_true(false); // it should throw + } catch (e) {} + }); + + expectAccess("objprop", () => { + Cu.evalInSandbox("outerObj.objprop.objprop;", sandbox); + gExpectedProp = undefined; + }); + + expectAccess("getterprop", () => { + do_check_eq(Cu.evalInSandbox("outerObj.getterprop;", sandbox), 200); + do_check_eq(getter_run, true); + getter_run = false; + }); + + expectAccess("getterprop", () => { + try { + Cu.evalInSandbox("'use strict'; outerObj.getterprop = 400;", sandbox); + do_check_true(false); // it should throw + } catch (e) {} + do_check_eq(getter_run, false); + }); + + expectAccess("setterprop", () => { + do_check_eq(Cu.evalInSandbox("outerObj.setterprop;", sandbox), 200); + do_check_eq(getter_run, true); + getter_run = false; + do_check_eq(setter_run, false); + }); + + expectAccess("setterprop", () => { + Cu.evalInSandbox("'use strict'; outerObj.setterprop = 300;", sandbox); + do_check_eq(getter_run, false); + do_check_eq(setter_run, true); + setter_run = false; + }); + + // Make sure that calling Object.defineProperty succeeds as long as + // we're not interposing on that property. + expectAccess("defineprop", () => { + Cu.evalInSandbox("'use strict'; Object.defineProperty(outerObj, 'defineprop', {configurable:true, enumerable:true, writable:true, value:10});", sandbox); + }); + + // Related to above: make sure we can delete those properties too. + expectAccess("defineprop", () => { + Cu.evalInSandbox("'use strict'; delete outerObj.defineprop;", sandbox); + }); + + // Make sure we get configurable=false even if the interposition + // sets it to true. + expectAccess("configurableprop", () => { + let desc = Cu.evalInSandbox("Object.getOwnPropertyDescriptor(outerObj, 'configurableprop')", sandbox); + do_check_eq(desc.configurable, false); + }); + + let moduleScope = Cu.Sandbox(this); + moduleScope.ADDONID = ADDONID; + moduleScope.do_check_eq = do_check_eq; + function funToIntercept(addonId) { + do_check_eq(addonId, ADDONID); + counter++; + } + sandbox.moduleFunction = Cu.evalInSandbox(funToIntercept.toSource() + "; funToIntercept", moduleScope); + Cu.evalInSandbox("var counter = 0;", moduleScope); + Cu.evalInSandbox("Components.utils.setAddonCallInterposition(this);", moduleScope); + Cu.evalInSandbox("moduleFunction()", sandbox); + do_check_eq(Cu.evalInSandbox("counter", moduleScope), 1); + Cu.setAddonInterposition(ADDONID, null); +} |