commit daf0f8e0b0f605973f7dd89d2aa6f3c389a42418
parent 03ef4723f8d9bcc7631c87c6b54167f538e7824c
Author: Dan Stillman <dstillman@zotero.org>
Date: Sun, 14 Aug 2016 17:27:36 -0400
Merge branch '4.0'
Diffstat:
4 files changed, 188 insertions(+), 218 deletions(-)
diff --git a/chrome/content/zotero/xpcom/translation/translate_firefox.js b/chrome/content/zotero/xpcom/translation/translate_firefox.js
@@ -37,10 +37,12 @@ const BOMs = {
Components.utils.import("resource://gre/modules/NetUtil.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
-Zotero.Translate.DOMWrapper = new function() {
+Zotero.Translate.DOMWrapper = new function() {
+ var Cu = Components.utils;
+
/*
* BEGIN SPECIAL POWERS WRAPPING CODE
- * https://mxr.mozilla.org/mozilla-central/source/testing/mochitest/tests/SimpleTest/specialpowersAPI.js?raw=1
+ * https://dxr.mozilla.org/mozilla-central/source/testing/specialpowers/content/specialpowersAPI.js
*/
function isWrappable(x) {
if (typeof x === "object")
@@ -56,22 +58,67 @@ Zotero.Translate.DOMWrapper = new function() {
return isWrapper(x) ? unwrapPrivileged(x) : x;
};
- function isXrayWrapper(x) {
- try {
- return x.toString().indexOf("XrayWrapper") !== -1;
- } catch(e) {
- // The toString() implementation could theoretically throw. But it never
- // throws for Xray, so we can just assume non-xray in that case.
+ function wrapIfUnwrapped(x) {
+ return isWrapper(x) ? x : wrapPrivileged(x);
+ }
+
+ function isObjectOrArray(obj) {
+ if (Object(obj) !== obj)
return false;
- }
+ let arrayClasses = ['Object', 'Array', 'Int8Array', 'Uint8Array',
+ 'Int16Array', 'Uint16Array', 'Int32Array',
+ 'Uint32Array', 'Float32Array', 'Float64Array',
+ 'Uint8ClampedArray'];
+ let className = Cu.getClassName(obj, true);
+ return arrayClasses.indexOf(className) != -1;
+ }
+
+ // In general, we want Xray wrappers for content DOM objects, because waiving
+ // Xray gives us Xray waiver wrappers that clamp the principal when we cross
+ // compartment boundaries. However, there are some exceptions where we want
+ // to use a waiver:
+ //
+ // * Xray adds some gunk to toString(), which has the potential to confuse
+ // consumers that aren't expecting Xray wrappers. Since toString() is a
+ // non-privileged method that returns only strings, we can just waive Xray
+ // for that case.
+ //
+ // * We implement Xrays to pure JS [[Object]] and [[Array]] instances that
+ // filter out tricky things like callables. This is the right thing for
+ // security in general, but tends to break tests that try to pass object
+ // literals into SpecialPowers. So we waive [[Object]] and [[Array]]
+ // instances before inspecting properties.
+ //
+ // * When we don't have meaningful Xray semantics, we create an Opaque
+ // XrayWrapper for security reasons. For test code, we generally want to see
+ // through that sort of thing.
+ function waiveXraysIfAppropriate(obj, propName) {
+ if (propName == 'toString' || isObjectOrArray(obj) ||
+ /Opaque/.test(Object.prototype.toString.call(obj)))
+ {
+ return XPCNativeWrapper.unwrap(obj);
+ }
+ return obj;
}
// We can't call apply() directy on Xray-wrapped functions, so we have to be
// clever.
function doApply(fun, invocant, args) {
- return Function.prototype.apply.call(fun, invocant, args);
+ // We implement Xrays to pure JS [[Object]] instances that filter out tricky
+ // things like callables. This is the right thing for security in general,
+ // but tends to break tests that try to pass object literals into
+ // SpecialPowers. So we waive [[Object]] instances when they're passed to a
+ // SpecialPowers-wrapped callable.
+ //
+ // Note that the transitive nature of Xray waivers means that any property
+ // pulled off such an object will also be waived, and so we'll get principal
+ // clamping for Xrayed DOM objects reached from literals, so passing things
+ // like {l : xoWin.location} won't work. Hopefully the rabbit hole doesn't
+ // go that deep.
+ args = args.map(x => isObjectOrArray(x) ? Cu.waiveXrays(x) : x);
+ return Reflect.apply(fun, invocant, args);
}
-
+
function wrapPrivileged(obj, overrides) {
// Primitives pass straight through.
@@ -82,38 +129,13 @@ Zotero.Translate.DOMWrapper = new function() {
if (isWrapper(obj))
throw "Trying to double-wrap object!";
- // Make our core wrapper object.
- var handler = new SpecialPowersHandler(obj, overrides);
-
- // If the object is callable, make a function proxy.
- if (typeof obj === "function") {
- var callTrap = function() {
- // The invocant and arguments may or may not be wrappers. Unwrap them if necessary.
- var invocant = unwrapIfWrapped(this);
- var unwrappedArgs = Array.prototype.slice.call(arguments).map(unwrapIfWrapped);
-
- return wrapPrivileged(doApply(obj, invocant, unwrappedArgs));
- };
- var constructTrap = function() {
- // The arguments may or may not be wrappers. Unwrap them if necessary.
- var unwrappedArgs = Array.prototype.slice.call(arguments).map(unwrapIfWrapped);
-
- // Constructors are tricky, because we can't easily call apply on them.
- // As a workaround, we create a wrapper constructor with the same
- // |prototype| property.
- var FakeConstructor = function() {
- doApply(obj, this, unwrappedArgs);
- };
- FakeConstructor.prototype = obj.prototype;
-
- return wrapPrivileged(new FakeConstructor());
- };
+ let dummy;
+ if (typeof obj === "function")
+ dummy = function() {};
+ else
+ dummy = Object.create(null);
- return Proxy.createFunction(handler, callTrap, constructTrap);
- }
-
- // Otherwise, just make a regular object proxy.
- return Proxy.create(handler);
+ return new Proxy(dummy, new SpecialPowersHandler(obj, overrides));
};
function unwrapPrivileged(x) {
@@ -129,16 +151,9 @@ Zotero.Translate.DOMWrapper = new function() {
if (!isWrapper(x))
throw "Trying to unwrap a non-wrapped object!";
- // Unwrap.
- return x.__wrappedObject;
- };
-
- function crawlProtoChain(obj, fn) {
- var rv = fn(obj);
- if (rv !== undefined)
- return rv;
- if (Object.getPrototypeOf(obj))
- return crawlProtoChain(Object.getPrototypeOf(obj), fn);
+ var obj = x.__wrappedObject;
+ // unwrapped.
+ return obj;
};
/*
@@ -160,188 +175,146 @@ Zotero.Translate.DOMWrapper = new function() {
};
ExposedPropsWaiver = new Proxy({}, ExposedPropsWaiverHandler());
- function SpecialPowersHandler(obj, overrides) {
- this.wrappedObject = obj;
- this.overrides = overrides ? overrides : {};
- };
-
- // Allow us to transitively maintain the membrane by wrapping descriptors
- // we return.
- SpecialPowersHandler.prototype.doGetPropertyDescriptor = function(name, own) {
-
- // Handle our special API.
- if (name == "__wrappedObject")
- return { value: this.wrappedObject, writeable: false, configurable: false, enumerable: false };
- if (name == "__wrapperOverrides")
- return { value: this.overrides, writeable: false, configurable: false, enumerable: false };
- // Handle __exposedProps__.
- if (name == "__exposedProps__")
- return { value: ExposedPropsWaiver, writable: false, configurable: false, enumerable: false };
-
- // In general, we want Xray wrappers for content DOM objects, because waiving
- // Xray gives us Xray waiver wrappers that clamp the principal when we cross
- // compartment boundaries. However, Xray adds some gunk to toString(), which
- // has the potential to confuse consumers that aren't expecting Xray wrappers.
- // Since toString() is a non-privileged method that returns only strings, we
- // can just waive Xray for that case.
- var obj = name == 'toString' ? XPCNativeWrapper.unwrap(this.wrappedObject)
- : this.wrappedObject;
+ function SpecialPowersHandler(wrappedObject, overrides) {
+ this.wrappedObject = wrappedObject;
+ this.overrides = overrides ? overrides: {};
+ }
- //
- // Call through to the wrapped object.
- //
- // Note that we have several cases here, each of which requires special handling.
- //
- var desc;
-
- // Hack for overriding some properties
- if (this.overrides.hasOwnProperty(name))
- return { "enumerable": true, "value": this.overrides[name] };
- // Case 1: Own Properties.
- //
- // This one is easy, thanks to Object.getOwnPropertyDescriptor().
- else if (own)
- desc = Object.getOwnPropertyDescriptor(obj, name);
+ SpecialPowersHandler.prototype = {
+ construct(target, args) {
+ // The arguments may or may not be wrappers. Unwrap them if necessary.
+ var unwrappedArgs = Array.prototype.slice.call(args).map(unwrapIfWrapped);
- // Case 2: Not own, not Xray-wrapped.
- //
- // Here, we can just crawl the prototype chain, calling
- // Object.getOwnPropertyDescriptor until we find what we want.
- //
- // NB: Make sure to check this.wrappedObject here, rather than obj, because
- // we may have waived Xray on obj above.
- else if (!isXrayWrapper(this.wrappedObject))
+ // We want to invoke "obj" as a constructor, but using unwrappedArgs as
+ // the arguments. Make sure to wrap and re-throw exceptions!
try {
- desc = crawlProtoChain(obj, function(o) {return Object.getOwnPropertyDescriptor(o, name);});
- } catch(e) {
- // we hit bug 560072 if DOM is not wrapped
- // https://bugzilla.mozilla.org/show_bug.cgi?id=560072
- if (name in obj) {
- // same guess as below
- desc = {value: obj[name], writable: false, configurable: true, enumerable: true};
- }
+ return wrapIfUnwrapped(Reflect.construct(this.wrappedObject, unwrappedArgs));
+ } catch (e) {
+ throw wrapIfUnwrapped(e);
}
+ },
- // Case 3: Not own, Xray-wrapped.
- //
- // This one is harder, because we Xray wrappers are flattened and don't have
- // a prototype. Xray wrappers are proxies themselves, so we'd love to just call
- // through to XrayWrapper<Base>::getPropertyDescriptor(). Unfortunately though,
- // we don't have any way to do that. :-(
- //
- // So we first try with a call to getOwnPropertyDescriptor(). If that fails,
- // we make up a descriptor, using some assumptions about what kinds of things
- // tend to live on the prototypes of Xray-wrapped objects.
- else {
- desc = Object.getOwnPropertyDescriptor(obj, name);
- if (!desc) {
- var getter = Object.prototype.__lookupGetter__.call(obj, name);
- var setter = Object.prototype.__lookupSetter__.call(obj, name);
- if (getter || setter)
- desc = {get: getter, set: setter, configurable: true, enumerable: true};
- else if (name in obj)
- desc = {value: obj[name], writable: false, configurable: true, enumerable: true};
+ apply(target, thisValue, args) {
+ // The invocant and arguments may or may not be wrappers. Unwrap
+ // them if necessary.
+ var invocant = unwrapIfWrapped(thisValue);
+ var unwrappedArgs = Array.prototype.slice.call(args).map(unwrapIfWrapped);
+
+ try {
+ return wrapIfUnwrapped(doApply(this.wrappedObject, invocant, unwrappedArgs));
+ } catch (e) {
+ // Wrap exceptions and re-throw them.
+ throw wrapIfUnwrapped(e);
}
- }
+ },
- // Bail if we've got nothing.
- if (typeof desc === 'undefined')
- return undefined;
-
- // When accessors are implemented as JSPropertyOps rather than JSNatives (ie,
- // QuickStubs), the js engine does the wrong thing and treats it as a value
- // descriptor rather than an accessor descriptor. Jorendorff suggested this
- // little hack to work around it. See bug 520882.
- if (desc && 'value' in desc && desc.value === undefined)
- desc.value = obj[name];
-
- // A trapping proxy's properties must always be configurable, but sometimes
- // this we get non-configurable properties from Object.getOwnPropertyDescriptor().
- // Tell a white lie.
- desc.configurable = true;
-
- // Transitively maintain the wrapper membrane.
- function wrapIfExists(key) { if (key in desc) desc[key] = wrapPrivileged(desc[key]); };
- wrapIfExists('value');
- wrapIfExists('get');
- wrapIfExists('set');
-
- return desc;
- };
+ has(target, prop) {
+ if (prop === "__wrappedObject")
+ return true;
- SpecialPowersHandler.prototype.getOwnPropertyDescriptor = function(name) {
- return this.doGetPropertyDescriptor(name, true);
- };
+ if (this.overrides[prop] !== undefined) {
+ return true;
+ }
+
+ return Reflect.has(this.wrappedObject, prop);
+ },
- SpecialPowersHandler.prototype.getPropertyDescriptor = function(name) {
- return this.doGetPropertyDescriptor(name, false);
- };
+ get(target, prop, receiver) {
+ if (prop === "__wrappedObject")
+ return this.wrappedObject;
- function doGetOwnPropertyNames(obj, props) {
+ if (prop in this.overrides) {
+ return this.overrides[prop];
+ }
+
+ let obj = waiveXraysIfAppropriate(this.wrappedObject, prop);
+ return wrapIfUnwrapped(Reflect.get(obj, prop));
+ },
+
+ set(target, prop, val, receiver) {
+ if (prop === "__wrappedObject")
+ return false;
+
+ let obj = waiveXraysIfAppropriate(this.wrappedObject, prop);
+ return Reflect.set(obj, prop, unwrapIfWrapped(val));
+ },
+
+ delete(target, prop) {
+ if (prop === "__wrappedObject")
+ return false;
+
+ return Reflect.deleteProperty(this.wrappedObject, prop);
+ },
+
+ defineProperty(target, prop, descriptor) {
+ throw "Can't call defineProperty on SpecialPowers wrapped object";
+ },
+
+ getOwnPropertyDescriptor(target, prop) {
+ // Handle our special API.
+ if (prop === "__wrappedObject") {
+ return { value: this.wrappedObject, writeable: true,
+ configurable: true, enumerable: false };
+ }
+ if (name == "__wrapperOverrides") {
+ return { value: this.overrides, writeable: false, configurable: false, enumerable: false };
+ }
+ // Handle __exposedProps__.
+ if (name == "__exposedProps__") {
+ return { value: ExposedPropsWaiver, writable: false, configurable: false, enumerable: false };
+ }
- // Insert our special API. It's not enumerable, but getPropertyNames()
- // includes non-enumerable properties.
- var specialAPI = '__wrappedObject';
- if (props.indexOf(specialAPI) == -1)
- props.push(specialAPI);
-
-
- // Do the normal thing.
- var flt = function(a) { return props.indexOf(a) == -1; };
- props = props.concat(Object.getOwnPropertyNames(obj).filter(flt));
+ if (prop in this.overrides) {
+ return this.overrides[prop];
+ }
+
+ let obj = waiveXraysIfAppropriate(this.wrappedObject, prop);
+ let desc = Reflect.getOwnPropertyDescriptor(obj, prop);
- // If we've got an Xray wrapper, include the expandos as well.
- if ('wrappedJSObject' in obj)
- props = props.concat(Object.getOwnPropertyNames(obj.wrappedJSObject)
- .filter(flt));
+ if (desc === undefined)
+ return undefined;
- return props;
- }
+ // Transitively maintain the wrapper membrane.
+ function wrapIfExists(key) {
+ if (key in desc)
+ desc[key] = wrapIfUnwrapped(desc[key]);
+ };
- SpecialPowersHandler.prototype.getOwnPropertyNames = function() {
- return doGetOwnPropertyNames(this.wrappedObject, []);
- };
+ wrapIfExists('value');
+ wrapIfExists('get');
+ wrapIfExists('set');
- SpecialPowersHandler.prototype.getPropertyNames = function() {
+ // A trapping proxy's properties must always be configurable, but sometimes
+ // we come across non-configurable properties. Tell a white lie.
+ desc.configurable = true;
- // Manually walk the prototype chain, making sure to add only property names
- // that haven't been overridden.
- //
- // There's some trickiness here with Xray wrappers. Xray wrappers don't have
- // a prototype, so we need to unwrap them if we want to get all of the names
- // with Object.getOwnPropertyNames(). But we don't really want to unwrap the
- // base object, because that will include expandos that are inaccessible via
- // our implementation of get{,Own}PropertyDescriptor(). So we unwrap just
- // before accessing the prototype. This ensures that we get Xray vision on
- // the base object, and no Xray vision for the rest of the way up.
- var obj = this.wrappedObject;
- var props = [];
- props = doGetOwnPropertyNames(this.overrides, props);
- while (obj) {
- props = doGetOwnPropertyNames(obj, props);
- obj = Object.getPrototypeOf(XPCNativeWrapper.unwrap(obj));
- }
- return props;
- };
+ return desc;
+ },
- SpecialPowersHandler.prototype.defineProperty = function(name, desc) {
- return Object.defineProperty(this.wrappedObject, name, desc);
- };
+ ownKeys(target) {
+ // Insert our special API. It's not enumerable, but ownKeys()
+ // includes non-enumerable properties.
+ let props = ['__wrappedObject'];
- SpecialPowersHandler.prototype.delete = function(name) {
- return delete this.wrappedObject[name];
- };
+ // Do the normal thing.
+ let flt = (a) => !props.includes(a);
+ props = props.concat(Object.keys(this.overrides).filter(flt));
+ props = props.concat(Reflect.ownKeys(this.wrappedObject).filter(flt));
+
+ // If we've got an Xray wrapper, include the expandos as well.
+ if ('wrappedJSObject' in this.wrappedObject) {
+ props = props.concat(Reflect.ownKeys(this.wrappedObject.wrappedJSObject)
+ .filter(flt));
+ }
- SpecialPowersHandler.prototype.fix = function() { return undefined; /* Throws a TypeError. */ };
+ return props;
+ },
- // Per the ES5 spec this is a derived trap, but it's fundamental in spidermonkey
- // for some reason. See bug 665198.
- SpecialPowersHandler.prototype.enumerate = function() {
- var t = this;
- var filt = function(name) { return t.getPropertyDescriptor(name).enumerable; };
- return this.getPropertyNames().filter(filt);
+ preventExtensions(target) {
+ throw "Can't call preventExtensions on SpecialPowers wrapped object";
+ }
};
-
+
/*
* END SPECIAL POWERS WRAPPING CODE
*/
diff --git a/components/zotero-service.js b/components/zotero-service.js
@@ -319,9 +319,6 @@ function makeZoteroContext(isConnector) {
subscriptLoader.loadSubScript("chrome://zotero/content/xpcom/standalone.js", zContext);
}
- // load nsTransferable (query: do we still use this?)
- subscriptLoader.loadSubScript("chrome://global/content/nsTransferable.js", zContext);
-
// add connector-related properties
zContext.Zotero.isConnector = isConnector;
zContext.Zotero.instanceID = instanceID;
diff --git a/resource/schema/repotime.txt b/resource/schema/repotime.txt
@@ -1 +1 @@
-2016-07-29 23:30:00
+2016-08-14 00:15:00
diff --git a/test/runtests.sh b/test/runtests.sh
@@ -16,7 +16,7 @@ function makePath {
if [ -z "$FX_EXECUTABLE" ]; then
if [ "`uname`" == "Darwin" ]; then
- FX_EXECUTABLE="/Applications/Firefox.app/Contents/MacOS/firefox"
+ FX_EXECUTABLE="/Applications/Firefox Unbranded.app/Contents/MacOS/firefox"
else
FX_EXECUTABLE="firefox"
fi