Index: extensions/renderer/resources/utils.js |
diff --git a/extensions/renderer/resources/utils.js b/extensions/renderer/resources/utils.js |
index eee1356272ed1fe7d182cb6a6343480f3f07b441..fd70c603ddffe295c6dc31d8efe495ca82c0760b 100644 |
--- a/extensions/renderer/resources/utils.js |
+++ b/extensions/renderer/resources/utils.js |
@@ -2,7 +2,6 @@ |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
-var createClassWrapper = requireNative('utils').createClassWrapper; |
var nativeDeepCopy = requireNative('utils').deepCopy; |
var schemaRegistry = requireNative('schema_registry'); |
var CHECK = requireNative('logging').CHECK; |
@@ -65,15 +64,21 @@ function loadTypeSchema(typeName, defaultSchema) { |
} |
/** |
- * Takes a private class implementation |cls| and exposes a subset of its |
- * methods |functions| and properties |properties| and |readonly| in a public |
- * wrapper class that it returns. Within bindings code, you can access the |
- * implementation from an instance of the wrapper class using |
+ * Takes a private class implementation |privateClass| and exposes a subset of |
+ * its methods |functions| and properties |properties| and |readonly| to a |
+ * public wrapper class that should be passed in. Within bindings code, you can |
+ * access the implementation from an instance of the wrapper class using |
* privates(instance).impl, and from the implementation class you can access |
* the wrapper using this.wrapper (or implInstance.wrapper if you have another |
* instance of the implementation class). |
- * @param {string} name The name of the exposed wrapper class. |
- * @param {Object} cls The class implementation. |
+ * |
+ * |publicClass| should be a constructor that calls constructPrivate() like so: |
+ * |
+ * privates(publicClass).constructPrivate(this, arguments); |
+ * |
+ * @param {function} publicClass The publicly exposed wrapper class. This must |
+ * be a named function, and the name appears in stack traces. |
+ * @param {Object} privateClass The class implementation. |
* @param {{superclass: ?Function, |
* functions: ?Array<string>, |
* properties: ?Array<string>, |
@@ -84,13 +89,49 @@ function loadTypeSchema(typeName, defaultSchema) { |
* delegated to the implementation; |properties| are gettable/settable |
* properties and |readonly| are read-only properties. |
*/ |
-function expose(name, cls, exposed) { |
- var publicClass = createClassWrapper(name, cls, exposed.superclass); |
+function expose(publicClass, privateClass, exposed) { |
+ // TODO(robwu): Fix callers and uncomment this assertion. |
+ // DCHECK(!(privateClass instanceof $Object.self)); |
+ |
+ $Object.setPrototypeOf(exposed, null); |
+ |
+ // This should be called by publicClass. |
+ privates(publicClass).constructPrivate = function(self, args) { |
+ if (!(self instanceof publicClass)) { |
+ throw new Error('Please use "new ' + publicClass.name + '"'); |
+ } |
+ // The "instanceof publicClass" check can easily be spoofed, so we check |
+ // whether the private impl is already set before continuing. |
+ var privateSelf = privates(self); |
+ if ('impl' in privateSelf) { |
+ throw new Error('Object ' + publicClass.name + ' is already constructed'); |
+ } |
+ var privateObj = $Object.create(privateClass.prototype); |
+ $Function.apply(privateClass, privateObj, args); |
+ privateObj.wrapper = self; |
+ privateSelf.impl = privateObj; |
+ }; |
+ |
+ function getPrivateImpl(self) { |
+ var impl = privates(self).impl; |
+ if (!(impl instanceof privateClass)) { |
+ // Either the object is not constructed, or the property descriptor is |
+ // used on a target that is not an instance of publicClass. |
+ throw new Error('impl is not an instance of ' + privateClass.name); |
+ } |
+ return impl; |
+ } |
+ |
+ var publicClassPrototype = { |
+ // The final prototype will be assigned at the end of this method. |
+ __proto__: null, |
+ constructor: publicClass, |
+ }; |
if ('functions' in exposed) { |
$Array.forEach(exposed.functions, function(func) { |
- publicClass.prototype[func] = function() { |
- var impl = privates(this).impl; |
+ publicClassPrototype[func] = function() { |
+ var impl = getPrivateImpl(this); |
return $Function.apply(impl[func], impl, arguments); |
}; |
}); |
@@ -98,13 +139,14 @@ function expose(name, cls, exposed) { |
if ('properties' in exposed) { |
$Array.forEach(exposed.properties, function(prop) { |
- $Object.defineProperty(publicClass.prototype, prop, { |
+ $Object.defineProperty(publicClassPrototype, prop, { |
+ __proto__: null, |
enumerable: true, |
get: function() { |
- return privates(this).impl[prop]; |
+ return getPrivateImpl(this)[prop]; |
}, |
set: function(value) { |
- var impl = privates(this).impl; |
+ var impl = getPrivateImpl(this); |
delete impl[prop]; |
impl[prop] = value; |
} |
@@ -114,15 +156,22 @@ function expose(name, cls, exposed) { |
if ('readonly' in exposed) { |
$Array.forEach(exposed.readonly, function(readonly) { |
- $Object.defineProperty(publicClass.prototype, readonly, { |
+ $Object.defineProperty(publicClassPrototype, readonly, { |
+ __proto__: null, |
enumerable: true, |
get: function() { |
- return privates(this).impl[readonly]; |
+ return getPrivateImpl(this)[readonly]; |
}, |
}); |
}); |
} |
+ // The prototype properties have been installed. Now we can safely assign an |
+ // unsafe prototype and export the class to the public. |
+ var superclass = exposed.superclass || $Object.self; |
+ $Object.setPrototypeOf(publicClassPrototype, superclass.prototype); |
+ publicClass.prototype = publicClassPrototype; |
+ |
return publicClass; |
} |