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

Side by Side 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: Simplify: Assume that opener port is open, only send reply if closed 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 unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "extensions/renderer/messaging_bindings.h" 5 #include "extensions/renderer/messaging_bindings.h"
6 6
7 #include <stdint.h> 7 #include <stdint.h>
8 8
9 #include <map> 9 #include <map>
10 #include <string> 10 #include <string>
11 11
12 #include "base/bind.h" 12 #include "base/bind.h"
13 #include "base/bind_helpers.h" 13 #include "base/bind_helpers.h"
14 #include "base/callback.h" 14 #include "base/callback.h"
15 #include "base/lazy_instance.h"
16 #include "base/macros.h" 15 #include "base/macros.h"
17 #include "base/memory/weak_ptr.h" 16 #include "base/memory/weak_ptr.h"
18 #include "base/message_loop/message_loop.h" 17 #include "base/message_loop/message_loop.h"
19 #include "base/values.h" 18 #include "base/values.h"
20 #include "components/guest_view/common/guest_view_constants.h" 19 #include "components/guest_view/common/guest_view_constants.h"
21 #include "content/public/child/v8_value_converter.h" 20 #include "content/public/child/v8_value_converter.h"
22 #include "content/public/common/child_process_host.h" 21 #include "content/public/common/child_process_host.h"
23 #include "content/public/renderer/render_frame.h" 22 #include "content/public/renderer/render_frame.h"
24 #include "content/public/renderer/render_thread.h" 23 #include "content/public/renderer/render_thread.h"
25 #include "extensions/common/api/messaging/message.h" 24 #include "extensions/common/api/messaging/message.h"
26 #include "extensions/common/extension_messages.h" 25 #include "extensions/common/extension_messages.h"
27 #include "extensions/common/manifest_handlers/externally_connectable.h" 26 #include "extensions/common/manifest_handlers/externally_connectable.h"
28 #include "extensions/renderer/dispatcher.h"
29 #include "extensions/renderer/event_bindings.h" 27 #include "extensions/renderer/event_bindings.h"
30 #include "extensions/renderer/extension_frame_helper.h" 28 #include "extensions/renderer/extension_frame_helper.h"
31 #include "extensions/renderer/gc_callback.h" 29 #include "extensions/renderer/gc_callback.h"
32 #include "extensions/renderer/object_backed_native_handler.h" 30 #include "extensions/renderer/object_backed_native_handler.h"
33 #include "extensions/renderer/script_context.h" 31 #include "extensions/renderer/script_context.h"
34 #include "extensions/renderer/script_context_set.h" 32 #include "extensions/renderer/script_context_set.h"
35 #include "extensions/renderer/v8_helpers.h" 33 #include "extensions/renderer/v8_helpers.h"
36 #include "third_party/WebKit/public/web/WebDocument.h" 34 #include "third_party/WebKit/public/web/WebDocument.h"
37 #include "third_party/WebKit/public/web/WebLocalFrame.h" 35 #include "third_party/WebKit/public/web/WebLocalFrame.h"
38 #include "third_party/WebKit/public/web/WebScopedUserGesture.h" 36 #include "third_party/WebKit/public/web/WebScopedUserGesture.h"
(...skipping 15 matching lines...) Expand all
54 using content::V8ValueConverter; 52 using content::V8ValueConverter;
55 53
56 namespace extensions { 54 namespace extensions {
57 55
58 using v8_helpers::ToV8String; 56 using v8_helpers::ToV8String;
59 using v8_helpers::ToV8StringUnsafe; 57 using v8_helpers::ToV8StringUnsafe;
60 using v8_helpers::IsEmptyOrUndefied; 58 using v8_helpers::IsEmptyOrUndefied;
61 59
62 namespace { 60 namespace {
63 61
64 // Tracks every reference between ScriptContexts and Ports, by ID.
65 class PortTracker {
66 public:
67 PortTracker() {}
68 ~PortTracker() {}
69
70 // Returns true if |context| references |port_id|.
71 bool HasReference(ScriptContext* context, int port_id) const {
72 auto ports = contexts_to_ports_.find(context);
73 return ports != contexts_to_ports_.end() &&
74 ports->second.count(port_id) > 0;
75 }
76
77 // Marks |context| and |port_id| as referencing each other.
78 void AddReference(ScriptContext* context, int port_id) {
79 contexts_to_ports_[context].insert(port_id);
80 }
81
82 // Removes the references between |context| and |port_id|.
83 // Returns true if a reference was removed, false if the reference didn't
84 // exist to be removed.
85 bool RemoveReference(ScriptContext* context, int port_id) {
86 auto ports = contexts_to_ports_.find(context);
87 if (ports == contexts_to_ports_.end() ||
88 ports->second.erase(port_id) == 0) {
89 return false;
90 }
91 if (ports->second.empty())
92 contexts_to_ports_.erase(context);
93 return true;
94 }
95
96 // Returns true if this tracker has any reference to |port_id|.
97 bool HasPort(int port_id) const {
98 for (auto it : contexts_to_ports_) {
99 if (it.second.count(port_id) > 0)
100 return true;
101 }
102 return false;
103 }
104
105 // Deletes all references to |port_id|.
106 void DeletePort(int port_id) {
107 for (auto it = contexts_to_ports_.begin();
108 it != contexts_to_ports_.end();) {
109 if (it->second.erase(port_id) > 0 && it->second.empty())
110 contexts_to_ports_.erase(it++);
111 else
112 ++it;
113 }
114 }
115
116 // Gets every port ID that has a reference to |context|.
117 std::set<int> GetPortsForContext(ScriptContext* context) const {
118 auto ports = contexts_to_ports_.find(context);
119 return ports == contexts_to_ports_.end() ? std::set<int>() : ports->second;
120 }
121
122 private:
123 // Maps ScriptContexts to the port IDs that have a reference to it.
124 std::map<ScriptContext*, std::set<int>> contexts_to_ports_;
125
126 DISALLOW_COPY_AND_ASSIGN(PortTracker);
127 };
128
129 base::LazyInstance<PortTracker> g_port_tracker = LAZY_INSTANCE_INITIALIZER;
130
131 const char kPortClosedError[] = "Attempting to use a disconnected port object";
132
133 class ExtensionImpl : public ObjectBackedNativeHandler { 62 class ExtensionImpl : public ObjectBackedNativeHandler {
134 public: 63 public:
135 ExtensionImpl(Dispatcher* dispatcher, ScriptContext* context) 64 ExtensionImpl(ScriptContext* context)
136 : ObjectBackedNativeHandler(context), 65 : ObjectBackedNativeHandler(context), weak_ptr_factory_(this) {
137 dispatcher_(dispatcher),
138 weak_ptr_factory_(this) {
139 RouteFunction( 66 RouteFunction(
140 "CloseChannel", 67 "CloseChannel",
141 base::Bind(&ExtensionImpl::CloseChannel, base::Unretained(this))); 68 base::Bind(&ExtensionImpl::CloseChannel, base::Unretained(this)));
142 RouteFunction( 69 RouteFunction(
143 "PortAddRef",
144 base::Bind(&ExtensionImpl::PortAddRef, base::Unretained(this)));
145 RouteFunction(
146 "PortRelease",
147 base::Bind(&ExtensionImpl::PortRelease, base::Unretained(this)));
148 RouteFunction(
149 "PostMessage", 70 "PostMessage",
150 base::Bind(&ExtensionImpl::PostMessage, base::Unretained(this))); 71 base::Bind(&ExtensionImpl::PostMessage, base::Unretained(this)));
151 // TODO(fsamuel, kalman): Move BindToGC out of messaging natives. 72 // TODO(fsamuel, kalman): Move BindToGC out of messaging natives.
152 RouteFunction("BindToGC", 73 RouteFunction("BindToGC",
153 base::Bind(&ExtensionImpl::BindToGC, base::Unretained(this))); 74 base::Bind(&ExtensionImpl::BindToGC, base::Unretained(this)));
154
155 // Observe |context| so that port references to it can be cleared.
156 context->AddInvalidationObserver(base::Bind(
157 &ExtensionImpl::OnContextInvalidated, weak_ptr_factory_.GetWeakPtr()));
158 } 75 }
159 76
160 ~ExtensionImpl() override {} 77 ~ExtensionImpl() override {}
161 78
162 private: 79 private:
163 void OnContextInvalidated() {
164 for (int port_id : g_port_tracker.Get().GetPortsForContext(context()))
165 ReleasePort(port_id);
166 }
167
168 void ClearPortDataAndNotifyDispatcher(int port_id) {
169 g_port_tracker.Get().DeletePort(port_id);
170 dispatcher_->ClearPortData(port_id);
171 }
172
173 // Sends a message along the given channel. 80 // Sends a message along the given channel.
174 void PostMessage(const v8::FunctionCallbackInfo<v8::Value>& args) { 81 void PostMessage(const v8::FunctionCallbackInfo<v8::Value>& args) {
175 content::RenderFrame* render_frame = context()->GetRenderFrame();
176 if (!render_frame)
177 return;
178
179 // Arguments are (int32_t port_id, string message). 82 // Arguments are (int32_t port_id, string message).
180 CHECK(args.Length() == 2 && args[0]->IsInt32() && args[1]->IsString()); 83 CHECK(args.Length() == 2 && args[0]->IsInt32() && args[1]->IsString());
181 84
182 int port_id = args[0].As<v8::Int32>()->Value(); 85 int port_id = args[0].As<v8::Int32>()->Value();
183 if (!g_port_tracker.Get().HasPort(port_id)) { 86
184 v8::Local<v8::String> error_message = 87 content::RenderFrame* render_frame = context()->GetRenderFrame();
185 ToV8StringUnsafe(args.GetIsolate(), kPortClosedError); 88 if (render_frame) {
186 args.GetIsolate()->ThrowException(v8::Exception::Error(error_message)); 89 render_frame->Send(new ExtensionHostMsg_PostMessage(
187 return; 90 render_frame->GetRoutingID(), port_id,
91 Message(*v8::String::Utf8Value(args[1]),
92 blink::WebUserGestureIndicator::isProcessingUserGesture())));
188 } 93 }
189
190 render_frame->Send(new ExtensionHostMsg_PostMessage(
191 render_frame->GetRoutingID(), port_id,
192 Message(*v8::String::Utf8Value(args[1]),
193 blink::WebUserGestureIndicator::isProcessingUserGesture())));
194 } 94 }
195 95
196 // Forcefully disconnects a port. 96 // Close a port, optionally forcefully (= close the whole channel instead of
Devlin 2016/05/17 19:50:29 nitty nit: s/=/meaning
robwu 2016/05/17 22:47:50 Done. (s/=/i.e./).
97 // just the given port).
197 void CloseChannel(const v8::FunctionCallbackInfo<v8::Value>& args) { 98 void CloseChannel(const v8::FunctionCallbackInfo<v8::Value>& args) {
198 // Arguments are (int32_t port_id, boolean notify_browser). 99 // Arguments are (int32_t port_id, boolean force_close).
Devlin 2016/05/17 19:50:30 nitty nit: don't mix language types - let's make r
robwu 2016/05/17 22:47:50 Done.
199 CHECK_EQ(2, args.Length()); 100 CHECK_EQ(2, args.Length());
200 CHECK(args[0]->IsInt32()); 101 CHECK(args[0]->IsInt32());
201 CHECK(args[1]->IsBoolean()); 102 CHECK(args[1]->IsBoolean());
202 103
203 int port_id = args[0].As<v8::Int32>()->Value(); 104 int port_id = args[0].As<v8::Int32>()->Value();
204 if (!g_port_tracker.Get().HasPort(port_id)) 105 bool force_close = args[1].As<v8::Boolean>()->Value();
205 return; 106 ClosePort(port_id, force_close);
206
207 // Send via the RenderThread because the RenderFrame might be closing.
208 bool notify_browser = args[1].As<v8::Boolean>()->Value();
209 content::RenderFrame* render_frame = context()->GetRenderFrame();
210 if (notify_browser && render_frame) {
211 render_frame->Send(new ExtensionHostMsg_CloseMessagePort(
212 render_frame->GetRoutingID(), port_id, true));
213 }
214
215 ClearPortDataAndNotifyDispatcher(port_id);
216 }
217
218 // A new port has been created for a context. This occurs both when script
219 // opens a connection, and when a connection is opened to this script.
220 void PortAddRef(const v8::FunctionCallbackInfo<v8::Value>& args) {
221 // Arguments are (int32_t port_id).
222 CHECK_EQ(1, args.Length());
223 CHECK(args[0]->IsInt32());
224
225 int port_id = args[0].As<v8::Int32>()->Value();
226 g_port_tracker.Get().AddReference(context(), port_id);
227 }
228
229 // The frame a port lived in has been destroyed. When there are no more
230 // frames with a reference to a given port, we will disconnect it and notify
231 // the other end of the channel.
232 // TODO(robwu): Port lifetime management has moved to the browser, this is no
233 // longer needed. See .destroy_() inmessaging.js for more details.
234 void PortRelease(const v8::FunctionCallbackInfo<v8::Value>& args) {
235 // Arguments are (int32_t port_id).
236 CHECK(args.Length() == 1 && args[0]->IsInt32());
237 ReleasePort(args[0].As<v8::Int32>()->Value());
238 }
239
240 // Releases the reference to |port_id| for this context, and clears all port
241 // data if there are no more references.
242 void ReleasePort(int port_id) {
243 content::RenderFrame* render_frame = context()->GetRenderFrame();
244 if (g_port_tracker.Get().RemoveReference(context(), port_id) &&
245 !g_port_tracker.Get().HasPort(port_id) && render_frame) {
246 render_frame->Send(new ExtensionHostMsg_CloseMessagePort(
247 render_frame->GetRoutingID(), port_id, false));
248 }
249 } 107 }
250 108
251 // void BindToGC(object, callback, port_id) 109 // void BindToGC(object, callback, port_id)
252 // 110 //
253 // Binds |callback| to be invoked *sometime after* |object| is garbage 111 // Binds |callback| to be invoked *sometime after* |object| is garbage
254 // collected. We don't call the method re-entrantly so as to avoid executing 112 // collected. We don't call the method re-entrantly so as to avoid executing
255 // JS in some bizarro undefined mid-GC state, nor do we then call into the 113 // JS in some bizarro undefined mid-GC state, nor do we then call into the
256 // script context if it's been invalidated. 114 // script context if it's been invalidated.
257 // 115 //
258 // If the script context *is* invalidated in the meantime, as a slight hack, 116 // If the script context *is* invalidated in the meantime, as a slight hack,
259 // release the port with ID |port_id| if it's >= 0. 117 // release the port with ID |port_id| if it's >= 0.
260 void BindToGC(const v8::FunctionCallbackInfo<v8::Value>& args) { 118 void BindToGC(const v8::FunctionCallbackInfo<v8::Value>& args) {
261 CHECK(args.Length() == 3 && args[0]->IsObject() && args[1]->IsFunction() && 119 CHECK(args.Length() == 3 && args[0]->IsObject() && args[1]->IsFunction() &&
262 args[2]->IsInt32()); 120 args[2]->IsInt32());
263 int port_id = args[2].As<v8::Int32>()->Value(); 121 int port_id = args[2].As<v8::Int32>()->Value();
264 base::Closure fallback = base::Bind(&base::DoNothing); 122 base::Closure fallback = base::Bind(&base::DoNothing);
265 if (port_id >= 0) { 123 if (port_id >= 0) {
266 fallback = base::Bind(&ExtensionImpl::ReleasePort, 124 fallback =
267 weak_ptr_factory_.GetWeakPtr(), port_id); 125 base::Bind(&ExtensionImpl::ClosePort, weak_ptr_factory_.GetWeakPtr(),
126 port_id, /* force_close */ false);
Devlin 2016/05/17 19:50:30 nit: put the comment after "false"
robwu 2016/05/17 22:47:50 Done.
268 } 127 }
269 // Destroys itself when the object is GC'd or context is invalidated. 128 // Destroys itself when the object is GC'd or context is invalidated.
270 new GCCallback(context(), args[0].As<v8::Object>(), 129 new GCCallback(context(), args[0].As<v8::Object>(),
271 args[1].As<v8::Function>(), fallback); 130 args[1].As<v8::Function>(), fallback);
272 } 131 }
273 132
274 // Dispatcher handle. Not owned. 133 void ClosePort(int port_id, bool force_close) {
Devlin 2016/05/17 19:50:29 function doc
robwu 2016/05/17 22:47:51 Done.
275 Dispatcher* dispatcher_; 134 content::RenderFrame* render_frame = context()->GetRenderFrame();
135 if (render_frame) {
136 render_frame->Send(new ExtensionHostMsg_CloseMessagePort(
137 render_frame->GetRoutingID(), port_id, force_close));
138 }
139 }
276 140
277 base::WeakPtrFactory<ExtensionImpl> weak_ptr_factory_; 141 base::WeakPtrFactory<ExtensionImpl> weak_ptr_factory_;
278 }; 142 };
279 143
144 void HasMessagePort(int port_id,
145 bool* has_port,
146 ScriptContext* script_context) {
147 if (*has_port)
148 return; // Stop checking if the port was found.
149
150 v8::Isolate* isolate = script_context->isolate();
151 v8::HandleScope handle_scope(isolate);
152
153 v8::Local<v8::Value> port_id_handle = v8::Integer::New(isolate, port_id);
154 v8::Local<v8::Value> v8_has_port =
155 script_context->module_system()->CallModuleMethod("messaging", "hasPort",
156 1, &port_id_handle);
157 if (IsEmptyOrUndefied(v8_has_port))
158 return;
159 CHECK(v8_has_port->IsBoolean());
160 if (!v8_has_port.As<v8::Boolean>()->Value())
161 return;
162 *has_port = true;
163 }
164
280 void DispatchOnConnectToScriptContext( 165 void DispatchOnConnectToScriptContext(
281 int target_port_id, 166 int target_port_id,
282 const std::string& channel_name, 167 const std::string& channel_name,
283 const ExtensionMsg_TabConnectionInfo* source, 168 const ExtensionMsg_TabConnectionInfo* source,
284 const ExtensionMsg_ExternalConnectionInfo& info, 169 const ExtensionMsg_ExternalConnectionInfo& info,
285 const std::string& tls_channel_id, 170 const std::string& tls_channel_id,
286 bool* port_created, 171 bool* port_created,
287 ScriptContext* script_context) { 172 ScriptContext* script_context) {
288 v8::Isolate* isolate = script_context->isolate(); 173 v8::Isolate* isolate = script_context->isolate();
289 v8::HandleScope handle_scope(isolate); 174 v8::HandleScope handle_scope(isolate);
(...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after
428 } else { 313 } else {
429 arguments.push_back(v8::Null(isolate)); 314 arguments.push_back(v8::Null(isolate));
430 } 315 }
431 316
432 script_context->module_system()->CallModuleMethod( 317 script_context->module_system()->CallModuleMethod(
433 "messaging", "dispatchOnDisconnect", &arguments); 318 "messaging", "dispatchOnDisconnect", &arguments);
434 } 319 }
435 320
436 } // namespace 321 } // namespace
437 322
438 ObjectBackedNativeHandler* MessagingBindings::Get(Dispatcher* dispatcher, 323 ObjectBackedNativeHandler* MessagingBindings::Get(ScriptContext* context) {
439 ScriptContext* context) { 324 return new ExtensionImpl(context);
440 return new ExtensionImpl(dispatcher, context); 325 }
326
327 void MessagingBindings::CheckHasMessagePort(
Devlin 2016/05/17 19:50:30 nit: given the functionality, maybe rename this Va
328 const ScriptContextSet& context_set,
329 int port_id,
330 content::RenderFrame* render_frame) {
331 int routing_id = render_frame->GetRoutingID();
Devlin 2016/05/17 19:50:30 nit: inline this on line 341
robwu 2016/05/17 22:47:51 No, this was done for a reason. I have added a com
332
333 bool has_port = false;
334 context_set.ForEach(render_frame,
335 base::Bind(&HasMessagePort, port_id, &has_port));
336
337 // A reply is only sent if the port is missing, because the port is assumed to
338 // exist unless stated otherwise.
339 if (!has_port) {
340 content::RenderThread::Get()->Send(
341 new ExtensionHostMsg_CloseMessagePort(routing_id, port_id, false));
342 }
441 } 343 }
442 344
443 // static 345 // static
444 void MessagingBindings::DispatchOnConnect( 346 void MessagingBindings::DispatchOnConnect(
445 const ScriptContextSet& context_set, 347 const ScriptContextSet& context_set,
446 int target_port_id, 348 int target_port_id,
447 const std::string& channel_name, 349 const std::string& channel_name,
448 const ExtensionMsg_TabConnectionInfo& source, 350 const ExtensionMsg_TabConnectionInfo& source,
449 const ExtensionMsg_ExternalConnectionInfo& info, 351 const ExtensionMsg_ExternalConnectionInfo& info,
450 const std::string& tls_channel_id, 352 const std::string& tls_channel_id,
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
484 const ScriptContextSet& context_set, 386 const ScriptContextSet& context_set,
485 int port_id, 387 int port_id,
486 const std::string& error_message, 388 const std::string& error_message,
487 content::RenderFrame* restrict_to_render_frame) { 389 content::RenderFrame* restrict_to_render_frame) {
488 context_set.ForEach( 390 context_set.ForEach(
489 restrict_to_render_frame, 391 restrict_to_render_frame,
490 base::Bind(&DispatchOnDisconnectToScriptContext, port_id, error_message)); 392 base::Bind(&DispatchOnDisconnectToScriptContext, port_id, error_message));
491 } 393 }
492 394
493 } // namespace extensions 395 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698