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(TOOLKIT_GTK) 10#include <gtk/gtk.h> 11#elif defined(OS_MACOSX) 12#include <CoreFoundation/CoreFoundation.h> 13#endif 14 15#include <string> 16#include <vector> 17 18#include "base/bind.h" 19#include "base/command_line.h" 20#include "base/lazy_instance.h" 21#include "base/process/kill.h" 22#include "base/process/process_handle.h" 23#include "base/threading/thread_local.h" 24#include "content/child/child_process.h" 25#include "content/child/npapi/npobject_util.h" 26#include "content/child/npapi/plugin_lib.h" 27#include "content/common/plugin_process_messages.h" 28#include "content/public/common/content_switches.h" 29#include "content/public/plugin/content_plugin_client.h" 30#include "ipc/ipc_channel_handle.h" 31 32#if defined(TOOLKIT_GTK) 33#include "ui/gfx/gtk_util.h" 34#endif 35 36#if defined(USE_X11) 37#include "ui/base/x/x11_util.h" 38#endif 39 40namespace content { 41 42namespace { 43 44class EnsureTerminateMessageFilter : public IPC::ChannelProxy::MessageFilter { 45 public: 46 EnsureTerminateMessageFilter() {} 47 48 protected: 49 virtual ~EnsureTerminateMessageFilter() {} 50 51 // IPC::ChannelProxy::MessageFilter: 52 virtual void OnChannelError() OVERRIDE { 53 // How long we wait before forcibly shutting down the process. 54 const base::TimeDelta kPluginProcessTerminateTimeout = 55 base::TimeDelta::FromSeconds(3); 56 // Ensure that we don't wait indefinitely for the plugin to shutdown. 57 // as the browser does not terminate plugin processes on shutdown. 58 // We achieve this by posting an exit process task on the IO thread. 59 base::MessageLoop::current()->PostDelayedTask( 60 FROM_HERE, 61 base::Bind(&EnsureTerminateMessageFilter::Terminate, this), 62 kPluginProcessTerminateTimeout); 63 } 64 65 private: 66 void Terminate() { 67 base::KillProcess(base::GetCurrentProcessHandle(), 0, false); 68 } 69}; 70 71} // namespace 72 73static base::LazyInstance<base::ThreadLocalPointer<PluginThread> > lazy_tls = 74 LAZY_INSTANCE_INITIALIZER; 75 76PluginThread::PluginThread() 77 : preloaded_plugin_module_(NULL), 78 forcefully_terminate_plugin_process_(false) { 79 base::FilePath plugin_path = 80 CommandLine::ForCurrentProcess()->GetSwitchValuePath( 81 switches::kPluginPath); 82 83 lazy_tls.Pointer()->Set(this); 84#if defined(USE_AURA) 85 // TODO(saintlou): 86#elif defined(TOOLKIT_GTK) 87 { 88 // XEmbed plugins assume they are hosted in a Gtk application, so we need 89 // to initialize Gtk in the plugin process. 90 // g_thread_init API is deprecated since glib 2.31.0, see release note: 91 // http://mail.gnome.org/archives/gnome-announce-list/2011-October/msg00041.html 92#if !(GLIB_CHECK_VERSION(2, 31, 0)) 93 g_thread_init(NULL); 94#endif 95 96 // Flash has problems receiving clicks with newer GTKs due to the 97 // client-side windows change. To be safe, we just always set the 98 // backwards-compat environment variable. 99 setenv("GDK_NATIVE_WINDOWS", "1", 1); 100 101 gfx::GtkInitFromCommandLine(*CommandLine::ForCurrentProcess()); 102 103 // GTK after 2.18 resets the environment variable. But if we're using 104 // nspluginwrapper, that means it'll spawn its subprocess without the 105 // environment variable! So set it again. 106 setenv("GDK_NATIVE_WINDOWS", "1", 1); 107 } 108 109 ui::SetDefaultX11ErrorHandlers(); 110#endif 111 112 PatchNPNFunctions(); 113 114 // Preload the library to avoid loading, unloading then reloading 115 preloaded_plugin_module_ = base::LoadNativeLibrary(plugin_path, NULL); 116 117 scoped_refptr<PluginLib> plugin(PluginLib::CreatePluginLib(plugin_path)); 118 if (plugin.get()) { 119 plugin->NP_Initialize(); 120 // For OOP plugins the plugin dll will be unloaded during process shutdown 121 // time. 122 plugin->set_defer_unload(true); 123 } 124 125 GetContentClient()->plugin()->PluginProcessStarted( 126 plugin.get() ? plugin->plugin_info().name : base::string16()); 127 128 // Certain plugins, such as flash, steal the unhandled exception filter 129 // thus we never get crash reports when they fault. This call fixes it. 130 message_loop()->set_exception_restoration(true); 131 channel()->AddFilter(new EnsureTerminateMessageFilter()); 132} 133 134PluginThread::~PluginThread() { 135} 136 137void PluginThread::SetForcefullyTerminatePluginProcess() { 138 forcefully_terminate_plugin_process_ = true; 139} 140 141void PluginThread::Shutdown() { 142 ChildThread::Shutdown(); 143 144 if (preloaded_plugin_module_) { 145 base::UnloadNativeLibrary(preloaded_plugin_module_); 146 preloaded_plugin_module_ = NULL; 147 } 148 NPChannelBase::CleanupChannels(); 149 PluginLib::UnloadAllPlugins(); 150 151 if (forcefully_terminate_plugin_process_) 152 base::KillProcess(base::GetCurrentProcessHandle(), 0, /* wait= */ false); 153 154 lazy_tls.Pointer()->Set(NULL); 155} 156 157PluginThread* PluginThread::current() { 158 return lazy_tls.Pointer()->Get(); 159} 160 161bool PluginThread::OnControlMessageReceived(const IPC::Message& msg) { 162 bool handled = true; 163 IPC_BEGIN_MESSAGE_MAP(PluginThread, msg) 164 IPC_MESSAGE_HANDLER(PluginProcessMsg_CreateChannel, OnCreateChannel) 165 IPC_MESSAGE_HANDLER(PluginProcessMsg_NotifyRenderersOfPendingShutdown, 166 OnNotifyRenderersOfPendingShutdown) 167 IPC_MESSAGE_UNHANDLED(handled = false) 168 IPC_END_MESSAGE_MAP() 169 return handled; 170} 171 172void PluginThread::OnCreateChannel(int renderer_id, 173 bool incognito) { 174 scoped_refptr<PluginChannel> channel(PluginChannel::GetPluginChannel( 175 renderer_id, ChildProcess::current()->io_message_loop_proxy())); 176 IPC::ChannelHandle channel_handle; 177 if (channel.get()) { 178 channel_handle.name = channel->channel_handle().name; 179#if defined(OS_POSIX) 180 // On POSIX, pass the renderer-side FD. 181 channel_handle.socket = 182 base::FileDescriptor(channel->TakeRendererFileDescriptor(), true); 183#endif 184 channel->set_incognito(incognito); 185 } 186 187 Send(new PluginProcessHostMsg_ChannelCreated(channel_handle)); 188} 189 190void PluginThread::OnNotifyRenderersOfPendingShutdown() { 191 PluginChannel::NotifyRenderersOfPendingShutdown(); 192} 193 194} // namespace content 195