Home | History | Annotate | Download | only in plugin
      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