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 3// found in the LICENSE file. 4 5#include "content/plugin/plugin_thread.h" 6 7#include "build/build_config.h" 8 9#if defined(OS_MACOSX) 10#include <CoreFoundation/CoreFoundation.h> 11#endif 12 13#include <string> 14#include <vector> 15 16#include "base/bind.h" 17#include "base/command_line.h" 18#include "base/lazy_instance.h" 19#include "base/process/kill.h" 20#include "base/process/process_handle.h" 21#include "base/threading/thread_local.h" 22#include "content/child/blink_platform_impl.h" 23#include "content/child/child_process.h" 24#include "content/child/npapi/npobject_util.h" 25#include "content/child/npapi/plugin_lib.h" 26#include "content/common/plugin_process_messages.h" 27#include "content/public/common/content_switches.h" 28#include "content/public/plugin/content_plugin_client.h" 29#include "third_party/WebKit/public/web/WebKit.h" 30#include "ipc/ipc_channel_handle.h" 31#include "ipc/message_filter.h" 32 33namespace content { 34 35namespace { 36 37class EnsureTerminateMessageFilter : public IPC::MessageFilter { 38 public: 39 EnsureTerminateMessageFilter() {} 40 41 protected: 42 virtual ~EnsureTerminateMessageFilter() {} 43 44 // IPC::MessageFilter: 45 virtual void OnChannelError() OVERRIDE { 46 // How long we wait before forcibly shutting down the process. 47 const base::TimeDelta kPluginProcessTerminateTimeout = 48 base::TimeDelta::FromSeconds(3); 49 // Ensure that we don't wait indefinitely for the plugin to shutdown. 50 // as the browser does not terminate plugin processes on shutdown. 51 // We achieve this by posting an exit process task on the IO thread. 52 base::MessageLoop::current()->PostDelayedTask( 53 FROM_HERE, 54 base::Bind(&EnsureTerminateMessageFilter::Terminate, this), 55 kPluginProcessTerminateTimeout); 56 } 57 58 private: 59 void Terminate() { 60 base::KillProcess(base::GetCurrentProcessHandle(), 0, false); 61 } 62}; 63 64} // namespace 65 66static base::LazyInstance<base::ThreadLocalPointer<PluginThread> > lazy_tls = 67 LAZY_INSTANCE_INITIALIZER; 68 69PluginThread::PluginThread() 70 : preloaded_plugin_module_(NULL), 71 forcefully_terminate_plugin_process_(false) { 72 base::FilePath plugin_path = 73 CommandLine::ForCurrentProcess()->GetSwitchValuePath( 74 switches::kPluginPath); 75 76 lazy_tls.Pointer()->Set(this); 77 78 PatchNPNFunctions(); 79 80 // Preload the library to avoid loading, unloading then reloading 81 preloaded_plugin_module_ = base::LoadNativeLibrary(plugin_path, NULL); 82 83 scoped_refptr<PluginLib> plugin(PluginLib::CreatePluginLib(plugin_path)); 84 if (plugin.get()) { 85 plugin->NP_Initialize(); 86 // For OOP plugins the plugin dll will be unloaded during process shutdown 87 // time. 88 plugin->set_defer_unload(true); 89 } 90 91 GetContentClient()->plugin()->PluginProcessStarted( 92 plugin.get() ? plugin->plugin_info().name : base::string16()); 93 94 channel()->AddFilter(new EnsureTerminateMessageFilter()); 95 96 // This is needed because we call some code which uses WebKit strings. 97 webkit_platform_support_.reset(new BlinkPlatformImpl); 98 blink::initialize(webkit_platform_support_.get()); 99} 100 101PluginThread::~PluginThread() { 102} 103 104void PluginThread::SetForcefullyTerminatePluginProcess() { 105 forcefully_terminate_plugin_process_ = true; 106} 107 108void PluginThread::Shutdown() { 109 ChildThread::Shutdown(); 110 111 if (preloaded_plugin_module_) { 112 base::UnloadNativeLibrary(preloaded_plugin_module_); 113 preloaded_plugin_module_ = NULL; 114 } 115 NPChannelBase::CleanupChannels(); 116 PluginLib::UnloadAllPlugins(); 117 118 if (forcefully_terminate_plugin_process_) 119 base::KillProcess(base::GetCurrentProcessHandle(), 0, /* wait= */ false); 120 121 lazy_tls.Pointer()->Set(NULL); 122} 123 124PluginThread* PluginThread::current() { 125 return lazy_tls.Pointer()->Get(); 126} 127 128bool PluginThread::OnControlMessageReceived(const IPC::Message& msg) { 129 bool handled = true; 130 IPC_BEGIN_MESSAGE_MAP(PluginThread, msg) 131 IPC_MESSAGE_HANDLER(PluginProcessMsg_CreateChannel, OnCreateChannel) 132 IPC_MESSAGE_HANDLER(PluginProcessMsg_NotifyRenderersOfPendingShutdown, 133 OnNotifyRenderersOfPendingShutdown) 134 IPC_MESSAGE_UNHANDLED(handled = false) 135 IPC_END_MESSAGE_MAP() 136 return handled; 137} 138 139void PluginThread::OnCreateChannel(int renderer_id, 140 bool incognito) { 141 scoped_refptr<PluginChannel> channel(PluginChannel::GetPluginChannel( 142 renderer_id, ChildProcess::current()->io_message_loop_proxy())); 143 IPC::ChannelHandle channel_handle; 144 if (channel.get()) { 145 channel_handle.name = channel->channel_handle().name; 146#if defined(OS_POSIX) 147 // On POSIX, pass the renderer-side FD. 148 channel_handle.socket = 149 base::FileDescriptor(channel->TakeRendererFileDescriptor(), true); 150#endif 151 channel->set_incognito(incognito); 152 } 153 154 Send(new PluginProcessHostMsg_ChannelCreated(channel_handle)); 155} 156 157void PluginThread::OnNotifyRenderersOfPendingShutdown() { 158 PluginChannel::NotifyRenderersOfPendingShutdown(); 159} 160 161} // namespace content 162