Commit 53a39796 authored by Devlin Cronin's avatar Devlin Cronin Committed by Commit Bot

[Extensions] Use SetLazyDataProperty on GinPort

Rather than using ObjectTemplateBuilder::SetProperty() on GinPort, use
ObjectTemplateBuilder::SetLazyDataProperty(). This allows author script
to reset the properties to be something else, which is more inline with
the majority of JS objects exposed to scripts.

Add tests for the same.

Bug: 927887
Change-Id: I8f983918014d69941992423eece5fd1eed9c58e0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1506771Reviewed-by: default avatarJeremy Roman <jbroman@chromium.org>
Commit-Queue: Devlin <rdevlin.cronin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#638592}
parent b26068fd
...@@ -6,6 +6,7 @@ var frame; ...@@ -6,6 +6,7 @@ var frame;
var frameRuntime; var frameRuntime;
var frameStorage; var frameStorage;
var frameTabs; var frameTabs;
var nativeBindingsEnabled;
function createFrame() { function createFrame() {
frame = document.createElement('iframe'); frame = document.createElement('iframe');
...@@ -16,7 +17,7 @@ function createFrame() { ...@@ -16,7 +17,7 @@ function createFrame() {
}); });
} }
function testPort(port) { function testPort(port, expectEventsValid) {
var result = { var result = {
disconnectThrow: false, disconnectThrow: false,
postMessageThrow: false, postMessageThrow: false,
...@@ -39,7 +40,7 @@ function testPort(port) { ...@@ -39,7 +40,7 @@ function testPort(port) {
} }
try { try {
result.onDisconnect = port.onDisconnect; result.onDisconnectEvent = port.onDisconnect;
} catch (e) { } catch (e) {
result.getOnDisconnectThrow = true; result.getOnDisconnectThrow = true;
} }
...@@ -52,13 +53,27 @@ function testPort(port) { ...@@ -52,13 +53,27 @@ function testPort(port) {
chrome.test.assertTrue(result.postMessageThrow); chrome.test.assertTrue(result.postMessageThrow);
chrome.test.assertTrue(result.disconnectThrow); chrome.test.assertTrue(result.disconnectThrow);
chrome.test.assertTrue(result.getOnMessageThrow);
chrome.test.assertTrue(result.getOnDisconnectThrow); // With native bindings, the event object instantiated on a Port is set as a
chrome.test.assertFalse(!!result.onMessageEvent); // lazy data property, and thus is safe to access even after the context has
chrome.test.assertFalse(!!result.onDisconnectEvent); // been removed. JS bindings always throw errors when trying to access them
// after context invalidation.
expectEventsValid &= nativeBindingsEnabled;
if (expectEventsValid) {
chrome.test.assertFalse(result.getOnMessageThrow);
chrome.test.assertFalse(result.getOnDisconnectThrow);
chrome.test.assertTrue(!!result.onMessageEvent);
chrome.test.assertTrue(!!result.onDisconnectEvent);
} else {
chrome.test.assertTrue(result.getOnMessageThrow);
chrome.test.assertTrue(result.getOnDisconnectThrow);
chrome.test.assertEq(undefined, result.onMessageEvent);
chrome.test.assertEq(undefined, result.onDisconnectEvent);
}
} }
chrome.test.runTests([ const tests = [
function useFrameStorageAndRuntime() { function useFrameStorageAndRuntime() {
createFrame().then(() => { createFrame().then(() => {
frameRuntime = frame.contentWindow.chrome.runtime; frameRuntime = frame.contentWindow.chrome.runtime;
...@@ -104,7 +119,7 @@ chrome.test.runTests([ ...@@ -104,7 +119,7 @@ chrome.test.runTests([
port.postMessage; port.postMessage;
document.body.removeChild(frame); document.body.removeChild(frame);
testPort(port); testPort(port, false);
chrome.test.succeed(); chrome.test.succeed();
}); });
}, },
...@@ -117,7 +132,7 @@ chrome.test.runTests([ ...@@ -117,7 +132,7 @@ chrome.test.runTests([
port.disconnect(); port.disconnect();
document.body.removeChild(frame); document.body.removeChild(frame);
testPort(port); testPort(port, false);
chrome.test.succeed(); chrome.test.succeed();
}); });
}, },
...@@ -131,8 +146,13 @@ chrome.test.runTests([ ...@@ -131,8 +146,13 @@ chrome.test.runTests([
port.onDisconnect; port.onDisconnect;
document.body.removeChild(frame); document.body.removeChild(frame);
testPort(port); testPort(port, true);
chrome.test.succeed(); chrome.test.succeed();
}); });
}, },
]); ];
chrome.test.getConfig((config) => {
nativeBindingsEnabled = config.nativeCrxBindingsEnabled;
chrome.test.runTests(tests);
});
...@@ -39,6 +39,7 @@ GinPort::GinPort(v8::Local<v8::Context> context, ...@@ -39,6 +39,7 @@ GinPort::GinPort(v8::Local<v8::Context> context,
name_(name), name_(name),
event_handler_(event_handler), event_handler_(event_handler),
delegate_(delegate), delegate_(delegate),
accessed_sender_(false),
weak_factory_(this) { weak_factory_(this) {
context_invalidation_listener_.emplace( context_invalidation_listener_.emplace(
context, base::BindOnce(&GinPort::OnContextInvalidated, context, base::BindOnce(&GinPort::OnContextInvalidated,
...@@ -54,10 +55,10 @@ gin::ObjectTemplateBuilder GinPort::GetObjectTemplateBuilder( ...@@ -54,10 +55,10 @@ gin::ObjectTemplateBuilder GinPort::GetObjectTemplateBuilder(
return Wrappable<GinPort>::GetObjectTemplateBuilder(isolate) return Wrappable<GinPort>::GetObjectTemplateBuilder(isolate)
.SetMethod("disconnect", &GinPort::DisconnectHandler) .SetMethod("disconnect", &GinPort::DisconnectHandler)
.SetMethod("postMessage", &GinPort::PostMessageHandler) .SetMethod("postMessage", &GinPort::PostMessageHandler)
.SetProperty("name", &GinPort::GetName) .SetLazyDataProperty("name", &GinPort::GetName)
.SetProperty("onDisconnect", &GinPort::GetOnDisconnectEvent) .SetLazyDataProperty("onDisconnect", &GinPort::GetOnDisconnectEvent)
.SetProperty("onMessage", &GinPort::GetOnMessageEvent) .SetLazyDataProperty("onMessage", &GinPort::GetOnMessageEvent)
.SetProperty("sender", &GinPort::GetSender); .SetLazyDataProperty("sender", &GinPort::GetSender);
} }
const char* GinPort::GetTypeName() { const char* GinPort::GetTypeName() {
...@@ -108,6 +109,8 @@ void GinPort::DispatchOnDisconnect(v8::Local<v8::Context> context) { ...@@ -108,6 +109,8 @@ void GinPort::DispatchOnDisconnect(v8::Local<v8::Context> context) {
void GinPort::SetSender(v8::Local<v8::Context> context, void GinPort::SetSender(v8::Local<v8::Context> context,
v8::Local<v8::Value> sender) { v8::Local<v8::Value> sender) {
DCHECK_EQ(kActive, state_); DCHECK_EQ(kActive, state_);
DCHECK(!accessed_sender_)
<< "|sender| can only be set before its first access.";
v8::Isolate* isolate = context->GetIsolate(); v8::Isolate* isolate = context->GetIsolate();
v8::HandleScope handle_scope(isolate); v8::HandleScope handle_scope(isolate);
...@@ -179,6 +182,7 @@ v8::Local<v8::Value> GinPort::GetOnMessageEvent(gin::Arguments* arguments) { ...@@ -179,6 +182,7 @@ v8::Local<v8::Value> GinPort::GetOnMessageEvent(gin::Arguments* arguments) {
} }
v8::Local<v8::Value> GinPort::GetSender(gin::Arguments* arguments) { v8::Local<v8::Value> GinPort::GetSender(gin::Arguments* arguments) {
accessed_sender_ = true;
v8::Isolate* isolate = arguments->isolate(); v8::Isolate* isolate = arguments->isolate();
v8::Local<v8::Object> wrapper = GetWrapper(isolate).ToLocalChecked(); v8::Local<v8::Object> wrapper = GetWrapper(isolate).ToLocalChecked();
v8::Local<v8::Private> key = v8::Local<v8::Private> key =
......
...@@ -70,7 +70,9 @@ class GinPort final : public gin::Wrappable<GinPort> { ...@@ -70,7 +70,9 @@ class GinPort final : public gin::Wrappable<GinPort> {
// the port. // the port.
void DispatchOnDisconnect(v8::Local<v8::Context> context); void DispatchOnDisconnect(v8::Local<v8::Context> context);
// Sets the |sender| property on the port. // Sets the |sender| property on the port. Note: this can only be called
// before the `sender` property is accessed on the JS object, since it is
// lazily set as a data property in first access.
void SetSender(v8::Local<v8::Context> context, v8::Local<v8::Value> sender); void SetSender(v8::Local<v8::Context> context, v8::Local<v8::Value> sender);
const PortId& port_id() const { return port_id_; } const PortId& port_id() const { return port_id_; }
...@@ -142,6 +144,10 @@ class GinPort final : public gin::Wrappable<GinPort> { ...@@ -142,6 +144,10 @@ class GinPort final : public gin::Wrappable<GinPort> {
// outlive this object. // outlive this object.
Delegate* const delegate_; Delegate* const delegate_;
// Whether the `sender` property has been accessed, and thus set on the
// port JS object.
bool accessed_sender_;
// A listener for context invalidation. Note: this isn't actually optional; // A listener for context invalidation. Note: this isn't actually optional;
// it just needs to be created after |weak_factory_|, which needs to be the // it just needs to be created after |weak_factory_|, which needs to be the
// final member. // final member.
......
...@@ -371,18 +371,24 @@ TEST_F(GinPortTest, TestSenderProperty) { ...@@ -371,18 +371,24 @@ TEST_F(GinPortTest, TestSenderProperty) {
v8::Local<v8::Context> context = MainContext(); v8::Local<v8::Context> context = MainContext();
PortId port_id(base::UnguessableToken::Create(), 0, true); PortId port_id(base::UnguessableToken::Create(), 0, true);
gin::Handle<GinPort> port = CreatePort(context, port_id);
v8::Local<v8::Object> port_obj = port.ToV8().As<v8::Object>();
EXPECT_EQ("undefined", {
GetStringPropertyFromObject(port_obj, context, "sender")); gin::Handle<GinPort> port = CreatePort(context, port_id);
v8::Local<v8::Object> port_obj = port.ToV8().As<v8::Object>();
port->SetSender(context, EXPECT_EQ("undefined",
gin::DataObjectBuilder(isolate()).Set("prop", 42).Build()); GetStringPropertyFromObject(port_obj, context, "sender"));
}
EXPECT_EQ(R"({"prop":42})", {
GetStringPropertyFromObject(port_obj, context, "sender")); // SetSender() can only be called before the `sender` property is accessed,
// so we need to create a new port here.
gin::Handle<GinPort> port = CreatePort(context, port_id);
port->SetSender(context,
gin::DataObjectBuilder(isolate()).Set("prop", 42).Build());
v8::Local<v8::Object> port_obj = port.ToV8().As<v8::Object>();
EXPECT_EQ(R"({"prop":42})",
GetStringPropertyFromObject(port_obj, context, "sender"));
}
} }
TEST_F(GinPortTest, TryUsingPortAfterInvalidation) { TEST_F(GinPortTest, TryUsingPortAfterInvalidation) {
...@@ -427,4 +433,22 @@ TEST_F(GinPortTest, TryUsingPortAfterInvalidation) { ...@@ -427,4 +433,22 @@ TEST_F(GinPortTest, TryUsingPortAfterInvalidation) {
} }
} }
TEST_F(GinPortTest, AlteringPortName) {
v8::HandleScope handle_scope(isolate());
v8::Local<v8::Context> context = MainContext();
PortId port_id(base::UnguessableToken::Create(), 0, true);
gin::Handle<GinPort> port = CreatePort(context, port_id);
v8::Local<v8::Object> port_obj = port.ToV8().As<v8::Object>();
v8::Local<v8::Function> change_port_name = FunctionFromString(
context, "(function(port) { port.name = 'foo'; return port.name; })");
v8::Local<v8::Value> args[] = {port_obj};
v8::Local<v8::Value> result =
RunFunction(change_port_name, context, base::size(args), args);
EXPECT_EQ(R"("foo")", V8ToString(result, context));
}
} // namespace extensions } // namespace extensions
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment