OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 "content/renderer/pepper/message_channel.h" | 5 #include "content/renderer/pepper/message_channel.h" |
6 | 6 |
7 #include <cstdlib> | 7 #include <cstdlib> |
8 #include <string> | 8 #include <string> |
9 | 9 |
10 #include "base/bind.h" | 10 #include "base/bind.h" |
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
97 MessageChannel* message_channel = new MessageChannel(instance); | 97 MessageChannel* message_channel = new MessageChannel(instance); |
98 v8::HandleScope handle_scope(instance->GetIsolate()); | 98 v8::HandleScope handle_scope(instance->GetIsolate()); |
99 v8::Context::Scope context_scope(instance->GetContext()); | 99 v8::Context::Scope context_scope(instance->GetContext()); |
100 gin::Handle<MessageChannel> handle = | 100 gin::Handle<MessageChannel> handle = |
101 gin::CreateHandle(instance->GetIsolate(), message_channel); | 101 gin::CreateHandle(instance->GetIsolate(), message_channel); |
102 result->Reset(instance->GetIsolate(), handle.ToV8()->ToObject()); | 102 result->Reset(instance->GetIsolate(), handle.ToV8()->ToObject()); |
103 return message_channel; | 103 return message_channel; |
104 } | 104 } |
105 | 105 |
106 MessageChannel::~MessageChannel() { | 106 MessageChannel::~MessageChannel() { |
107 // Note it's unlikely but possible that we outlive the instance, and so we | |
108 // might have already unregistered in InstanceDeleted. That's OK, because | |
109 // removing an observer that's already removed is a no-op. | |
110 UnregisterSyncMessageStatusObserver(); | |
111 | |
112 passthrough_object_.Reset(); | 107 passthrough_object_.Reset(); |
113 if (instance_) | 108 if (instance_) |
114 instance_->MessageChannelDestroyed(); | 109 instance_->MessageChannelDestroyed(); |
115 } | 110 } |
116 | 111 |
117 void MessageChannel::InstanceDeleted() { | 112 void MessageChannel::InstanceDeleted() { |
118 UnregisterSyncMessageStatusObserver(); | |
119 instance_ = NULL; | 113 instance_ = NULL; |
120 } | 114 } |
121 | 115 |
122 void MessageChannel::PostMessageToJavaScript(PP_Var message_data) { | 116 void MessageChannel::PostMessageToJavaScript(PP_Var message_data) { |
123 v8::HandleScope scope(v8::Isolate::GetCurrent()); | 117 v8::HandleScope scope(v8::Isolate::GetCurrent()); |
124 | 118 |
125 // Because V8 is probably not on the stack for Native->JS calls, we need to | 119 // Because V8 is probably not on the stack for Native->JS calls, we need to |
126 // enter the appropriate context for the plugin. | 120 // enter the appropriate context for the plugin. |
127 v8::Local<v8::Context> context = instance_->GetContext(); | 121 v8::Local<v8::Context> context = instance_->GetContext(); |
128 if (context.IsEmpty()) | 122 if (context.IsEmpty()) |
129 return; | 123 return; |
130 | 124 |
131 v8::Context::Scope context_scope(context); | 125 v8::Context::Scope context_scope(context); |
132 | 126 |
133 v8::Handle<v8::Value> v8_val; | 127 v8::Handle<v8::Value> v8_val; |
134 if (!V8VarConverter(instance_->pp_instance()) | 128 if (!V8VarConverter(instance_->pp_instance()) |
135 .ToV8Value(message_data, context, &v8_val)) { | 129 .ToV8Value(message_data, context, &v8_val)) { |
136 PpapiGlobals::Get()->LogWithSource(instance_->pp_instance(), | 130 PpapiGlobals::Get()->LogWithSource(instance_->pp_instance(), |
137 PP_LOGLEVEL_ERROR, | 131 PP_LOGLEVEL_ERROR, |
138 std::string(), | 132 std::string(), |
139 kVarToV8ConversionError); | 133 kVarToV8ConversionError); |
140 return; | 134 return; |
141 } | 135 } |
142 | 136 |
143 WebSerializedScriptValue serialized_val = | 137 WebSerializedScriptValue serialized_val = |
144 WebSerializedScriptValue::serialize(v8_val); | 138 WebSerializedScriptValue::serialize(v8_val); |
145 | 139 |
146 if (js_message_queue_state_ != SEND_DIRECTLY) { | 140 if (early_message_queue_state_ != SEND_DIRECTLY) { |
147 // We can't just PostTask here; the messages would arrive out of | 141 // We can't just PostTask here; the messages would arrive out of |
148 // order. Instead, we queue them up until we're ready to post | 142 // order. Instead, we queue them up until we're ready to post |
149 // them. | 143 // them. |
150 js_message_queue_.push_back(serialized_val); | 144 early_message_queue_.push_back(serialized_val); |
151 } else { | 145 } else { |
152 // The proxy sent an asynchronous message, so the plugin is already | 146 // The proxy sent an asynchronous message, so the plugin is already |
153 // unblocked. Therefore, there's no need to PostTask. | 147 // unblocked. Therefore, there's no need to PostTask. |
154 DCHECK(js_message_queue_.empty()); | 148 DCHECK(early_message_queue_.empty()); |
155 PostMessageToJavaScriptImpl(serialized_val); | 149 PostMessageToJavaScriptImpl(serialized_val); |
156 } | 150 } |
157 } | 151 } |
158 | 152 |
159 void MessageChannel::Start() { | 153 void MessageChannel::Start() { |
160 DCHECK_EQ(WAITING_TO_START, js_message_queue_state_); | 154 // We PostTask here instead of draining the message queue directly |
161 DCHECK_EQ(WAITING_TO_START, plugin_message_queue_state_); | 155 // since we haven't finished initializing the PepperWebPluginImpl yet, so |
162 | 156 // the plugin isn't available in the DOM. |
163 ppapi::proxy::HostDispatcher* dispatcher = | 157 base::MessageLoop::current()->PostTask( |
164 ppapi::proxy::HostDispatcher::GetForInstance(instance_->pp_instance()); | 158 FROM_HERE, |
165 // The dispatcher is NULL for in-process. | 159 base::Bind(&MessageChannel::DrainEarlyMessageQueue, |
166 if (dispatcher) | 160 weak_ptr_factory_.GetWeakPtr())); |
167 dispatcher->AddSyncMessageStatusObserver(this); | |
168 | |
169 // We can't drain the JS message queue directly since we haven't finished | |
170 // initializing the PepperWebPluginImpl yet, so the plugin isn't available in | |
171 // the DOM. | |
172 DrainJSMessageQueueSoon(); | |
173 | |
174 plugin_message_queue_state_ = SEND_DIRECTLY; | |
175 DrainCompletedPluginMessages(); | |
176 } | 161 } |
177 | 162 |
178 void MessageChannel::SetPassthroughObject(v8::Handle<v8::Object> passthrough) { | 163 void MessageChannel::SetPassthroughObject(v8::Handle<v8::Object> passthrough) { |
179 passthrough_object_.Reset(instance_->GetIsolate(), passthrough); | 164 passthrough_object_.Reset(instance_->GetIsolate(), passthrough); |
180 } | 165 } |
181 | 166 |
182 void MessageChannel::SetReadOnlyProperty(PP_Var key, PP_Var value) { | 167 void MessageChannel::SetReadOnlyProperty(PP_Var key, PP_Var value) { |
183 StringVar* key_string = StringVar::FromPPVar(key); | 168 StringVar* key_string = StringVar::FromPPVar(key); |
184 if (key_string) { | 169 if (key_string) { |
185 internal_named_properties_[key_string->value()] = ScopedPPVar(value); | 170 internal_named_properties_[key_string->value()] = ScopedPPVar(value); |
186 } else { | 171 } else { |
187 NOTREACHED(); | 172 NOTREACHED(); |
188 } | 173 } |
189 } | 174 } |
190 | 175 |
191 MessageChannel::MessageChannel(PepperPluginInstanceImpl* instance) | 176 MessageChannel::MessageChannel(PepperPluginInstanceImpl* instance) |
192 : gin::NamedPropertyInterceptor(instance->GetIsolate(), this), | 177 : gin::NamedPropertyInterceptor(instance->GetIsolate(), this), |
193 instance_(instance), | 178 instance_(instance), |
194 js_message_queue_state_(WAITING_TO_START), | 179 early_message_queue_state_(QUEUE_MESSAGES), |
195 blocking_message_depth_(0), | |
196 plugin_message_queue_state_(WAITING_TO_START), | |
197 weak_ptr_factory_(this) { | 180 weak_ptr_factory_(this) { |
198 } | 181 } |
199 | 182 |
200 gin::ObjectTemplateBuilder MessageChannel::GetObjectTemplateBuilder( | 183 gin::ObjectTemplateBuilder MessageChannel::GetObjectTemplateBuilder( |
201 v8::Isolate* isolate) { | 184 v8::Isolate* isolate) { |
202 return Wrappable<MessageChannel>::GetObjectTemplateBuilder(isolate) | 185 return Wrappable<MessageChannel>::GetObjectTemplateBuilder(isolate) |
203 .AddNamedPropertyInterceptor(); | 186 .AddNamedPropertyInterceptor(); |
204 } | 187 } |
205 | 188 |
206 void MessageChannel::BeginBlockOnSyncMessage() { | |
207 js_message_queue_state_ = QUEUE_MESSAGES; | |
208 ++blocking_message_depth_; | |
209 } | |
210 | |
211 void MessageChannel::EndBlockOnSyncMessage() { | |
212 DCHECK_GT(blocking_message_depth_, 0); | |
213 --blocking_message_depth_; | |
214 if (!blocking_message_depth_) | |
215 DrainJSMessageQueueSoon(); | |
216 } | |
217 | |
218 v8::Local<v8::Value> MessageChannel::GetNamedProperty( | 189 v8::Local<v8::Value> MessageChannel::GetNamedProperty( |
219 v8::Isolate* isolate, | 190 v8::Isolate* isolate, |
220 const std::string& identifier) { | 191 const std::string& identifier) { |
221 if (!instance_) | 192 if (!instance_) |
222 return v8::Local<v8::Value>(); | 193 return v8::Local<v8::Value>(); |
223 | 194 |
224 PepperTryCatchV8 try_catch(instance_, V8VarConverter::kDisallowObjectVars, | 195 PepperTryCatchV8 try_catch(instance_, V8VarConverter::kDisallowObjectVars, |
225 isolate); | 196 isolate); |
226 if (identifier == kPostMessage) { | 197 if (identifier == kPostMessage) { |
227 return gin::CreateFunctionTemplate(isolate, | 198 return gin::CreateFunctionTemplate(isolate, |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
308 try_catch.ThrowException( | 279 try_catch.ThrowException( |
309 "postMessageAndAwaitResponse requires one argument"); | 280 "postMessageAndAwaitResponse requires one argument"); |
310 return; | 281 return; |
311 } | 282 } |
312 | 283 |
313 v8::Handle<v8::Value> message_data; | 284 v8::Handle<v8::Value> message_data; |
314 if (!args->GetNext(&message_data)) { | 285 if (!args->GetNext(&message_data)) { |
315 NOTREACHED(); | 286 NOTREACHED(); |
316 } | 287 } |
317 | 288 |
318 if (plugin_message_queue_state_ == WAITING_TO_START) { | 289 if (early_message_queue_state_ == QUEUE_MESSAGES) { |
319 try_catch.ThrowException( | 290 try_catch.ThrowException( |
320 "Attempted to call a synchronous method on a plugin that was not " | 291 "Attempted to call a synchronous method on a plugin that was not " |
321 "yet loaded."); | 292 "yet loaded."); |
322 return; | 293 return; |
323 } | 294 } |
324 | 295 |
325 // If the queue of messages to the plugin is non-empty, we're still waiting on | 296 // If the queue of messages to the plugin is non-empty, we're still waiting on |
326 // pending Var conversions. This means at some point in the past, JavaScript | 297 // pending Var conversions. This means at some point in the past, JavaScript |
327 // called postMessage (the async one) and passed us something with a browser- | 298 // called postMessage (the async one) and passed us something with a browser- |
328 // side host (e.g., FileSystem) and we haven't gotten a response from the | 299 // side host (e.g., FileSystem) and we haven't gotten a response from the |
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
421 const ScopedPPVar& result, | 392 const ScopedPPVar& result, |
422 bool success) { | 393 bool success) { |
423 if (!instance_) | 394 if (!instance_) |
424 return; | 395 return; |
425 result_holder->ConversionCompleted(result, success); | 396 result_holder->ConversionCompleted(result, success); |
426 DrainCompletedPluginMessages(); | 397 DrainCompletedPluginMessages(); |
427 } | 398 } |
428 | 399 |
429 void MessageChannel::DrainCompletedPluginMessages() { | 400 void MessageChannel::DrainCompletedPluginMessages() { |
430 DCHECK(instance_); | 401 DCHECK(instance_); |
431 if (plugin_message_queue_state_ == WAITING_TO_START) | 402 if (early_message_queue_state_ == QUEUE_MESSAGES) |
432 return; | 403 return; |
433 | 404 |
434 while (!plugin_message_queue_.empty() && | 405 while (!plugin_message_queue_.empty() && |
435 plugin_message_queue_.front().conversion_completed()) { | 406 plugin_message_queue_.front().conversion_completed()) { |
436 const VarConversionResult& front = plugin_message_queue_.front(); | 407 const VarConversionResult& front = plugin_message_queue_.front(); |
437 if (front.success()) { | 408 if (front.success()) { |
438 instance_->HandleMessage(front.var()); | 409 instance_->HandleMessage(front.var()); |
439 } else { | 410 } else { |
440 PpapiGlobals::Get()->LogWithSource(instance()->pp_instance(), | 411 PpapiGlobals::Get()->LogWithSource(instance()->pp_instance(), |
441 PP_LOGLEVEL_ERROR, | 412 PP_LOGLEVEL_ERROR, |
442 std::string(), | 413 std::string(), |
443 kV8ToVarConversionError); | 414 kV8ToVarConversionError); |
444 } | 415 } |
445 plugin_message_queue_.pop_front(); | 416 plugin_message_queue_.pop_front(); |
446 } | 417 } |
447 } | 418 } |
448 | 419 |
449 void MessageChannel::DrainJSMessageQueue() { | 420 void MessageChannel::DrainEarlyMessageQueue() { |
450 if (!instance_) | 421 if (!instance_) |
451 return; | 422 return; |
452 if (js_message_queue_state_ == SEND_DIRECTLY) | 423 DCHECK(early_message_queue_state_ == QUEUE_MESSAGES); |
453 return; | |
454 | 424 |
455 // Take a reference on the PluginInstance. This is because JavaScript code | 425 // Take a reference on the PluginInstance. This is because JavaScript code |
456 // may delete the plugin, which would destroy the PluginInstance and its | 426 // may delete the plugin, which would destroy the PluginInstance and its |
457 // corresponding MessageChannel. | 427 // corresponding MessageChannel. |
458 scoped_refptr<PepperPluginInstanceImpl> instance_ref(instance_); | 428 scoped_refptr<PepperPluginInstanceImpl> instance_ref(instance_); |
459 while (!js_message_queue_.empty()) { | 429 while (!early_message_queue_.empty()) { |
460 PostMessageToJavaScriptImpl(js_message_queue_.front()); | 430 PostMessageToJavaScriptImpl(early_message_queue_.front()); |
461 js_message_queue_.pop_front(); | 431 early_message_queue_.pop_front(); |
462 } | 432 } |
463 js_message_queue_state_ = SEND_DIRECTLY; | 433 early_message_queue_state_ = SEND_DIRECTLY; |
464 } | |
465 | 434 |
466 void MessageChannel::DrainJSMessageQueueSoon() { | 435 DrainCompletedPluginMessages(); |
467 base::MessageLoop::current()->PostTask( | |
468 FROM_HERE, | |
469 base::Bind(&MessageChannel::DrainJSMessageQueue, | |
470 weak_ptr_factory_.GetWeakPtr())); | |
471 } | |
472 | |
473 void MessageChannel::UnregisterSyncMessageStatusObserver() { | |
474 if (!instance_) | |
475 return; | |
476 ppapi::proxy::HostDispatcher* dispatcher = | |
477 ppapi::proxy::HostDispatcher::GetForInstance(instance_->pp_instance()); | |
478 // The dispatcher is NULL for in-process. | |
479 if (dispatcher) | |
480 dispatcher->RemoveSyncMessageStatusObserver(this); | |
481 } | 436 } |
482 | 437 |
483 } // namespace content | 438 } // namespace content |
OLD | NEW |