| // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "ppapi/proxy/plugin_globals.h" | 
 |  | 
 | #include "base/location.h" | 
 | #include "base/macros.h" | 
 | #include "base/single_thread_task_runner.h" | 
 | #include "base/task_runner.h" | 
 | #include "base/threading/thread.h" | 
 | #include "ipc/ipc_message.h" | 
 | #include "ipc/ipc_sender.h" | 
 | #include "ppapi/proxy/plugin_dispatcher.h" | 
 | #include "ppapi/proxy/plugin_message_filter.h" | 
 | #include "ppapi/proxy/plugin_proxy_delegate.h" | 
 | #include "ppapi/proxy/ppapi_messages.h" | 
 | #include "ppapi/proxy/ppb_message_loop_proxy.h" | 
 | #include "ppapi/proxy/resource_reply_thread_registrar.h" | 
 | #include "ppapi/proxy/udp_socket_filter.h" | 
 | #include "ppapi/shared_impl/ppapi_constants.h" | 
 | #include "ppapi/shared_impl/proxy_lock.h" | 
 | #include "ppapi/thunk/enter.h" | 
 |  | 
 | namespace ppapi { | 
 | namespace proxy { | 
 |  | 
 | // It performs necessary locking/unlocking of the proxy lock, and forwards all | 
 | // messages to the underlying sender. | 
 | class PluginGlobals::BrowserSender : public IPC::Sender { | 
 |  public: | 
 |   // |underlying_sender| must outlive this object. | 
 |   explicit BrowserSender(IPC::Sender* underlying_sender) | 
 |       : underlying_sender_(underlying_sender) { | 
 |   } | 
 |  | 
 |   ~BrowserSender() override {} | 
 |  | 
 |   // IPC::Sender implementation. | 
 |   bool Send(IPC::Message* msg) override { | 
 |     if (msg->is_sync()) { | 
 |       // Synchronous messages might be re-entrant, so we need to drop the lock. | 
 |       ProxyAutoUnlock unlock; | 
 |       return underlying_sender_->Send(msg); | 
 |     } | 
 |  | 
 |     return underlying_sender_->Send(msg); | 
 |   } | 
 |  | 
 |  private: | 
 |   // Non-owning pointer. | 
 |   IPC::Sender* underlying_sender_; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(BrowserSender); | 
 | }; | 
 |  | 
 | PluginGlobals* PluginGlobals::plugin_globals_ = NULL; | 
 |  | 
 | PluginGlobals::PluginGlobals( | 
 |     const scoped_refptr<base::TaskRunner>& ipc_task_runner) | 
 |     : ppapi::PpapiGlobals(), | 
 |       plugin_proxy_delegate_(NULL), | 
 |       callback_tracker_(new CallbackTracker), | 
 |       ipc_task_runner_(ipc_task_runner), | 
 |       resource_reply_thread_registrar_( | 
 |           new ResourceReplyThreadRegistrar(GetMainThreadMessageLoop())), | 
 |       udp_socket_filter_(new UDPSocketFilter()), | 
 |       plugin_recently_active_(false), | 
 |       keepalive_throttle_interval_milliseconds_( | 
 |           ppapi::kKeepaliveThrottleIntervalDefaultMilliseconds), | 
 |       weak_factory_(this) { | 
 |   DCHECK(!plugin_globals_); | 
 |   plugin_globals_ = this; | 
 |  | 
 |   // ResourceTracker asserts that we have the lock when we add new resources, | 
 |   // so we lock when creating the MessageLoopResource even though there is no | 
 |   // chance of race conditions. | 
 |   ProxyAutoLock lock; | 
 |   loop_for_main_thread_ = | 
 |       new MessageLoopResource(MessageLoopResource::ForMainThread()); | 
 | } | 
 |  | 
 | PluginGlobals::PluginGlobals( | 
 |     PerThreadForTest per_thread_for_test, | 
 |     const scoped_refptr<base::TaskRunner>& ipc_task_runner) | 
 |     : ppapi::PpapiGlobals(per_thread_for_test), | 
 |       plugin_proxy_delegate_(NULL), | 
 |       callback_tracker_(new CallbackTracker), | 
 |       ipc_task_runner_(ipc_task_runner), | 
 |       resource_reply_thread_registrar_( | 
 |           new ResourceReplyThreadRegistrar(GetMainThreadMessageLoop())), | 
 |       plugin_recently_active_(false), | 
 |       keepalive_throttle_interval_milliseconds_( | 
 |           kKeepaliveThrottleIntervalDefaultMilliseconds), | 
 |       weak_factory_(this) { | 
 |   DCHECK(!plugin_globals_); | 
 | } | 
 |  | 
 | PluginGlobals::~PluginGlobals() { | 
 |   DCHECK(plugin_globals_ == this || !plugin_globals_); | 
 |   { | 
 |     ProxyAutoLock lock; | 
 |     // Release the main-thread message loop. We should have the last reference | 
 |     // count, so this will delete the MessageLoop resource. We do this before | 
 |     // we clear plugin_globals_, because the Resource destructor tries to access | 
 |     // this PluginGlobals. | 
 |     DCHECK(!loop_for_main_thread_.get() || loop_for_main_thread_->HasOneRef()); | 
 |     loop_for_main_thread_ = NULL; | 
 |   } | 
 |   plugin_globals_ = NULL; | 
 | } | 
 |  | 
 | ResourceTracker* PluginGlobals::GetResourceTracker() { | 
 |   return &plugin_resource_tracker_; | 
 | } | 
 |  | 
 | VarTracker* PluginGlobals::GetVarTracker() { | 
 |   return &plugin_var_tracker_; | 
 | } | 
 |  | 
 | CallbackTracker* PluginGlobals::GetCallbackTrackerForInstance( | 
 |     PP_Instance instance) { | 
 |   // In the plugin process, the callback tracker is always the same, regardless | 
 |   // of the instance. | 
 |   return callback_tracker_.get(); | 
 | } | 
 |  | 
 | thunk::PPB_Instance_API* PluginGlobals::GetInstanceAPI(PP_Instance instance) { | 
 |   PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance); | 
 |   if (dispatcher) | 
 |     return dispatcher->GetInstanceAPI(); | 
 |   return NULL; | 
 | } | 
 |  | 
 | thunk::ResourceCreationAPI* PluginGlobals::GetResourceCreationAPI( | 
 |     PP_Instance instance) { | 
 |   PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance); | 
 |   if (dispatcher) | 
 |     return dispatcher->GetResourceCreationAPI(); | 
 |   return NULL; | 
 | } | 
 |  | 
 | PP_Module PluginGlobals::GetModuleForInstance(PP_Instance instance) { | 
 |   // Currently proxied plugins don't use the PP_Module for anything useful. | 
 |   return 0; | 
 | } | 
 |  | 
 | std::string PluginGlobals::GetCmdLine() { | 
 |   return command_line_; | 
 | } | 
 |  | 
 | void PluginGlobals::PreCacheFontForFlash(const void* logfontw) { | 
 |   ProxyAutoUnlock unlock; | 
 |   plugin_proxy_delegate_->PreCacheFontForFlash(logfontw); | 
 | } | 
 |  | 
 | void PluginGlobals::LogWithSource(PP_Instance instance, | 
 |                                   PP_LogLevel level, | 
 |                                   const std::string& source, | 
 |                                   const std::string& value) { | 
 |   const std::string& fixed_up_source = source.empty() ? plugin_name_ : source; | 
 |   PluginDispatcher::LogWithSource(instance, level, fixed_up_source, value); | 
 | } | 
 |  | 
 | void PluginGlobals::BroadcastLogWithSource(PP_Module /* module */, | 
 |                                            PP_LogLevel level, | 
 |                                            const std::string& source, | 
 |                                            const std::string& value) { | 
 |   // Since we have only one module in a plugin process, broadcast is always | 
 |   // the same as "send to everybody" which is what the dispatcher implements | 
 |   // for the "instance = 0" case. | 
 |   LogWithSource(0, level, source, value); | 
 | } | 
 |  | 
 | MessageLoopShared* PluginGlobals::GetCurrentMessageLoop() { | 
 |   return MessageLoopResource::GetCurrent(); | 
 | } | 
 |  | 
 | base::TaskRunner* PluginGlobals::GetFileTaskRunner() { | 
 |   if (!file_thread_.get()) { | 
 |     file_thread_.reset(new base::Thread("Plugin::File")); | 
 |     base::Thread::Options options; | 
 |     options.message_loop_type = base::MessageLoop::TYPE_IO; | 
 |     file_thread_->StartWithOptions(options); | 
 |   } | 
 |   return file_thread_->task_runner().get(); | 
 | } | 
 |  | 
 | void PluginGlobals::MarkPluginIsActive() { | 
 |   if (!plugin_recently_active_) { | 
 |     plugin_recently_active_ = true; | 
 |     if (!GetBrowserSender() || !base::MessageLoop::current()) | 
 |       return; | 
 |     GetBrowserSender()->Send(new PpapiHostMsg_Keepalive()); | 
 |     DCHECK(keepalive_throttle_interval_milliseconds_); | 
 |     GetMainThreadMessageLoop()->PostDelayedTask( | 
 |         FROM_HERE, | 
 |         RunWhileLocked(base::Bind(&PluginGlobals::OnReleaseKeepaliveThrottle, | 
 |                                   weak_factory_.GetWeakPtr())), | 
 |         base::TimeDelta::FromMilliseconds( | 
 |             keepalive_throttle_interval_milliseconds_)); | 
 |   } | 
 | } | 
 |  | 
 | IPC::Sender* PluginGlobals::GetBrowserSender() { | 
 |   // CAUTION: This function is called without the ProxyLock. See also | 
 |   // InterfaceList::GetInterfaceForPPB. | 
 |   // | 
 |   // See also SetPluginProxyDelegate. That initializes browser_sender_ before | 
 |   // the plugin can start threads, and it may be cleared after the | 
 |   // plugin has torn down threads. So this pointer is expected to remain valid | 
 |   // during the lifetime of the plugin. | 
 |   return browser_sender_.get(); | 
 | } | 
 |  | 
 | std::string PluginGlobals::GetUILanguage() { | 
 |   return plugin_proxy_delegate_->GetUILanguage(); | 
 | } | 
 |  | 
 | void PluginGlobals::SetActiveURL(const std::string& url) { | 
 |   plugin_proxy_delegate_->SetActiveURL(url); | 
 | } | 
 |  | 
 | PP_Resource PluginGlobals::CreateBrowserFont( | 
 |     Connection connection, | 
 |     PP_Instance instance, | 
 |     const PP_BrowserFont_Trusted_Description& desc, | 
 |     const ppapi::Preferences& prefs) { | 
 |   return plugin_proxy_delegate_->CreateBrowserFont( | 
 |       connection, instance, desc, prefs); | 
 | } | 
 |  | 
 | void PluginGlobals::SetPluginProxyDelegate(PluginProxyDelegate* delegate) { | 
 |   DCHECK(delegate && !plugin_proxy_delegate_); | 
 |   plugin_proxy_delegate_ = delegate; | 
 |   browser_sender_.reset( | 
 |       new BrowserSender(plugin_proxy_delegate_->GetBrowserSender())); | 
 | } | 
 |  | 
 | void PluginGlobals::ResetPluginProxyDelegate() { | 
 |   DCHECK(plugin_proxy_delegate_); | 
 |   plugin_proxy_delegate_ = NULL; | 
 |   browser_sender_.reset(); | 
 | } | 
 |  | 
 | MessageLoopResource* PluginGlobals::loop_for_main_thread() { | 
 |   return loop_for_main_thread_.get(); | 
 | } | 
 |  | 
 | void PluginGlobals::RegisterResourceMessageFilters( | 
 |     ppapi::proxy::PluginMessageFilter* plugin_filter) { | 
 |   plugin_filter->AddResourceMessageFilter(udp_socket_filter_.get()); | 
 | } | 
 |  | 
 | void PluginGlobals::set_keepalive_throttle_interval_milliseconds(unsigned i) { | 
 |   keepalive_throttle_interval_milliseconds_ = i; | 
 | } | 
 |  | 
 | bool PluginGlobals::IsPluginGlobals() const { | 
 |   return true; | 
 | } | 
 |  | 
 | void PluginGlobals::OnReleaseKeepaliveThrottle() { | 
 |   ppapi::ProxyLock::AssertAcquiredDebugOnly(); | 
 |   plugin_recently_active_ = false; | 
 | } | 
 |  | 
 | }  // namespace proxy | 
 | }  // namespace ppapi |