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