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 |