Ensure that privates are private.
- Remove JS code injection functionality from UtilsNativeHandler.
- Ensure that utils.expose only exposes public properties.
- Prevent privates from getting poisoned via arbitrary constructor invocations.
- Prevent privates from leaking through prototypes.
BUG=603748
Review URL: https://codereview.chromium.org/1903303002
Cr-Commit-Position: refs/heads/master@{#389292}
(cherry picked from commit 77e0fbe12e32b16d5c1d7c0380b45fde363004b2)
Review URL: https://codereview.chromium.org/1938123002 .
Cr-Commit-Position: refs/branch-heads/2704@{#337}
Cr-Branched-From: 6e53600def8f60d8c632fadc70d7c1939ccea347-refs/heads/master@{#386251}
diff --git a/chrome/renderer/resources/extensions/automation/automation_event.js b/chrome/renderer/resources/extensions/automation/automation_event.js
index 7d04ecc..0ba408d 100644
--- a/chrome/renderer/resources/extensions/automation/automation_event.js
+++ b/chrome/renderer/resources/extensions/automation/automation_event.js
@@ -19,8 +19,18 @@
}
};
-exports.AutomationEvent = utils.expose(
- 'AutomationEvent',
- AutomationEventImpl,
- { functions: ['stopPropagation'],
- readonly: ['type', 'target', 'eventPhase'] });
+function AutomationEvent() {
+ privates(AutomationEvent).constructPrivate(this, arguments);
+}
+utils.expose(AutomationEvent, AutomationEventImpl, {
+ functions: [
+ 'stopPropagation',
+ ],
+ readonly: [
+ 'type',
+ 'target',
+ 'eventPhase',
+ ],
+});
+
+exports.$set('AutomationEvent', AutomationEvent);
diff --git a/chrome/renderer/resources/extensions/automation/automation_node.js b/chrome/renderer/resources/extensions/automation/automation_node.js
index ade163d..ab5d26a 100644
--- a/chrome/renderer/resources/extensions/automation/automation_node.js
+++ b/chrome/renderer/resources/extensions/automation/automation_node.js
@@ -937,46 +937,57 @@
},
};
-var AutomationNode = utils.expose('AutomationNode',
- AutomationNodeImpl,
- { functions: ['doDefault',
- 'find',
- 'findAll',
- 'focus',
- 'makeVisible',
- 'matches',
- 'setSelection',
- 'showContextMenu',
- 'addEventListener',
- 'removeEventListener',
- 'domQuerySelector',
- 'toString',
- 'boundsForRange'],
- readonly: publicAttributes.concat(
- ['parent',
- 'firstChild',
- 'lastChild',
- 'children',
- 'previousSibling',
- 'nextSibling',
- 'isRootNode',
- 'role',
- 'state',
- 'location',
- 'indexInParent',
- 'root']) });
+function AutomationNode() {
+ privates(AutomationNode).constructPrivate(this, arguments);
+}
+utils.expose(AutomationNode, AutomationNodeImpl, {
+ functions: [
+ 'doDefault',
+ 'find',
+ 'findAll',
+ 'focus',
+ 'makeVisible',
+ 'matches',
+ 'setSelection',
+ 'showContextMenu',
+ 'addEventListener',
+ 'removeEventListener',
+ 'domQuerySelector',
+ 'toString',
+ 'boundsForRange',
+ ],
+ readonly: $Array.concat(publicAttributes, [
+ 'parent',
+ 'firstChild',
+ 'lastChild',
+ 'children',
+ 'previousSibling',
+ 'nextSibling',
+ 'isRootNode',
+ 'role',
+ 'state',
+ 'location',
+ 'indexInParent',
+ 'root',
+ ]),
+});
-var AutomationRootNode = utils.expose('AutomationRootNode',
- AutomationRootNodeImpl,
- { superclass: AutomationNode,
- readonly: ['docTitle',
- 'docUrl',
- 'docLoaded',
- 'docLoadingProgress',
- 'anchorObject',
- 'anchorOffset',
- 'focusObject',
- 'focusOffset'] });
+function AutomationRootNode() {
+ privates(AutomationRootNode).constructPrivate(this, arguments);
+}
+utils.expose(AutomationRootNode, AutomationRootNodeImpl, {
+ superclass: AutomationNode,
+ readonly: [
+ 'docTitle',
+ 'docUrl',
+ 'docLoaded',
+ 'docLoadingProgress',
+ 'anchorObject',
+ 'anchorOffset',
+ 'focusObject',
+ 'focusOffset',
+ ],
+});
AutomationRootNode.get = function(treeID) {
return AutomationRootNodeImpl.get(treeID);
diff --git a/chrome/renderer/resources/extensions/enterprise_platform_keys/key_pair.js b/chrome/renderer/resources/extensions/enterprise_platform_keys/key_pair.js
index dc06f16..1cab18f 100644
--- a/chrome/renderer/resources/extensions/enterprise_platform_keys/key_pair.js
+++ b/chrome/renderer/resources/extensions/enterprise_platform_keys/key_pair.js
@@ -30,6 +30,14 @@
false /* not extractable */);
};
-exports.KeyPair = utils.expose('KeyPair',
- KeyPairImpl,
- {readonly:['publicKey', 'privateKey']});
+function KeyPair() {
+ privates(KeyPair).constructPrivate(this, arguments);
+}
+utils.expose(KeyPair, KeyPairImpl, {
+ readonly: [
+ 'publicKey',
+ 'privateKey',
+ ],
+});
+
+exports.$set('KeyPair', KeyPair);
diff --git a/chrome/renderer/resources/extensions/enterprise_platform_keys/subtle_crypto.js b/chrome/renderer/resources/extensions/enterprise_platform_keys/subtle_crypto.js
index ece24305..c53bec3 100644
--- a/chrome/renderer/resources/extensions/enterprise_platform_keys/subtle_crypto.js
+++ b/chrome/renderer/resources/extensions/enterprise_platform_keys/subtle_crypto.js
@@ -6,7 +6,6 @@
var internalAPI = require('enterprise.platformKeys.internalAPI');
var intersect = require('platformKeys.utils').intersect;
var subtleCryptoModule = require('platformKeys.SubtleCrypto');
-var SubtleCrypto = subtleCryptoModule.SubtleCrypto;
var SubtleCryptoImpl = subtleCryptoModule.SubtleCryptoImpl;
var KeyPair = require('enterprise.platformKeys.KeyPair').KeyPair;
var KeyUsage = require('platformKeys.Key').KeyUsage;
@@ -133,11 +132,15 @@
});
};
-exports.SubtleCrypto =
- utils.expose('SubtleCrypto',
- EnterpriseSubtleCryptoImpl,
- {
- superclass: SubtleCrypto,
- functions: ['generateKey']
- // ['sign', 'exportKey'] are exposed by the base class
- });
+function SubtleCrypto() {
+ privates(SubtleCrypto).constructPrivate(this, arguments);
+}
+utils.expose(SubtleCrypto, EnterpriseSubtleCryptoImpl, {
+ superclass: subtleCryptoModule.SubtleCrypto,
+ functions: [
+ 'generateKey',
+ // 'sign', 'exportKey' are exposed by the base class
+ ],
+});
+
+exports.$set('SubtleCrypto', SubtleCrypto);
diff --git a/chrome/renderer/resources/extensions/enterprise_platform_keys/token.js b/chrome/renderer/resources/extensions/enterprise_platform_keys/token.js
index dee7952..266db59 100644
--- a/chrome/renderer/resources/extensions/enterprise_platform_keys/token.js
+++ b/chrome/renderer/resources/extensions/enterprise_platform_keys/token.js
@@ -15,5 +15,14 @@
this.subtleCrypto = new SubtleCrypto(id);
};
-exports.Token =
- utils.expose('Token', TokenImpl, {readonly:['id', 'subtleCrypto']});
+function Token() {
+ privates(Token).constructPrivate(this, arguments);
+}
+utils.expose(Token, TokenImpl, {
+ readonly: [
+ 'id',
+ 'subtleCrypto',
+ ],
+});
+
+exports.$set('Token', Token);
diff --git a/chrome/renderer/resources/extensions/platform_keys/key.js b/chrome/renderer/resources/extensions/platform_keys/key.js
index ee29bf0..bea1fc40 100644
--- a/chrome/renderer/resources/extensions/platform_keys/key.js
+++ b/chrome/renderer/resources/extensions/platform_keys/key.js
@@ -49,10 +49,17 @@
}
});
-var Key = utils.expose(
- 'Key',
- KeyImpl,
- {superclass: KeyBase, readonly: ['extractable', 'type', 'usages']});
+function Key() {
+ privates(Key).constructPrivate(this, arguments);
+}
+utils.expose(Key, KeyImpl, {
+ superclass: KeyBase,
+ readonly: [
+ 'extractable',
+ 'type',
+ 'usages',
+ ],
+});
/**
* Returns |key|'s Subject Public Key Info. Throws an exception if |key| is not
diff --git a/chrome/renderer/resources/extensions/platform_keys/subtle_crypto.js b/chrome/renderer/resources/extensions/platform_keys/subtle_crypto.js
index 14a5388..3904003 100644
--- a/chrome/renderer/resources/extensions/platform_keys/subtle_crypto.js
+++ b/chrome/renderer/resources/extensions/platform_keys/subtle_crypto.js
@@ -110,10 +110,16 @@
});
};
-// Required for subclassing.
-exports.SubtleCryptoImpl = SubtleCryptoImpl
+function SubtleCrypto() {
+ privates(SubtleCrypto).constructPrivate(this, arguments);
+}
+utils.expose(SubtleCrypto, SubtleCryptoImpl, {
+ functions: [
+ 'sign',
+ 'exportKey',
+ ],
+});
-exports.SubtleCrypto =
- utils.expose('SubtleCrypto',
- SubtleCryptoImpl,
- {functions:['sign', 'exportKey']});
+// Required for subclassing.
+exports.$set('SubtleCryptoImpl', SubtleCryptoImpl);
+exports.$set('SubtleCrypto', SubtleCrypto);
diff --git a/chrome/renderer/resources/extensions/web_view/chrome_web_view.js b/chrome/renderer/resources/extensions/web_view/chrome_web_view.js
index 2d8c445..abad162 100644
--- a/chrome/renderer/resources/extensions/web_view/chrome_web_view.js
+++ b/chrome/renderer/resources/extensions/web_view/chrome_web_view.js
@@ -12,7 +12,7 @@
var EventBindings = require('event_bindings');
var GuestViewInternalNatives = requireNative('guest_view_internal');
var idGeneratorNatives = requireNative('id_generator');
-var Utils = require('utils');
+var utils = require('utils');
var WebViewImpl = require('webView').WebViewImpl;
// This is the only "webViewInternal.onClicked" named event for this renderer.
@@ -122,9 +122,17 @@
return $Function.apply(ChromeWebView.contextMenusUpdate, null, args);
};
-var WebViewContextMenus = Utils.expose(
- 'WebViewContextMenus', WebViewContextMenusImpl,
- { functions: ['create', 'remove', 'removeAll', 'update'] });
+function WebViewContextMenus() {
+ privates(WebViewContextMenus).constructPrivate(this, arguments);
+}
+utils.expose(WebViewContextMenus, WebViewContextMenusImpl, {
+ functions: [
+ 'create',
+ 'remove',
+ 'removeAll',
+ 'update',
+ ],
+});
// -----------------------------------------------------------------------------
@@ -132,7 +140,7 @@
if (!this.contextMenusOnContextMenuEvent_) {
var eventName = 'chromeWebViewInternal.onContextMenuShow';
var eventSchema =
- Utils.lookup(ChromeWebViewSchema.events, 'name', 'onShow');
+ utils.lookup(ChromeWebViewSchema.events, 'name', 'onShow');
var eventOptions = {supportsListeners: true};
this.contextMenusOnContextMenuEvent_ = new ContextMenusOnContextMenuEvent(
this.viewInstanceId, eventName, eventSchema, eventOptions);
@@ -152,7 +160,7 @@
if (!this.contextMenusOnClickedEvent_) {
var eventName = 'chromeWebViewInternal.onClicked';
var eventSchema =
- Utils.lookup(ChromeWebViewSchema.events, 'name', 'onClicked');
+ utils.lookup(ChromeWebViewSchema.events, 'name', 'onClicked');
var eventOptions = {supportsListeners: true};
var onClickedEvent = new ContextMenusOnClickedEvent(
this.viewInstanceId, eventName, eventSchema, eventOptions);
diff --git a/extensions/renderer/module_system.cc b/extensions/renderer/module_system.cc
index 692939e..4ff9b5c 100644
--- a/extensions/renderer/module_system.cc
+++ b/extensions/renderer/module_system.cc
@@ -615,6 +615,10 @@
ToV8StringUnsafe(GetIsolate(), "Failed to create privates"));
return;
}
+ v8::Maybe<bool> maybe =
+ privates.As<v8::Object>()->SetPrototype(context()->v8_context(),
+ v8::Null(args.GetIsolate()));
+ CHECK(maybe.IsJust() && maybe.FromJust());
SetPrivate(obj, "privates", privates);
}
args.GetReturnValue().Set(privates);
diff --git a/extensions/renderer/module_system_unittest.cc b/extensions/renderer/module_system_unittest.cc
index f49b3b9..5803ad0 100644
--- a/extensions/renderer/module_system_unittest.cc
+++ b/extensions/renderer/module_system_unittest.cc
@@ -505,4 +505,14 @@
RunResolvedPromises();
}
+TEST_F(ModuleSystemTest, TestPrivatesIsPrivate) {
+ ModuleSystem::NativesEnabledScope natives_enabled_scope(
+ env()->module_system());
+ env()->RegisterModule(
+ "test",
+ "var v = privates({});"
+ "requireNative('assert').AssertFalse(v instanceof Object);");
+ env()->module_system()->Require("test");
+}
+
} // namespace extensions
diff --git a/extensions/renderer/resources/event.js b/extensions/renderer/resources/event.js
index a6d64cf..105f7114 100644
--- a/extensions/renderer/resources/event.js
+++ b/extensions/renderer/resources/event.js
@@ -490,17 +490,22 @@
ruleFunctionSchemas.getRules.parameters);
}
- var Event = utils.expose('Event', EventImpl, { functions: [
- 'addListener',
- 'removeListener',
- 'hasListener',
- 'hasListeners',
- 'dispatchToListener',
- 'dispatch',
- 'addRules',
- 'removeRules',
- 'getRules'
- ] });
+ function Event() {
+ privates(Event).constructPrivate(this, arguments);
+ }
+ utils.expose(Event, EventImpl, {
+ functions: [
+ 'addListener',
+ 'removeListener',
+ 'hasListener',
+ 'hasListeners',
+ 'dispatchToListener',
+ 'dispatch',
+ 'addRules',
+ 'removeRules',
+ 'getRules',
+ ],
+ });
// NOTE: Event is (lazily) exposed as chrome.Event from dispatcher.cc.
exports.$set('Event', Event);
diff --git a/extensions/renderer/resources/messaging.js b/extensions/renderer/resources/messaging.js
index 3b98432..afd7244 100644
--- a/extensions/renderer/resources/messaging.js
+++ b/extensions/renderer/resources/messaging.js
@@ -400,15 +400,20 @@
return alignedArgs;
}
-var Port = utils.expose('Port', PortImpl, { functions: [
- 'disconnect',
- 'postMessage'
- ],
- properties: [
- 'name',
- 'onDisconnect',
- 'onMessage'
- ] });
+ function Port() {
+ privates(Port).constructPrivate(this, arguments);
+ }
+ utils.expose(Port, PortImpl, {
+ functions: [
+ 'disconnect',
+ 'postMessage',
+ ],
+ properties: [
+ 'name',
+ 'onDisconnect',
+ 'onMessage',
+ ],
+ });
exports.$set('kRequestChannel', kRequestChannel);
exports.$set('kMessageChannel', kMessageChannel);
diff --git a/extensions/renderer/resources/utils.js b/extensions/renderer/resources/utils.js
index eee1356..fd70c60 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 @@
}
/**
- * 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 @@
* 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 @@
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 @@
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;
}
diff --git a/extensions/renderer/resources/web_request_internal_custom_bindings.js b/extensions/renderer/resources/web_request_internal_custom_bindings.js
index 7c9e279a..df10729 100644
--- a/extensions/renderer/resources/web_request_internal_custom_bindings.js
+++ b/extensions/renderer/resources/web_request_internal_custom_bindings.js
@@ -175,17 +175,20 @@
});
});
-var WebRequestEvent = utils.expose('WebRequestEvent',
- WebRequestEventImpl,
- { functions: [
- 'hasListener',
- 'hasListeners',
- 'addListener',
- 'removeListener',
- 'addRules',
- 'removeRules',
- 'getRules'
-] });
+function WebRequestEvent() {
+ privates(WebRequestEvent).constructPrivate(this, arguments);
+}
+utils.expose(WebRequestEvent, WebRequestEventImpl, {
+ functions: [
+ 'hasListener',
+ 'hasListeners',
+ 'addListener',
+ 'removeListener',
+ 'addRules',
+ 'removeRules',
+ 'getRules',
+ ],
+});
webRequestInternal = binding.generate();
exports.$set('binding', webRequestInternal);
diff --git a/extensions/renderer/safe_builtins.cc b/extensions/renderer/safe_builtins.cc
index 2933e4d..7669a21 100644
--- a/extensions/renderer/safe_builtins.cc
+++ b/extensions/renderer/safe_builtins.cc
@@ -71,7 +71,7 @@
" ['hasOwnProperty'],\n"
" ['create', 'defineProperty', 'freeze',\n"
" 'getOwnPropertyDescriptor', 'getPrototypeOf', 'keys',\n"
- " 'assign']);\n"
+ " 'assign', 'setPrototypeOf']);\n"
"saveBuiltin(Function,\n"
" ['apply', 'bind', 'call']);\n"
"saveBuiltin(Array,\n"
diff --git a/extensions/renderer/utils_native_handler.cc b/extensions/renderer/utils_native_handler.cc
index 1ded40c6..741a111 100644
--- a/extensions/renderer/utils_native_handler.cc
+++ b/extensions/renderer/utils_native_handler.cc
@@ -5,7 +5,6 @@
#include "extensions/renderer/utils_native_handler.h"
#include "base/macros.h"
-#include "base/strings/stringprintf.h"
#include "extensions/renderer/script_context.h"
#include "third_party/WebKit/public/web/WebSerializedScriptValue.h"
@@ -13,9 +12,6 @@
UtilsNativeHandler::UtilsNativeHandler(ScriptContext* context)
: ObjectBackedNativeHandler(context) {
- RouteFunction("createClassWrapper",
- base::Bind(&UtilsNativeHandler::CreateClassWrapper,
- base::Unretained(this)));
RouteFunction(
"deepCopy",
base::Bind(&UtilsNativeHandler::DeepCopy, base::Unretained(this)));
@@ -23,67 +19,6 @@
UtilsNativeHandler::~UtilsNativeHandler() {}
-void UtilsNativeHandler::CreateClassWrapper(
- const v8::FunctionCallbackInfo<v8::Value>& args) {
- CHECK_EQ(3, args.Length());
- CHECK(args[0]->IsString());
- std::string name = *v8::String::Utf8Value(args[0]);
- CHECK(args[1]->IsObject());
- v8::Local<v8::Object> cls = args[1].As<v8::Object>();
- CHECK(args[2]->IsObject() || args[2]->IsUndefined());
- v8::Local<v8::Value> superclass = args[2];
-
- v8::HandleScope handle_scope(GetIsolate());
- // TODO(fsamuel): Consider moving the source wrapping to ModuleSystem.
- v8::Local<v8::String> source = v8::String::NewFromUtf8(
- GetIsolate(),
- base::StringPrintf(
- "(function($Object, $Function, privates, cls, superclass) {"
- "'use strict';\n"
- " function %s() {\n"
- " var privateObj = $Object.create(cls.prototype);\n"
- " $Function.apply(cls, privateObj, arguments);\n"
- " privateObj.wrapper = this;\n"
- " privates(this).impl = privateObj;\n"
- " };\n"
- " if (superclass) {\n"
- " %s.prototype = Object.create(superclass.prototype);\n"
- " }\n"
- " return %s;\n"
- "})",
- name.c_str(), name.c_str(), name.c_str()).c_str());
- v8::Local<v8::Value> func_as_value = context()->module_system()->RunString(
- source, v8::String::NewFromUtf8(GetIsolate(), name.c_str()));
- if (func_as_value.IsEmpty() || func_as_value->IsUndefined()) {
- args.GetReturnValue().SetUndefined();
- return;
- }
-
- // TODO(fsamuel): Move privates from ModuleSystem to a shared location.
- v8::Local<v8::Object> natives(context()->module_system()->NewInstance());
- CHECK(!natives.IsEmpty()); // this can happen if v8 has issues
-
- v8::Local<v8::Function> func = func_as_value.As<v8::Function>();
- v8::Local<v8::Value> func_args[] = {
- context()->safe_builtins()->GetObjekt(),
- context()->safe_builtins()->GetFunction(),
- natives->Get(v8::String::NewFromUtf8(GetIsolate(), "privates",
- v8::String::kInternalizedString)),
- cls,
- superclass};
- v8::Local<v8::Value> result;
- {
- v8::TryCatch try_catch(GetIsolate());
- try_catch.SetCaptureMessage(true);
- result = context()->CallFunction(func, arraysize(func_args), func_args);
- if (try_catch.HasCaught()) {
- args.GetReturnValue().SetUndefined();
- return;
- }
- }
- args.GetReturnValue().Set(result);
-}
-
void UtilsNativeHandler::DeepCopy(
const v8::FunctionCallbackInfo<v8::Value>& args) {
CHECK_EQ(1, args.Length());
diff --git a/extensions/renderer/utils_native_handler.h b/extensions/renderer/utils_native_handler.h
index c69860f..6a2ae4da 100644
--- a/extensions/renderer/utils_native_handler.h
+++ b/extensions/renderer/utils_native_handler.h
@@ -17,11 +17,6 @@
~UtilsNativeHandler() override;
private:
- // |args| consists of two arguments: a public class name, and a reference
- // to the implementation class. CreateClassWrapper returns a new class
- // that wraps the implementation, while hiding its members.
- void CreateClassWrapper(const v8::FunctionCallbackInfo<v8::Value>& args);
-
// |args| consists of one argument: an arbitrary value. Returns a deep copy of
// that value. The copy will have no references to nested values of the
// argument.
diff --git a/extensions/test/data/utils_unittest.js b/extensions/test/data/utils_unittest.js
index 1ecc421..64adec4 100644
--- a/extensions/test/data/utils_unittest.js
+++ b/extensions/test/data/utils_unittest.js
@@ -29,16 +29,22 @@
subFunc: function() { return 'subFunc'; }
};
- var SuperClass = utils.expose('SuperClass',
- SuperClassImpl,
- { functions: ['func', 'superFunc'],
- properties: ['attrA', 'attrB'] });
+ function SuperClass() {
+ privates(SuperClass).constructPrivate(this, arguments);
+ }
+ utils.expose(SuperClass, SuperClassImpl, {
+ functions: ['func', 'superFunc'],
+ properties: ['attrA', 'attrB'],
+ });
- var SubClass = utils.expose('SubClass',
- SubClassImpl,
- { superclass: SuperClass,
- functions: ['subFunc'],
- properties: ['attrC'] });
+ function SubClass() {
+ privates(SubClass).constructPrivate(this, arguments);
+ }
+ utils.expose(SubClass, SubClassImpl, {
+ superclass: SuperClass,
+ functions: ['subFunc'],
+ properties: ['attrC'],
+ });
var supe = new SuperClass();
AssertTrue(supe.attrA == 'aSuper');
@@ -63,12 +69,16 @@
function SubSubClassImpl() {}
SubSubClassImpl.prototype = Object.create(SubClassImpl.prototype);
- SubSubClassImpl.prototype.subSubFunc = function() { return 'subsub'; }
+ SubSubClassImpl.prototype.subSubFunc = function() { return 'subsub'; };
- var SubSubClass = utils.expose('SubSubClass',
- SubSubClassImpl,
- { superclass: SubClass,
- functions: ['subSubFunc'] });
+ function SubSubClass() {
+ privates(SubSubClass).constructPrivate(this, arguments);
+ }
+ utils.expose(SubSubClass, SubSubClassImpl, {
+ superclass: SubClass,
+ functions: ['subSubFunc'],
+ });
+
var subsub = new SubSubClass();
AssertTrue(subsub.attrA == 'aSub');
AssertTrue(subsub.attrB == 'bSuper');