// Copyright (c) 2006-2008 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 #include #include "chrome/renderer/render_thread.h" #include "base/lazy_instance.h" #include "base/shared_memory.h" #include "base/thread_local.h" #include "chrome/common/chrome_plugin_lib.h" #include "chrome/common/ipc_logging.h" #include "chrome/common/notification_service.h" #include "chrome/plugin/plugin_channel.h" #include "chrome/renderer/net/render_dns_master.h" #include "chrome/renderer/greasemonkey_slave.h" #include "chrome/renderer/render_process.h" #include "chrome/renderer/render_view.h" #include "chrome/renderer/visitedlink_slave.h" #include "webkit/glue/cache_manager.h" static const unsigned int kCacheStatsDelayMS = 2000 /* milliseconds */; // V8 needs a 1MB stack size. static const size_t kStackSize = 1024 * 1024; static base::LazyInstance > lazy_tls_ptr(base::LINKER_INITIALIZED); //----------------------------------------------------------------------------- // Methods below are only called on the owner's thread: // static RenderThread* RenderThread::current() { return lazy_tls_ptr.Pointer()->Get(); } RenderThread::RenderThread(const std::wstring& channel_name) : Thread("Chrome_RenderThread"), channel_name_(channel_name), owner_loop_(MessageLoop::current()), visited_link_slave_(NULL), greasemonkey_slave_(NULL), render_dns_master_(NULL), in_send_(0) { DCHECK(owner_loop_); base::Thread::Options options; options.stack_size = kStackSize; // When we run plugins in process, we actually run them on the render thread, // which means that we need to make the render thread pump UI events. if (RenderProcess::ShouldLoadPluginsInProcess()) options.message_loop_type = MessageLoop::TYPE_UI; StartWithOptions(options); } RenderThread::~RenderThread() { Stop(); } void RenderThread::OnChannelError() { owner_loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask()); } bool RenderThread::Send(IPC::Message* msg) { in_send_++; bool rv = channel_->Send(msg); in_send_--; return rv; } void RenderThread::AddFilter(IPC::ChannelProxy::MessageFilter* filter) { channel_->AddFilter(filter); } void RenderThread::RemoveFilter(IPC::ChannelProxy::MessageFilter* filter) { channel_->RemoveFilter(filter); } void RenderThread::Resolve(const char* name, size_t length) { return render_dns_master_->Resolve(name, length); } void RenderThread::AddRoute(int32 routing_id, IPC::Channel::Listener* listener) { DCHECK(MessageLoop::current() == message_loop()); // This corresponds to the AddRoute call done in CreateView. router_.AddRoute(routing_id, listener); } void RenderThread::RemoveRoute(int32 routing_id) { DCHECK(MessageLoop::current() == message_loop()); router_.RemoveRoute(routing_id); } void RenderThread::Init() { DCHECK(!current()) << "should only have one RenderThread per thread"; notification_service_.reset(new NotificationService); cache_stats_factory_.reset( new ScopedRunnableMethodFactory(this)); channel_.reset(new IPC::SyncChannel(channel_name_, IPC::Channel::MODE_CLIENT, this, NULL, owner_loop_, true, RenderProcess::GetShutDownEvent())); lazy_tls_ptr.Pointer()->Set(this); // The renderer thread should wind-up COM. CoInitialize(0); visited_link_slave_ = new VisitedLinkSlave(); greasemonkey_slave_ = new GreasemonkeySlave(); render_dns_master_.reset(new RenderDnsMaster()); #ifdef IPC_MESSAGE_LOG_ENABLED IPC::Logging::current()->SetIPCSender(this); #endif } void RenderThread::CleanUp() { DCHECK(current() == this); // Need to destruct the SyncChannel to the browser before we go away because // it caches a pointer to this thread. channel_.reset(); // Clean up plugin channels before this thread goes away. PluginChannelBase::CleanupChannels(); #ifdef IPC_MESSAGE_LOG_ENABLED IPC::Logging::current()->SetIPCSender(NULL); #endif notification_service_.reset(); delete visited_link_slave_; visited_link_slave_ = NULL; delete greasemonkey_slave_; greasemonkey_slave_ = NULL; CoUninitialize(); } void RenderThread::OnUpdateVisitedLinks(SharedMemoryHandle table) { DCHECK(table) << "Bad table handle"; visited_link_slave_->Init(table); } void RenderThread::OnUpdateGreasemonkeyScripts(SharedMemoryHandle scripts) { DCHECK(scripts) << "Bad scripts handle"; greasemonkey_slave_->UpdateScripts(scripts); } void RenderThread::OnMessageReceived(const IPC::Message& msg) { // NOTE: We could subclass router_ to intercept OnControlMessageReceived, but // it seems simpler to just process any control messages that we care about // up-front and then send the rest of the messages onto router_. if (msg.routing_id() == MSG_ROUTING_CONTROL) { IPC_BEGIN_MESSAGE_MAP(RenderThread, msg) IPC_MESSAGE_HANDLER(ViewMsg_VisitedLink_NewTable, OnUpdateVisitedLinks) IPC_MESSAGE_HANDLER(ViewMsg_SetNextPageID, OnSetNextPageID) IPC_MESSAGE_HANDLER(ViewMsg_New, OnCreateNewView) IPC_MESSAGE_HANDLER(ViewMsg_SetCacheCapacities, OnSetCacheCapacities) IPC_MESSAGE_HANDLER(ViewMsg_GetCacheResourceStats, OnGetCacheResourceStats) IPC_MESSAGE_HANDLER(ViewMsg_PluginMessage, OnPluginMessage) IPC_MESSAGE_HANDLER(ViewMsg_Greasemonkey_NewScripts, OnUpdateGreasemonkeyScripts) // send the rest to the router IPC_MESSAGE_UNHANDLED(router_.OnMessageReceived(msg)) IPC_END_MESSAGE_MAP() } else { router_.OnMessageReceived(msg); } } void RenderThread::OnPluginMessage(const std::wstring& dll_path, const std::vector& data) { CHECK(ChromePluginLib::IsPluginThread()); ChromePluginLib *chrome_plugin = ChromePluginLib::Find(dll_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); } } void RenderThread::OnSetNextPageID(int32 next_page_id) { // This should only be called at process initialization time, so we shouldn't // have to worry about thread-safety. RenderView::SetNextPageID(next_page_id); } void RenderThread::OnCreateNewView(HWND parent_hwnd, HANDLE modal_dialog_event, const WebPreferences& webkit_prefs, int32 view_id) { // TODO(darin): once we have a RenderThread per RenderView, this will need to // change to assert that we are not creating more than one view. RenderView::Create( parent_hwnd, modal_dialog_event, MSG_ROUTING_NONE, webkit_prefs, new SharedRenderViewCounter(0), view_id); } void RenderThread::OnSetCacheCapacities(size_t min_dead_capacity, size_t max_dead_capacity, size_t capacity) { CacheManager::SetCapacities(min_dead_capacity, max_dead_capacity, capacity); } void RenderThread::OnGetCacheResourceStats() { CacheManager::ResourceTypeStats stats; CacheManager::GetResourceTypeStats(&stats); Send(new ViewHostMsg_ResourceTypeStats(stats)); } void RenderThread::InformHostOfCacheStats() { CacheManager::UsageStats stats; CacheManager::GetUsageStats(&stats); Send(new ViewHostMsg_UpdatedCacheStats(stats)); } void RenderThread::InformHostOfCacheStatsLater() { // Rate limit informing the host of our cache stats. if (!cache_stats_factory_->empty()) return; MessageLoop::current()->PostDelayedTask(FROM_HERE, cache_stats_factory_->NewRunnableMethod( &RenderThread::InformHostOfCacheStats), kCacheStatsDelayMS); }