diff --git a/chrome/content/zotero/xpcom/translation/translate.js b/chrome/content/zotero/xpcom/translation/translate.js index fcf1fa637..358583a2c 100644 --- a/chrome/content/zotero/xpcom/translation/translate.js +++ b/chrome/content/zotero/xpcom/translation/translate.js @@ -242,6 +242,9 @@ Zotero.Translate.Sandbox = { }; safeTranslator.setDocument = function(arg) { if (Zotero.isFx && !Zotero.isBookmarklet) { + if (arg.wrappedJSObject && arg.wrappedJSObject.__wrappedObject) { + arg = arg.wrappedJSObject.__wrappedObject; + } return translation.setDocument(new XPCNativeWrapper(arg)); } else { return translation.setDocument(arg); @@ -388,7 +391,7 @@ Zotero.Translate.Sandbox = { if (typeof(safeTranslator[i]) === "function") { safeTranslator[i] = translate._sandboxManager._makeContentForwarder(function(func) { return function() { - func.apply(safeTranslator, this.args.wrappedJSObject); + func.apply(safeTranslator, this.args.wrappedJSObject || this.args); } }(safeTranslator[i])); } @@ -1169,7 +1172,7 @@ Zotero.Translate.Base.prototype = { // translate try { - this._sandboxManager.sandbox["do"+this._entryFunctionSuffix].apply(null, this._getParameters()); + Function.prototype.apply.apply(this._sandboxManager.sandbox["do"+this._entryFunctionSuffix], [null, this._getParameters()]); } catch(e) { this.complete(false, e); return false; @@ -1442,7 +1445,7 @@ Zotero.Translate.Base.prototype = { this.incrementAsyncProcesses("Zotero.Translate#getTranslators"); try { - var returnValue = this._sandboxManager.sandbox["detect"+this._entryFunctionSuffix].apply(null, this._getParameters()); + var returnValue = Function.prototype.apply.apply(this._sandboxManager.sandbox["detect"+this._entryFunctionSuffix], [null, this._getParameters()]); } catch(e) { this.complete(false, e); return; @@ -1987,13 +1990,13 @@ Zotero.Translate.Import.prototype._loadTranslatorPrepareIO = function(translator if(!this._io) { if(Zotero.Translate.IO.Read && this.location && this.location instanceof Components.interfaces.nsIFile) { try { - this._io = new Zotero.Translate.IO.Read(this.location); + this._io = new Zotero.Translate.IO.Read(this.location, this._sandboxManager); } catch(e) { err = e; } } else { try { - this._io = new Zotero.Translate.IO.String(this._string, this.path ? this.path : ""); + this._io = new Zotero.Translate.IO.String(this._string, this.path ? this.path : "", this._sandboxManager); } catch(e) { err = e; } @@ -2156,7 +2159,7 @@ Zotero.Translate.Export.prototype._prepareTranslation = function() { // this is currently hackish since we pass null callbacks to the init function (they have // callbacks to be consistent with import, but they are synchronous, so we ignore them) if(!this.location) { - this._io = new Zotero.Translate.IO.String(null, this.path ? this.path : ""); + this._io = new Zotero.Translate.IO.String(null, this.path ? this.path : "", this._sandboxManager); this._io.init(configOptions["dataMode"], function() {}); } else if(!Zotero.Translate.IO.Write) { throw new Error("Writing to files is not supported in this build of Zotero."); @@ -2344,7 +2347,7 @@ Zotero.Translate.IO = { /** * @class Translate backend for translating from a string */ -Zotero.Translate.IO.String = function(string, uri) { +Zotero.Translate.IO.String = function(string, uri, sandboxManager) { if(string && typeof string === "string") { this.string = string; } else { @@ -2353,6 +2356,7 @@ Zotero.Translate.IO.String = function(string, uri) { this.contentLength = this.string.length; this.bytesRead = 0; this._uri = uri; + this._sandboxManager = sandboxManager; } Zotero.Translate.IO.String.prototype = { @@ -2442,7 +2446,7 @@ Zotero.Translate.IO.String.prototype = { this._xmlInvalid = true; throw e; } - return (Zotero.isFx ? Zotero.Translate.DOMWrapper.wrap(xml) : xml); + return (Zotero.isFx && !Zotero.isBookmarklet ? this._sandboxManager.wrap(xml) : xml); }, "init":function(newMode, callback) { diff --git a/chrome/content/zotero/xpcom/translation/translate_firefox.js b/chrome/content/zotero/xpcom/translation/translate_firefox.js index 6bfcc587a..67594daa6 100644 --- a/chrome/content/zotero/xpcom/translation/translate_firefox.js +++ b/chrome/content/zotero/xpcom/translation/translate_firefox.js @@ -49,7 +49,7 @@ Zotero.Translate.DOMWrapper = new function() { }; function isWrapper(x) { - return isWrappable(x) && (typeof x.SpecialPowers_wrappedObject !== "undefined"); + return isWrappable(x) && (typeof x.__wrappedObject !== "undefined"); }; function unwrapIfWrapped(x) { @@ -130,7 +130,7 @@ Zotero.Translate.DOMWrapper = new function() { throw "Trying to unwrap a non-wrapped object!"; // Unwrap. - return x.SpecialPowers_wrappedObject; + return x.__wrappedObject; }; function crawlProtoChain(obj, fn) { @@ -172,7 +172,7 @@ Zotero.Translate.DOMWrapper = new function() { SpecialPowersHandler.prototype.doGetPropertyDescriptor = function(name, own) { // Handle our special API. - if (name == "SpecialPowers_wrappedObject") + if (name == "__wrappedObject") return { value: this.wrappedObject, writeable: false, configurable: false, enumerable: false }; // Handle __exposedProps__. if (name == "__exposedProps__") @@ -281,7 +281,7 @@ Zotero.Translate.DOMWrapper = new function() { // Insert our special API. It's not enumerable, but getPropertyNames() // includes non-enumerable properties. - var specialAPI = 'SpecialPowers_wrappedObject'; + var specialAPI = '__wrappedObject'; if (props.indexOf(specialAPI) == -1) props.push(specialAPI); @@ -366,6 +366,14 @@ Zotero.Translate.DOMWrapper = new function() { return obj; } } + + /** + * Wraps an object in the same sandbox as another object + */ + this.wrapIn = function(obj, insamebox) { + if(insamebox.__wrappingManager) return insamebox.__wrappingManager.wrap(obj); + return this.wrap(obj); + } /** * Checks whether an object is wrapped by a DOM wrapper @@ -381,7 +389,8 @@ Zotero.Translate.DOMWrapper = new function() { * @param {String|window} sandboxLocation */ Zotero.Translate.SandboxManager = function(sandboxLocation) { - this.sandbox = new Components.utils.Sandbox(sandboxLocation); + // sandboxLocation = Components.classes["@mozilla.org/systemprincipal;1"].createInstance(Components.interfaces.nsIPrincipal); + var sandbox = this.sandbox = new Components.utils.Sandbox(sandboxLocation, {wantComponents:false, wantGlobalProperties:["XMLHttpRequest"]}); this.sandbox.Zotero = {}; // import functions missing from global scope into Fx sandbox @@ -389,28 +398,14 @@ Zotero.Translate.SandboxManager = function(sandboxLocation) { if(typeof sandboxLocation === "object" && "DOMParser" in sandboxLocation) { this.sandbox.DOMParser = sandboxLocation.DOMParser; } else { - var sandbox = this.sandbox; this.sandbox.DOMParser = function() { - var uri, principal; - // get URI - if(typeof sandboxLocation === "string") { // if sandbox specified by URI - var secMan = Services.scriptSecurityManager; - uri = Services.io.newURI(sandboxLocation, "UTF-8", null); - principal = (secMan.getCodebasePrincipal || secMan.getSimpleCodebasePrincipal)(uri); - } else { // if sandbox specified by DOM document - principal = sandboxLocation.document.nodePrincipal; - uri = sandboxLocation.document.documentURIObject; - } - - // initialize DOM parser - var _DOMParser = Components.classes["@mozilla.org/xmlextras/domparser;1"] - .createInstance(Components.interfaces.nsIDOMParser); - _DOMParser.init(principal, uri, uri); - - // expose parseFromString this.__exposedProps__ = {"parseFromString":"r"}; this.parseFromString = function(str, contentType) { - return Zotero.Translate.DOMWrapper.wrap(_DOMParser.parseFromString(str, contentType)); + var xhr = sandbox.XMLHttpRequest(); + xhr.open("GET", "data:"+contentType+";base64,"+btoa(str), false); + xhr.send(); + if (!xhr.responseXML) throw new Error("error parsing XML"); + return xhr.responseXML; } }; } @@ -427,7 +422,55 @@ Zotero.Translate.SandboxManager = function(sandboxLocation) { this.sandbox.XMLSerializer.prototype = {"__exposedProps__":{"serializeToString":"r"}}; var expr = "(function(x) { return function() { this.args = arguments; return x.apply(this); }.bind({}); })"; - this._makeContentForwarder = Components.utils.evalInSandbox(expr, this.sandbox); + this._makeContentForwarder = Components.utils.evalInSandbox(expr, sandbox); + + if (Zotero.platformMajorVersion >= 35) { + var _proxy = Components.utils.evalInSandbox('(function (target, x) {'+ + ' return new Proxy(x, ProxyHandler(target));'+ + '})', sandbox); + var wrap = this.wrap = function(target, x) { + if (target === null || (typeof target !== "object" && typeof target !== "function")) return target; + if (!x) x = new sandbox.Object(); + return _proxy(target, x); + }; + var me = this; + sandbox.ProxyHandler = this._makeContentForwarder(function() { + var target = (this.args.wrappedJSObject || this.args)[0]; + if(target instanceof Components.interfaces.nsISupports) { + target = new XPCNativeWrapper(target); + } + var ret = new sandbox.Object(); + ret.wrappedJSObject.has = function(x, prop) { + return prop in target; + }; + ret.wrappedJSObject.get = function(x, prop, receiver) { + if (prop === "__wrappedObject") return target; + if (prop === "__wrappingManager") return me; + var y = target[prop]; + if (y === null || (typeof y !== "object" && typeof y !== "function")) return y; + return wrap(y, typeof y === "function" ? function() { + var args = Array.prototype.slice.apply(arguments); + for (var i = 0; i < args.length; i++) { + if (typeof args[i] === "object" && args[i] !== null && + args[i].wrappedJSObject && args[i].wrappedJSObject.__wrappedObject) + args[i] = new XPCNativeWrapper(args[i].wrappedJSObject.__wrappedObject); + } + return wrap(y.apply(target, args)); + } : new sandbox.Object()); + }; + ret.wrappedJSObject.ownKeys = function(x) { + return Components.utils.cloneInto(target.getOwnPropertyNames(), sandbox); + }; + ret.wrappedJSObject.enumerate = function(x) { + var y = new sandbox.Array(); + for (var i in target) y.wrappedJSObject.push(i); + return y; + }; + return ret; + }); + } else { + this.wrap = Zotero.Translate.DOMWrapper.wrap; + } } Zotero.Translate.SandboxManager.prototype = { @@ -463,7 +506,7 @@ Zotero.Translate.SandboxManager.prototype = { if(isFunction) { if (Zotero.platformMajorVersion >= 33) { attachTo[localKey] = this._makeContentForwarder(function() { - var args = Array.prototype.slice.apply(this.args.wrappedJSObject); + var args = Array.prototype.slice.apply(this.args.wrappedJSObject || this.args); for(var i = 0; i