Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(6903)

Unified Diff: extensions/renderer/messaging_bindings.cc

Issue 1966283002: Remove port lifetime management from renderer (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Devlin's nits up to #10 Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: extensions/renderer/messaging_bindings.cc
diff --git a/extensions/renderer/messaging_bindings.cc b/extensions/renderer/messaging_bindings.cc
index c1da6263306e594c3e8f3844de314a9a8db01c08..7db8b3e57fd74ca81e11c01998f2fcf952168773 100644
--- a/extensions/renderer/messaging_bindings.cc
+++ b/extensions/renderer/messaging_bindings.cc
@@ -12,7 +12,6 @@
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/callback.h"
-#include "base/lazy_instance.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_loop.h"
@@ -25,7 +24,6 @@
#include "extensions/common/api/messaging/message.h"
#include "extensions/common/extension_messages.h"
#include "extensions/common/manifest_handlers/externally_connectable.h"
-#include "extensions/renderer/dispatcher.h"
#include "extensions/renderer/event_bindings.h"
#include "extensions/renderer/extension_frame_helper.h"
#include "extensions/renderer/gc_callback.h"
@@ -61,191 +59,51 @@ using v8_helpers::IsEmptyOrUndefied;
namespace {
-// Tracks every reference between ScriptContexts and Ports, by ID.
-class PortTracker {
- public:
- PortTracker() {}
- ~PortTracker() {}
-
- // Returns true if |context| references |port_id|.
- bool HasReference(ScriptContext* context, int port_id) const {
- auto ports = contexts_to_ports_.find(context);
- return ports != contexts_to_ports_.end() &&
- ports->second.count(port_id) > 0;
- }
-
- // Marks |context| and |port_id| as referencing each other.
- void AddReference(ScriptContext* context, int port_id) {
- contexts_to_ports_[context].insert(port_id);
- }
-
- // Removes the references between |context| and |port_id|.
- // Returns true if a reference was removed, false if the reference didn't
- // exist to be removed.
- bool RemoveReference(ScriptContext* context, int port_id) {
- auto ports = contexts_to_ports_.find(context);
- if (ports == contexts_to_ports_.end() ||
- ports->second.erase(port_id) == 0) {
- return false;
- }
- if (ports->second.empty())
- contexts_to_ports_.erase(context);
- return true;
- }
-
- // Returns true if this tracker has any reference to |port_id|.
- bool HasPort(int port_id) const {
- for (auto it : contexts_to_ports_) {
- if (it.second.count(port_id) > 0)
- return true;
- }
- return false;
- }
-
- // Deletes all references to |port_id|.
- void DeletePort(int port_id) {
- for (auto it = contexts_to_ports_.begin();
- it != contexts_to_ports_.end();) {
- if (it->second.erase(port_id) > 0 && it->second.empty())
- contexts_to_ports_.erase(it++);
- else
- ++it;
- }
- }
-
- // Gets every port ID that has a reference to |context|.
- std::set<int> GetPortsForContext(ScriptContext* context) const {
- auto ports = contexts_to_ports_.find(context);
- return ports == contexts_to_ports_.end() ? std::set<int>() : ports->second;
- }
-
- private:
- // Maps ScriptContexts to the port IDs that have a reference to it.
- std::map<ScriptContext*, std::set<int>> contexts_to_ports_;
-
- DISALLOW_COPY_AND_ASSIGN(PortTracker);
-};
-
-base::LazyInstance<PortTracker> g_port_tracker = LAZY_INSTANCE_INITIALIZER;
-
-const char kPortClosedError[] = "Attempting to use a disconnected port object";
-
class ExtensionImpl : public ObjectBackedNativeHandler {
public:
- ExtensionImpl(Dispatcher* dispatcher, ScriptContext* context)
- : ObjectBackedNativeHandler(context),
- dispatcher_(dispatcher),
- weak_ptr_factory_(this) {
+ ExtensionImpl(ScriptContext* context)
Devlin 2016/05/18 17:55:09 nit: explicit
+ : ObjectBackedNativeHandler(context), weak_ptr_factory_(this) {
RouteFunction(
"CloseChannel",
base::Bind(&ExtensionImpl::CloseChannel, base::Unretained(this)));
RouteFunction(
- "PortAddRef",
- base::Bind(&ExtensionImpl::PortAddRef, base::Unretained(this)));
- RouteFunction(
- "PortRelease",
- base::Bind(&ExtensionImpl::PortRelease, base::Unretained(this)));
- RouteFunction(
"PostMessage",
base::Bind(&ExtensionImpl::PostMessage, base::Unretained(this)));
// TODO(fsamuel, kalman): Move BindToGC out of messaging natives.
RouteFunction("BindToGC",
base::Bind(&ExtensionImpl::BindToGC, base::Unretained(this)));
-
- // Observe |context| so that port references to it can be cleared.
- context->AddInvalidationObserver(base::Bind(
- &ExtensionImpl::OnContextInvalidated, weak_ptr_factory_.GetWeakPtr()));
}
~ExtensionImpl() override {}
private:
- void OnContextInvalidated() {
- for (int port_id : g_port_tracker.Get().GetPortsForContext(context()))
- ReleasePort(port_id);
- }
-
- void ClearPortDataAndNotifyDispatcher(int port_id) {
- g_port_tracker.Get().DeletePort(port_id);
- dispatcher_->ClearPortData(port_id);
- }
-
// Sends a message along the given channel.
void PostMessage(const v8::FunctionCallbackInfo<v8::Value>& args) {
- content::RenderFrame* render_frame = context()->GetRenderFrame();
- if (!render_frame)
- return;
-
// Arguments are (int32_t port_id, string message).
CHECK(args.Length() == 2 && args[0]->IsInt32() && args[1]->IsString());
int port_id = args[0].As<v8::Int32>()->Value();
- if (!g_port_tracker.Get().HasPort(port_id)) {
- v8::Local<v8::String> error_message =
- ToV8StringUnsafe(args.GetIsolate(), kPortClosedError);
- args.GetIsolate()->ThrowException(v8::Exception::Error(error_message));
- return;
- }
- render_frame->Send(new ExtensionHostMsg_PostMessage(
- render_frame->GetRoutingID(), port_id,
- Message(*v8::String::Utf8Value(args[1]),
- blink::WebUserGestureIndicator::isProcessingUserGesture())));
+ content::RenderFrame* render_frame = context()->GetRenderFrame();
+ if (render_frame) {
+ render_frame->Send(new ExtensionHostMsg_PostMessage(
+ render_frame->GetRoutingID(), port_id,
+ Message(*v8::String::Utf8Value(args[1]),
+ blink::WebUserGestureIndicator::isProcessingUserGesture())));
+ }
}
- // Forcefully disconnects a port.
+ // Close a port, optionally forcefully (i.e. close the whole channel instead
+ // of just the given port).
void CloseChannel(const v8::FunctionCallbackInfo<v8::Value>& args) {
- // Arguments are (int32_t port_id, boolean notify_browser).
+ // Arguments are (int32_t port_id, bool force_close).
CHECK_EQ(2, args.Length());
CHECK(args[0]->IsInt32());
CHECK(args[1]->IsBoolean());
int port_id = args[0].As<v8::Int32>()->Value();
- if (!g_port_tracker.Get().HasPort(port_id))
- return;
-
- // Send via the RenderThread because the RenderFrame might be closing.
- bool notify_browser = args[1].As<v8::Boolean>()->Value();
- content::RenderFrame* render_frame = context()->GetRenderFrame();
- if (notify_browser && render_frame) {
- render_frame->Send(new ExtensionHostMsg_CloseMessagePort(
- render_frame->GetRoutingID(), port_id, true));
- }
-
- ClearPortDataAndNotifyDispatcher(port_id);
- }
-
- // A new port has been created for a context. This occurs both when script
- // opens a connection, and when a connection is opened to this script.
- void PortAddRef(const v8::FunctionCallbackInfo<v8::Value>& args) {
- // Arguments are (int32_t port_id).
- CHECK_EQ(1, args.Length());
- CHECK(args[0]->IsInt32());
-
- int port_id = args[0].As<v8::Int32>()->Value();
- g_port_tracker.Get().AddReference(context(), port_id);
- }
-
- // The frame a port lived in has been destroyed. When there are no more
- // frames with a reference to a given port, we will disconnect it and notify
- // the other end of the channel.
- // TODO(robwu): Port lifetime management has moved to the browser, this is no
- // longer needed. See .destroy_() inmessaging.js for more details.
- void PortRelease(const v8::FunctionCallbackInfo<v8::Value>& args) {
- // Arguments are (int32_t port_id).
- CHECK(args.Length() == 1 && args[0]->IsInt32());
- ReleasePort(args[0].As<v8::Int32>()->Value());
- }
-
- // Releases the reference to |port_id| for this context, and clears all port
- // data if there are no more references.
- void ReleasePort(int port_id) {
- content::RenderFrame* render_frame = context()->GetRenderFrame();
- if (g_port_tracker.Get().RemoveReference(context(), port_id) &&
- !g_port_tracker.Get().HasPort(port_id) && render_frame) {
- render_frame->Send(new ExtensionHostMsg_CloseMessagePort(
- render_frame->GetRoutingID(), port_id, false));
- }
+ bool force_close = args[1].As<v8::Boolean>()->Value();
+ ClosePort(port_id, force_close);
}
// void BindToGC(object, callback, port_id)
@@ -263,20 +121,55 @@ class ExtensionImpl : public ObjectBackedNativeHandler {
int port_id = args[2].As<v8::Int32>()->Value();
base::Closure fallback = base::Bind(&base::DoNothing);
if (port_id >= 0) {
- fallback = base::Bind(&ExtensionImpl::ReleasePort,
- weak_ptr_factory_.GetWeakPtr(), port_id);
+ // TODO(robwu): Falling back to closing the port shouldn't be needed. If
+ // the script context is destroyed, then the frame has navigated. But that
+ // is already detected by the browser, so this logic is redundant. Remove
+ // this fallback (and move BindToGC out of messaging because it is also
+ // used in other places that have nothing to do with messaging...).
+ fallback =
+ base::Bind(&ExtensionImpl::ClosePort, weak_ptr_factory_.GetWeakPtr(),
+ port_id, false /* force_close */);
}
// Destroys itself when the object is GC'd or context is invalidated.
new GCCallback(context(), args[0].As<v8::Object>(),
args[1].As<v8::Function>(), fallback);
}
- // Dispatcher handle. Not owned.
- Dispatcher* dispatcher_;
+ // See ExtensionImpl::CloseChannel for documentation.
+ // TODO(robwu): Merge this logic with CloseChannel once the TODO in BindToGC
+ // has been addressed.
+ void ClosePort(int port_id, bool force_close) {
+ content::RenderFrame* render_frame = context()->GetRenderFrame();
+ if (render_frame) {
+ render_frame->Send(new ExtensionHostMsg_CloseMessagePort(
+ render_frame->GetRoutingID(), port_id, force_close));
+ }
+ }
base::WeakPtrFactory<ExtensionImpl> weak_ptr_factory_;
};
+void HasMessagePort(int port_id,
+ bool* has_port,
+ ScriptContext* script_context) {
+ if (*has_port)
+ return; // Stop checking if the port was found.
+
+ v8::Isolate* isolate = script_context->isolate();
+ v8::HandleScope handle_scope(isolate);
+
+ v8::Local<v8::Value> port_id_handle = v8::Integer::New(isolate, port_id);
+ v8::Local<v8::Value> v8_has_port =
+ script_context->module_system()->CallModuleMethod("messaging", "hasPort",
+ 1, &port_id_handle);
+ if (IsEmptyOrUndefied(v8_has_port))
+ return;
+ CHECK(v8_has_port->IsBoolean());
+ if (!v8_has_port.As<v8::Boolean>()->Value())
+ return;
+ *has_port = true;
+}
+
void DispatchOnConnectToScriptContext(
int target_port_id,
const std::string& channel_name,
@@ -435,9 +328,29 @@ void DispatchOnDisconnectToScriptContext(int port_id,
} // namespace
-ObjectBackedNativeHandler* MessagingBindings::Get(Dispatcher* dispatcher,
- ScriptContext* context) {
- return new ExtensionImpl(dispatcher, context);
+ObjectBackedNativeHandler* MessagingBindings::Get(ScriptContext* context) {
+ return new ExtensionImpl(context);
+}
+
+void MessagingBindings::ValidateMessagePort(
+ const ScriptContextSet& context_set,
+ int port_id,
+ content::RenderFrame* render_frame) {
+ int routing_id = render_frame->GetRoutingID();
+
+ bool has_port = false;
+ context_set.ForEach(render_frame,
+ base::Bind(&HasMessagePort, port_id, &has_port));
+ // Note: HasMessagePort invokes a JavaScript function. If the runtime of the
+ // extension bindings in JS have been compromised, then |render_frame| may be
+ // invalid at this point.
+
+ // A reply is only sent if the port is missing, because the port is assumed to
+ // exist unless stated otherwise.
+ if (!has_port) {
+ content::RenderThread::Get()->Send(
+ new ExtensionHostMsg_CloseMessagePort(routing_id, port_id, false));
+ }
}
// static

Powered by Google App Engine
This is Rietveld 408576698