| 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 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 112 // Function to run when |object_| bound to this GCCallback is GC'd. | 112 // Function to run when |object_| bound to this GCCallback is GC'd. |
| 113 v8::Global<v8::Function> callback_; | 113 v8::Global<v8::Function> callback_; |
| 114 | 114 |
| 115 // Function to run if context is invalidated before we have a chance | 115 // Function to run if context is invalidated before we have a chance |
| 116 // to execute |callback_|. | 116 // to execute |callback_|. |
| 117 base::Closure fallback_; | 117 base::Closure fallback_; |
| 118 | 118 |
| 119 DISALLOW_COPY_AND_ASSIGN(GCCallback); | 119 DISALLOW_COPY_AND_ASSIGN(GCCallback); |
| 120 }; | 120 }; |
| 121 | 121 |
| 122 struct ExtensionData { | 122 // Maintains the set of ScriptContexts that reference each Port, by ID. |
| 123 struct PortData { | 123 class PortTracker { |
| 124 int ref_count; // how many contexts have a handle to this port | 124 public: |
| 125 PortData() : ref_count(0) {} | 125 PortTracker() {} |
| 126 }; | 126 |
| 127 std::map<int, PortData> ports; // port ID -> data | 127 // Returns true if |context| references |port_id|. |
| 128 bool HasReference(ScriptContext* context, int port_id) const { |
| 129 auto ports = contexts_to_ports_.find(context); |
| 130 if (ports == contexts_to_ports_.end()) |
| 131 return false; |
| 132 return ports->second.count(port_id) > 0; |
| 133 } |
| 134 |
| 135 // Marks |context| and |port_id| as referencing each other. |
| 136 void AddReference(ScriptContext* context, int port_id) { |
| 137 if (contexts_to_ports_[context].insert(port_id).second) |
| 138 ++port_refcount_[port_id]; |
| 139 } |
| 140 |
| 141 // Removes the references between |context| and |port_id|. |
| 142 void RemoveReference(ScriptContext* context, int port_id) { |
| 143 auto ports = contexts_to_ports_.find(context); |
| 144 if (ports == contexts_to_ports_.end()) { |
| 145 return; |
| 146 } |
| 147 if (ports->second.erase(port_id) == 0) |
| 148 return; |
| 149 if (ports->second.size() == 0) |
| 150 contexts_to_ports_.erase(context); |
| 151 if (--port_refcount_[port_id] == 0) |
| 152 port_refcount_.erase(port_id); |
| 153 } |
| 154 |
| 155 // Returns true if this tracker has any reference to |port_id|. |
| 156 bool HasPort(int port_id) const { |
| 157 return port_refcount_.find(port_id) != port_refcount_.end(); |
| 158 } |
| 159 |
| 160 // Deletes all references to |port_id|. |
| 161 void DeletePort(int port_id) { |
| 162 port_refcount_.erase(port_id); |
| 163 for (auto it = contexts_to_ports_.begin(); |
| 164 it != contexts_to_ports_.end();) { |
| 165 if (it->second.erase(port_id) > 0 && it->second.empty()) |
| 166 contexts_to_ports_.erase(it++); |
| 167 else |
| 168 ++it; |
| 169 } |
| 170 } |
| 171 |
| 172 // Gets every port ID that has a reference to |context|. |
| 173 std::set<int> GetPorts(ScriptContext* context) const { |
| 174 auto ports = contexts_to_ports_.find(context); |
| 175 return ports == contexts_to_ports_.end() ? std::set<int>() : ports->second; |
| 176 } |
| 177 |
| 178 private: |
| 179 // Maps ScriptContexts to the port IDs that have a reference to it. |
| 180 std::map<ScriptContext*, std::set<int>> contexts_to_ports_; |
| 181 |
| 182 // Maps port IDs to the number of ScriptContexts that have a reference to it. |
| 183 std::map<int, int> port_refcount_; |
| 184 |
| 185 DISALLOW_COPY_AND_ASSIGN(PortTracker); |
| 128 }; | 186 }; |
| 129 | 187 |
| 130 base::LazyInstance<ExtensionData> g_extension_data = LAZY_INSTANCE_INITIALIZER; | 188 base::LazyInstance<PortTracker> g_port_tracker = LAZY_INSTANCE_INITIALIZER; |
| 131 | |
| 132 bool HasPortData(int port_id) { | |
| 133 return g_extension_data.Get().ports.find(port_id) != | |
| 134 g_extension_data.Get().ports.end(); | |
| 135 } | |
| 136 | |
| 137 ExtensionData::PortData& GetPortData(int port_id) { | |
| 138 return g_extension_data.Get().ports[port_id]; | |
| 139 } | |
| 140 | |
| 141 void ClearPortData(int port_id) { | |
| 142 g_extension_data.Get().ports.erase(port_id); | |
| 143 } | |
| 144 | 189 |
| 145 const char kPortClosedError[] = "Attempting to use a disconnected port object"; | 190 const char kPortClosedError[] = "Attempting to use a disconnected port object"; |
| 146 const char kReceivingEndDoesntExistError[] = | 191 const char kReceivingEndDoesntExistError[] = |
| 147 "Could not establish connection. Receiving end does not exist."; | 192 "Could not establish connection. Receiving end does not exist."; |
| 148 | 193 |
| 149 class ExtensionImpl : public ObjectBackedNativeHandler { | 194 class ExtensionImpl : public ObjectBackedNativeHandler { |
| 150 public: | 195 public: |
| 151 ExtensionImpl(Dispatcher* dispatcher, ScriptContext* context) | 196 ExtensionImpl(Dispatcher* dispatcher, ScriptContext* context) |
| 152 : ObjectBackedNativeHandler(context), | 197 : ObjectBackedNativeHandler(context), |
| 153 dispatcher_(dispatcher), | 198 dispatcher_(dispatcher), |
| 154 weak_ptr_factory_(this) { | 199 weak_ptr_factory_(this) { |
| 155 RouteFunction( | 200 RouteFunction( |
| 156 "CloseChannel", | 201 "CloseChannel", |
| 157 base::Bind(&ExtensionImpl::CloseChannel, base::Unretained(this))); | 202 base::Bind(&ExtensionImpl::CloseChannel, base::Unretained(this))); |
| 158 RouteFunction( | 203 RouteFunction( |
| 159 "PortAddRef", | 204 "PortAddRef", |
| 160 base::Bind(&ExtensionImpl::PortAddRef, base::Unretained(this))); | 205 base::Bind(&ExtensionImpl::PortAddRef, base::Unretained(this))); |
| 161 RouteFunction( | 206 RouteFunction( |
| 162 "PortRelease", | 207 "PortRelease", |
| 163 base::Bind(&ExtensionImpl::PortRelease, base::Unretained(this))); | 208 base::Bind(&ExtensionImpl::PortRelease, base::Unretained(this))); |
| 164 RouteFunction( | 209 RouteFunction( |
| 165 "PostMessage", | 210 "PostMessage", |
| 166 base::Bind(&ExtensionImpl::PostMessage, base::Unretained(this))); | 211 base::Bind(&ExtensionImpl::PostMessage, base::Unretained(this))); |
| 167 // TODO(fsamuel, kalman): Move BindToGC out of messaging natives. | 212 // TODO(fsamuel, kalman): Move BindToGC out of messaging natives. |
| 168 RouteFunction("BindToGC", | 213 RouteFunction("BindToGC", |
| 169 base::Bind(&ExtensionImpl::BindToGC, base::Unretained(this))); | 214 base::Bind(&ExtensionImpl::BindToGC, base::Unretained(this))); |
| 215 |
| 216 // Observe |context| so that port references to it can be cleared. |
| 217 context->AddInvalidationObserver(base::Bind( |
| 218 &ExtensionImpl::OnContextInvalidated, weak_ptr_factory_.GetWeakPtr())); |
| 170 } | 219 } |
| 171 | 220 |
| 172 ~ExtensionImpl() override {} | 221 ~ExtensionImpl() override {} |
| 173 | 222 |
| 174 private: | 223 private: |
| 224 void OnContextInvalidated() { |
| 225 for (int port_id : g_port_tracker.Get().GetPorts(context())) |
| 226 ReleasePort(port_id); |
| 227 } |
| 228 |
| 175 void ClearPortDataAndNotifyDispatcher(int port_id) { | 229 void ClearPortDataAndNotifyDispatcher(int port_id) { |
| 176 ClearPortData(port_id); | 230 g_port_tracker.Get().DeletePort(port_id); |
| 177 dispatcher_->ClearPortData(port_id); | 231 dispatcher_->ClearPortData(port_id); |
| 178 } | 232 } |
| 179 | 233 |
| 180 // Sends a message along the given channel. | 234 // Sends a message along the given channel. |
| 181 void PostMessage(const v8::FunctionCallbackInfo<v8::Value>& args) { | 235 void PostMessage(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 182 content::RenderFrame* renderframe = context()->GetRenderFrame(); | 236 content::RenderFrame* renderframe = context()->GetRenderFrame(); |
| 183 if (!renderframe) | 237 if (!renderframe) |
| 184 return; | 238 return; |
| 185 | 239 |
| 186 // Arguments are (int32 port_id, string message). | 240 // Arguments are (int32 port_id, string message). |
| 187 CHECK(args.Length() == 2 && args[0]->IsInt32() && args[1]->IsString()); | 241 CHECK(args.Length() == 2 && args[0]->IsInt32() && args[1]->IsString()); |
| 188 | 242 |
| 189 int port_id = args[0]->Int32Value(); | 243 int port_id = args[0]->Int32Value(); |
| 190 if (!HasPortData(port_id)) { | 244 if (!g_port_tracker.Get().HasPort(port_id)) { |
| 191 args.GetIsolate()->ThrowException(v8::Exception::Error( | 245 args.GetIsolate()->ThrowException(v8::Exception::Error( |
| 192 v8::String::NewFromUtf8(args.GetIsolate(), kPortClosedError))); | 246 v8::String::NewFromUtf8(args.GetIsolate(), kPortClosedError))); |
| 193 return; | 247 return; |
| 194 } | 248 } |
| 195 | 249 |
| 196 renderframe->Send(new ExtensionHostMsg_PostMessage( | 250 renderframe->Send(new ExtensionHostMsg_PostMessage( |
| 197 renderframe->GetRoutingID(), port_id, | 251 renderframe->GetRoutingID(), port_id, |
| 198 Message(*v8::String::Utf8Value(args[1]), | 252 Message(*v8::String::Utf8Value(args[1]), |
| 199 blink::WebUserGestureIndicator::isProcessingUserGesture()))); | 253 blink::WebUserGestureIndicator::isProcessingUserGesture()))); |
| 200 } | 254 } |
| 201 | 255 |
| 202 // Forcefully disconnects a port. | 256 // Forcefully disconnects a port. |
| 203 void CloseChannel(const v8::FunctionCallbackInfo<v8::Value>& args) { | 257 void CloseChannel(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 204 // Arguments are (int32 port_id, boolean notify_browser). | 258 // Arguments are (int32 port_id, boolean notify_browser). |
| 205 CHECK_EQ(2, args.Length()); | 259 CHECK_EQ(2, args.Length()); |
| 206 CHECK(args[0]->IsInt32()); | 260 CHECK(args[0]->IsInt32()); |
| 207 CHECK(args[1]->IsBoolean()); | 261 CHECK(args[1]->IsBoolean()); |
| 208 | 262 |
| 209 int port_id = args[0]->Int32Value(); | 263 int port_id = args[0]->Int32Value(); |
| 210 if (!HasPortData(port_id)) | 264 if (!g_port_tracker.Get().HasPort(port_id)) |
| 211 return; | 265 return; |
| 212 | 266 |
| 213 // Send via the RenderThread because the RenderFrame might be closing. | 267 // Send via the RenderThread because the RenderFrame might be closing. |
| 214 bool notify_browser = args[1]->BooleanValue(); | 268 bool notify_browser = args[1]->BooleanValue(); |
| 215 if (notify_browser) { | 269 if (notify_browser) { |
| 216 content::RenderThread::Get()->Send( | 270 content::RenderThread::Get()->Send( |
| 217 new ExtensionHostMsg_CloseChannel(port_id, std::string())); | 271 new ExtensionHostMsg_CloseChannel(port_id, std::string())); |
| 218 } | 272 } |
| 219 | 273 |
| 220 ClearPortDataAndNotifyDispatcher(port_id); | 274 ClearPortDataAndNotifyDispatcher(port_id); |
| 221 } | 275 } |
| 222 | 276 |
| 223 // A new port has been created for a context. This occurs both when script | 277 // A new port has been created for a context. This occurs both when script |
| 224 // opens a connection, and when a connection is opened to this script. | 278 // opens a connection, and when a connection is opened to this script. |
| 225 void PortAddRef(const v8::FunctionCallbackInfo<v8::Value>& args) { | 279 void PortAddRef(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 226 // Arguments are (int32 port_id). | 280 // Arguments are (int32 port_id). |
| 227 CHECK_EQ(1, args.Length()); | 281 CHECK_EQ(1, args.Length()); |
| 228 CHECK(args[0]->IsInt32()); | 282 CHECK(args[0]->IsInt32()); |
| 229 | 283 |
| 230 int port_id = args[0]->Int32Value(); | 284 int port_id = args[0]->Int32Value(); |
| 231 ++GetPortData(port_id).ref_count; | 285 g_port_tracker.Get().AddReference(context(), port_id); |
| 232 } | 286 } |
| 233 | 287 |
| 234 // The frame a port lived in has been destroyed. When there are no more | 288 // The frame a port lived in has been destroyed. When there are no more |
| 235 // frames with a reference to a given port, we will disconnect it and notify | 289 // frames with a reference to a given port, we will disconnect it and notify |
| 236 // the other end of the channel. | 290 // the other end of the channel. |
| 237 void PortRelease(const v8::FunctionCallbackInfo<v8::Value>& args) { | 291 void PortRelease(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 238 // Arguments are (int32 port_id). | 292 // Arguments are (int32 port_id). |
| 239 CHECK(args.Length() == 1 && args[0]->IsInt32()); | 293 CHECK(args.Length() == 1 && args[0]->IsInt32()); |
| 240 ReleasePort(args[0]->Int32Value()); | 294 ReleasePort(args[0]->Int32Value()); |
| 241 } | 295 } |
| 242 | 296 |
| 243 // Implementation of both the PortRelease native handler call, and callback | 297 // Releases the reference to |port_id| for this context, and clears all port |
| 244 // when contexts are invalidated to release their ports. | 298 // data if there are no more references. |
| 245 void ReleasePort(int port_id) { | 299 void ReleasePort(int port_id) { |
| 246 if (HasPortData(port_id) && --GetPortData(port_id).ref_count == 0) { | 300 g_port_tracker.Get().RemoveReference(context(), port_id); |
| 301 if (!g_port_tracker.Get().HasPort(port_id)) { |
| 247 // Send via the RenderThread because the RenderFrame might be closing. | 302 // Send via the RenderThread because the RenderFrame might be closing. |
| 248 content::RenderThread::Get()->Send( | 303 content::RenderThread::Get()->Send( |
| 249 new ExtensionHostMsg_CloseChannel(port_id, std::string())); | 304 new ExtensionHostMsg_CloseChannel(port_id, std::string())); |
| 250 ClearPortDataAndNotifyDispatcher(port_id); | 305 ClearPortDataAndNotifyDispatcher(port_id); |
| 251 } | 306 } |
| 252 } | 307 } |
| 253 | 308 |
| 254 // void BindToGC(object, callback, port_id) | 309 // void BindToGC(object, callback, port_id) |
| 255 // | 310 // |
| 256 // Binds |callback| to be invoked *sometime after* |object| is garbage | 311 // Binds |callback| to be invoked *sometime after* |object| is garbage |
| (...skipping 227 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 484 // TODO(robwu): ScriptContextSet.ForEach should accept RenderFrame*. | 539 // TODO(robwu): ScriptContextSet.ForEach should accept RenderFrame*. |
| 485 content::RenderView* restrict_to_render_view = | 540 content::RenderView* restrict_to_render_view = |
| 486 restrict_to_render_frame ? restrict_to_render_frame->GetRenderView() | 541 restrict_to_render_frame ? restrict_to_render_frame->GetRenderView() |
| 487 : NULL; | 542 : NULL; |
| 488 context_set.ForEach( | 543 context_set.ForEach( |
| 489 restrict_to_render_view, | 544 restrict_to_render_view, |
| 490 base::Bind(&DispatchOnDisconnectToScriptContext, port_id, error_message)); | 545 base::Bind(&DispatchOnDisconnectToScriptContext, port_id, error_message)); |
| 491 } | 546 } |
| 492 | 547 |
| 493 } // namespace extensions | 548 } // namespace extensions |
| OLD | NEW |