• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
40 namespace content {
41 
42 namespace {
43 
44 class EnsureTerminateMessageFilter : public IPC::ChannelProxy::MessageFilter {
45  public:
EnsureTerminateMessageFilter()46   EnsureTerminateMessageFilter() {}
47 
48  protected:
~EnsureTerminateMessageFilter()49   virtual ~EnsureTerminateMessageFilter() {}
50 
51   // IPC::ChannelProxy::MessageFilter:
OnChannelError()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:
Terminate()66   void Terminate() {
67     base::KillProcess(base::GetCurrentProcessHandle(), 0, false);
68   }
69 };
70 
71 }  // namespace
72 
73 static base::LazyInstance<base::ThreadLocalPointer<PluginThread> > lazy_tls =
74     LAZY_INSTANCE_INITIALIZER;
75 
PluginThread()76 PluginThread::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 
~PluginThread()134 PluginThread::~PluginThread() {
135 }
136 
SetForcefullyTerminatePluginProcess()137 void PluginThread::SetForcefullyTerminatePluginProcess() {
138   forcefully_terminate_plugin_process_ = true;
139 }
140 
Shutdown()141 void 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 
current()157 PluginThread* PluginThread::current() {
158   return lazy_tls.Pointer()->Get();
159 }
160 
OnControlMessageReceived(const IPC::Message & msg)161 bool 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 
OnCreateChannel(int renderer_id,bool incognito)172 void 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 
OnNotifyRenderersOfPendingShutdown()190 void PluginThread::OnNotifyRenderersOfPendingShutdown() {
191   PluginChannel::NotifyRenderersOfPendingShutdown();
192 }
193 
194 }  // namespace content
195