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