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 33 namespace content { 34 35 namespace { 36 37 class 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 66 static base::LazyInstance<base::ThreadLocalPointer<PluginThread> > lazy_tls = 67 LAZY_INSTANCE_INITIALIZER; 68 69 PluginThread::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 101 PluginThread::~PluginThread() { 102 } 103 104 void PluginThread::SetForcefullyTerminatePluginProcess() { 105 forcefully_terminate_plugin_process_ = true; 106 } 107 108 void 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 124 PluginThread* PluginThread::current() { 125 return lazy_tls.Pointer()->Get(); 126 } 127 128 bool 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 139 void 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 157 void PluginThread::OnNotifyRenderersOfPendingShutdown() { 158 PluginChannel::NotifyRenderersOfPendingShutdown(); 159 } 160 161 } // namespace content 162