Chromium Code Reviews| 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> |
| 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/macros.h" | |
| 16 #include "base/memory/weak_ptr.h" | |
| 17 #include "base/message_loop/message_loop.h" | 15 #include "base/message_loop/message_loop.h" |
| 18 #include "base/values.h" | 16 #include "base/values.h" |
| 19 #include "components/guest_view/common/guest_view_constants.h" | |
| 20 #include "content/public/child/v8_value_converter.h" | 17 #include "content/public/child/v8_value_converter.h" |
| 21 #include "content/public/common/child_process_host.h" | 18 #include "content/public/common/child_process_host.h" |
| 22 #include "content/public/renderer/render_frame.h" | 19 #include "content/public/renderer/render_frame.h" |
| 23 #include "content/public/renderer/render_thread.h" | 20 #include "content/public/renderer/render_thread.h" |
| 24 #include "extensions/common/api/messaging/message.h" | 21 #include "extensions/common/api/messaging/message.h" |
| 25 #include "extensions/common/extension_messages.h" | 22 #include "extensions/common/extension_messages.h" |
| 26 #include "extensions/common/manifest_handlers/externally_connectable.h" | 23 #include "extensions/common/manifest_handlers/externally_connectable.h" |
| 27 #include "extensions/renderer/event_bindings.h" | |
| 28 #include "extensions/renderer/extension_frame_helper.h" | 24 #include "extensions/renderer/extension_frame_helper.h" |
| 29 #include "extensions/renderer/gc_callback.h" | 25 #include "extensions/renderer/gc_callback.h" |
| 30 #include "extensions/renderer/object_backed_native_handler.h" | |
| 31 #include "extensions/renderer/script_context.h" | 26 #include "extensions/renderer/script_context.h" |
| 32 #include "extensions/renderer/script_context_set.h" | 27 #include "extensions/renderer/script_context_set.h" |
| 33 #include "extensions/renderer/v8_helpers.h" | 28 #include "extensions/renderer/v8_helpers.h" |
| 34 #include "third_party/WebKit/public/web/WebDocument.h" | 29 #include "third_party/WebKit/public/web/WebDocument.h" |
| 35 #include "third_party/WebKit/public/web/WebLocalFrame.h" | 30 #include "third_party/WebKit/public/web/WebLocalFrame.h" |
| 36 #include "third_party/WebKit/public/web/WebScopedUserGesture.h" | 31 #include "third_party/WebKit/public/web/WebScopedUserGesture.h" |
| 37 #include "third_party/WebKit/public/web/WebScopedWindowFocusAllowedIndicator.h" | 32 #include "third_party/WebKit/public/web/WebScopedWindowFocusAllowedIndicator.h" |
| 38 #include "third_party/WebKit/public/web/WebUserGestureIndicator.h" | 33 #include "third_party/WebKit/public/web/WebUserGestureIndicator.h" |
| 39 #include "v8/include/v8.h" | 34 #include "v8/include/v8.h" |
| 40 | 35 |
| 41 // Message passing API example (in a content script): | 36 // Message passing API example (in a content script): |
| 42 // var extension = | 37 // var extension = |
| 43 // new chrome.Extension('00123456789abcdef0123456789abcdef0123456'); | 38 // new chrome.Extension('00123456789abcdef0123456789abcdef0123456'); |
| 44 // var port = runtime.connect(); | 39 // var port = runtime.connect(); |
| 45 // port.postMessage('Can you hear me now?'); | 40 // port.postMessage('Can you hear me now?'); |
| 46 // port.onmessage.addListener(function(msg, port) { | 41 // port.onmessage.addListener(function(msg, port) { |
| 47 // alert('response=' + msg); | 42 // alert('response=' + msg); |
| 48 // port.postMessage('I got your reponse'); | 43 // port.postMessage('I got your reponse'); |
| 49 // }); | 44 // }); |
| 50 | 45 |
| 51 using content::RenderThread; | |
| 52 using content::V8ValueConverter; | |
| 53 | |
| 54 namespace extensions { | 46 namespace extensions { |
| 55 | 47 |
| 56 using v8_helpers::ToV8String; | 48 using v8_helpers::ToV8String; |
| 57 using v8_helpers::ToV8StringUnsafe; | |
| 58 using v8_helpers::IsEmptyOrUndefied; | 49 using v8_helpers::IsEmptyOrUndefied; |
| 59 | 50 |
| 60 namespace { | 51 namespace { |
| 61 | 52 |
| 62 class ExtensionImpl : public ObjectBackedNativeHandler { | |
|
Devlin
2016/08/30 22:40:56
All these are copy-pasted with no behavior changes
| |
| 63 public: | |
| 64 explicit ExtensionImpl(ScriptContext* context) | |
| 65 : ObjectBackedNativeHandler(context), weak_ptr_factory_(this) { | |
| 66 RouteFunction( | |
| 67 "CloseChannel", | |
| 68 base::Bind(&ExtensionImpl::CloseChannel, base::Unretained(this))); | |
| 69 RouteFunction( | |
| 70 "PostMessage", | |
| 71 base::Bind(&ExtensionImpl::PostMessage, base::Unretained(this))); | |
| 72 // TODO(fsamuel, kalman): Move BindToGC out of messaging natives. | |
| 73 RouteFunction("BindToGC", | |
| 74 base::Bind(&ExtensionImpl::BindToGC, base::Unretained(this))); | |
| 75 } | |
| 76 | |
| 77 ~ExtensionImpl() override {} | |
| 78 | |
| 79 private: | |
| 80 // Sends a message along the given channel. | |
| 81 void PostMessage(const v8::FunctionCallbackInfo<v8::Value>& args) { | |
| 82 // Arguments are (int32_t port_id, string message). | |
| 83 CHECK(args.Length() == 2 && args[0]->IsInt32() && args[1]->IsString()); | |
| 84 | |
| 85 int port_id = args[0].As<v8::Int32>()->Value(); | |
| 86 | |
| 87 content::RenderFrame* render_frame = context()->GetRenderFrame(); | |
| 88 if (render_frame) { | |
| 89 render_frame->Send(new ExtensionHostMsg_PostMessage( | |
| 90 render_frame->GetRoutingID(), port_id, | |
| 91 Message(*v8::String::Utf8Value(args[1]), | |
| 92 blink::WebUserGestureIndicator::isProcessingUserGesture()))); | |
| 93 } | |
| 94 } | |
| 95 | |
| 96 // Close a port, optionally forcefully (i.e. close the whole channel instead | |
| 97 // of just the given port). | |
| 98 void CloseChannel(const v8::FunctionCallbackInfo<v8::Value>& args) { | |
| 99 // Arguments are (int32_t port_id, bool force_close). | |
| 100 CHECK_EQ(2, args.Length()); | |
| 101 CHECK(args[0]->IsInt32()); | |
| 102 CHECK(args[1]->IsBoolean()); | |
| 103 | |
| 104 int port_id = args[0].As<v8::Int32>()->Value(); | |
| 105 bool force_close = args[1].As<v8::Boolean>()->Value(); | |
| 106 ClosePort(port_id, force_close); | |
| 107 } | |
| 108 | |
| 109 // void BindToGC(object, callback, port_id) | |
| 110 // | |
| 111 // Binds |callback| to be invoked *sometime after* |object| is garbage | |
| 112 // collected. We don't call the method re-entrantly so as to avoid executing | |
| 113 // JS in some bizarro undefined mid-GC state, nor do we then call into the | |
| 114 // script context if it's been invalidated. | |
| 115 // | |
| 116 // If the script context *is* invalidated in the meantime, as a slight hack, | |
| 117 // release the port with ID |port_id| if it's >= 0. | |
| 118 void BindToGC(const v8::FunctionCallbackInfo<v8::Value>& args) { | |
| 119 CHECK(args.Length() == 3 && args[0]->IsObject() && args[1]->IsFunction() && | |
| 120 args[2]->IsInt32()); | |
| 121 int port_id = args[2].As<v8::Int32>()->Value(); | |
| 122 base::Closure fallback = base::Bind(&base::DoNothing); | |
| 123 if (port_id >= 0) { | |
| 124 // TODO(robwu): Falling back to closing the port shouldn't be needed. If | |
| 125 // the script context is destroyed, then the frame has navigated. But that | |
| 126 // is already detected by the browser, so this logic is redundant. Remove | |
| 127 // this fallback (and move BindToGC out of messaging because it is also | |
| 128 // used in other places that have nothing to do with messaging...). | |
| 129 fallback = | |
| 130 base::Bind(&ExtensionImpl::ClosePort, weak_ptr_factory_.GetWeakPtr(), | |
| 131 port_id, false /* force_close */); | |
| 132 } | |
| 133 // Destroys itself when the object is GC'd or context is invalidated. | |
| 134 new GCCallback(context(), args[0].As<v8::Object>(), | |
| 135 args[1].As<v8::Function>(), fallback); | |
| 136 } | |
| 137 | |
| 138 // See ExtensionImpl::CloseChannel for documentation. | |
| 139 // TODO(robwu): Merge this logic with CloseChannel once the TODO in BindToGC | |
| 140 // has been addressed. | |
| 141 void ClosePort(int port_id, bool force_close) { | |
| 142 content::RenderFrame* render_frame = context()->GetRenderFrame(); | |
| 143 if (render_frame) { | |
| 144 render_frame->Send(new ExtensionHostMsg_CloseMessagePort( | |
| 145 render_frame->GetRoutingID(), port_id, force_close)); | |
| 146 } | |
| 147 } | |
| 148 | |
| 149 base::WeakPtrFactory<ExtensionImpl> weak_ptr_factory_; | |
| 150 }; | |
| 151 | |
| 152 void HasMessagePort(int port_id, | 53 void HasMessagePort(int port_id, |
| 153 bool* has_port, | 54 bool* has_port, |
| 154 ScriptContext* script_context) { | 55 ScriptContext* script_context) { |
| 155 if (*has_port) | 56 if (*has_port) |
| 156 return; // Stop checking if the port was found. | 57 return; // Stop checking if the port was found. |
| 157 | 58 |
| 158 v8::Isolate* isolate = script_context->isolate(); | 59 v8::Isolate* isolate = script_context->isolate(); |
| 159 v8::HandleScope handle_scope(isolate); | 60 v8::HandleScope handle_scope(isolate); |
| 160 | 61 |
| 161 v8::Local<v8::Value> port_id_handle = v8::Integer::New(isolate, port_id); | 62 v8::Local<v8::Value> port_id_handle = v8::Integer::New(isolate, port_id); |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 174 int target_port_id, | 75 int target_port_id, |
| 175 const std::string& channel_name, | 76 const std::string& channel_name, |
| 176 const ExtensionMsg_TabConnectionInfo* source, | 77 const ExtensionMsg_TabConnectionInfo* source, |
| 177 const ExtensionMsg_ExternalConnectionInfo& info, | 78 const ExtensionMsg_ExternalConnectionInfo& info, |
| 178 const std::string& tls_channel_id, | 79 const std::string& tls_channel_id, |
| 179 bool* port_created, | 80 bool* port_created, |
| 180 ScriptContext* script_context) { | 81 ScriptContext* script_context) { |
| 181 v8::Isolate* isolate = script_context->isolate(); | 82 v8::Isolate* isolate = script_context->isolate(); |
| 182 v8::HandleScope handle_scope(isolate); | 83 v8::HandleScope handle_scope(isolate); |
| 183 | 84 |
| 184 std::unique_ptr<V8ValueConverter> converter(V8ValueConverter::create()); | 85 std::unique_ptr<content::V8ValueConverter> converter( |
| 86 content::V8ValueConverter::create()); | |
| 185 | 87 |
| 186 const std::string& source_url_spec = info.source_url.spec(); | 88 const std::string& source_url_spec = info.source_url.spec(); |
| 187 std::string target_extension_id = script_context->GetExtensionID(); | 89 std::string target_extension_id = script_context->GetExtensionID(); |
| 188 const Extension* extension = script_context->extension(); | 90 const Extension* extension = script_context->extension(); |
| 189 | 91 |
| 190 v8::Local<v8::Value> tab = v8::Null(isolate); | 92 v8::Local<v8::Value> tab = v8::Null(isolate); |
| 191 v8::Local<v8::Value> tls_channel_id_value = v8::Undefined(isolate); | 93 v8::Local<v8::Value> tls_channel_id_value = v8::Undefined(isolate); |
| 192 v8::Local<v8::Value> guest_process_id = v8::Undefined(isolate); | 94 v8::Local<v8::Value> guest_process_id = v8::Undefined(isolate); |
| 193 v8::Local<v8::Value> guest_render_frame_routing_id = v8::Undefined(isolate); | 95 v8::Local<v8::Value> guest_render_frame_routing_id = v8::Undefined(isolate); |
| 194 | 96 |
| (...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 321 } else { | 223 } else { |
| 322 arguments.push_back(v8::Null(isolate)); | 224 arguments.push_back(v8::Null(isolate)); |
| 323 } | 225 } |
| 324 | 226 |
| 325 script_context->module_system()->CallModuleMethod( | 227 script_context->module_system()->CallModuleMethod( |
| 326 "messaging", "dispatchOnDisconnect", &arguments); | 228 "messaging", "dispatchOnDisconnect", &arguments); |
| 327 } | 229 } |
| 328 | 230 |
| 329 } // namespace | 231 } // namespace |
| 330 | 232 |
| 331 ObjectBackedNativeHandler* MessagingBindings::Get(ScriptContext* context) { | 233 MessagingBindings::MessagingBindings(ScriptContext* context) |
| 332 return new ExtensionImpl(context); | 234 : ObjectBackedNativeHandler(context), weak_ptr_factory_(this) { |
| 235 RouteFunction("CloseChannel", base::Bind(&MessagingBindings::CloseChannel, | |
| 236 base::Unretained(this))); | |
| 237 RouteFunction("PostMessage", base::Bind(&MessagingBindings::PostMessage, | |
| 238 base::Unretained(this))); | |
| 239 // TODO(fsamuel, kalman): Move BindToGC out of messaging natives. | |
| 240 RouteFunction("BindToGC", base::Bind(&MessagingBindings::BindToGC, | |
| 241 base::Unretained(this))); | |
| 333 } | 242 } |
| 334 | 243 |
| 244 MessagingBindings::~MessagingBindings() {} | |
| 245 | |
| 246 // static | |
| 335 void MessagingBindings::ValidateMessagePort( | 247 void MessagingBindings::ValidateMessagePort( |
| 336 const ScriptContextSet& context_set, | 248 const ScriptContextSet& context_set, |
| 337 int port_id, | 249 int port_id, |
| 338 content::RenderFrame* render_frame) { | 250 content::RenderFrame* render_frame) { |
| 339 int routing_id = render_frame->GetRoutingID(); | 251 int routing_id = render_frame->GetRoutingID(); |
| 340 | 252 |
| 341 bool has_port = false; | 253 bool has_port = false; |
| 342 context_set.ForEach(render_frame, | 254 context_set.ForEach(render_frame, |
| 343 base::Bind(&HasMessagePort, port_id, &has_port)); | 255 base::Bind(&HasMessagePort, port_id, &has_port)); |
| 344 // Note: HasMessagePort invokes a JavaScript function. If the runtime of the | 256 // Note: HasMessagePort invokes a JavaScript function. If the runtime of the |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 396 void MessagingBindings::DispatchOnDisconnect( | 308 void MessagingBindings::DispatchOnDisconnect( |
| 397 const ScriptContextSet& context_set, | 309 const ScriptContextSet& context_set, |
| 398 int port_id, | 310 int port_id, |
| 399 const std::string& error_message, | 311 const std::string& error_message, |
| 400 content::RenderFrame* restrict_to_render_frame) { | 312 content::RenderFrame* restrict_to_render_frame) { |
| 401 context_set.ForEach( | 313 context_set.ForEach( |
| 402 restrict_to_render_frame, | 314 restrict_to_render_frame, |
| 403 base::Bind(&DispatchOnDisconnectToScriptContext, port_id, error_message)); | 315 base::Bind(&DispatchOnDisconnectToScriptContext, port_id, error_message)); |
| 404 } | 316 } |
| 405 | 317 |
| 318 void MessagingBindings::PostMessage( | |
| 319 const v8::FunctionCallbackInfo<v8::Value>& args) { | |
| 320 // Arguments are (int32_t port_id, string message). | |
| 321 CHECK(args.Length() == 2 && args[0]->IsInt32() && args[1]->IsString()); | |
| 322 | |
| 323 int port_id = args[0].As<v8::Int32>()->Value(); | |
| 324 | |
| 325 content::RenderFrame* render_frame = context()->GetRenderFrame(); | |
| 326 if (render_frame) { | |
| 327 render_frame->Send(new ExtensionHostMsg_PostMessage( | |
| 328 render_frame->GetRoutingID(), port_id, | |
| 329 Message(*v8::String::Utf8Value(args[1]), | |
| 330 blink::WebUserGestureIndicator::isProcessingUserGesture()))); | |
| 331 } | |
| 332 } | |
| 333 | |
| 334 void MessagingBindings::CloseChannel( | |
| 335 const v8::FunctionCallbackInfo<v8::Value>& args) { | |
| 336 // Arguments are (int32_t port_id, bool force_close). | |
| 337 CHECK_EQ(2, args.Length()); | |
| 338 CHECK(args[0]->IsInt32()); | |
| 339 CHECK(args[1]->IsBoolean()); | |
| 340 | |
| 341 int port_id = args[0].As<v8::Int32>()->Value(); | |
| 342 bool force_close = args[1].As<v8::Boolean>()->Value(); | |
| 343 ClosePort(port_id, force_close); | |
| 344 } | |
| 345 | |
| 346 void MessagingBindings::BindToGC( | |
| 347 const v8::FunctionCallbackInfo<v8::Value>& args) { | |
| 348 CHECK(args.Length() == 3 && args[0]->IsObject() && args[1]->IsFunction() && | |
| 349 args[2]->IsInt32()); | |
| 350 int port_id = args[2].As<v8::Int32>()->Value(); | |
| 351 base::Closure fallback = base::Bind(&base::DoNothing); | |
| 352 if (port_id >= 0) { | |
| 353 // TODO(robwu): Falling back to closing the port shouldn't be needed. If | |
| 354 // the script context is destroyed, then the frame has navigated. But that | |
| 355 // is already detected by the browser, so this logic is redundant. Remove | |
| 356 // this fallback (and move BindToGC out of messaging because it is also | |
| 357 // used in other places that have nothing to do with messaging...). | |
| 358 fallback = base::Bind(&MessagingBindings::ClosePort, | |
| 359 weak_ptr_factory_.GetWeakPtr(), port_id, | |
| 360 false /* force_close */); | |
| 361 } | |
| 362 // Destroys itself when the object is GC'd or context is invalidated. | |
| 363 new GCCallback(context(), args[0].As<v8::Object>(), | |
| 364 args[1].As<v8::Function>(), fallback); | |
| 365 } | |
| 366 | |
| 367 void MessagingBindings::ClosePort(int port_id, bool force_close) { | |
| 368 // TODO(robwu): Merge this logic with CloseChannel once the TODO in BindToGC | |
| 369 // has been addressed. | |
| 370 content::RenderFrame* render_frame = context()->GetRenderFrame(); | |
| 371 if (render_frame) { | |
| 372 render_frame->Send(new ExtensionHostMsg_CloseMessagePort( | |
| 373 render_frame->GetRoutingID(), port_id, force_close)); | |
| 374 } | |
| 375 } | |
| 376 | |
| 406 } // namespace extensions | 377 } // namespace extensions |
| OLD | NEW |