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 <map> | 7 #include <map> |
| 8 #include <string> | 8 #include <string> |
| 9 | 9 |
| 10 #include "base/basictypes.h" | 10 #include "base/basictypes.h" |
| 11 #include "base/bind.h" | 11 #include "base/bind.h" |
| 12 #include "base/bind_helpers.h" | 12 #include "base/bind_helpers.h" |
| 13 #include "base/callback.h" | |
| 13 #include "base/lazy_instance.h" | 14 #include "base/lazy_instance.h" |
| 15 #include "base/memory/weak_ptr.h" | |
| 14 #include "base/message_loop/message_loop.h" | 16 #include "base/message_loop/message_loop.h" |
| 15 #include "base/values.h" | 17 #include "base/values.h" |
| 16 #include "components/guest_view/common/guest_view_constants.h" | 18 #include "components/guest_view/common/guest_view_constants.h" |
| 17 #include "content/public/child/v8_value_converter.h" | 19 #include "content/public/child/v8_value_converter.h" |
| 18 #include "content/public/common/child_process_host.h" | 20 #include "content/public/common/child_process_host.h" |
| 19 #include "content/public/renderer/render_frame.h" | 21 #include "content/public/renderer/render_frame.h" |
| 20 #include "content/public/renderer/render_thread.h" | 22 #include "content/public/renderer/render_thread.h" |
| 21 #include "extensions/common/api/messaging/message.h" | 23 #include "extensions/common/api/messaging/message.h" |
| 22 #include "extensions/common/extension_messages.h" | 24 #include "extensions/common/extension_messages.h" |
| 23 #include "extensions/common/manifest_handlers/externally_connectable.h" | 25 #include "extensions/common/manifest_handlers/externally_connectable.h" |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 44 // port.postMessage('I got your reponse'); | 46 // port.postMessage('I got your reponse'); |
| 45 // }); | 47 // }); |
| 46 | 48 |
| 47 using content::RenderThread; | 49 using content::RenderThread; |
| 48 using content::V8ValueConverter; | 50 using content::V8ValueConverter; |
| 49 | 51 |
| 50 namespace extensions { | 52 namespace extensions { |
| 51 | 53 |
| 52 namespace { | 54 namespace { |
| 53 | 55 |
| 56 // Binds |callback| to run when |object| is garbage collected. So as to not | |
| 57 // re-entrantly call into v8, we execute this function asynchronously, at | |
| 58 // which point |context| may have been invalidated. If so, |callback| is not | |
| 59 // run, and |fallback| will be called instead. | |
| 60 // | |
| 61 // Deletes itself when the object args[0] is garbage collected or when the | |
| 62 // context is invalidated. | |
| 63 class GCCallback : public base::SupportsWeakPtr<GCCallback> { | |
|
not at google - send to devlin
2015/05/15 18:14:24
Moved this up here because that's where it belongs
| |
| 64 public: | |
| 65 GCCallback(ScriptContext* context, | |
| 66 const v8::Local<v8::Object>& object, | |
| 67 const v8::Local<v8::Function>& callback, | |
| 68 const base::Closure& fallback) | |
| 69 : context_(context), | |
| 70 object_(context->isolate(), object), | |
| 71 callback_(context->isolate(), callback), | |
| 72 fallback_(fallback) { | |
| 73 object_.SetWeak(this, FirstWeakCallback, v8::WeakCallbackType::kParameter); | |
| 74 context->AddInvalidationObserver( | |
|
not at google - send to devlin
2015/05/15 18:14:24
Now watches for context invalidation.
| |
| 75 base::Bind(&GCCallback::OnContextInvalidated, AsWeakPtr())); | |
| 76 } | |
| 77 | |
| 78 private: | |
| 79 static void FirstWeakCallback(const v8::WeakCallbackInfo<GCCallback>& data) { | |
| 80 // v8 says we need to explicitly reset weak handles from their callbacks. | |
| 81 // It's not implicit as one might expect. | |
| 82 data.GetParameter()->object_.Reset(); | |
| 83 data.SetSecondPassCallback(SecondWeakCallback); | |
| 84 } | |
| 85 | |
| 86 static void SecondWeakCallback(const v8::WeakCallbackInfo<GCCallback>& data) { | |
| 87 base::MessageLoop::current()->PostTask( | |
| 88 FROM_HERE, | |
| 89 base::Bind(&GCCallback::RunCallback, data.GetParameter()->AsWeakPtr())); | |
|
not at google - send to devlin
2015/05/15 18:14:24
Changed this from being base::Owned to a WeakPtr w
| |
| 90 } | |
| 91 | |
| 92 void RunCallback() { | |
| 93 CHECK(context_); | |
| 94 v8::Isolate* isolate = context_->isolate(); | |
| 95 v8::HandleScope handle_scope(isolate); | |
| 96 context_->CallFunction(v8::Local<v8::Function>::New(isolate, callback_)); | |
|
not at google - send to devlin
2015/05/15 18:14:24
Now uses the proper CallFunction method on context
| |
| 97 delete this; | |
| 98 } | |
| 99 | |
| 100 void OnContextInvalidated() { | |
| 101 fallback_.Run(); | |
| 102 context_ = NULL; | |
| 103 delete this; | |
| 104 } | |
| 105 | |
| 106 // ScriptContext which owns this GCCallback. | |
| 107 ScriptContext* context_; | |
| 108 | |
| 109 // Holds a global handle to the object this GCCallback is bound to. | |
| 110 v8::Global<v8::Object> object_; | |
| 111 | |
| 112 // Function to run when |object_| bound to this GCCallback is GC'd. | |
| 113 v8::Global<v8::Function> callback_; | |
| 114 | |
| 115 // Function to run if context is invalidated before we have a chance | |
| 116 // to execute |callback_|. | |
| 117 base::Closure fallback_; | |
| 118 | |
| 119 DISALLOW_COPY_AND_ASSIGN(GCCallback); | |
| 120 }; | |
| 121 | |
| 54 struct ExtensionData { | 122 struct ExtensionData { |
| 55 struct PortData { | 123 struct PortData { |
| 56 int ref_count; // how many contexts have a handle to this port | 124 int ref_count; // how many contexts have a handle to this port |
| 57 PortData() : ref_count(0) {} | 125 PortData() : ref_count(0) {} |
| 58 }; | 126 }; |
| 59 std::map<int, PortData> ports; // port ID -> data | 127 std::map<int, PortData> ports; // port ID -> data |
| 60 }; | 128 }; |
| 61 | 129 |
| 62 base::LazyInstance<ExtensionData> g_extension_data = LAZY_INSTANCE_INITIALIZER; | 130 base::LazyInstance<ExtensionData> g_extension_data = LAZY_INSTANCE_INITIALIZER; |
| 63 | 131 |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 74 g_extension_data.Get().ports.erase(port_id); | 142 g_extension_data.Get().ports.erase(port_id); |
| 75 } | 143 } |
| 76 | 144 |
| 77 const char kPortClosedError[] = "Attempting to use a disconnected port object"; | 145 const char kPortClosedError[] = "Attempting to use a disconnected port object"; |
| 78 const char kReceivingEndDoesntExistError[] = | 146 const char kReceivingEndDoesntExistError[] = |
| 79 "Could not establish connection. Receiving end does not exist."; | 147 "Could not establish connection. Receiving end does not exist."; |
| 80 | 148 |
| 81 class ExtensionImpl : public ObjectBackedNativeHandler { | 149 class ExtensionImpl : public ObjectBackedNativeHandler { |
| 82 public: | 150 public: |
| 83 ExtensionImpl(Dispatcher* dispatcher, ScriptContext* context) | 151 ExtensionImpl(Dispatcher* dispatcher, ScriptContext* context) |
| 84 : ObjectBackedNativeHandler(context), dispatcher_(dispatcher) { | 152 : ObjectBackedNativeHandler(context), |
| 153 dispatcher_(dispatcher), | |
| 154 weak_ptr_factory_(this) { | |
| 85 RouteFunction( | 155 RouteFunction( |
| 86 "CloseChannel", | 156 "CloseChannel", |
| 87 base::Bind(&ExtensionImpl::CloseChannel, base::Unretained(this))); | 157 base::Bind(&ExtensionImpl::CloseChannel, base::Unretained(this))); |
| 88 RouteFunction( | 158 RouteFunction( |
| 89 "PortAddRef", | 159 "PortAddRef", |
| 90 base::Bind(&ExtensionImpl::PortAddRef, base::Unretained(this))); | 160 base::Bind(&ExtensionImpl::PortAddRef, base::Unretained(this))); |
| 91 RouteFunction( | 161 RouteFunction( |
| 92 "PortRelease", | 162 "PortRelease", |
| 93 base::Bind(&ExtensionImpl::PortRelease, base::Unretained(this))); | 163 base::Bind(&ExtensionImpl::PortRelease, base::Unretained(this))); |
| 94 RouteFunction( | 164 RouteFunction( |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 159 | 229 |
| 160 int port_id = args[0]->Int32Value(); | 230 int port_id = args[0]->Int32Value(); |
| 161 ++GetPortData(port_id).ref_count; | 231 ++GetPortData(port_id).ref_count; |
| 162 } | 232 } |
| 163 | 233 |
| 164 // The frame a port lived in has been destroyed. When there are no more | 234 // The frame a port lived in has been destroyed. When there are no more |
| 165 // frames with a reference to a given port, we will disconnect it and notify | 235 // frames with a reference to a given port, we will disconnect it and notify |
| 166 // the other end of the channel. | 236 // the other end of the channel. |
| 167 void PortRelease(const v8::FunctionCallbackInfo<v8::Value>& args) { | 237 void PortRelease(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 168 // Arguments are (int32 port_id). | 238 // Arguments are (int32 port_id). |
| 169 CHECK_EQ(1, args.Length()); | 239 CHECK(args.Length() == 1 && args[0]->IsInt32()); |
| 170 CHECK(args[0]->IsInt32()); | 240 ReleasePort(args[0]->Int32Value()); |
| 241 } | |
| 171 | 242 |
| 172 int port_id = args[0]->Int32Value(); | 243 // Implementation of both the PortRelease native handler call, and callback |
| 244 // when contexts are invalidated to release their ports. | |
| 245 void ReleasePort(int port_id) { | |
| 173 if (HasPortData(port_id) && --GetPortData(port_id).ref_count == 0) { | 246 if (HasPortData(port_id) && --GetPortData(port_id).ref_count == 0) { |
| 174 // Send via the RenderThread because the RenderFrame might be closing. | 247 // Send via the RenderThread because the RenderFrame might be closing. |
| 175 content::RenderThread::Get()->Send( | 248 content::RenderThread::Get()->Send( |
| 176 new ExtensionHostMsg_CloseChannel(port_id, std::string())); | 249 new ExtensionHostMsg_CloseChannel(port_id, std::string())); |
| 177 ClearPortDataAndNotifyDispatcher(port_id); | 250 ClearPortDataAndNotifyDispatcher(port_id); |
| 178 } | 251 } |
| 179 } | 252 } |
| 180 | 253 |
| 181 // Holds a |callback| to run sometime after |object| is GC'ed. |callback| will | 254 // void BindToGC(object, callback, port_id) |
| 182 // not be executed re-entrantly to avoid running JS in an unexpected state. | |
| 183 class GCCallback { | |
| 184 public: | |
| 185 static void Bind(v8::Local<v8::Object> object, | |
| 186 v8::Local<v8::Function> callback, | |
| 187 v8::Isolate* isolate) { | |
| 188 GCCallback* cb = new GCCallback(object, callback, isolate); | |
| 189 cb->object_.SetWeak(cb, FirstWeakCallback, | |
| 190 v8::WeakCallbackType::kParameter); | |
| 191 } | |
| 192 | |
| 193 private: | |
| 194 static void FirstWeakCallback( | |
| 195 const v8::WeakCallbackInfo<GCCallback>& data) { | |
| 196 // v8 says we need to explicitly reset weak handles from their callbacks. | |
| 197 // It's not implicit as one might expect. | |
| 198 data.GetParameter()->object_.Reset(); | |
| 199 data.SetSecondPassCallback(SecondWeakCallback); | |
| 200 } | |
| 201 | |
| 202 static void SecondWeakCallback( | |
| 203 const v8::WeakCallbackInfo<GCCallback>& data) { | |
| 204 base::MessageLoop::current()->PostTask( | |
| 205 FROM_HERE, | |
| 206 base::Bind(&GCCallback::RunCallback, | |
| 207 base::Owned(data.GetParameter()))); | |
| 208 } | |
| 209 | |
| 210 GCCallback(v8::Local<v8::Object> object, | |
| 211 v8::Local<v8::Function> callback, | |
| 212 v8::Isolate* isolate) | |
| 213 : object_(isolate, object), | |
| 214 callback_(isolate, callback), | |
| 215 isolate_(isolate) {} | |
| 216 | |
| 217 void RunCallback() { | |
| 218 v8::HandleScope handle_scope(isolate_); | |
| 219 v8::Local<v8::Function> callback = | |
| 220 v8::Local<v8::Function>::New(isolate_, callback_); | |
| 221 v8::Local<v8::Context> context = callback->CreationContext(); | |
| 222 if (context.IsEmpty()) | |
| 223 return; | |
| 224 v8::Context::Scope context_scope(context); | |
| 225 blink::WebScopedMicrotaskSuppression suppression; | |
| 226 callback->Call(context->Global(), 0, NULL); | |
| 227 } | |
| 228 | |
| 229 v8::Global<v8::Object> object_; | |
| 230 v8::Global<v8::Function> callback_; | |
| 231 v8::Isolate* isolate_; | |
| 232 | |
| 233 DISALLOW_COPY_AND_ASSIGN(GCCallback); | |
| 234 }; | |
| 235 | |
| 236 // void BindToGC(object, callback) | |
| 237 // | 255 // |
| 238 // Binds |callback| to be invoked *sometime after* |object| is garbage | 256 // Binds |callback| to be invoked *sometime after* |object| is garbage |
| 239 // collected. We don't call the method re-entrantly so as to avoid executing | 257 // collected. We don't call the method re-entrantly so as to avoid executing |
| 240 // JS in some bizarro undefined mid-GC state. | 258 // JS in some bizarro undefined mid-GC state, nor do we then call into the |
| 259 // script context if it's been invalidated. | |
| 260 // | |
| 261 // If the script context *is* invalidated in the meantime, as a slight hack, | |
| 262 // release the port with ID |port_id| if it's >= 0. | |
| 241 void BindToGC(const v8::FunctionCallbackInfo<v8::Value>& args) { | 263 void BindToGC(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 242 CHECK(args.Length() == 2 && args[0]->IsObject() && args[1]->IsFunction()); | 264 CHECK(args.Length() == 3 && args[0]->IsObject() && args[1]->IsFunction() && |
| 243 GCCallback::Bind(args[0].As<v8::Object>(), | 265 args[2]->IsInt32()); |
| 244 args[1].As<v8::Function>(), | 266 int port_id = args[2]->Int32Value(); |
| 245 args.GetIsolate()); | 267 base::Closure fallback = base::Bind(&base::DoNothing); |
| 268 if (port_id >= 0) { | |
|
not at google - send to devlin
2015/05/15 18:14:24
The hack in question.
| |
| 269 fallback = base::Bind(&ExtensionImpl::ReleasePort, | |
| 270 weak_ptr_factory_.GetWeakPtr(), port_id); | |
| 271 } | |
| 272 new GCCallback(context(), args[0].As<v8::Object>(), | |
|
Ken Rockot(use gerrit already)
2015/05/15 21:47:54
nit: maybe document that this kills itself
not at google - send to devlin
2015/05/15 21:53:12
Done.
| |
| 273 args[1].As<v8::Function>(), fallback); | |
| 246 } | 274 } |
| 247 | 275 |
| 248 // Dispatcher handle. Not owned. | 276 // Dispatcher handle. Not owned. |
| 249 Dispatcher* dispatcher_; | 277 Dispatcher* dispatcher_; |
| 278 | |
| 279 base::WeakPtrFactory<ExtensionImpl> weak_ptr_factory_; | |
| 250 }; | 280 }; |
| 251 | 281 |
| 252 void DispatchOnConnectToScriptContext( | 282 void DispatchOnConnectToScriptContext( |
| 253 int target_port_id, | 283 int target_port_id, |
| 254 const std::string& channel_name, | 284 const std::string& channel_name, |
| 255 const ExtensionMsg_TabConnectionInfo* source, | 285 const ExtensionMsg_TabConnectionInfo* source, |
| 256 const ExtensionMsg_ExternalConnectionInfo& info, | 286 const ExtensionMsg_ExternalConnectionInfo& info, |
| 257 const std::string& tls_channel_id, | 287 const std::string& tls_channel_id, |
| 258 bool* port_created, | 288 bool* port_created, |
| 259 ScriptContext* script_context) { | 289 ScriptContext* script_context) { |
| (...skipping 193 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 453 // TODO(robwu): ScriptContextSet.ForEach should accept RenderFrame*. | 483 // TODO(robwu): ScriptContextSet.ForEach should accept RenderFrame*. |
| 454 content::RenderView* restrict_to_render_view = | 484 content::RenderView* restrict_to_render_view = |
| 455 restrict_to_render_frame ? restrict_to_render_frame->GetRenderView() | 485 restrict_to_render_frame ? restrict_to_render_frame->GetRenderView() |
| 456 : NULL; | 486 : NULL; |
| 457 context_set.ForEach( | 487 context_set.ForEach( |
| 458 restrict_to_render_view, | 488 restrict_to_render_view, |
| 459 base::Bind(&DispatchOnDisconnectToScriptContext, port_id, error_message)); | 489 base::Bind(&DispatchOnDisconnectToScriptContext, port_id, error_message)); |
| 460 } | 490 } |
| 461 | 491 |
| 462 } // namespace extensions | 492 } // namespace extensions |
| OLD | NEW |