// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/plugin/plugin_thread.h" #include "build/build_config.h" #if defined(USE_X11) #include #elif defined(OS_MACOSX) #include #endif #include #include #include "base/command_line.h" #include "base/lazy_instance.h" #include "base/process_util.h" #include "base/thread_local.h" #include "chrome/common/child_process.h" #include "chrome/common/chrome_plugin_lib.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/plugin_messages.h" #include "chrome/common/render_messages.h" #include "chrome/plugin/chrome_plugin_host.h" #include "chrome/plugin/npobject_util.h" #include "chrome/renderer/render_thread.h" #include "ipc/ipc_channel_handle.h" #include "net/base/net_errors.h" #include "webkit/glue/plugins/plugin_lib.h" #include "webkit/glue/webkit_glue.h" #include "webkit/glue/plugins/webplugin_delegate_impl.h" #if defined(USE_X11) #include "app/x11_util.h" #elif defined(OS_MACOSX) #include "app/l10n_util.h" #include "base/mac_util.h" #include "base/scoped_cftyperef.h" #include "base/sys_string_conversions.h" #include "grit/chromium_strings.h" #endif static base::LazyInstance > lazy_tls( base::LINKER_INITIALIZED); PluginThread::PluginThread() : preloaded_plugin_module_(NULL) { plugin_path_ = CommandLine::ForCurrentProcess()->GetSwitchValuePath( switches::kPluginPath); lazy_tls.Pointer()->Set(this); #if defined(OS_LINUX) { // XEmbed plugins assume they are hosted in a Gtk application, so we need // to initialize Gtk in the plugin process. g_thread_init(NULL); const std::vector& args = CommandLine::ForCurrentProcess()->argv(); int argc = args.size(); scoped_array argv(new char *[argc + 1]); for (size_t i = 0; i < args.size(); ++i) { // TODO(piman@google.com): can gtk_init modify argv? Just being safe // here. argv[i] = strdup(args[i].c_str()); } argv[argc] = NULL; char **argv_pointer = argv.get(); // Flash has problems receiving clicks with newer GTKs due to the // client-side windows change. To be safe, we just always set the // backwards-compat environment variable. setenv("GDK_NATIVE_WINDOWS", "1", 1); gtk_init(&argc, &argv_pointer); // GTK after 2.18 resets the environment variable. But if we're using // nspluginwrapper, that means it'll spawn its subprocess without the // environment variable! So set it again. setenv("GDK_NATIVE_WINDOWS", "1", 1); for (size_t i = 0; i < args.size(); ++i) { free(argv[i]); } } x11_util::SetDefaultX11ErrorHandlers(); #endif PatchNPNFunctions(); // Preload the library to avoid loading, unloading then reloading preloaded_plugin_module_ = base::LoadNativeLibrary(plugin_path_); ChromePluginLib::Create(plugin_path_, GetCPBrowserFuncsForPlugin()); scoped_refptr plugin = NPAPI::PluginLib::CreatePluginLib(plugin_path_); if (plugin.get()) { plugin->NP_Initialize(); #if defined(OS_MACOSX) scoped_cftyperef plugin_name(base::SysUTF16ToCFStringRef( plugin->plugin_info().name)); scoped_cftyperef app_name(base::SysUTF16ToCFStringRef( l10n_util::GetStringUTF16(IDS_SHORT_PLUGIN_APP_NAME))); scoped_cftyperef process_name(CFStringCreateWithFormat( kCFAllocatorDefault, NULL, CFSTR("%@ (%@)"), plugin_name.get(), app_name.get())); mac_util::SetProcessName(process_name); #endif } // Certain plugins, such as flash, steal the unhandled exception filter // thus we never get crash reports when they fault. This call fixes it. message_loop()->set_exception_restoration(true); } PluginThread::~PluginThread() { if (preloaded_plugin_module_) { base::UnloadNativeLibrary(preloaded_plugin_module_); preloaded_plugin_module_ = NULL; } PluginChannelBase::CleanupChannels(); NPAPI::PluginLib::UnloadAllPlugins(); ChromePluginLib::UnloadAllPlugins(); if (webkit_glue::ShouldForcefullyTerminatePluginProcess()) base::KillProcess(base::GetCurrentProcessHandle(), 0, /* wait= */ false); lazy_tls.Pointer()->Set(NULL); } PluginThread* PluginThread::current() { return lazy_tls.Pointer()->Get(); } void PluginThread::OnControlMessageReceived(const IPC::Message& msg) { IPC_BEGIN_MESSAGE_MAP(PluginThread, msg) IPC_MESSAGE_HANDLER(PluginProcessMsg_CreateChannel, OnCreateChannel) IPC_MESSAGE_HANDLER(PluginProcessMsg_PluginMessage, OnPluginMessage) IPC_MESSAGE_HANDLER(PluginProcessMsg_NotifyRenderersOfPendingShutdown, OnNotifyRenderersOfPendingShutdown) IPC_END_MESSAGE_MAP() } void PluginThread::OnCreateChannel(int renderer_id, bool off_the_record) { scoped_refptr channel = PluginChannel::GetPluginChannel( renderer_id, ChildProcess::current()->io_message_loop()); IPC::ChannelHandle channel_handle; if (channel.get()) { channel_handle.name = channel->channel_name(); #if defined(OS_POSIX) // On POSIX, pass the renderer-side FD. channel_handle.socket = base::FileDescriptor(channel->renderer_fd(), false); #endif channel->set_off_the_record(off_the_record); } Send(new PluginProcessHostMsg_ChannelCreated(channel_handle)); } void PluginThread::OnPluginMessage(const std::vector &data) { // We Add/Release ref here to ensure that something will trigger the // shutdown mechanism for processes started in the absence of renderer's // opening a plugin channel. ChildProcess::current()->AddRefProcess(); ChromePluginLib *chrome_plugin = ChromePluginLib::Find(plugin_path_); if (chrome_plugin) { void *data_ptr = const_cast(reinterpret_cast(&data[0])); uint32 data_len = static_cast(data.size()); chrome_plugin->functions().on_message(data_ptr, data_len); } ChildProcess::current()->ReleaseProcess(); } void PluginThread::OnNotifyRenderersOfPendingShutdown() { PluginChannel::NotifyRenderersOfPendingShutdown(); } namespace webkit_glue { #if defined(OS_WIN) bool DownloadUrl(const std::string& url, HWND caller_window) { PluginThread* plugin_thread = PluginThread::current(); if (!plugin_thread) { return false; } IPC::Message* message = new PluginProcessHostMsg_DownloadUrl(MSG_ROUTING_NONE, url, ::GetCurrentProcessId(), caller_window); return plugin_thread->Send(message); } #endif bool GetPluginFinderURL(std::string* plugin_finder_url) { if (!plugin_finder_url) { NOTREACHED(); return false; } PluginThread* plugin_thread = PluginThread::current(); if (!plugin_thread) return false; plugin_thread->Send( new PluginProcessHostMsg_GetPluginFinderUrl(plugin_finder_url)); DCHECK(!plugin_finder_url->empty()); return true; } bool IsDefaultPluginEnabled() { return true; } // Dispatch the resolve proxy resquest to the right code, depending on which // process the plugin is running in {renderer, browser, plugin}. bool FindProxyForUrl(const GURL& url, std::string* proxy_list) { int net_error; std::string proxy_result; bool result; if (IsPluginProcess()) { result = PluginThread::current()->Send( new PluginProcessHostMsg_ResolveProxy(url, &net_error, &proxy_result)); } else { result = RenderThread::current()->Send( new ViewHostMsg_ResolveProxy(url, &net_error, &proxy_result)); } if (!result || net_error != net::OK) return false; *proxy_list = proxy_result; return true; } } // namespace webkit_glue