// Copyright (c) 2006-2009 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. // Represents the browser side of the browser <--> renderer communication // channel. There will be one RenderProcessHost per renderer process. #include "chrome/browser/renderer_host/browser_render_process_host.h" #include "build/build_config.h" #include #include #include #if defined(OS_POSIX) #include // for pair<> #endif #include "app/app_switches.h" #include "base/command_line.h" #include "base/field_trial.h" #include "base/linked_ptr.h" #include "base/logging.h" #include "base/path_service.h" #include "base/process_util.h" #include "base/rand_util.h" #include "base/scoped_ptr.h" #include "base/shared_memory.h" #include "base/singleton.h" #include "base/string_util.h" #include "base/thread.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/child_process_security_policy.h" #include "chrome/browser/extensions/extension_message_service.h" #include "chrome/browser/extensions/extensions_service.h" #include "chrome/browser/extensions/user_script_master.h" #include "chrome/browser/history/history.h" #include "chrome/browser/plugin_service.h" #include "chrome/browser/profile.h" #include "chrome/browser/renderer_host/render_view_host.h" #include "chrome/browser/renderer_host/render_widget_helper.h" #include "chrome/browser/renderer_host/render_widget_host.h" #include "chrome/browser/renderer_host/resource_message_filter.h" #include "chrome/browser/renderer_host/web_cache_manager.h" #include "chrome/browser/spellchecker.h" #include "chrome/browser/visitedlink_master.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/child_process_info.h" #include "chrome/common/chrome_descriptors.h" #include "chrome/common/logging_chrome.h" #include "chrome/common/notification_service.h" #include "chrome/common/process_watcher.h" #include "chrome/common/render_messages.h" #include "chrome/common/result_codes.h" #include "chrome/renderer/render_process.h" #include "chrome/renderer/render_thread.h" #include "chrome/installer/util/google_update_settings.h" #include "grit/generated_resources.h" #include "ipc/ipc_switches.h" #if defined(OS_WIN) #include "app/win_util.h" #include "chrome/browser/sandbox_policy.h" #elif defined(OS_LINUX) #include "base/linux_util.h" #include "chrome/browser/zygote_host_linux.h" #include "chrome/browser/renderer_host/render_crash_handler_host_linux.h" #include "chrome/browser/renderer_host/render_sandbox_host_linux.h" #endif using WebKit::WebCache; #include "third_party/skia/include/core/SkBitmap.h" // This class creates the IO thread for the renderer when running in // single-process mode. It's not used in multi-process mode. class RendererMainThread : public base::Thread { public: explicit RendererMainThread(const std::string& channel_id) : base::Thread("Chrome_InProcRendererThread"), channel_id_(channel_id), render_process_(NULL) { } ~RendererMainThread() { Stop(); } protected: virtual void Init() { #if defined(OS_WIN) CoInitialize(NULL); #endif render_process_ = new RenderProcess(); render_process_->set_main_thread(new RenderThread(channel_id_)); // It's a little lame to manually set this flag. But the single process // RendererThread will receive the WM_QUIT. We don't need to assert on // this thread, so just force the flag manually. // If we want to avoid this, we could create the InProcRendererThread // directly with _beginthreadex() rather than using the Thread class. base::Thread::SetThreadWasQuitProperly(true); } virtual void CleanUp() { delete render_process_; #if defined(OS_WIN) CoUninitialize(); #endif } private: std::string channel_id_; // Deleted in CleanUp() on the renderer thread, so don't use a smart pointer. RenderProcess* render_process_; }; #if defined(OS_LINUX) // This is defined in chrome/browser/google_update_settings_linux.cc, it's the // static string containing the user's unique GUID. We send this in the crash // report. namespace google_update { extern std::string linux_guid; } #endif // Size of the buffer after which individual link updates deemed not warranted // and the overall update should be used instead. static const unsigned kVisitedLinkBufferThreshold = 50; // This class manages buffering and sending visited link hashes (fingerprints) // to renderer based on widget visibility. // As opposed to the VisitedLinkEventListener in profile.cc, which coalesces to // reduce the rate of messages being send to render processes, this class // ensures that the updates occur only when explicitly requested. This is // used by BrowserRenderProcessHost to only send Add/Reset link events to the // renderers when their tabs are visible. class VisitedLinkUpdater { public: VisitedLinkUpdater() : threshold_reached_(false) {} void Buffer(const VisitedLinkCommon::Fingerprints& links) { if (threshold_reached_) return; if (pending_.size() + links.size() > kVisitedLinkBufferThreshold) { threshold_reached_ = true; // Once the threshold is reached, there's no need to store pending visited // links. pending_.clear(); return; } pending_.insert(pending_.end(), links.begin(), links.end()); } void Clear() { pending_.clear(); } void Update(IPC::Channel::Sender* sender) { if (threshold_reached_) { sender->Send(new ViewMsg_VisitedLink_Reset()); threshold_reached_ = false; return; } if (pending_.size() == 0) return; sender->Send(new ViewMsg_VisitedLink_Add(pending_)); pending_.clear(); } private: bool threshold_reached_; VisitedLinkCommon::Fingerprints pending_; }; // Used for a View_ID where the renderer has not been attached yet const int32 kInvalidViewID = -1; // Get the path to the renderer executable, which is the same as the // current executable. bool GetRendererPath(std::wstring* cmd_line) { return PathService::Get(base::FILE_EXE, cmd_line); } BrowserRenderProcessHost::BrowserRenderProcessHost(Profile* profile) : RenderProcessHost(profile), visible_widgets_(0), backgrounded_(true), view_created_(false), ALLOW_THIS_IN_INITIALIZER_LIST(cached_dibs_cleaner_( base::TimeDelta::FromSeconds(5), this, &BrowserRenderProcessHost::ClearTransportDIBCache)), zygote_child_(false) { widget_helper_ = new RenderWidgetHelper(); registrar_.Add(this, NotificationType::USER_SCRIPTS_UPDATED, NotificationService::AllSources()); if (run_renderer_in_process()) { // We need a "renderer pid", but we don't have one when there's no renderer // process. So pick a value that won't clash with other child process pids. // Linux has PID_MAX_LIMIT which is 2^22. Windows always uses pids that are // divisible by 4. So... static int next_pid = 4 * 1024 * 1024; next_pid += 3; SetProcessID(next_pid); } visited_link_updater_.reset(new VisitedLinkUpdater()); // Note: When we create the BrowserRenderProcessHost, it's technically // backgrounded, because it has no visible listeners. But the process // doesn't actually exist yet, so we'll Background it later, after // creation. } BrowserRenderProcessHost::~BrowserRenderProcessHost() { if (pid() >= 0) { WebCacheManager::GetInstance()->Remove(pid()); ChildProcessSecurityPolicy::GetInstance()->Remove(pid()); } // We may have some unsent messages at this point, but that's OK. channel_.reset(); // Destroy the AudioRendererHost properly. if (audio_renderer_host_.get()) audio_renderer_host_->Destroy(); if (process_.handle() && !run_renderer_in_process()) { if (zygote_child_) { #if defined(OS_LINUX) Singleton()->EnsureProcessTerminated(process_.handle()); #endif } else { ProcessWatcher::EnsureProcessTerminated(process_.handle()); } } ClearTransportDIBCache(); } bool BrowserRenderProcessHost::Init() { // calling Init() more than once does nothing, this makes it more convenient // for the view host which may not be sure in some cases if (channel_.get()) return true; // run the IPC channel on the shared IO thread. base::Thread* io_thread = g_browser_process->io_thread(); // Construct the AudioRendererHost with the IO thread. audio_renderer_host_ = new AudioRendererHost(io_thread->message_loop()); scoped_refptr resource_message_filter = new ResourceMessageFilter(g_browser_process->resource_dispatcher_host(), audio_renderer_host_.get(), PluginService::GetInstance(), g_browser_process->print_job_manager(), profile(), widget_helper_, profile()->GetSpellChecker()); const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess(); // setup IPC channel const std::string channel_id = ChildProcessInfo::GenerateRandomChannelID(this); channel_.reset( new IPC::SyncChannel(channel_id, IPC::Channel::MODE_SERVER, this, resource_message_filter, io_thread->message_loop(), true, g_browser_process->shutdown_event())); // As a preventive mesure, we DCHECK if someone sends a synchronous message // with no time-out, which in the context of the browser process we should not // be doing. channel_->set_sync_messages_with_no_timeout_allowed(false); // Build command line for renderer, we have to quote the executable name to // deal with spaces. std::wstring renderer_path = browser_command_line.GetSwitchValue(switches::kBrowserSubprocessPath); if (renderer_path.empty()) { if (!GetRendererPath(&renderer_path)) { // Need to reset the channel we created above or others might think the // connection is live. channel_.reset(); return false; } } CommandLine cmd_line(renderer_path); if (logging::DialogsAreSuppressed()) cmd_line.AppendSwitch(switches::kNoErrorDialogs); // propagate the following switches to the renderer command line // (along with any associated values) if present in the browser command line static const wchar_t* const switch_names[] = { switches::kRendererAssertTest, switches::kRendererCrashTest, switches::kRendererStartupDialog, switches::kNoSandbox, switches::kTestSandbox, #if !defined (GOOGLE_CHROME_BUILD) // This is an unsupported and not fully tested mode, so don't enable it for // official Chrome builds. switches::kInProcessPlugins, #endif switches::kDomAutomationController, switches::kUserAgent, switches::kJavaScriptFlags, switches::kRecordMode, switches::kPlaybackMode, switches::kNoJsRandomness, switches::kDisableBreakpad, switches::kFullMemoryCrashReport, switches::kEnableLogging, switches::kDumpHistogramsOnExit, switches::kDisableLogging, switches::kLoggingLevel, switches::kDebugPrint, switches::kAllowAllActiveX, switches::kMemoryProfiling, switches::kEnableWatchdog, switches::kMessageLoopHistogrammer, switches::kEnableDCHECK, switches::kSilentDumpOnDCHECK, switches::kUseLowFragHeapCrt, switches::kEnableStatsTable, switches::kAutoSpellCorrect, switches::kDisableAudio, switches::kSimpleDataSource, switches::kEnableBenchmarking, switches::kEnableDatabases, }; for (size_t i = 0; i < arraysize(switch_names); ++i) { if (browser_command_line.HasSwitch(switch_names[i])) { cmd_line.AppendSwitchWithValue(switch_names[i], browser_command_line.GetSwitchValue(switch_names[i])); } } // Tell the renderer to enable extensions if there are any extensions loaded. // // NOTE: This is subtly different than just passing along whether // --enable-extenisons is present in the browser process. For example, there // is also an extensions.enabled preference, and there may be various special // cases about whether to allow extensions to load. // // This introduces a race condition where the first renderer never gets // extensions enabled, so we also set the flag if extensions_enabled(). This // isn't perfect though, because of the special cases above. // // TODO(aa): We need to get rid of the need to pass this flag at all. It is // only used in one place in the renderer. if (profile()->GetExtensionsService()) { if (profile()->GetExtensionsService()->extensions()->size() > 0 || profile()->GetExtensionsService()->extensions_enabled()) cmd_line.AppendSwitch(switches::kEnableExtensions); } // Pass on the browser locale. const std::string locale = g_browser_process->GetApplicationLocale(); cmd_line.AppendSwitchWithValue(switches::kLang, ASCIIToWide(locale)); // If we run FieldTrials, we want to pass to their state to the renderer so // that it can act in accordance with each state, or record histograms // relating to the FieldTrial states. std::string field_trial_states; FieldTrialList::StatesToString(&field_trial_states); if (!field_trial_states.empty()) cmd_line.AppendSwitchWithValue(switches::kForceFieldTestNameAndValue, ASCIIToWide(field_trial_states)); #if defined(OS_POSIX) const bool has_cmd_prefix = browser_command_line.HasSwitch(switches::kRendererCmdPrefix); if (has_cmd_prefix) { // launch the renderer child with some prefix (usually "gdb --args") const std::wstring prefix = browser_command_line.GetSwitchValue(switches::kRendererCmdPrefix); cmd_line.PrependWrapper(prefix); } #endif // OS_POSIX #if defined(OS_LINUX) if (GoogleUpdateSettings::GetCollectStatsConsent()) cmd_line.AppendSwitchWithValue(switches::kRendererCrashDump, ASCIIToWide(google_update::linux_guid + "," + base::GetLinuxDistro())); #endif cmd_line.AppendSwitchWithValue(switches::kProcessType, switches::kRendererProcess); cmd_line.AppendSwitchWithValue(switches::kProcessChannelID, ASCIIToWide(channel_id)); const std::wstring& profile_path = browser_command_line.GetSwitchValue(switches::kUserDataDir); if (!profile_path.empty()) cmd_line.AppendSwitchWithValue(switches::kUserDataDir, profile_path); if (run_renderer_in_process()) { // Crank up a thread and run the initialization there. With the way that // messages flow between the browser and renderer, this thread is required // to prevent a deadlock in single-process mode. Since the primordial // thread in the renderer process runs the WebKit code and can sometimes // make blocking calls to the UI thread (i.e. this thread), they need to run // on separate threads. in_process_renderer_.reset(new RendererMainThread(channel_id)); base::Thread::Options options; #if !defined(OS_LINUX) // In-process plugins require this to be a UI message loop. options.message_loop_type = MessageLoop::TYPE_UI; #else // We can't have multiple UI loops on Linux, so we don't support // in-process plugins. options.message_loop_type = MessageLoop::TYPE_DEFAULT; #endif in_process_renderer_->StartWithOptions(options); } else { base::ProcessHandle process = 0; #if defined(OS_WIN) process = sandbox::StartProcess(&cmd_line); #elif defined(OS_POSIX) #if defined(OS_LINUX) if (!has_cmd_prefix) { base::GlobalDescriptors::Mapping mapping; const int ipcfd = channel_->GetClientFileDescriptor(); mapping.push_back(std::pair(kPrimaryIPCChannel, ipcfd)); const int crash_signal_fd = Singleton()->GetDeathSignalSocket(); if (crash_signal_fd >= 0) { mapping.push_back(std::pair(kCrashDumpSignal, crash_signal_fd)); } process = Singleton()->ForkRenderer(cmd_line.argv(), mapping); zygote_child_ = true; } else { #endif // defined(OS_LINUX) // NOTE: This code is duplicated with plugin_process_host.cc, but // there's not a good place to de-duplicate it. base::file_handle_mapping_vector fds_to_map; const int ipcfd = channel_->GetClientFileDescriptor(); fds_to_map.push_back(std::make_pair(ipcfd, kPrimaryIPCChannel + 3)); #if defined(OS_LINUX) const int crash_signal_fd = Singleton()->GetDeathSignalSocket(); if (crash_signal_fd >= 0) { fds_to_map.push_back(std::make_pair(crash_signal_fd, kCrashDumpSignal + 3)); } const int sandbox_fd = Singleton()->GetRendererSocket(); fds_to_map.push_back(std::make_pair(sandbox_fd, kSandboxIPCChannel + 3)); #endif // defined(OS_LINUX) base::LaunchApp(cmd_line.argv(), fds_to_map, false, &process); zygote_child_ = false; #if defined(OS_LINUX) } #endif // defined(OS_LINUX) #endif // defined(OS_WIN) if (!process) { channel_.reset(); return false; } process_.set_handle(process); SetProcessID(process_.pid()); } resource_message_filter->Init(pid()); WebCacheManager::GetInstance()->Add(pid()); ChildProcessSecurityPolicy::GetInstance()->Add(pid()); // Now that the process is created, set its backgrounding accordingly. SetBackgrounded(backgrounded_); InitVisitedLinks(); InitUserScripts(); InitExtensions(); if (max_page_id_ != -1) channel_->Send(new ViewMsg_SetNextPageID(max_page_id_ + 1)); return true; } int BrowserRenderProcessHost::GetNextRoutingID() { return widget_helper_->GetNextRoutingID(); } void BrowserRenderProcessHost::CancelResourceRequests(int render_widget_id) { widget_helper_->CancelResourceRequests(render_widget_id); } void BrowserRenderProcessHost::CrossSiteClosePageACK( const ViewMsg_ClosePage_Params& params) { widget_helper_->CrossSiteClosePageACK(params); } bool BrowserRenderProcessHost::WaitForPaintMsg(int render_widget_id, const base::TimeDelta& max_delay, IPC::Message* msg) { return widget_helper_->WaitForPaintMsg(render_widget_id, max_delay, msg); } void BrowserRenderProcessHost::ReceivedBadMessage(uint16 msg_type) { BadMessageTerminateProcess(msg_type, process_.handle()); } void BrowserRenderProcessHost::ViewCreated() { view_created_ = true; visited_link_updater_->Update(this); } void BrowserRenderProcessHost::WidgetRestored() { // Verify we were properly backgrounded. DCHECK_EQ(backgrounded_, (visible_widgets_ == 0)); visible_widgets_++; if (view_created_) visited_link_updater_->Update(this); SetBackgrounded(false); } void BrowserRenderProcessHost::WidgetHidden() { // On startup, the browser will call Hide if (backgrounded_) return; DCHECK_EQ(backgrounded_, (visible_widgets_ == 0)); visible_widgets_--; DCHECK_GE(visible_widgets_, 0); if (visible_widgets_ == 0) { DCHECK(!backgrounded_); SetBackgrounded(true); } } void BrowserRenderProcessHost::AddWord(const std::wstring& word) { base::Thread* io_thread = g_browser_process->io_thread(); if (profile()->GetSpellChecker()) { io_thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod( profile()->GetSpellChecker(), &SpellChecker::AddWord, word)); } } void BrowserRenderProcessHost::AddVisitedLinks( const VisitedLinkCommon::Fingerprints& links) { visited_link_updater_->Buffer(links); if (visible_widgets_ == 0) return; visited_link_updater_->Update(this); } void BrowserRenderProcessHost::ResetVisitedLinks() { visited_link_updater_->Clear(); Send(new ViewMsg_VisitedLink_Reset()); } base::ProcessHandle BrowserRenderProcessHost::GetRendererProcessHandle() { if (run_renderer_in_process()) return base::Process::Current().handle(); return process_.handle(); } void BrowserRenderProcessHost::InitVisitedLinks() { VisitedLinkMaster* visitedlink_master = profile()->GetVisitedLinkMaster(); if (!visitedlink_master) { return; } base::SharedMemoryHandle handle_for_process; bool r = visitedlink_master->ShareToProcess(GetRendererProcessHandle(), &handle_for_process); DCHECK(r); if (base::SharedMemory::IsHandleValid(handle_for_process)) { Send(new ViewMsg_VisitedLink_NewTable(handle_for_process)); } } void BrowserRenderProcessHost::InitUserScripts() { UserScriptMaster* user_script_master = profile()->GetUserScriptMaster(); DCHECK(user_script_master); if (!user_script_master->ScriptsReady()) { // No scripts ready. :( return; } // Update the renderer process with the current scripts. SendUserScriptsUpdate(user_script_master->GetSharedMemory()); } void BrowserRenderProcessHost::InitExtensions() { // TODO(aa): Should only bother sending these function names if this is an // extension process. std::vector function_names; ExtensionFunctionDispatcher::GetAllFunctionNames(&function_names); Send(new ViewMsg_Extension_SetFunctionNames(function_names)); } void BrowserRenderProcessHost::SendUserScriptsUpdate( base::SharedMemory *shared_memory) { base::SharedMemoryHandle handle_for_process; if (!shared_memory->ShareToProcess(GetRendererProcessHandle(), &handle_for_process)) { // This can legitimately fail if the renderer asserts at startup. return; } if (base::SharedMemory::IsHandleValid(handle_for_process)) { Send(new ViewMsg_UserScripts_UpdatedScripts(handle_for_process)); } } bool BrowserRenderProcessHost::FastShutdownIfPossible() { if (!process_.handle()) return false; // Render process is probably crashed. if (BrowserRenderProcessHost::run_renderer_in_process()) return false; // Since process mode can't do fast shutdown. // Test if there's an unload listener. // NOTE: It's possible that an onunload listener may be installed // while we're shutting down, so there's a small race here. Given that // the window is small, it's unlikely that the web page has much // state that will be lost by not calling its unload handlers properly. if (!sudden_termination_allowed()) return false; // Check for any external tab containers, since they may still be running even // though this window closed. BrowserRenderProcessHost::listeners_iterator iter; // NOTE: This is a bit dangerous. We know that for now, listeners are // always RenderWidgetHosts. But in theory, they don't have to be. for (iter = listeners_begin(); iter != listeners_end(); ++iter) { RenderWidgetHost* widget = static_cast(iter->second); DCHECK(widget); if (!widget || !widget->IsRenderView()) continue; RenderViewHost* rvh = static_cast(widget); if (rvh->delegate()->IsExternalTabContainer()) return false; } // Otherwise, we're allowed to just terminate the process. Using exit code 0 // means that UMA won't treat this as a renderer crash. process_.Terminate(ResultCodes::NORMAL_EXIT); return true; } bool BrowserRenderProcessHost::SendWithTimeout(IPC::Message* msg, int timeout_ms) { if (!channel_.get()) { delete msg; return false; } return channel_->SendWithTimeout(msg, timeout_ms); } // This is a platform specific function for mapping a transport DIB given its id TransportDIB* BrowserRenderProcessHost::MapTransportDIB( TransportDIB::Id dib_id) { #if defined(OS_WIN) // On Windows we need to duplicate the handle from the remote process HANDLE section = win_util::GetSectionFromProcess( dib_id.handle, GetRendererProcessHandle(), false /* read write */); return TransportDIB::Map(section); #elif defined(OS_MACOSX) // On OSX, the browser allocates all DIBs and keeps a file descriptor around // for each. return widget_helper_->MapTransportDIB(dib_id); #elif defined(OS_LINUX) return TransportDIB::Map(dib_id); #endif // defined(OS_LINUX) } TransportDIB* BrowserRenderProcessHost::GetTransportDIB( TransportDIB::Id dib_id) { const std::map::iterator i = cached_dibs_.find(dib_id); if (i != cached_dibs_.end()) { cached_dibs_cleaner_.Reset(); return i->second; } TransportDIB* dib = MapTransportDIB(dib_id); if (!dib) return NULL; if (cached_dibs_.size() >= MAX_MAPPED_TRANSPORT_DIBS) { // Clean a single entry from the cache std::map::iterator smallest_iterator; size_t smallest_size = std::numeric_limits::max(); for (std::map::iterator i = cached_dibs_.begin(); i != cached_dibs_.end(); ++i) { if (i->second->size() <= smallest_size) { smallest_iterator = i; smallest_size = i->second->size(); } } delete smallest_iterator->second; cached_dibs_.erase(smallest_iterator); } cached_dibs_[dib_id] = dib; cached_dibs_cleaner_.Reset(); return dib; } void BrowserRenderProcessHost::ClearTransportDIBCache() { for (std::map::iterator i = cached_dibs_.begin(); i != cached_dibs_.end(); ++i) { delete i->second; } cached_dibs_.clear(); } bool BrowserRenderProcessHost::Send(IPC::Message* msg) { if (!channel_.get()) { delete msg; return false; } return channel_->Send(msg); } void BrowserRenderProcessHost::OnMessageReceived(const IPC::Message& msg) { if (msg.routing_id() == MSG_ROUTING_CONTROL) { // dispatch control messages bool msg_is_ok = true; IPC_BEGIN_MESSAGE_MAP_EX(BrowserRenderProcessHost, msg, msg_is_ok) IPC_MESSAGE_HANDLER(ViewHostMsg_PageContents, OnPageContents) IPC_MESSAGE_HANDLER(ViewHostMsg_UpdatedCacheStats, OnUpdatedCacheStats) IPC_MESSAGE_HANDLER(ViewHostMsg_SuddenTerminationChanged, SuddenTerminationChanged); IPC_MESSAGE_HANDLER(ViewHostMsg_ExtensionAddListener, OnExtensionAddListener) IPC_MESSAGE_HANDLER(ViewHostMsg_ExtensionRemoveListener, OnExtensionRemoveListener) IPC_MESSAGE_HANDLER(ViewHostMsg_ExtensionCloseChannel, OnExtensionCloseChannel) IPC_MESSAGE_UNHANDLED_ERROR() IPC_END_MESSAGE_MAP_EX() if (!msg_is_ok) { // The message had a handler, but its de-serialization failed. // We consider this a capital crime. Kill the renderer if we have one. ReceivedBadMessage(msg.type()); } return; } // dispatch incoming messages to the appropriate TabContents IPC::Channel::Listener* listener = GetListenerByID(msg.routing_id()); if (!listener) { if (msg.is_sync()) { // The listener has gone away, so we must respond or else the caller will // hang waiting for a reply. IPC::Message* reply = IPC::SyncMessage::GenerateReply(&msg); reply->set_reply_error(); Send(reply); } return; } listener->OnMessageReceived(msg); } void BrowserRenderProcessHost::OnChannelConnected(int32 peer_pid) { // process_ is not NULL if we created the renderer process if (!process_.handle()) { if (base::GetCurrentProcId() == peer_pid) { // We are in single-process mode. In theory we should have access to // ourself but it may happen that we don't. process_.set_handle(base::GetCurrentProcessHandle()); } else { #if defined(OS_WIN) // Request MAXIMUM_ALLOWED to match the access a handle // returned by CreateProcess() has to the process object. process_.set_handle(OpenProcess(MAXIMUM_ALLOWED, FALSE, peer_pid)); #elif defined(OS_POSIX) // ProcessHandle is just a pid. process_.set_handle(peer_pid); #endif DCHECK(process_.handle()); } } else { // Need to verify that the peer_pid is actually the process we know, if // it is not, we need to panic now. See bug 1002150. if (peer_pid != process_.pid()) { // In the case that we are running the renderer in a wrapper, this check // is invalid as it's the wrapper PID that we'll have, not the actual // renderer const CommandLine& cmd_line = *CommandLine::ForCurrentProcess(); if (cmd_line.HasSwitch(switches::kRendererCmdPrefix)) return; CHECK(peer_pid == process_.pid()) << peer_pid << " " << process_.pid(); } } } // Static. This function can be called from the IO Thread or from the UI thread. void BrowserRenderProcessHost::BadMessageTerminateProcess( uint16 msg_type, base::ProcessHandle process) { LOG(ERROR) << "bad message " << msg_type << " terminating renderer."; if (BrowserRenderProcessHost::run_renderer_in_process()) { // In single process mode it is better if we don't suicide but just crash. CHECK(false); } NOTREACHED(); base::KillProcess(process, ResultCodes::KILLED_BAD_MESSAGE, false); } void BrowserRenderProcessHost::OnChannelError() { // Our child process has died. If we didn't expect it, it's a crash. // In any case, we need to let everyone know it's gone. DCHECK(process_.handle()); DCHECK(channel_.get()); bool child_exited; bool did_crash; if (zygote_child_) { #if defined(OS_LINUX) did_crash = Singleton()->DidProcessCrash( process_.handle(), &child_exited); #else NOTREACHED(); did_crash = true; #endif } else { did_crash = base::DidProcessCrash(&child_exited, process_.handle()); } NotificationService::current()->Notify( NotificationType::RENDERER_PROCESS_CLOSED, Source(this), Details(&did_crash)); // POSIX: If the process crashed, then the kernel closed the socket for it // and so the child has already died by the time we get here. Since // DidProcessCrash called waitpid with WNOHANG, it'll reap the process. // However, if DidProcessCrash didn't reap the child, we'll need to in // ~BrowserRenderProcessHost via ProcessWatcher. So we can't close the handle // here. // // This is moot on Windows where |child_exited| will always be true. if (child_exited) process_.Close(); WebCacheManager::GetInstance()->Remove(pid()); ChildProcessSecurityPolicy::GetInstance()->Remove(pid()); channel_.reset(); // This process should detach all the listeners, causing the object to be // deleted. We therefore need a stack copy of the web view list to avoid // crashing when checking for the termination condition the last time. IDMap local_listeners(listeners_); for (listeners_iterator i = local_listeners.begin(); i != local_listeners.end(); ++i) { i->second->OnMessageReceived(ViewHostMsg_RenderViewGone(i->first)); } ClearTransportDIBCache(); // this object is not deleted at this point and may be reused later. // TODO(darin): clean this up } void BrowserRenderProcessHost::OnPageContents(const GURL& url, int32 page_id, const std::wstring& contents) { Profile* p = profile(); if (!p || p->IsOffTheRecord()) return; HistoryService* hs = p->GetHistoryService(Profile::IMPLICIT_ACCESS); if (hs) hs->SetPageContents(url, contents); } void BrowserRenderProcessHost::OnUpdatedCacheStats( const WebCache::UsageStats& stats) { WebCacheManager::GetInstance()->ObserveStats(pid(), stats); } void BrowserRenderProcessHost::SuddenTerminationChanged(bool enabled) { set_sudden_termination_allowed(enabled); } void BrowserRenderProcessHost::SetBackgrounded(bool backgrounded) { // If the process_ is NULL, the process hasn't been created yet. if (process_.handle()) { bool should_set_backgrounded = true; #if defined(OS_WIN) // The cbstext.dll loads as a global GetMessage hook in the browser process // and intercepts/unintercepts the kernel32 API SetPriorityClass in a // background thread. If the UI thread invokes this API just when it is // intercepted the stack is messed up on return from the interceptor // which causes random crashes in the browser process. Our hack for now // is to not invoke the SetPriorityClass API if the dll is loaded. should_set_backgrounded = (GetModuleHandle(L"cbstext.dll") == NULL); #endif // OS_WIN if (should_set_backgrounded) { bool rv = process_.SetProcessBackgrounded(backgrounded); if (!rv) { return; } } // Now tune the memory footprint of the renderer. // If the OS needs to page, we'd rather it page idle renderers. BrowserProcess::MemoryModel model = g_browser_process->memory_model(); if (model < BrowserProcess::HIGH_MEMORY_MODEL) { if (backgrounded) { if (model == BrowserProcess::LOW_MEMORY_MODEL) process_.EmptyWorkingSet(); else if (model == BrowserProcess::MEDIUM_MEMORY_MODEL) process_.ReduceWorkingSet(); } else { if (model == BrowserProcess::MEDIUM_MEMORY_MODEL) process_.UnReduceWorkingSet(); } } } // Note: we always set the backgrounded_ value. If the process is NULL // (and hence hasn't been created yet), we will set the process priority // later when we create the process. backgrounded_ = backgrounded; } // NotificationObserver implementation. void BrowserRenderProcessHost::Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details) { switch (type.value) { case NotificationType::USER_SCRIPTS_UPDATED: { base::SharedMemory* shared_memory = Details(details).ptr(); if (shared_memory) { SendUserScriptsUpdate(shared_memory); } break; } default: { NOTREACHED(); break; } } } void BrowserRenderProcessHost::OnExtensionAddListener( const std::string& event_name) { if (profile()->GetExtensionMessageService()) { profile()->GetExtensionMessageService()->AddEventListener(event_name, pid()); } } void BrowserRenderProcessHost::OnExtensionRemoveListener( const std::string& event_name) { if (profile()->GetExtensionMessageService()) { profile()->GetExtensionMessageService()->RemoveEventListener(event_name, pid()); } } void BrowserRenderProcessHost::OnExtensionCloseChannel(int port_id) { if (profile()->GetExtensionMessageService()) { profile()->GetExtensionMessageService()->CloseChannel(port_id); } }