OLD | NEW |
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 <map> | 7 #include <map> |
8 #include <string> | 8 #include <string> |
9 | 9 |
10 #include "base/basictypes.h" | 10 #include "base/basictypes.h" |
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
121 private: | 121 private: |
122 // Maps ScriptContexts to the port IDs that have a reference to it. | 122 // Maps ScriptContexts to the port IDs that have a reference to it. |
123 std::map<ScriptContext*, std::set<int>> contexts_to_ports_; | 123 std::map<ScriptContext*, std::set<int>> contexts_to_ports_; |
124 | 124 |
125 DISALLOW_COPY_AND_ASSIGN(PortTracker); | 125 DISALLOW_COPY_AND_ASSIGN(PortTracker); |
126 }; | 126 }; |
127 | 127 |
128 base::LazyInstance<PortTracker> g_port_tracker = LAZY_INSTANCE_INITIALIZER; | 128 base::LazyInstance<PortTracker> g_port_tracker = LAZY_INSTANCE_INITIALIZER; |
129 | 129 |
130 const char kPortClosedError[] = "Attempting to use a disconnected port object"; | 130 const char kPortClosedError[] = "Attempting to use a disconnected port object"; |
131 const char kReceivingEndDoesntExistError[] = | |
132 "Could not establish connection. Receiving end does not exist."; | |
133 | 131 |
134 class ExtensionImpl : public ObjectBackedNativeHandler { | 132 class ExtensionImpl : public ObjectBackedNativeHandler { |
135 public: | 133 public: |
136 ExtensionImpl(Dispatcher* dispatcher, ScriptContext* context) | 134 ExtensionImpl(Dispatcher* dispatcher, ScriptContext* context) |
137 : ObjectBackedNativeHandler(context), | 135 : ObjectBackedNativeHandler(context), |
138 dispatcher_(dispatcher), | 136 dispatcher_(dispatcher), |
139 weak_ptr_factory_(this) { | 137 weak_ptr_factory_(this) { |
140 RouteFunction( | 138 RouteFunction( |
141 "CloseChannel", | 139 "CloseChannel", |
142 base::Bind(&ExtensionImpl::CloseChannel, base::Unretained(this))); | 140 base::Bind(&ExtensionImpl::CloseChannel, base::Unretained(this))); |
(...skipping 23 matching lines...) Expand all Loading... |
166 ReleasePort(port_id); | 164 ReleasePort(port_id); |
167 } | 165 } |
168 | 166 |
169 void ClearPortDataAndNotifyDispatcher(int port_id) { | 167 void ClearPortDataAndNotifyDispatcher(int port_id) { |
170 g_port_tracker.Get().DeletePort(port_id); | 168 g_port_tracker.Get().DeletePort(port_id); |
171 dispatcher_->ClearPortData(port_id); | 169 dispatcher_->ClearPortData(port_id); |
172 } | 170 } |
173 | 171 |
174 // Sends a message along the given channel. | 172 // Sends a message along the given channel. |
175 void PostMessage(const v8::FunctionCallbackInfo<v8::Value>& args) { | 173 void PostMessage(const v8::FunctionCallbackInfo<v8::Value>& args) { |
176 content::RenderFrame* renderframe = context()->GetRenderFrame(); | 174 content::RenderFrame* render_frame = context()->GetRenderFrame(); |
177 if (!renderframe) | 175 if (!render_frame) |
178 return; | 176 return; |
179 | 177 |
180 // Arguments are (int32 port_id, string message). | 178 // Arguments are (int32 port_id, string message). |
181 CHECK(args.Length() == 2 && args[0]->IsInt32() && args[1]->IsString()); | 179 CHECK(args.Length() == 2 && args[0]->IsInt32() && args[1]->IsString()); |
182 | 180 |
183 int port_id = args[0].As<v8::Int32>()->Value(); | 181 int port_id = args[0].As<v8::Int32>()->Value(); |
184 if (!g_port_tracker.Get().HasPort(port_id)) { | 182 if (!g_port_tracker.Get().HasPort(port_id)) { |
185 v8::Local<v8::String> error_message = | 183 v8::Local<v8::String> error_message = |
186 ToV8StringUnsafe(args.GetIsolate(), kPortClosedError); | 184 ToV8StringUnsafe(args.GetIsolate(), kPortClosedError); |
187 args.GetIsolate()->ThrowException(v8::Exception::Error(error_message)); | 185 args.GetIsolate()->ThrowException(v8::Exception::Error(error_message)); |
188 return; | 186 return; |
189 } | 187 } |
190 | 188 |
191 renderframe->Send(new ExtensionHostMsg_PostMessage( | 189 render_frame->Send(new ExtensionHostMsg_PostMessage( |
192 renderframe->GetRoutingID(), port_id, | 190 render_frame->GetRoutingID(), port_id, |
193 Message(*v8::String::Utf8Value(args[1]), | 191 Message(*v8::String::Utf8Value(args[1]), |
194 blink::WebUserGestureIndicator::isProcessingUserGesture()))); | 192 blink::WebUserGestureIndicator::isProcessingUserGesture()))); |
195 } | 193 } |
196 | 194 |
197 // Forcefully disconnects a port. | 195 // Forcefully disconnects a port. |
198 void CloseChannel(const v8::FunctionCallbackInfo<v8::Value>& args) { | 196 void CloseChannel(const v8::FunctionCallbackInfo<v8::Value>& args) { |
199 // Arguments are (int32 port_id, boolean notify_browser). | 197 // Arguments are (int32 port_id, boolean notify_browser). |
200 CHECK_EQ(2, args.Length()); | 198 CHECK_EQ(2, args.Length()); |
201 CHECK(args[0]->IsInt32()); | 199 CHECK(args[0]->IsInt32()); |
202 CHECK(args[1]->IsBoolean()); | 200 CHECK(args[1]->IsBoolean()); |
203 | 201 |
204 int port_id = args[0].As<v8::Int32>()->Value(); | 202 int port_id = args[0].As<v8::Int32>()->Value(); |
205 if (!g_port_tracker.Get().HasPort(port_id)) | 203 if (!g_port_tracker.Get().HasPort(port_id)) |
206 return; | 204 return; |
207 | 205 |
208 // Send via the RenderThread because the RenderFrame might be closing. | 206 // Send via the RenderThread because the RenderFrame might be closing. |
209 bool notify_browser = args[1].As<v8::Boolean>()->Value(); | 207 bool notify_browser = args[1].As<v8::Boolean>()->Value(); |
210 if (notify_browser) { | 208 content::RenderFrame* render_frame = context()->GetRenderFrame(); |
211 content::RenderThread::Get()->Send( | 209 if (notify_browser && render_frame) { |
212 new ExtensionHostMsg_CloseChannel(port_id, std::string())); | 210 render_frame->Send(new ExtensionHostMsg_CloseMessagePort( |
| 211 render_frame->GetRoutingID(), port_id, true)); |
213 } | 212 } |
214 | 213 |
215 ClearPortDataAndNotifyDispatcher(port_id); | 214 ClearPortDataAndNotifyDispatcher(port_id); |
216 } | 215 } |
217 | 216 |
218 // A new port has been created for a context. This occurs both when script | 217 // 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. | 218 // opens a connection, and when a connection is opened to this script. |
220 void PortAddRef(const v8::FunctionCallbackInfo<v8::Value>& args) { | 219 void PortAddRef(const v8::FunctionCallbackInfo<v8::Value>& args) { |
221 // Arguments are (int32 port_id). | 220 // Arguments are (int32 port_id). |
222 CHECK_EQ(1, args.Length()); | 221 CHECK_EQ(1, args.Length()); |
223 CHECK(args[0]->IsInt32()); | 222 CHECK(args[0]->IsInt32()); |
224 | 223 |
225 int port_id = args[0].As<v8::Int32>()->Value(); | 224 int port_id = args[0].As<v8::Int32>()->Value(); |
226 g_port_tracker.Get().AddReference(context(), port_id); | 225 g_port_tracker.Get().AddReference(context(), port_id); |
227 } | 226 } |
228 | 227 |
229 // The frame a port lived in has been destroyed. When there are no more | 228 // 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 | 229 // frames with a reference to a given port, we will disconnect it and notify |
231 // the other end of the channel. | 230 // the other end of the channel. |
| 231 // TODO(robwu): Port lifetime management has moved to the browser, this is no |
| 232 // longer needed. See .destroy_() inmessaging.js for more details. |
232 void PortRelease(const v8::FunctionCallbackInfo<v8::Value>& args) { | 233 void PortRelease(const v8::FunctionCallbackInfo<v8::Value>& args) { |
233 // Arguments are (int32 port_id). | 234 // Arguments are (int32 port_id). |
234 CHECK(args.Length() == 1 && args[0]->IsInt32()); | 235 CHECK(args.Length() == 1 && args[0]->IsInt32()); |
235 ReleasePort(args[0].As<v8::Int32>()->Value()); | 236 ReleasePort(args[0].As<v8::Int32>()->Value()); |
236 } | 237 } |
237 | 238 |
238 // Releases the reference to |port_id| for this context, and clears all port | 239 // Releases the reference to |port_id| for this context, and clears all port |
239 // data if there are no more references. | 240 // data if there are no more references. |
240 void ReleasePort(int port_id) { | 241 void ReleasePort(int port_id) { |
| 242 content::RenderFrame* render_frame = context()->GetRenderFrame(); |
241 if (g_port_tracker.Get().RemoveReference(context(), port_id) && | 243 if (g_port_tracker.Get().RemoveReference(context(), port_id) && |
242 !g_port_tracker.Get().HasPort(port_id)) { | 244 !g_port_tracker.Get().HasPort(port_id) && render_frame) { |
243 // Send via the RenderThread because the RenderFrame might be closing. | 245 render_frame->Send(new ExtensionHostMsg_CloseMessagePort( |
244 content::RenderThread::Get()->Send( | 246 render_frame->GetRoutingID(), port_id, false)); |
245 new ExtensionHostMsg_CloseChannel(port_id, std::string())); | |
246 ClearPortDataAndNotifyDispatcher(port_id); | |
247 } | 247 } |
248 } | 248 } |
249 | 249 |
250 // void BindToGC(object, callback, port_id) | 250 // void BindToGC(object, callback, port_id) |
251 // | 251 // |
252 // Binds |callback| to be invoked *sometime after* |object| is garbage | 252 // Binds |callback| to be invoked *sometime after* |object| is garbage |
253 // collected. We don't call the method re-entrantly so as to avoid executing | 253 // collected. We don't call the method re-entrantly so as to avoid executing |
254 // JS in some bizarro undefined mid-GC state, nor do we then call into the | 254 // JS in some bizarro undefined mid-GC state, nor do we then call into the |
255 // script context if it's been invalidated. | 255 // script context if it's been invalidated. |
256 // | 256 // |
(...skipping 20 matching lines...) Expand all Loading... |
277 }; | 277 }; |
278 | 278 |
279 void DispatchOnConnectToScriptContext( | 279 void DispatchOnConnectToScriptContext( |
280 int target_port_id, | 280 int target_port_id, |
281 const std::string& channel_name, | 281 const std::string& channel_name, |
282 const ExtensionMsg_TabConnectionInfo* source, | 282 const ExtensionMsg_TabConnectionInfo* source, |
283 const ExtensionMsg_ExternalConnectionInfo& info, | 283 const ExtensionMsg_ExternalConnectionInfo& info, |
284 const std::string& tls_channel_id, | 284 const std::string& tls_channel_id, |
285 bool* port_created, | 285 bool* port_created, |
286 ScriptContext* script_context) { | 286 ScriptContext* script_context) { |
287 // Only dispatch the events if this is the requested target frame (0 = main | |
288 // frame; positive = child frame). | |
289 content::RenderFrame* renderframe = script_context->GetRenderFrame(); | |
290 if (info.target_frame_id == 0 && renderframe->GetWebFrame()->parent() != NULL) | |
291 return; | |
292 if (info.target_frame_id > 0 && | |
293 renderframe->GetRoutingID() != info.target_frame_id) | |
294 return; | |
295 | |
296 // Bandaid fix for crbug.com/520303. | |
297 // TODO(rdevlin.cronin): Fix this properly by routing messages to the correct | |
298 // RenderFrame from the browser (same with |target_frame_id| in fact). | |
299 if (info.target_tab_id != -1 && | |
300 info.target_tab_id != ExtensionFrameHelper::Get(renderframe)->tab_id()) { | |
301 return; | |
302 } | |
303 | |
304 v8::Isolate* isolate = script_context->isolate(); | 287 v8::Isolate* isolate = script_context->isolate(); |
305 v8::HandleScope handle_scope(isolate); | 288 v8::HandleScope handle_scope(isolate); |
306 | 289 |
307 scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create()); | 290 scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create()); |
308 | 291 |
309 const std::string& source_url_spec = info.source_url.spec(); | 292 const std::string& source_url_spec = info.source_url.spec(); |
310 std::string target_extension_id = script_context->GetExtensionID(); | 293 std::string target_extension_id = script_context->GetExtensionID(); |
311 const Extension* extension = script_context->extension(); | 294 const Extension* extension = script_context->extension(); |
312 | 295 |
313 v8::Local<v8::Value> tab = v8::Null(isolate); | 296 v8::Local<v8::Value> tab = v8::Null(isolate); |
(...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
463 const ExtensionMsg_TabConnectionInfo& source, | 446 const ExtensionMsg_TabConnectionInfo& source, |
464 const ExtensionMsg_ExternalConnectionInfo& info, | 447 const ExtensionMsg_ExternalConnectionInfo& info, |
465 const std::string& tls_channel_id, | 448 const std::string& tls_channel_id, |
466 content::RenderFrame* restrict_to_render_frame) { | 449 content::RenderFrame* restrict_to_render_frame) { |
467 bool port_created = false; | 450 bool port_created = false; |
468 context_set.ForEach( | 451 context_set.ForEach( |
469 info.target_id, restrict_to_render_frame, | 452 info.target_id, restrict_to_render_frame, |
470 base::Bind(&DispatchOnConnectToScriptContext, target_port_id, | 453 base::Bind(&DispatchOnConnectToScriptContext, target_port_id, |
471 channel_name, &source, info, tls_channel_id, &port_created)); | 454 channel_name, &source, info, tls_channel_id, &port_created)); |
472 | 455 |
473 // If we didn't create a port, notify the other end of the channel (treat it | 456 int routing_id = restrict_to_render_frame |
474 // as a disconnect). | 457 ? restrict_to_render_frame->GetRoutingID() |
475 if (!port_created) { | 458 : MSG_ROUTING_NONE; |
476 content::RenderThread::Get()->Send(new ExtensionHostMsg_CloseChannel( | 459 if (port_created) { |
477 target_port_id, kReceivingEndDoesntExistError)); | 460 content::RenderThread::Get()->Send( |
| 461 new ExtensionHostMsg_OpenMessagePort(routing_id, target_port_id)); |
| 462 } else { |
| 463 content::RenderThread::Get()->Send(new ExtensionHostMsg_CloseMessagePort( |
| 464 routing_id, target_port_id, false)); |
478 } | 465 } |
479 } | 466 } |
480 | 467 |
481 // static | 468 // static |
482 void MessagingBindings::DeliverMessage( | 469 void MessagingBindings::DeliverMessage( |
483 const ScriptContextSet& context_set, | 470 const ScriptContextSet& context_set, |
484 int target_port_id, | 471 int target_port_id, |
485 const Message& message, | 472 const Message& message, |
486 content::RenderFrame* restrict_to_render_frame) { | 473 content::RenderFrame* restrict_to_render_frame) { |
487 context_set.ForEach( | 474 context_set.ForEach( |
488 restrict_to_render_frame, | 475 restrict_to_render_frame, |
489 base::Bind(&DeliverMessageToScriptContext, message, target_port_id)); | 476 base::Bind(&DeliverMessageToScriptContext, message, target_port_id)); |
490 } | 477 } |
491 | 478 |
492 // static | 479 // static |
493 void MessagingBindings::DispatchOnDisconnect( | 480 void MessagingBindings::DispatchOnDisconnect( |
494 const ScriptContextSet& context_set, | 481 const ScriptContextSet& context_set, |
495 int port_id, | 482 int port_id, |
496 const std::string& error_message, | 483 const std::string& error_message, |
497 content::RenderFrame* restrict_to_render_frame) { | 484 content::RenderFrame* restrict_to_render_frame) { |
498 context_set.ForEach( | 485 context_set.ForEach( |
499 restrict_to_render_frame, | 486 restrict_to_render_frame, |
500 base::Bind(&DispatchOnDisconnectToScriptContext, port_id, error_message)); | 487 base::Bind(&DispatchOnDisconnectToScriptContext, port_id, error_message)); |
501 } | 488 } |
502 | 489 |
503 } // namespace extensions | 490 } // namespace extensions |
OLD | NEW |