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