diff options
Diffstat (limited to 'tools/addon-sdk-1.7/packages/api-utils/data')
5 files changed, 1122 insertions, 0 deletions
diff --git a/tools/addon-sdk-1.7/packages/api-utils/data/content-proxy.js b/tools/addon-sdk-1.7/packages/api-utils/data/content-proxy.js new file mode 100644 index 0000000..f0a39d3 --- /dev/null +++ b/tools/addon-sdk-1.7/packages/api-utils/data/content-proxy.js @@ -0,0 +1,847 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +let Ci = Components.interfaces; + +/** + * Access key that allows privileged code to unwrap proxy wrappers through + * valueOf: + * let xpcWrapper = proxyWrapper.valueOf(UNWRAP_ACCESS_KEY); + * This key should only be used by proxy unit test. + */ + const UNWRAP_ACCESS_KEY = {}; + + + /** + * Returns a closure that wraps arguments before calling the given function, + * which can be given to native functions that accept a function, such that when + * the closure is called, the given function is called with wrapped arguments. + * + * @param fun {Function} + * the function for which to create a closure wrapping its arguments + * @param obj {Object} + * target object from which `fun` comes from + * (optional, for debugging purpose) + * @param name {String} + * name of the attribute from which `fun` is binded on `obj` + * (optional, for debugging purpose) + * + * Example: + * function contentScriptListener(event) {} + * let wrapper = ContentScriptFunctionWrapper(contentScriptListener); + * xray.addEventListener("...", wrapper, false); + * -> Allow to `event` to be wrapped + */ +function ContentScriptFunctionWrapper(fun, obj, name) { + if ("___proxy" in fun && typeof fun.___proxy == "function") + return fun.___proxy; + + let wrappedFun = function () { + let args = []; + for (let i = 0, l = arguments.length; i < l; i++) + args.push(wrap(arguments[i])); + + //console.log("Called from native :"+obj+"."+name); + //console.log(">args "+arguments.length); + //console.log(fun); + + // Native code can execute this callback with `this` being the wrapped + // function. For example, window.mozRequestAnimationFrame. + if (this == wrappedFun) + return fun.apply(fun, args); + + return fun.apply(wrap(this), args); + }; + + Object.defineProperty(fun, "___proxy", {value : wrappedFun, + writable : false, + enumerable : false, + configurable : false}); + + return wrappedFun; +} + +/** + * Returns a closure that unwraps arguments before calling the `fun` function, + * which can be used to build a wrapper for a native function that accepts + * wrapped arguments, since native function only accept unwrapped arguments. + * + * @param fun {Function} + * the function to wrap + * @param originalObject {Object} + * target object from which `fun` comes from + * (optional, for debugging purpose) + * @param name {String} + * name of the attribute from which `fun` is binded on `originalObject` + * (optional, for debugging purpose) + * + * Example: + * wrapper.appendChild = NativeFunctionWrapper(xray.appendChild, xray); + * wrapper.appendChild(anotherWrapper); + * -> Allow to call xray.appendChild with unwrapped version of anotherWrapper + */ +function NativeFunctionWrapper(fun, originalObject, name) { + return function () { + let args = []; + let obj = this && typeof this.valueOf == "function" ? + this.valueOf(UNWRAP_ACCESS_KEY) : this; + + for (let i = 0, l = arguments.length; i < l; i++) + args.push( unwrap(arguments[i], obj, name) ); + + //if (name != "toString") + //console.log(">>calling native ["+(name?name:'#closure#')+"]: \n"+fun.apply+"\n"+obj+"\n("+args.join(', ')+")\nthis :"+obj+"from:"+originalObject+"\n"); + + // Need to use Function.prototype.apply.apply because XMLHttpRequest + // is a function (typeof return 'function') and fun.apply is null :/ + let unwrapResult = Function.prototype.apply.apply(fun, [obj, args]); + let result = wrap(unwrapResult, obj, name); + + //console.log("<< "+rr+" -> "+r); + + return result; + }; +} + +/* + * Unwrap a JS value that comes from the content script. + * Mainly converts proxy wrapper to XPCNativeWrapper. + */ +function unwrap(value, obj, name) { + //console.log("unwrap : "+value+" ("+name+")"); + if (!value) + return value; + let type = typeof value; + + // In case of proxy, unwrap them recursively + // (it should not be recursive, just in case of) + if (["object", "function"].indexOf(type) !== -1 && + "__isWrappedProxy" in value) { + while("__isWrappedProxy" in value) + value = value.valueOf(UNWRAP_ACCESS_KEY); + return value; + } + + // In case of functions we need to return a wrapper that converts native + // arguments applied to this function into proxies. + if (type == "function") + return ContentScriptFunctionWrapper(value, obj, name); + + // We must wrap objects coming from content script too, as they may have + // a function that will be called by a native method. + // For example: + // addEventListener(..., { handleEvent: function(event) {} }, ...); + if (type == "object") + return ContentScriptObjectWrapper(value); + + if (["string", "number", "boolean"].indexOf(type) !== -1) + return value; + //console.log("return non-wrapped to native : "+typeof value+" -- "+value); + return value; +} + +/** + * Returns an XrayWrapper proxy object that allow to wrap any of its function + * though `ContentScriptFunctionWrapper`. These proxies are given to + * XrayWrappers in order to automatically wrap values when they call a method + * of these proxies. So that they are only used internaly and content script, + * nor web page have ever access to them. As a conclusion, we can consider + * this code as being safe regarding web pages overload. + * + * + * @param obj {Object} + * object coming from content script context to wrap + * + * Example: + * let myListener = { handleEvent: function (event) {} }; + * node.addEventListener("click", myListener, false); + * `event` has to be wrapped, so handleEvent has to be wrapped using + * `ContentScriptFunctionWrapper` function. + * In order to do so, we build this new kind of proxies. + */ +function ContentScriptObjectWrapper(obj) { + if ("___proxy" in obj && typeof obj.___proxy == "object") + return obj.___proxy; + + function valueOf(key) { + if (key === UNWRAP_ACCESS_KEY) + return obj; + return this; + } + + let proxy = Proxy.create({ + // Fundamental traps + getPropertyDescriptor: function(name) { + return Object.getOwnPropertyDescriptor(obj, name); + }, + defineProperty: function(name, desc) { + return Object.defineProperty(obj, name, desc); + }, + getOwnPropertyNames: function () { + return Object.getOwnPropertyNames(obj); + }, + delete: function(name) { + return delete obj[name]; + }, + // derived traps + has: function(name) { + return name === "__isXrayWrapperProxy" || + name in obj; + }, + hasOwn: function(name) { + return Object.prototype.hasOwnProperty.call(obj, name); + }, + get: function(receiver, name) { + if (name == "valueOf") + return valueOf; + let value = obj[name]; + if (!value) + return value; + + return unwrap(value); + }, + set: function(receiver, name, val) { + obj[name] = val; + return true; + }, + enumerate: function() { + var result = []; + for each (let name in obj) { + result.push(name); + }; + return result; + }, + keys: function() { + return Object.keys(obj); + } + }); + + Object.defineProperty(obj, "___proxy", {value : proxy, + writable : false, + enumerable : false, + configurable : false}); + + return proxy; +} + +// List of all existing typed arrays. +// Can be found here: +// http://mxr.mozilla.org/mozilla-central/source/js/src/jsapi.cpp#1790 +const typedArraysCtor = [ + ArrayBuffer, + Int8Array, + Uint8Array, + Int16Array, + Uint16Array, + Int32Array, + Uint32Array, + Float32Array, + Float64Array, + Uint8ClampedArray +]; + +/* + * Wrap a JS value coming from the document by building a proxy wrapper. + */ +function wrap(value, obj, name, debug) { + if (!value) + return value; + let type = typeof value; + if (type == "object") { + // Bug 671016: Typed arrays don't need to be proxified. + // We avoid checking the whole constructor list on all objects + // by doing this check only on non-extensible objects: + if (!Object.isExtensible(value) && + typedArraysCtor.indexOf(value.constructor) !== -1) + return value; + + // Bug 715755: do not proxify COW wrappers + // These wrappers throw an exception when trying to access + // any attribute that is not in a white list + try { + ("nonExistantAttribute" in value); + } + catch(e) { + if (e.message.indexOf("Permission denied to access property") !== -1) + return value; + } + + // We may have a XrayWrapper proxy. + // For example: + // let myListener = { handleEvent: function () {} }; + // node.addEventListener("click", myListener, false); + // When native code want to call handleEvent, + // we go though ContentScriptFunctionWrapper that calls `wrap(this)` + // `this` is the XrayWrapper proxy of myListener. + // We return this object without building a CS proxy as it is already + // a value coming from the CS. + if ("__isXrayWrapperProxy" in value) + return value.valueOf(UNWRAP_ACCESS_KEY); + + // Unwrap object before wrapping it. + // It should not happen with CS proxy objects. + while("__isWrappedProxy" in value) { + value = value.valueOf(UNWRAP_ACCESS_KEY); + } + + if (XPCNativeWrapper.unwrap(value) !== value) + return getProxyForObject(value); + // In case of Event, HTMLCollection or NodeList or ??? + // XPCNativeWrapper.unwrap(value) === value + // but it's still a XrayWrapper so let's build a proxy + return getProxyForObject(value); + } + if (type == "function") { + if (XPCNativeWrapper.unwrap(value) !== value + || (typeof value.toString === "function" && + value.toString().match(/\[native code\]/))) { + return getProxyForFunction(value, NativeFunctionWrapper(value, obj, name)); + } + return value; + } + if (type == "string") + return value; + if (type == "number") + return value; + if (type == "boolean") + return value; + //console.log("return non-wrapped to wrapped : "+value); + return value; +} + +/* + * Wrap an object from the document to a proxy wrapper + */ +function getProxyForObject(obj) { + if (typeof obj != "object") { + let msg = "tried to proxify something other than an object: " + typeof obj; + console.warn(msg); + throw msg; + } + if ("__isWrappedProxy" in obj) { + return obj; + } + // Check if there is a proxy cached on this wrapper, + // but take care of prototype ___proxy attribute inheritance! + if (obj && obj.___proxy && obj.___proxy.valueOf(UNWRAP_ACCESS_KEY) === obj) { + return obj.___proxy; + } + + let proxy = Proxy.create(handlerMaker(obj)); + + Object.defineProperty(obj, "___proxy", {value : proxy, + writable : false, + enumerable : false, + configurable : false}); + return proxy; +} + +/* + * Wrap a function from the document to a proxy wrapper + */ +function getProxyForFunction(fun, callTrap) { + if (typeof fun != "function") { + let msg = "tried to proxify something other than a function: " + typeof fun; + console.warn(msg); + throw msg; + } + if ("__isWrappedProxy" in fun) + return obj; + if ("___proxy" in fun) + return fun.___proxy; + + let proxy = Proxy.createFunction(handlerMaker(fun), callTrap); + + Object.defineProperty(fun, "___proxy", {value : proxy, + writable : false, + enumerable : false, + configurable : false}); + + return proxy; +} + +/* + * Check if a DOM attribute name is an event name. + */ +function isEventName(id) { + if (id.indexOf("on") != 0 || id.length == 2) + return false; + // Taken from: + // http://mxr.mozilla.org/mozilla-central/source/dom/base/nsDOMClassInfo.cpp#7616 + switch (id[2]) { + case 'a' : + return (id == "onabort" || + id == "onafterscriptexecute" || + id == "onafterprint"); + case 'b' : + return (id == "onbeforeunload" || + id == "onbeforescriptexecute" || + id == "onblur" || + id == "onbeforeprint"); + case 'c' : + return (id == "onchange" || + id == "onclick" || + id == "oncontextmenu" || + id == "oncopy" || + id == "oncut" || + id == "oncanplay" || + id == "oncanplaythrough"); + case 'd' : + return (id == "ondblclick" || + id == "ondrag" || + id == "ondragend" || + id == "ondragenter" || + id == "ondragleave" || + id == "ondragover" || + id == "ondragstart" || + id == "ondrop" || + id == "ondurationchange"); + case 'e' : + return (id == "onerror" || + id == "onemptied" || + id == "onended"); + case 'f' : + return id == "onfocus"; + case 'h' : + return id == "onhashchange"; + case 'i' : + return (id == "oninput" || + id == "oninvalid"); + case 'k' : + return (id == "onkeydown" || + id == "onkeypress" || + id == "onkeyup"); + case 'l' : + return (id == "onload" || + id == "onloadeddata" || + id == "onloadedmetadata" || + id == "onloadstart"); + case 'm' : + return (id == "onmousemove" || + id == "onmouseout" || + id == "onmouseover" || + id == "onmouseup" || + id == "onmousedown" || + id == "onmessage"); + case 'p' : + return (id == "onpaint" || + id == "onpageshow" || + id == "onpagehide" || + id == "onpaste" || + id == "onpopstate" || + id == "onpause" || + id == "onplay" || + id == "onplaying" || + id == "onprogress"); + case 'r' : + return (id == "onreadystatechange" || + id == "onreset" || + id == "onresize" || + id == "onratechange"); + case 's' : + return (id == "onscroll" || + id == "onselect" || + id == "onsubmit" || + id == "onseeked" || + id == "onseeking" || + id == "onstalled" || + id == "onsuspend"); + case 't': + return id == "ontimeupdate" + /* + // TODO: Make it work for mobile version + || + (nsDOMTouchEvent::PrefEnabled() && + (id == "ontouchstart" || + id == "ontouchend" || + id == "ontouchmove" || + id == "ontouchenter" || + id == "ontouchleave" || + id == "ontouchcancel"))*/; + + case 'u' : + return id == "onunload"; + case 'v': + return id == "onvolumechange"; + case 'w': + return id == "onwaiting"; + } + + return false; +} + +// XrayWrappers miss some attributes. +// Here is a list of functions that return a value when it detects a miss: +const NODES_INDEXED_BY_NAME = ["IMG", "FORM", "APPLET", "EMBED", "OBJECT"]; +const xRayWrappersMissFixes = [ + + // Fix bug with XPCNativeWrapper on HTMLCollection + // We can only access array item once, then it's undefined :o + function (obj, name) { + let i = parseInt(name); + if (obj.toString().match(/HTMLCollection|NodeList/) && + i >= 0 && i < obj.length) { + return wrap(XPCNativeWrapper(obj.wrappedJSObject[name]), obj, name); + } + return null; + }, + + // Trap access to document["form name"] + // that may refer to an existing form node + // http://mxr.mozilla.org/mozilla-central/source/dom/base/nsDOMClassInfo.cpp#9285 + function (obj, name) { + if ("nodeType" in obj && obj.nodeType == 9) { + let node = obj.wrappedJSObject[name]; + // List of supported tag: + // http://mxr.mozilla.org/mozilla-central/source/content/html/content/src/nsGenericHTMLElement.cpp#1267 + if (node && NODES_INDEXED_BY_NAME.indexOf(node.tagName) != -1) + return wrap(XPCNativeWrapper(node)); + } + return null; + }, + + // Trap access to window["frame name"] and window.frames[i] + // that refer to an (i)frame internal window object + // http://mxr.mozilla.org/mozilla-central/source/dom/base/nsDOMClassInfo.cpp#6824 + function (obj, name) { + if (typeof obj == "object" && "document" in obj) { + // Ensure that we are on a window object + try { + obj.QueryInterface(Ci.nsIDOMWindow); + } + catch(e) { + return null; + } + + // Integer case: + let i = parseInt(name); + if (i >= 0 && i in obj) { + return wrap(XPCNativeWrapper(obj[i])); + } + + // String name case: + if (name in obj.wrappedJSObject) { + let win = obj.wrappedJSObject[name]; + let nodes = obj.document.getElementsByName(name); + for (let i = 0, l = nodes.length; i < l; i++) { + let node = nodes[i]; + if ("contentWindow" in node && node.contentWindow == win) + return wrap(node.contentWindow); + } + } + } + return null; + }, + + // Trap access to form["node name"] + // http://mxr.mozilla.org/mozilla-central/source/dom/base/nsDOMClassInfo.cpp#9477 + function (obj, name) { + if (typeof obj == "object" && obj.tagName == "FORM") { + let match = obj.wrappedJSObject[name]; + let nodes = obj.ownerDocument.getElementsByName(name); + for (let i = 0, l = nodes.length; i < l; i++) { + let node = nodes[i]; + if (node == match) + return wrap(node); + } + } + return null; + }, + + // Fix XPathResult's constants being undefined on XrayWrappers + // these constants are defined here: + // http://mxr.mozilla.org/mozilla-central/source/dom/interfaces/xpath/nsIDOMXPathResult.idl + // and are only numbers. + // The platform bug 665279 was fixed in Gecko 10.0a1. + // FIXME: remove this workaround once the SDK no longer supports Firefox 9. + function (obj, name) { + if (typeof obj == "object" && name in Ci.nsIDOMXPathResult) { + let value = Ci.nsIDOMXPathResult[name]; + if (typeof value == "number" && value === obj.wrappedJSObject[name]) + return value; + } + return null; + } + +]; + +// XrayWrappers have some buggy methods. +// Here is the list of them with functions returning some replacement +// for a given object `obj`: +const xRayWrappersMethodsFixes = { + // postMessage method is checking the Javascript global + // and it expects it to be a window object. + // But in our case, the global object is our sandbox global object. + // See nsGlobalWindow::CallerInnerWindow(): + // http://mxr.mozilla.org/mozilla-central/source/dom/base/nsGlobalWindow.cpp#5893 + // nsCOMPtr<nsPIDOMWindow> win = do_QueryWrappedNative(wrapper); + // win is null + postMessage: function (obj) { + // Ensure that we are on a window object + try { + obj.QueryInterface(Ci.nsIDOMWindow); + } + catch(e) { + return null; + } + // Create a wrapper that is going to call `postMessage` through `eval` + let f = function postMessage(message, targetOrigin) { + message = message.toString().replace(/['\\]/g,"\\$&"); + targetOrigin = targetOrigin.toString().replace(/['\\]/g,"\\$&"); + + let jscode = "this.postMessage('" + message + "', '" + + targetOrigin + "')"; + return this.wrappedJSObject.eval(jscode); + }; + return getProxyForFunction(f, NativeFunctionWrapper(f)); + }, + + // Fix mozMatchesSelector uses that is broken on XrayWrappers + // when we use document.documentElement.mozMatchesSelector.call(node, expr) + // It's only working if we call mozMatchesSelector on the node itself. + // SEE BUG 658909: mozMatchesSelector returns incorrect results with XrayWrappers + mozMatchesSelector: function (obj) { + // Ensure that we are on an object to expose this buggy method + try { + // Bug 707576 removed nsIDOMNSElement. + // Can be simplified as soon as Firefox 11 become the minversion + obj.QueryInterface("nsIDOMElement" in Ci ? Ci.nsIDOMElement : + Ci.nsIDOMNSElement); + } + catch(e) { + return null; + } + // We can't use `wrap` function as `f` is not a native function, + // so wrap it manually: + let f = function mozMatchesSelector(selectors) { + return this.mozMatchesSelector(selectors); + }; + + return getProxyForFunction(f, NativeFunctionWrapper(f)); + }, + + // Bug 679054: History API doesn't work with Proxy objects. We have to pass + // regular JS objects on `pushState` and `replaceState` methods. + // In addition, the first argument has to come from the same compartment. + pushState: function (obj) { + // Ensure that we are on an object that expose History API + try { + obj.QueryInterface(Ci.nsIDOMHistory); + } + catch(e) { + return null; + } + let f = function fix() { + // Call native method with JSON objects + // (need to convert `arguments` to an array via `slice`) + return this.pushState.apply(this, JSON.parse(JSON.stringify(Array.slice(arguments)))); + }; + + return getProxyForFunction(f, NativeFunctionWrapper(f)); + }, + replaceState: function (obj) { + // Ensure that we are on an object that expose History API + try { + obj.QueryInterface(Ci.nsIDOMHistory); + } + catch(e) { + return null; + } + let f = function fix() { + // Call native method with JSON objects + // (need to convert `arguments` to an array via `slice`) + return this.replaceState.apply(this, JSON.parse(JSON.stringify(Array.slice(arguments)))); + }; + + return getProxyForFunction(f, NativeFunctionWrapper(f)); + } +}; + +/* + * Generate handler for proxy wrapper + */ +function handlerMaker(obj) { + // Overloaded attributes dictionary + let overload = {}; + // Expando attributes dictionary (i.e. onclick, onfocus, on* ...) + let expando = {}; + // Cache of methods overloaded to fix XrayWrapper bug + let methodFixes = {}; + return { + // Fundamental traps + getPropertyDescriptor: function(name) { + return Object.getOwnPropertyDescriptor(obj, name); + }, + defineProperty: function(name, desc) { + return Object.defineProperty(obj, name, desc); + }, + getOwnPropertyNames: function () { + return Object.getOwnPropertyNames(obj); + }, + delete: function(name) { + delete expando[name]; + delete overload[name]; + return delete obj[name]; + }, + + // derived traps + has: function(name) { + if (name == "___proxy") return false; + if (isEventName(name)) { + // XrayWrappers throw exception when we try to access expando attributes + // even on "name in wrapper". So avoid doing it! + return name in expando; + } + return name in obj || name in overload || name == "__isWrappedProxy" || + undefined !== this.get(null, name); + }, + hasOwn: function(name) { + return Object.prototype.hasOwnProperty.call(obj, name); + }, + get: function(receiver, name) { + if (name == "___proxy") + return undefined; + + // Overload toString in order to avoid returning "[XrayWrapper [object HTMLElement]]" + // or "[object Function]" for function's Proxy + if (name == "toString") { + // Bug 714778: we should not pass obj.wrappedJSObject.toString + // in order to avoid sharing its proxy between two contents scripts. + // (not that `unwrappedObj` can be equal to `obj` when `obj` isn't + // an xraywrapper) + let unwrappedObj = XPCNativeWrapper.unwrap(obj); + return wrap(function () { + return unwrappedObj.toString.call( + this.valueOf(UNWRAP_ACCESS_KEY), arguments); + }, obj, name); + } + + // Offer a way to retrieve XrayWrapper from a proxified node through `valueOf` + if (name == "valueOf") + return function (key) { + if (key === UNWRAP_ACCESS_KEY) + return obj; + return this; + }; + + // Return overloaded value if there is one. + // It allows to overload native methods like addEventListener that + // are not saved, even on the wrapper itself. + // (And avoid some methods like toSource from being returned here! [__proto__ test]) + if (name in overload && + overload[name] != Object.getPrototypeOf(overload)[name] && + name != "__proto__") { + return overload[name]; + } + + // Catch exceptions thrown by XrayWrappers when we try to access on* + // attributes like onclick, onfocus, ... + if (isEventName(name)) { + //console.log("expando:"+obj+" - "+obj.nodeType); + return name in expando ? expando[name].original : undefined; + } + + // Overload some XrayWrappers method in order to fix its bugs + if (name in methodFixes && + methodFixes[name] != Object.getPrototypeOf(methodFixes)[name] && + name != "__proto__") + return methodFixes[name]; + if (Object.keys(xRayWrappersMethodsFixes).indexOf(name) !== -1) { + let fix = xRayWrappersMethodsFixes[name](obj); + if (fix) + return methodFixes[name] = fix; + } + + let o = obj[name]; + + // XrayWrapper miss some attributes, try to catch these and return a value + if (!o) { + for each(let atttributeFixer in xRayWrappersMissFixes) { + let fix = atttributeFixer(obj, name); + if (fix) + return fix; + } + } + + // Generic case + return wrap(o, obj, name); + + }, + + set: function(receiver, name, val) { + + if (isEventName(name)) { + //console.log("SET on* attribute : " + name + " / " + val + "/" + obj); + let shortName = name.replace(/^on/,""); + + // Unregister previously set listener + if (expando[name]) { + obj.removeEventListener(shortName, expando[name], true); + delete expando[name]; + } + + // Only accept functions + if (typeof val != "function") + return false; + + // Register a new listener + let original = val; + val = ContentScriptFunctionWrapper(val); + expando[name] = val; + val.original = original; + obj.addEventListener(name.replace(/^on/, ""), val, true); + return true; + } + + obj[name] = val; + + // Handle native method not overloaded on XrayWrappers: + // obj.addEventListener = val; -> obj.addEventlistener = native method + // And, XPCNativeWrapper bug where nested values appear to be wrapped: + // obj.customNestedAttribute = val -> obj.customNestedAttribute !== val + // obj.customNestedAttribute = "waive wrapper something" + // SEE BUG 658560: Fix identity problem with CrossOriginWrappers + // TODO: check that DOM can't be updated by the document itself and so overloaded value becomes wrong + // but I think such behavior is limited to primitive type + if ((typeof val == "function" || typeof val == "object") && name) { + overload[name] = val; + } + + return true; + }, + + enumerate: function() { + var result = []; + for each (let name in Object.keys(obj)) { + result.push(name); + }; + return result; + }, + + keys: function() { + return Object.keys(obj); + } + }; +}; + + +/* + * Wrap an object from the document to a proxy wrapper. + */ +function create(object) { + if ("wrappedJSObject" in object) + object = object.wrappedJSObject; + let xpcWrapper = XPCNativeWrapper(object); + // If we can't build an XPCNativeWrapper, it doesn't make sense to build + // a proxy. All proxy code is based on having such wrapper that store + // different JS attributes set. + // (we can't build XPCNativeWrapper when object is from the same + // principal/domain) + if (object === xpcWrapper) { + return object; + } + return getProxyForObject(xpcWrapper); +} diff --git a/tools/addon-sdk-1.7/packages/api-utils/data/test-content-symbiont.js b/tools/addon-sdk-1.7/packages/api-utils/data/test-content-symbiont.js new file mode 100644 index 0000000..65a2a21 --- /dev/null +++ b/tools/addon-sdk-1.7/packages/api-utils/data/test-content-symbiont.js @@ -0,0 +1,5 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// test-content-symbiont diff --git a/tools/addon-sdk-1.7/packages/api-utils/data/test-message-manager.js b/tools/addon-sdk-1.7/packages/api-utils/data/test-message-manager.js new file mode 100644 index 0000000..d647bd8 --- /dev/null +++ b/tools/addon-sdk-1.7/packages/api-utils/data/test-message-manager.js @@ -0,0 +1,6 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const TEST_VALUE = 11; + diff --git a/tools/addon-sdk-1.7/packages/api-utils/data/test-trusted-document.html b/tools/addon-sdk-1.7/packages/api-utils/data/test-trusted-document.html new file mode 100644 index 0000000..5845cf5 --- /dev/null +++ b/tools/addon-sdk-1.7/packages/api-utils/data/test-trusted-document.html @@ -0,0 +1,17 @@ +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> + +<html>
+<head>
+ <title>Worker test</title>
+</head>
+<body>
+ <p id="paragraph">Lorem ipsum dolor sit amet.</p>
+ <script>
+ addon.port.on('addon-to-document', function (arg) {
+ addon.port.emit('document-to-addon', arg);
+ });
+ </script>
+</body>
+</html>
diff --git a/tools/addon-sdk-1.7/packages/api-utils/data/worker.js b/tools/addon-sdk-1.7/packages/api-utils/data/worker.js new file mode 100644 index 0000000..aba594d --- /dev/null +++ b/tools/addon-sdk-1.7/packages/api-utils/data/worker.js @@ -0,0 +1,247 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const ContentWorker = Object.freeze({ + // TODO: Bug 727854 Use same implementation than common JS modules, + // i.e. EventEmitter module + + /** + * Create an EventEmitter instance. + */ + createEventEmitter: function createEventEmitter(emit) { + let listeners = Object.create(null); + let eventEmitter = Object.freeze({ + emit: emit, + on: function on(name, callback) { + if (typeof callback !== "function") + return this; + if (!(name in listeners)) + listeners[name] = []; + listeners[name].push(callback); + return this; + }, + once: function once(name, callback) { + eventEmitter.on(name, function onceCallback() { + eventEmitter.removeListener(name, onceCallback); + callback.apply(callback, arguments); + }); + }, + removeListener: function removeListener(name, callback) { + if (!(name in listeners)) + return; + let index = listeners[name].indexOf(name); + if (index == -1) + return; + listeners[name].splice(index, 1); + } + }); + function onEvent(name) { + if (!(name in listeners)) + return []; + let args = Array.slice(arguments, 1); + let results = []; + for each (let callback in listeners[name]) { + results.push(callback.apply(null, args)); + } + return results; + } + function hasListenerFor(name) { + if (!(name in listeners)) + return false; + return listeners[name].length > 0; + } + return { + eventEmitter: eventEmitter, + emit: onEvent, + hasListenerFor: hasListenerFor + }; + }, + + /** + * Create an EventEmitter instance to communicate with chrome module + * by passing only strings between compartments. + * This function expects `emitToChrome` function, that allows to send + * events to the chrome module. It returns the EventEmitter as `pipe` + * attribute, and, `onChromeEvent` a function that allows chrome module + * to send event into the EventEmitter. + * + * pipe.emit --> emitToChrome + * onChromeEvent --> callback registered through pipe.on + */ + createPipe: function createPipe(emitToChrome) { + function onEvent() { + // Convert to real array + let args = Array.slice(arguments); + // JSON.stringify is buggy with cross-sandbox values, + // it may return "{}" on functions. Use a replacer to match them correctly. + function replacer(k, v) { + return typeof v === "function" ? undefined : v; + } + let str = JSON.stringify(args, replacer); + emitToChrome(str); + } + + let { eventEmitter, emit, hasListenerFor } = + ContentWorker.createEventEmitter(onEvent); + + return { + pipe: eventEmitter, + onChromeEvent: function onChromeEvent(array) { + // We either receive a stringified array, or a real array. + // We still allow to pass an array of objects, in WorkerSandbox.emitSync + // in order to allow sending DOM node reference between content script + // and modules (only used for context-menu API) + let args = typeof array == "string" ? JSON.parse(array) : array; + return emit.apply(null, args); + }, + hasListenerFor: hasListenerFor + }; + }, + + injectConsole: function injectConsole(exports, pipe) { + exports.console = Object.freeze({ + log: pipe.emit.bind(null, "console", "log"), + info: pipe.emit.bind(null, "console", "info"), + warn: pipe.emit.bind(null, "console", "warn"), + error: pipe.emit.bind(null, "console", "error"), + debug: pipe.emit.bind(null, "console", "debug"), + exception: pipe.emit.bind(null, "console", "exception"), + trace: pipe.emit.bind(null, "console", "trace") + }); + }, + + injectTimers: function injectTimers(exports, chromeAPI, pipe, console) { + // wrapped functions from `'timer'` module. + // Wrapper adds `try catch` blocks to the callbacks in order to + // emit `error` event on a symbiont if exception is thrown in + // the Worker global scope. + // @see http://www.w3.org/TR/workers/#workerutils + + // List of all living timeouts/intervals + let _timers = Object.create(null); + + // Keep a reference to original timeout functions + let { + setTimeout: chromeSetTimeout, + setInterval: chromeSetInterval, + clearTimeout: chromeClearTimeout, + clearInterval: chromeClearInterval + } = chromeAPI.timers; + + exports.setTimeout = function ContentScriptSetTimeout(callback, delay) { + let params = Array.slice(arguments, 2); + let id = chromeSetTimeout(function() { + try { + delete _timers[id]; + callback.apply(null, params); + } catch(e) { + console.exception(e); + } + }, delay); + _timers[id] = "timeout"; + return id; + }; + exports.clearTimeout = function ContentScriptClearTimeout(id) { + delete _timers[id]; + return chromeClearTimeout(id); + }; + + exports.setInterval = function ContentScriptSetInterval(callback, delay) { + let params = Array.slice(arguments, 2); + let id = chromeSetInterval(function() { + try { + callback.apply(null, params); + } catch(e) { + console.exception(e); + } + }, delay); + _timers[id] = "interval"; + return id; + }; + exports.clearInterval = function ContentScriptClearInterval(id) { + delete _timers[id]; + return chromeClearInterval(id); + }; + pipe.on("destroy", function clearTimeouts() { + // Unregister all setTimeout/setInterval on page unload + for (let id in _timers) { + let kind = _timers[id]; + if (kind == "timeout") + chromeClearTimeout(id); + else + chromeClearInterval(id); + } + }); + }, + + injectMessageAPI: function injectMessageAPI(exports, pipe) { + + let { eventEmitter: port, emit : portEmit } = + ContentWorker.createEventEmitter(pipe.emit.bind(null, "event")); + pipe.on("event", portEmit); + + let self = Object.freeze({ + port: port, + postMessage: pipe.emit.bind(null, "message"), + on: pipe.on.bind(null), + once: pipe.once.bind(null), + removeListener: pipe.removeListener.bind(null), + }); + Object.defineProperty(exports, "self", { + value: self + }); + + // Deprecated use of on/postMessage from globals + exports.postMessage = function deprecatedPostMessage() { + console.warn("The global `postMessage()` function in content " + + "scripts is deprecated in favor of the " + + "`self.postMessage()` function, which works the same. " + + "Replace calls to `postMessage()` with calls to " + + "`self.postMessage()`." + + "For more info on `self.on`, see " + + "<https://addons.mozilla.org/en-US/developers/docs/sdk/latest/dev-guide/addon-development/web-content.html>."); + return self.postMessage.apply(null, arguments); + }; + exports.on = function deprecatedOn() { + console.warn("The global `on()` function in content scripts is " + + "deprecated in favor of the `self.on()` function, " + + "which works the same. Replace calls to `on()` with " + + "calls to `self.on()`" + + "For more info on `self.on`, see " + + "<https://addons.mozilla.org/en-US/developers/docs/sdk/latest/dev-guide/addon-development/web-content.html>."); + return self.on.apply(null, arguments); + }; + + // Deprecated use of `onMessage` from globals + let onMessage = null; + Object.defineProperty(exports, "onMessage", { + get: function () onMessage, + set: function (v) { + if (onMessage) + self.removeListener("message", onMessage); + console.warn("The global `onMessage` function in content scripts " + + "is deprecated in favor of the `self.on()` function. " + + "Replace `onMessage = function (data){}` definitions " + + "with calls to `self.on('message', function (data){})`. " + + "For more info on `self.on`, see " + + "<https://addons.mozilla.org/en-US/developers/docs/sdk/latest/dev-guide/addon-development/web-content.html>."); + onMessage = v; + if (typeof onMessage == "function") + self.on("message", onMessage); + } + }); + }, + + inject: function (exports, chromeAPI, emitToChrome) { + let { pipe, onChromeEvent, hasListenerFor } = + ContentWorker.createPipe(emitToChrome); + ContentWorker.injectConsole(exports, pipe); + ContentWorker.injectTimers(exports, chromeAPI, pipe, exports.console); + ContentWorker.injectMessageAPI(exports, pipe); + return { + emitToContent: onChromeEvent, + hasListenerFor: hasListenerFor + }; + } +}); |