diff options
author | brettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-01-22 17:48:25 +0000 |
---|---|---|
committer | brettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-01-22 17:48:25 +0000 |
commit | 6524b5f9cb595b601fe95396439508efc7ecc8b3 (patch) | |
tree | d3b028ebba0b8a223ce0508131d8b69fe5f85354 /chrome/browser/renderer_host | |
parent | c8159813d30739744a18e901f5ac27ac27424bb4 (diff) | |
download | chromium_src-6524b5f9cb595b601fe95396439508efc7ecc8b3.zip chromium_src-6524b5f9cb595b601fe95396439508efc7ecc8b3.tar.gz chromium_src-6524b5f9cb595b601fe95396439508efc7ecc8b3.tar.bz2 |
Move files out of browser and into either renderer_host or tab_contents.
This also fixes a crash in the web contents unit test in a commented-out test and re-enable it.
Review URL: http://codereview.chromium.org/18504
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@8470 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/renderer_host')
18 files changed, 5961 insertions, 11 deletions
diff --git a/chrome/browser/renderer_host/browser_render_process_host.cc b/chrome/browser/renderer_host/browser_render_process_host.cc index 7380510..a93cbbe 100644 --- a/chrome/browser/renderer_host/browser_render_process_host.cc +++ b/chrome/browser/renderer_host/browser_render_process_host.cc @@ -29,8 +29,8 @@ #include "chrome/browser/extensions/user_script_master.h" #include "chrome/browser/history/history.h" #include "chrome/browser/plugin_service.h" -#include "chrome/browser/render_widget_helper.h" -#include "chrome/browser/renderer_security_policy.h" +#include "chrome/browser/renderer_host/render_widget_helper.h" +#include "chrome/browser/renderer_host/renderer_security_policy.h" #include "chrome/browser/resource_message_filter.h" #include "chrome/browser/spellchecker.h" #include "chrome/browser/visitedlink_master.h" @@ -48,7 +48,7 @@ #if defined(OS_WIN) // TODO(port): see comment by the only usage of RenderViewHost in this file. -#include "chrome/browser/render_view_host.h" +#include "chrome/browser/renderer_host/render_view_host.h" // Once the above TODO is finished, then this block is all Windows-specific // files. diff --git a/chrome/browser/renderer_host/cross_site_resource_handler.cc b/chrome/browser/renderer_host/cross_site_resource_handler.cc index 91695df..a172352 100644 --- a/chrome/browser/renderer_host/cross_site_resource_handler.cc +++ b/chrome/browser/renderer_host/cross_site_resource_handler.cc @@ -4,7 +4,7 @@ #include "chrome/browser/renderer_host/cross_site_resource_handler.h" -#include "chrome/browser/render_view_host.h" +#include "chrome/browser/renderer_host/render_view_host.h" #include "chrome/browser/tab_contents/tab_util.h" #include "chrome/browser/tab_contents/web_contents.h" diff --git a/chrome/browser/renderer_host/render_view_host.cc b/chrome/browser/renderer_host/render_view_host.cc new file mode 100644 index 0000000..dca1a1d --- /dev/null +++ b/chrome/browser/renderer_host/render_view_host.cc @@ -0,0 +1,1310 @@ +// 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 "chrome/browser/renderer_host/render_view_host.h" + +#include <string> +#include <vector> + +#include "base/string_util.h" +#include "base/waitable_event.h" +#include "chrome/app/result_codes.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/cross_site_request_manager.h" +#include "chrome/browser/debugger/debugger_wrapper.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/metrics/user_metrics.h" +#include "chrome/browser/renderer_host/render_process_host.h" +#include "chrome/browser/renderer_host/render_view_host_delegate.h" +#include "chrome/browser/renderer_host/render_widget_host.h" +#include "chrome/browser/renderer_host/render_widget_host_view.h" +#include "chrome/browser/renderer_host/renderer_security_policy.h" +#include "chrome/browser/tab_contents/navigation_entry.h" +#include "chrome/browser/tab_contents/site_instance.h" +#include "chrome/browser/tab_contents/web_contents.h" +#include "chrome/common/resource_bundle.h" +#include "chrome/common/thumbnail_score.h" +#include "net/base/net_util.h" +#include "skia/include/SkBitmap.h" + +using base::TimeDelta; + +namespace { + +void FilterURL(RendererSecurityPolicy* policy, int renderer_id, GURL* url) { + if (!url->is_valid()) + return; // We don't need to block invalid URLs. + + if (url->SchemeIs("about")) { + // The renderer treats all URLs in the about: scheme as being about:blank. + // Canonicalize about: URLs to about:blank. + *url = GURL("about:blank"); + } + + if (!policy->CanRequestURL(renderer_id, *url)) { + // If this renderer is not permitted to request this URL, we invalidate the + // URL. This prevents us from storing the blocked URL and becoming confused + // later. + LOG(INFO) << "Blocked URL " << url->spec(); + *url = GURL(); + } +} + +// Delay to wait on closing the tab for a beforeunload/unload handler to fire. +const int kUnloadTimeoutMS = 1000; + +} // namespace + +/////////////////////////////////////////////////////////////////////////////// +// RenderViewHost, public: + +// static +RenderViewHost* RenderViewHost::FromID(int render_process_id, + int render_view_id) { + RenderProcessHost* process = RenderProcessHost::FromID(render_process_id); + if (!process) + return NULL; + RenderWidgetHost* widget = static_cast<RenderWidgetHost*>( + process->GetListenerByID(render_view_id)); + if (!widget || !widget->IsRenderView()) + return NULL; + return static_cast<RenderViewHost*>(widget); +} + +RenderViewHost::RenderViewHost(SiteInstance* instance, + RenderViewHostDelegate* delegate, + int routing_id, + base::WaitableEvent* modal_dialog_event) + : RenderWidgetHost(instance->GetProcess(), routing_id), + instance_(instance), + enable_dom_ui_bindings_(false), + enable_external_host_bindings_(false), + delegate_(delegate), + renderer_initialized_(false), + waiting_for_drag_context_response_(false), + debugger_attached_(false), + modal_dialog_count_(0), + navigations_suspended_(false), + suspended_nav_message_(NULL), + run_modal_reply_msg_(NULL), + has_unload_listener_(false), + is_waiting_for_unload_ack_(false), + are_javascript_messages_suppressed_(false) { + DCHECK(instance_); + DCHECK(delegate_); + if (modal_dialog_event == NULL) + modal_dialog_event = new base::WaitableEvent(true, false); + + modal_dialog_event_.reset(modal_dialog_event); +#ifdef CHROME_PERSONALIZATION + personalization_ = Personalization::CreateHostPersonalization(this); +#endif +} + +RenderViewHost::~RenderViewHost() { + OnDebugDisconnect(); + +#ifdef CHROME_PERSONALIZATION + Personalization::CleanupHostPersonalization(personalization_); + personalization_ = NULL; +#endif + + // Be sure to clean up any leftover state from cross-site requests. + Singleton<CrossSiteRequestManager>()->SetHasPendingCrossSiteRequest( + process()->host_id(), routing_id_, false); +} + +bool RenderViewHost::CreateRenderView() { + DCHECK(!IsRenderViewLive()) << "Creating view twice"; + + // The process may (if we're sharing a process with another host that already + // initialized it) or may not (we have our own process or the old process + // crashed) have been initialized. Calling Init multiple times will be + // ignored, so this is safe. + if (!process_->Init()) + return false; + DCHECK(process_->channel()); + DCHECK(process_->profile()); + + renderer_initialized_ = true; + + HANDLE modal_dialog_event; + HANDLE renderer_process_handle = process()->process().handle(); + if (renderer_process_handle == NULL) + renderer_process_handle = GetCurrentProcess(); + + BOOL result = DuplicateHandle(GetCurrentProcess(), + modal_dialog_event_->handle(), + renderer_process_handle, + &modal_dialog_event, + SYNCHRONIZE, + FALSE, + 0); + DCHECK(result) << "Couldn't duplicate the modal dialog handle for the renderer."; + + DCHECK(view_); + Send(new ViewMsg_New(view_->GetPluginHWND(), + modal_dialog_event, + delegate_->GetWebkitPrefs(), + routing_id_)); + + // Set the alternate error page, which is profile specific, in the renderer. + GURL url = delegate_->GetAlternateErrorPageURL(); + SetAlternateErrorPageURL(url); + + // If it's enabled, tell the renderer to set up the Javascript bindings for + // sending messages back to the browser. + Send(new ViewMsg_AllowBindings( + routing_id_, enable_dom_ui_bindings_, enable_external_host_bindings_)); + + // Let our delegate know that we created a RenderView. + delegate_->RendererCreated(this); + + return true; +} + +bool RenderViewHost::IsRenderViewLive() const { + return process_->channel() && renderer_initialized_; +} + +void RenderViewHost::Init() { + RenderWidgetHost::Init(); + renderer_initialized_ = true; +} + +void RenderViewHost::NavigateToEntry(const NavigationEntry& entry, + bool is_reload) { + ViewMsg_Navigate_Params params; + MakeNavigateParams(entry, is_reload, ¶ms); + + RendererSecurityPolicy::GetInstance()->GrantRequestURL( + process()->host_id(), params.url); + + DoNavigate(new ViewMsg_Navigate(routing_id_, params)); +} + +void RenderViewHost::NavigateToURL(const GURL& url) { + ViewMsg_Navigate_Params params; + params.page_id = -1; + params.url = url; + params.transition = PageTransition::LINK; + params.reload = false; + + RendererSecurityPolicy::GetInstance()->GrantRequestURL( + process()->host_id(), params.url); + + DoNavigate(new ViewMsg_Navigate(routing_id_, params)); +} + +void RenderViewHost::DoNavigate(ViewMsg_Navigate* nav_message) { + // Only send the message if we aren't suspended at the start of a cross-site + // request. + if (navigations_suspended_) { + // Shouldn't be possible to have a second navigation while suspended, since + // navigations will only be suspended during a cross-site request. If a + // second navigation occurs, WebContents will cancel this pending RVH + // create a new pending RVH. + DCHECK(!suspended_nav_message_.get()); + suspended_nav_message_.reset(nav_message); + } else { + Send(nav_message); + } +} + +void RenderViewHost::LoadAlternateHTMLString(const std::string& html_text, + bool new_navigation, + const GURL& display_url, + const std::string& security_info) { + Send(new ViewMsg_LoadAlternateHTMLText(routing_id_, html_text, + new_navigation, display_url, + security_info)); +} + +void RenderViewHost::SetNavigationsSuspended(bool suspend) { + DCHECK(navigations_suspended_ != suspend); + navigations_suspended_ = suspend; + if (!suspend && suspended_nav_message_.get()) { + // Resume navigation + Send(suspended_nav_message_.release()); + } +} + +void RenderViewHost::FirePageBeforeUnload() { + if (!IsRenderViewLive()) { + // This RenderViewHost doesn't have a live renderer, so just skip running + // the onbeforeunload handler. + OnMsgShouldCloseACK(true); + return; + } + + // This may be called more than once (if the user clicks the tab close button + // several times, or if she clicks the tab close button than the browser close + // button), so this test makes sure we only send the message once. + if (!is_waiting_for_unload_ack_) { + // Start the hang monitor in case the renderer hangs in the beforeunload + // handler. + is_waiting_for_unload_ack_ = true; + StartHangMonitorTimeout(TimeDelta::FromMilliseconds(kUnloadTimeoutMS)); + Send(new ViewMsg_ShouldClose(routing_id_)); + } +} + +void RenderViewHost::FirePageUnload() { + ClosePage(site_instance()->process_host_id(), + routing_id()); +} + +// static +void RenderViewHost::ClosePageIgnoringUnloadEvents(int render_process_host_id, + int request_id) { + RenderViewHost* rvh = RenderViewHost::FromID(render_process_host_id, + request_id); + if (!rvh) + return; + + rvh->StopHangMonitorTimeout(); + rvh->is_waiting_for_unload_ack_ = false; + + rvh->UnloadListenerHasFired(); + rvh->delegate()->Close(rvh); +} + +void RenderViewHost::ClosePage(int new_render_process_host_id, + int new_request_id) { + // Start the hang monitor in case the renderer hangs in the unload handler. + is_waiting_for_unload_ack_ = true; + StartHangMonitorTimeout(TimeDelta::FromMilliseconds(kUnloadTimeoutMS)); + + if (IsRenderViewLive()) { + Send(new ViewMsg_ClosePage(routing_id_, + new_render_process_host_id, + new_request_id)); + } else { + // This RenderViewHost doesn't have a live renderer, so just skip closing + // the page. We must notify the ResourceDispatcherHost on the IO thread, + // which we will do through the RenderProcessHost's widget helper. + process()->CrossSiteClosePageACK(new_render_process_host_id, + new_request_id); + } +} + +void RenderViewHost::SetHasPendingCrossSiteRequest(bool has_pending_request, + int request_id) { + Singleton<CrossSiteRequestManager>()->SetHasPendingCrossSiteRequest( + process()->host_id(), routing_id_, has_pending_request); + pending_request_id_ = request_id; +} + +int RenderViewHost::GetPendingRequestId() { + return pending_request_id_; +} + +void RenderViewHost::OnCrossSiteResponse(int new_render_process_host_id, + int new_request_id) { + delegate_->OnCrossSiteResponse(new_render_process_host_id, new_request_id); +} + +void RenderViewHost::Stop() { + Send(new ViewMsg_Stop(routing_id_)); +} + +bool RenderViewHost::GetPrintedPagesCount(const ViewMsg_Print_Params& params) { + return Send(new ViewMsg_GetPrintedPagesCount(routing_id_, params)); +} + +bool RenderViewHost::PrintPages(const ViewMsg_PrintPages_Params& params) { + return Send(new ViewMsg_PrintPages(routing_id_, params)); +} + +void RenderViewHost::StartFinding(int request_id, + const std::wstring& search_string, + bool forward, + bool match_case, + bool find_next) { + if (search_string.empty()) + return; + + FindInPageRequest request; + request.request_id = request_id; + request.search_string = search_string; + request.forward = forward; + request.match_case = match_case; + request.find_next = find_next; + Send(new ViewMsg_Find(routing_id_, request)); + + // This call is asynchronous and returns immediately. + // The result of the search is sent as a notification message by the renderer. +} + +void RenderViewHost::StopFinding(bool clear_selection) { + Send(new ViewMsg_StopFinding(routing_id_, clear_selection)); +} + +void RenderViewHost::Zoom(PageZoom::Function function) { + Send(new ViewMsg_Zoom(routing_id_, function)); +} + +void RenderViewHost::SetPageEncoding(const std::wstring& encoding_name) { + Send(new ViewMsg_SetPageEncoding(routing_id_, encoding_name)); +} + +void RenderViewHost::SetAlternateErrorPageURL(const GURL& url) { + Send(new ViewMsg_SetAltErrorPageURL(routing_id_, url)); +} + +void RenderViewHost::FillForm(const FormData& form_data) { + Send(new ViewMsg_FormFill(routing_id_, form_data)); +} + +void RenderViewHost::FillPasswordForm( + const PasswordFormDomManager::FillData& form_data) { + Send(new ViewMsg_FillPasswordForm(routing_id_, form_data)); +} + +void RenderViewHost::DragTargetDragEnter(const WebDropData& drop_data, + const gfx::Point& client_pt, const gfx::Point& screen_pt) { + // Grant the renderer the ability to load the drop_data. + RendererSecurityPolicy* policy = RendererSecurityPolicy::GetInstance(); + policy->GrantRequestURL(process()->host_id(), drop_data.url); + for (std::vector<std::wstring>::const_iterator iter(drop_data.filenames.begin()); + iter != drop_data.filenames.end(); ++iter) { + policy->GrantRequestURL(process()->host_id(), + net::FilePathToFileURL(*iter)); + policy->GrantUploadFile(process()->host_id(), *iter); + } + Send(new ViewMsg_DragTargetDragEnter(routing_id_, drop_data, client_pt, + screen_pt)); +} + +void RenderViewHost::DragTargetDragOver( + const gfx::Point& client_pt, const gfx::Point& screen_pt) { + Send(new ViewMsg_DragTargetDragOver(routing_id_, client_pt, screen_pt)); +} + +void RenderViewHost::DragTargetDragLeave() { + Send(new ViewMsg_DragTargetDragLeave(routing_id_)); +} + +void RenderViewHost::DragTargetDrop( + const gfx::Point& client_pt, const gfx::Point& screen_pt) { + Send(new ViewMsg_DragTargetDrop(routing_id_, client_pt, screen_pt)); +} + +void RenderViewHost::ReservePageIDRange(int size) { + Send(new ViewMsg_ReservePageIDRange(routing_id_, size)); +} + +void RenderViewHost::ExecuteJavascriptInWebFrame( + const std::wstring& frame_xpath, const std::wstring& jscript) { + Send(new ViewMsg_ScriptEvalRequest(routing_id_, frame_xpath, jscript)); +} + +void RenderViewHost::AddMessageToConsole( + const std::wstring& frame_xpath, const std::wstring& msg, + ConsoleMessageLevel level) { + Send(new ViewMsg_AddMessageToConsole(routing_id_, frame_xpath, msg, level)); +} + +void RenderViewHost::DebugCommand(const std::wstring& cmd) { + Send(new ViewMsg_DebugCommand(routing_id_, cmd)); +} + +void RenderViewHost::DebugAttach() { + if (!debugger_attached_) + Send(new ViewMsg_DebugAttach(routing_id_)); +} + +void RenderViewHost::DebugDetach() { + if (debugger_attached_) { + Send(new ViewMsg_DebugDetach(routing_id_)); + debugger_attached_ = false; + } +} + +void RenderViewHost::DebugBreak(bool force) { + if (debugger_attached_) + Send(new ViewMsg_DebugBreak(routing_id_, force)); +} + +void RenderViewHost::Undo() { + Send(new ViewMsg_Undo(routing_id_)); +} + +void RenderViewHost::Redo() { + Send(new ViewMsg_Redo(routing_id_)); +} + +void RenderViewHost::Cut() { + Send(new ViewMsg_Cut(routing_id_)); +} + +void RenderViewHost::Copy() { + Send(new ViewMsg_Copy(routing_id_)); +} + +void RenderViewHost::Paste() { + Send(new ViewMsg_Paste(routing_id_)); +} + +void RenderViewHost::Replace(const std::wstring& text_to_replace) { + Send(new ViewMsg_Replace(routing_id_, text_to_replace)); +} + +void RenderViewHost::ToggleSpellCheck() { + Send(new ViewMsg_ToggleSpellCheck(routing_id_)); +} + +void RenderViewHost::AddToDictionary(const std::wstring& word) { + process_->AddWord(word); +} + +void RenderViewHost::Delete() { + Send(new ViewMsg_Delete(routing_id_)); +} + +void RenderViewHost::SelectAll() { + Send(new ViewMsg_SelectAll(routing_id_)); +} + +int RenderViewHost::DownloadImage(const GURL& url, int image_size) { + if (!url.is_valid()) { + NOTREACHED(); + return 0; + } + static int next_id = 1; + int id = next_id++; + Send(new ViewMsg_DownloadImage(routing_id_, id, url, image_size)); + return id; +} + +void RenderViewHost::GetApplicationInfo(int32 page_id) { + Send(new ViewMsg_GetApplicationInfo(routing_id_, page_id)); +} + +void RenderViewHost::CaptureThumbnail() { + Send(new ViewMsg_CaptureThumbnail(routing_id_)); +} + +void RenderViewHost::JavaScriptMessageBoxClosed(IPC::Message* reply_msg, + bool success, + const std::wstring& prompt) { + if (is_waiting_for_unload_ack_) { + if (are_javascript_messages_suppressed_) { + delegate_->RendererUnresponsive(this, is_waiting_for_unload_ack_); + return; + } + + StartHangMonitorTimeout(TimeDelta::FromMilliseconds(kUnloadTimeoutMS)); + } + + if (--modal_dialog_count_ == 0) + modal_dialog_event_->Reset(); + ViewHostMsg_RunJavaScriptMessage::WriteReplyParams(reply_msg, success, prompt); + Send(reply_msg); +} + +void RenderViewHost::ModalHTMLDialogClosed(IPC::Message* reply_msg, + const std::string& json_retval) { + if (is_waiting_for_unload_ack_) + StartHangMonitorTimeout(TimeDelta::FromMilliseconds(kUnloadTimeoutMS)); + + if (--modal_dialog_count_ == 0) + modal_dialog_event_->Reset(); + + ViewHostMsg_ShowModalHTMLDialog::WriteReplyParams(reply_msg, json_retval); + Send(reply_msg); +} + +void RenderViewHost::CopyImageAt(int x, int y) { + Send(new ViewMsg_CopyImageAt(routing_id_, x, y)); +} + +void RenderViewHost::InspectElementAt(int x, int y) { + RendererSecurityPolicy::GetInstance()->GrantInspectElement( + process()->host_id()); + Send(new ViewMsg_InspectElement(routing_id_, x, y)); +} + +void RenderViewHost::ShowJavaScriptConsole() { + RendererSecurityPolicy::GetInstance()->GrantInspectElement( + process()->host_id()); + + Send(new ViewMsg_ShowJavaScriptConsole(routing_id_)); +} + +void RenderViewHost::DragSourceEndedAt( + int client_x, int client_y, int screen_x, int screen_y) { + Send(new ViewMsg_DragSourceEndedOrMoved( + routing_id_, client_x, client_y, screen_x, screen_y, true)); +} + +void RenderViewHost::DragSourceMovedTo( + int client_x, int client_y, int screen_x, int screen_y) { + Send(new ViewMsg_DragSourceEndedOrMoved( + routing_id_, client_x, client_y, screen_x, screen_y, false)); +} + +void RenderViewHost::DragSourceSystemDragEnded() { + Send(new ViewMsg_DragSourceSystemDragEnded(routing_id_)); +} + +void RenderViewHost::AllowDomAutomationBindings() { + // Expose the binding that allows the DOM to send messages here. + Send(new ViewMsg_AllowDomAutomationBindings(routing_id_, true)); +} + +void RenderViewHost::AllowDOMUIBindings() { + DCHECK(!renderer_initialized_); + enable_dom_ui_bindings_ = true; + RendererSecurityPolicy::GetInstance()->GrantDOMUIBindings(process()->host_id()); +} + +void RenderViewHost::AllowExternalHostBindings() { + enable_external_host_bindings_ = true; +} + +void RenderViewHost::SetDOMUIProperty(const std::string& name, + const std::string& value) { + DCHECK(enable_dom_ui_bindings_); + Send(new ViewMsg_SetDOMUIProperty(routing_id_, name, value)); +} + +// static +void RenderViewHost::MakeNavigateParams(const NavigationEntry& entry, + bool reload, + ViewMsg_Navigate_Params* params) { + params->page_id = entry.page_id(); + params->url = entry.url(); + params->referrer = entry.referrer(); + params->transition = entry.transition_type(); + params->state = entry.content_state(); + params->reload = reload; +} + +bool RenderViewHost::CanBlur() const { + return delegate_->CanBlur(); +} + +void RenderViewHost::SetInitialFocus(bool reverse) { + Send(new ViewMsg_SetInitialFocus(routing_id_, reverse)); +} + +void RenderViewHost::UpdateWebPreferences(const WebPreferences& prefs) { + Send(new ViewMsg_UpdateWebPreferences(routing_id_, prefs)); +} + +void RenderViewHost::InstallMissingPlugin() { + Send(new ViewMsg_InstallMissingPlugin(routing_id_)); +} + +void RenderViewHost::FileSelected(const std::wstring& path) { + RendererSecurityPolicy::GetInstance()->GrantUploadFile(process()->host_id(), + path); + std::vector<std::wstring> files; + files.push_back(path); + Send(new ViewMsg_RunFileChooserResponse(routing_id_, files)); +} + +void RenderViewHost::MultiFilesSelected( + const std::vector<std::wstring>& files) { + for (std::vector<std::wstring>::const_iterator file = files.begin(); + file != files.end(); ++file) { + RendererSecurityPolicy::GetInstance()->GrantUploadFile( + process()->host_id(), *file); + } + Send(new ViewMsg_RunFileChooserResponse(routing_id_, files)); +} + +void RenderViewHost::LoadStateChanged(const GURL& url, + net::LoadState load_state) { + delegate_->LoadStateChanged(url, load_state); +} + +bool RenderViewHost::CanTerminate() const { + if (!delegate_->CanTerminate()) + return false; + + return has_unload_listener_; +} + +/////////////////////////////////////////////////////////////////////////////// +// RenderViewHost, IPC message handlers: + +void RenderViewHost::OnMessageReceived(const IPC::Message& msg) { + if (msg.is_sync() && !msg.is_caller_pumping_messages()) { + NOTREACHED() << "Can't send sync messages to UI thread without pumping " \ + "messages in the renderer or else deadlocks can occur if the page" \ + "has windowed plugins!"; + IPC::Message* reply = IPC::SyncMessage::GenerateReply(&msg); + reply->set_reply_error(); + Send(reply); + return; + } + + bool msg_is_ok = true; + IPC_BEGIN_MESSAGE_MAP_EX(RenderViewHost, msg, msg_is_ok) + IPC_MESSAGE_HANDLER(ViewHostMsg_CreateWindowWithRoute, OnMsgCreateWindow) + IPC_MESSAGE_HANDLER(ViewHostMsg_CreateWidgetWithRoute, OnMsgCreateWidget) + IPC_MESSAGE_HANDLER(ViewHostMsg_ShowView, OnMsgShowView) + IPC_MESSAGE_HANDLER(ViewHostMsg_ShowWidget, OnMsgShowWidget) + IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_RunModal, OnMsgRunModal) + IPC_MESSAGE_HANDLER(ViewHostMsg_RendererReady, OnMsgRendererReady) + IPC_MESSAGE_HANDLER(ViewHostMsg_RendererGone, OnMsgRendererGone) + IPC_MESSAGE_HANDLER_GENERIC(ViewHostMsg_FrameNavigate, OnMsgNavigate(msg)) + IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateState, OnMsgUpdateState) + IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateTitle, OnMsgUpdateTitle) + IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateEncoding, OnMsgUpdateEncoding) + IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateTargetURL, OnMsgUpdateTargetURL) + IPC_MESSAGE_HANDLER_GENERIC(ViewHostMsg_Thumbnail, OnMsgThumbnail(msg)) + IPC_MESSAGE_HANDLER(ViewHostMsg_Close, OnMsgClose) + IPC_MESSAGE_HANDLER(ViewHostMsg_RequestMove, OnMsgRequestMove) + IPC_MESSAGE_HANDLER(ViewHostMsg_DidStartLoading, OnMsgDidStartLoading) + IPC_MESSAGE_HANDLER(ViewHostMsg_DidStopLoading, OnMsgDidStopLoading) + IPC_MESSAGE_HANDLER(ViewHostMsg_DidLoadResourceFromMemoryCache, + OnMsgDidLoadResourceFromMemoryCache) + IPC_MESSAGE_HANDLER(ViewHostMsg_DidRedirectProvisionalLoad, + OnMsgDidRedirectProvisionalLoad) + IPC_MESSAGE_HANDLER(ViewHostMsg_DidStartProvisionalLoadForFrame, + OnMsgDidStartProvisionalLoadForFrame) + IPC_MESSAGE_HANDLER(ViewHostMsg_DidFailProvisionalLoadWithError, + OnMsgDidFailProvisionalLoadWithError) + IPC_MESSAGE_HANDLER(ViewHostMsg_Find_Reply, OnMsgFindReply) + IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateFavIconURL, OnMsgUpdateFavIconURL) + IPC_MESSAGE_HANDLER(ViewHostMsg_DidDownloadImage, OnMsgDidDownloadImage) + IPC_MESSAGE_HANDLER(ViewHostMsg_ContextMenu, OnMsgContextMenu) + IPC_MESSAGE_HANDLER(ViewHostMsg_OpenURL, OnMsgOpenURL) + IPC_MESSAGE_HANDLER(ViewHostMsg_DomOperationResponse, + OnMsgDomOperationResponse) + IPC_MESSAGE_HANDLER(ViewHostMsg_DOMUISend, + OnMsgDOMUISend) + IPC_MESSAGE_HANDLER(ViewHostMsg_ForwardMessageToExternalHost, + OnMsgForwardMessageToExternalHost) +#ifdef CHROME_PERSONALIZATION + IPC_MESSAGE_HANDLER(ViewHostMsg_PersonalizationEvent, + OnPersonalizationEvent) +#endif + IPC_MESSAGE_HANDLER(ViewHostMsg_GoToEntryAtOffset, + OnMsgGoToEntryAtOffset) + IPC_MESSAGE_HANDLER(ViewHostMsg_SetTooltipText, OnMsgSetTooltipText) + IPC_MESSAGE_HANDLER(ViewHostMsg_RunFileChooser, OnMsgRunFileChooser) + IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_RunJavaScriptMessage, + OnMsgRunJavaScriptMessage) + IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_RunBeforeUnloadConfirm, + OnMsgRunBeforeUnloadConfirm) + IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_ShowModalHTMLDialog, + OnMsgShowModalHTMLDialog) + IPC_MESSAGE_HANDLER(ViewHostMsg_PasswordFormsSeen, OnMsgPasswordFormsSeen) + IPC_MESSAGE_HANDLER(ViewHostMsg_AutofillFormSubmitted, + OnMsgAutofillFormSubmitted) + IPC_MESSAGE_HANDLER(ViewHostMsg_StartDragging, OnMsgStartDragging) + IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateDragCursor, OnUpdateDragCursor) + IPC_MESSAGE_HANDLER(ViewHostMsg_TakeFocus, OnTakeFocus) + IPC_MESSAGE_HANDLER(ViewHostMsg_PageHasOSDD, OnMsgPageHasOSDD) + IPC_MESSAGE_HANDLER(ViewHostMsg_InspectElement_Reply, + OnMsgInspectElementReply) + IPC_MESSAGE_FORWARD(ViewHostMsg_DidGetPrintedPagesCount, + delegate_, + RenderViewHostDelegate::DidGetPrintedPagesCount) + IPC_MESSAGE_HANDLER(ViewHostMsg_DidPrintPage, DidPrintPage) + IPC_MESSAGE_HANDLER(ViewHostMsg_AddMessageToConsole, OnAddMessageToConsole) + IPC_MESSAGE_HANDLER(ViewHostMsg_DebuggerOutput, OnDebuggerOutput); + IPC_MESSAGE_HANDLER(ViewHostMsg_DidDebugAttach, DidDebugAttach); + IPC_MESSAGE_HANDLER(ViewHostMsg_UserMetricsRecordAction, + OnUserMetricsRecordAction) + IPC_MESSAGE_HANDLER(ViewHostMsg_MissingPluginStatus, OnMissingPluginStatus); + IPC_MESSAGE_FORWARD(ViewHostMsg_CrashedPlugin, delegate_, + RenderViewHostDelegate::OnCrashedPlugin); + IPC_MESSAGE_HANDLER(ViewHostMsg_SendCurrentPageAllSavableResourceLinks, + OnReceivedSavableResourceLinksForCurrentPage); + IPC_MESSAGE_HANDLER(ViewHostMsg_SendSerializedHtmlData, + OnReceivedSerializedHtmlData); + IPC_MESSAGE_HANDLER(ViewHostMsg_DidGetApplicationInfo, + OnDidGetApplicationInfo); + IPC_MESSAGE_FORWARD(ViewHostMsg_JSOutOfMemory, delegate_, + RenderViewHostDelegate::OnJSOutOfMemory); + IPC_MESSAGE_HANDLER(ViewHostMsg_ShouldClose_ACK, OnMsgShouldCloseACK); + IPC_MESSAGE_HANDLER(ViewHostMsg_UnloadListenerChanged, + OnUnloadListenerChanged); + IPC_MESSAGE_HANDLER(ViewHostMsg_QueryFormFieldAutofill, + OnQueryFormFieldAutofill) + // Have the super handle all other messages. + IPC_MESSAGE_UNHANDLED(RenderWidgetHost::OnMessageReceived(msg)) + IPC_END_MESSAGE_MAP_EX() + + if (!msg_is_ok) { + // The message had a handler, but its de-serialization failed. + // Kill the renderer. + process()->ReceivedBadMessage(msg.type()); + } +} + +void RenderViewHost::Shutdown() { + // If we are being run modally (see RunModal), then we need to cleanup. + if (run_modal_reply_msg_) { + if (--modal_dialog_count_ == 0) + modal_dialog_event_->Reset(); + Send(run_modal_reply_msg_); + run_modal_reply_msg_ = NULL; + } + RenderWidgetHost::Shutdown(); +} + +void RenderViewHost::OnMsgCreateWindow(int route_id, + HANDLE modal_dialog_event) { + RenderViewHostDelegate::View* view = delegate_->GetViewDelegate(); + if (view) + view->CreateNewWindow(route_id, + new base::WaitableEvent(modal_dialog_event)); +} + +void RenderViewHost::OnMsgCreateWidget(int route_id, bool activatable) { + RenderViewHostDelegate::View* view = delegate_->GetViewDelegate(); + if (view) + view->CreateNewWidget(route_id, activatable); +} + +void RenderViewHost::OnMsgShowView(int route_id, + WindowOpenDisposition disposition, + const gfx::Rect& initial_pos, + bool user_gesture) { + RenderViewHostDelegate::View* view = delegate_->GetViewDelegate(); + if (view) + view->ShowCreatedWindow(route_id, disposition, initial_pos, user_gesture); +} + +void RenderViewHost::OnMsgShowWidget(int route_id, + const gfx::Rect& initial_pos) { + RenderViewHostDelegate::View* view = delegate_->GetViewDelegate(); + if (view) + view->ShowCreatedWidget(route_id, initial_pos); +} + +void RenderViewHost::OnMsgRunModal(IPC::Message* reply_msg) { + DCHECK(!run_modal_reply_msg_); + if (modal_dialog_count_++ == 0) + modal_dialog_event_->Reset(); + run_modal_reply_msg_ = reply_msg; + + // TODO(darin): Bug 1107929: Need to inform our delegate to show this view in + // an app-modal fashion. +} + +void RenderViewHost::OnMsgRendererReady() { + WasResized(); + delegate_->RendererReady(this); +} + +void RenderViewHost::OnMsgRendererGone() { + // Must reset these to ensure that mouse move events work with a new renderer. + mouse_move_pending_ = false; + next_mouse_move_.reset(); + + // Clearing this flag causes us to re-create the renderer when recovering + // from a crashed renderer. + renderer_initialized_ = false; + + // Reset some fields in preparation for recovering from a crash. + resize_ack_pending_ = false; + current_size_ = gfx::Size(); + is_hidden_ = false; + + RendererExited(); + + if (view_) { + view_->RendererGone(); + view_ = NULL; // The View should be deleted by RendererGone. + } + delegate_->RendererGone(this); + OnDebugDisconnect(); +} + +// Called when the renderer navigates. For every frame loaded, we'll get this +// notification containing parameters identifying the navigation. +// +// Subframes are identified by the page transition type. For subframes loaded +// as part of a wider page load, the page_id will be the same as for the top +// level frame. If the user explicitly requests a subframe navigation, we will +// get a new page_id because we need to create a new navigation entry for that +// action. +void RenderViewHost::OnMsgNavigate(const IPC::Message& msg) { + // Read the parameters out of the IPC message directly to avoid making another + // copy when we filter the URLs. + void* iter = NULL; + ViewHostMsg_FrameNavigate_Params validated_params; + if (!IPC::ParamTraits<ViewHostMsg_FrameNavigate_Params>:: + Read(&msg, &iter, &validated_params)) + return; + + const int renderer_id = process()->host_id(); + RendererSecurityPolicy* policy = RendererSecurityPolicy::GetInstance(); + // Without this check, an evil renderer can trick the browser into creating + // a navigation entry for a banned URL. If the user clicks the back button + // followed by the forward button (or clicks reload, or round-trips through + // session restore, etc), we'll think that the browser commanded the + // renderer to load the URL and grant the renderer the privileges to request + // the URL. To prevent this attack, we block the renderer from inserting + // banned URLs into the navigation controller in the first place. + FilterURL(policy, renderer_id, &validated_params.url); + FilterURL(policy, renderer_id, &validated_params.referrer); + for (std::vector<GURL>::iterator it(validated_params.redirects.begin()); + it != validated_params.redirects.end(); ++it) { + FilterURL(policy, renderer_id, &(*it)); + } + FilterURL(policy, renderer_id, &validated_params.searchable_form_url); + FilterURL(policy, renderer_id, &validated_params.password_form.origin); + FilterURL(policy, renderer_id, &validated_params.password_form.action); + + delegate_->DidNavigate(this, validated_params); + + UpdateBackForwardListCount(); +} + +void RenderViewHost::OnMsgUpdateState(int32 page_id, + const std::string& state) { + delegate_->UpdateState(this, page_id, state); +} + +void RenderViewHost::OnMsgUpdateTitle(int32 page_id, + const std::wstring& title) { + delegate_->UpdateTitle(this, page_id, title); +} + +void RenderViewHost::OnMsgUpdateEncoding(const std::wstring& encoding_name) { + delegate_->UpdateEncoding(this, encoding_name); +} + +void RenderViewHost::OnMsgUpdateTargetURL(int32 page_id, + const GURL& url) { + delegate_->UpdateTargetURL(page_id, url); + + // Send a notification back to the renderer that we are ready to + // receive more target urls. + Send(new ViewMsg_UpdateTargetURL_ACK(routing_id_)); +} + +void RenderViewHost::OnMsgThumbnail(const IPC::Message& msg) { + // crack the message + void* iter = NULL; + GURL url; + if (!IPC::ParamTraits<GURL>::Read(&msg, &iter, &url)) + return; + + ThumbnailScore score; + if (!IPC::ParamTraits<ThumbnailScore>::Read(&msg, &iter, &score)) + return; + + // thumbnail data + SkBitmap bitmap; + if (!IPC::ParamTraits<SkBitmap>::Read(&msg, &iter, &bitmap)) + return; + + delegate_->UpdateThumbnail(url, bitmap, score); +} + +void RenderViewHost::OnMsgClose() { + delegate_->Close(this); +} + +void RenderViewHost::OnMsgRequestMove(const gfx::Rect& pos) { + delegate_->RequestMove(pos); +} + +void RenderViewHost::OnMsgDidRedirectProvisionalLoad(int32 page_id, + const GURL& source_url, + const GURL& target_url) { + delegate_->DidRedirectProvisionalLoad(page_id, source_url, target_url); +} + +void RenderViewHost::OnMsgDidStartLoading(int32 page_id) { + delegate_->DidStartLoading(this, page_id); + + if (view_) { + view_->UpdateCursorIfOverSelf(); + } +} + +void RenderViewHost::OnMsgDidStopLoading(int32 page_id) { + delegate_->DidStopLoading(this, page_id); + + if (view_) { + view_->UpdateCursorIfOverSelf(); + } +} + +void RenderViewHost::OnMsgDidLoadResourceFromMemoryCache( + const GURL& url, + const std::string& security_info) { + delegate_->DidLoadResourceFromMemoryCache(url, security_info); +} + +void RenderViewHost::OnMsgDidStartProvisionalLoadForFrame(bool is_main_frame, + const GURL& url) { + GURL validated_url(url); + FilterURL(RendererSecurityPolicy::GetInstance(), + process()->host_id(), &validated_url); + + delegate_->DidStartProvisionalLoadForFrame(this, is_main_frame, validated_url); +} + +void RenderViewHost::OnMsgDidFailProvisionalLoadWithError( + bool is_main_frame, + int error_code, + const GURL& url, + bool showing_repost_interstitial) { + GURL validated_url(url); + FilterURL(RendererSecurityPolicy::GetInstance(), + process()->host_id(), &validated_url); + + delegate_->DidFailProvisionalLoadWithError(this, is_main_frame, + error_code, validated_url, + showing_repost_interstitial); +} + +void RenderViewHost::OnMsgFindReply(int request_id, + int number_of_matches, + const gfx::Rect& selection_rect, + int active_match_ordinal, + bool final_update) { + RenderViewHostDelegate::View* view = delegate_->GetViewDelegate(); + if (!view) + return; + view->OnFindReply(request_id, number_of_matches, selection_rect, + active_match_ordinal, final_update); + + // Send a notification to the renderer that we are ready to receive more + // results from the scoping effort of the Find operation. The FindInPage + // scoping is asynchronous and periodically sends results back up to the + // browser using IPC. In an effort to not spam the browser we have the + // browser send an ACK for each FindReply message and have the renderer + // queue up the latest status message while waiting for this ACK. + Send(new ViewMsg_FindReplyACK(routing_id_)); +} + +void RenderViewHost::OnMsgUpdateFavIconURL(int32 page_id, + const GURL& icon_url) { + delegate_->UpdateFavIconURL(this, page_id, icon_url); +} + +void RenderViewHost::OnMsgDidDownloadImage( + int id, + const GURL& image_url, + bool errored, + const SkBitmap& image) { + delegate_->DidDownloadImage(this, id, image_url, errored, image); +} + +void RenderViewHost::OnMsgContextMenu( + const ViewHostMsg_ContextMenu_Params& params) { + RenderViewHostDelegate::View* view = delegate_->GetViewDelegate(); + if (!view) + return; + + // Validate the URLs in |params|. If the renderer can't request the URLs + // directly, don't show them in the context menu. + ViewHostMsg_ContextMenu_Params validated_params(params); + const int renderer_id = process()->host_id(); + RendererSecurityPolicy* policy = RendererSecurityPolicy::GetInstance(); + + FilterURL(policy, renderer_id, &validated_params.link_url); + FilterURL(policy, renderer_id, &validated_params.image_url); + FilterURL(policy, renderer_id, &validated_params.page_url); + FilterURL(policy, renderer_id, &validated_params.frame_url); + + view->ShowContextMenu(validated_params); +} + +void RenderViewHost::OnMsgOpenURL(const GURL& url, + const GURL& referrer, + WindowOpenDisposition disposition) { + GURL validated_url(url); + FilterURL(RendererSecurityPolicy::GetInstance(), + process()->host_id(), &validated_url); + + delegate_->RequestOpenURL(validated_url, referrer, disposition); +} + +void RenderViewHost::OnMsgDomOperationResponse( + const std::string& json_string, int automation_id) { + delegate_->DomOperationResponse(json_string, automation_id); +} + +void RenderViewHost::OnMsgDOMUISend( + const std::string& message, const std::string& content) { + if (!RendererSecurityPolicy::GetInstance()-> + HasDOMUIBindings(process()->host_id())) { + NOTREACHED() << "Blocked unauthorized use of DOMUIBindings."; + return; + } + delegate_->ProcessDOMUIMessage(message, content); +} + +void RenderViewHost::OnMsgForwardMessageToExternalHost( + const std::string& receiver, + const std::string& message) { + delegate_->ProcessExternalHostMessage(receiver, message); +} + +#ifdef CHROME_PERSONALIZATION +void RenderViewHost::OnPersonalizationEvent(const std::string& message, + const std::string& content) { + Personalization::HandlePersonalizationEvent(this, message, content); +} +#endif + +void RenderViewHost::DisassociateFromPopupCount() { + Send(new ViewMsg_DisassociateFromPopupCount(routing_id_)); +} + +void RenderViewHost::PopupNotificationVisibilityChanged(bool visible) { + Send(new ViewMsg_PopupNotificationVisiblityChanged(routing_id_, visible)); +} + +void RenderViewHost::OnMsgGoToEntryAtOffset(int offset) { + delegate_->GoToEntryAtOffset(offset); +} + +void RenderViewHost::OnMsgSetTooltipText(const std::wstring& tooltip_text) { + if (view_) { + view_->SetTooltipText(tooltip_text); + } +} + +void RenderViewHost::OnMsgRunFileChooser(bool multiple_files, + const std::wstring& title, + const std::wstring& default_file, + const std::wstring& filter) { + std::wstring real_filter = filter; + std::replace(real_filter.begin(), real_filter.end(), '|', '\0'); + delegate_->RunFileChooser(multiple_files, title, default_file, real_filter); +} + +void RenderViewHost::OnMsgRunJavaScriptMessage( + const std::wstring& message, + const std::wstring& default_prompt, + const int flags, + IPC::Message* reply_msg) { + StopHangMonitorTimeout(); + if (modal_dialog_count_++ == 0) + modal_dialog_event_->Signal(); + bool did_suppress_message = false; + delegate_->RunJavaScriptMessage(message, default_prompt, flags, reply_msg, + &are_javascript_messages_suppressed_); +} + +void RenderViewHost::OnMsgRunBeforeUnloadConfirm(const std::wstring& message, + IPC::Message* reply_msg) { + StopHangMonitorTimeout(); + if (modal_dialog_count_++ == 0) + modal_dialog_event_->Signal(); + delegate_->RunBeforeUnloadConfirm(message, reply_msg); +} + +void RenderViewHost::OnMsgShowModalHTMLDialog( + const GURL& url, int width, int height, const std::string& json_arguments, + IPC::Message* reply_msg) { + StopHangMonitorTimeout(); + if (modal_dialog_count_++ == 0) + modal_dialog_event_->Signal(); + delegate_->ShowModalHTMLDialog(url, width, height, json_arguments, reply_msg); +} + +void RenderViewHost::OnMsgPasswordFormsSeen( + const std::vector<PasswordForm>& forms) { + delegate_->PasswordFormsSeen(forms); +} + +void RenderViewHost::OnMsgAutofillFormSubmitted( + const AutofillForm& form) { + delegate_->AutofillFormSubmitted(form); +} + +void RenderViewHost::OnMsgStartDragging( + const WebDropData& drop_data) { + RenderViewHostDelegate::View* view = delegate_->GetViewDelegate(); + if (view) + view->StartDragging(drop_data); +} + +void RenderViewHost::OnUpdateDragCursor(bool is_drop_target) { + RenderViewHostDelegate::View* view = delegate_->GetViewDelegate(); + if (view) + view->UpdateDragCursor(is_drop_target); +} + +void RenderViewHost::OnTakeFocus(bool reverse) { + RenderViewHostDelegate::View* view = delegate_->GetViewDelegate(); + if (view) + view->TakeFocus(reverse); +} + +void RenderViewHost::OnMsgPageHasOSDD(int32 page_id, const GURL& doc_url, + bool autodetected) { + delegate_->PageHasOSDD(this, page_id, doc_url, autodetected); +} + +void RenderViewHost::OnMsgInspectElementReply(int num_resources) { + delegate_->InspectElementReply(num_resources); +} + +void RenderViewHost::DidPrintPage( + const ViewHostMsg_DidPrintPage_Params& params) { + delegate_->DidPrintPage(params); +} + +void RenderViewHost::OnAddMessageToConsole(const std::wstring& message, + int32 line_no, + const std::wstring& source_id) { + std::wstring msg = StringPrintf(L"\"%ls,\" source: %ls (%d)", message.c_str(), + source_id.c_str(), line_no); + logging::LogMessage("CONSOLE", 0).stream() << msg; + if (debugger_attached_) + g_browser_process->debugger_wrapper()->DebugMessage(msg); +} + +void RenderViewHost::OnDebuggerOutput(const std::wstring& output) { + if (debugger_attached_) + g_browser_process->debugger_wrapper()->DebugMessage(output); +} + +void RenderViewHost::DidDebugAttach() { + if (!debugger_attached_) { + debugger_attached_ = true; + g_browser_process->debugger_wrapper()->OnDebugAttach(); + } +} + +void RenderViewHost::OnUserMetricsRecordAction(const std::wstring& action) { + UserMetrics::RecordComputedAction(action.c_str(), process_->profile()); +} + +void RenderViewHost::UnhandledInputEvent(const WebInputEvent& event) { + RenderViewHostDelegate::View* view = delegate_->GetViewDelegate(); + if (view) { + // TODO(brettw) why do we have to filter these types of events here. Can't + // the renderer just send us the ones we care abount, or maybe the view + // should be able to decide which ones it wants or not? + if ((event.type == WebInputEvent::KEY_DOWN) || + (event.type == WebInputEvent::CHAR)) { + view->HandleKeyboardEvent( + static_cast<const WebKeyboardEvent&>(event)); + } + } +} + +void RenderViewHost::ForwardKeyboardEvent(const WebKeyboardEvent& key_event) { + if (key_event.type == WebKeyboardEvent::CHAR && + (key_event.key_code == VK_RETURN || key_event.key_code == VK_SPACE)) { + delegate_->OnEnterOrSpace(); + } + RenderWidgetHost::ForwardKeyboardEvent(key_event); +} + +void RenderViewHost::OnMissingPluginStatus(int status) { + delegate_->OnMissingPluginStatus(status); +} + +void RenderViewHost::UpdateBackForwardListCount() { + int back_list_count, forward_list_count; + delegate_->GetHistoryListCount(&back_list_count, &forward_list_count); + Send(new ViewMsg_UpdateBackForwardListCount( + routing_id_, back_list_count, forward_list_count)); +} + +void RenderViewHost::GetAllSavableResourceLinksForCurrentPage( + const GURL& page_url) { + Send(new ViewMsg_GetAllSavableResourceLinksForCurrentPage(routing_id_, + page_url)); +} + +void RenderViewHost::OnReceivedSavableResourceLinksForCurrentPage( + const std::vector<GURL>& resources_list, + const std::vector<GURL>& referrers_list, + const std::vector<GURL>& frames_list) { + RenderViewHostDelegate::Save* save_delegate = delegate_->GetSaveDelegate(); + if (save_delegate) { + save_delegate->OnReceivedSavableResourceLinksForCurrentPage( + resources_list, referrers_list, frames_list); + } +} + +void RenderViewHost::OnDidGetApplicationInfo( + int32 page_id, + const webkit_glue::WebApplicationInfo& info) { + delegate_->OnDidGetApplicationInfo(page_id, info); +} + +void RenderViewHost::GetSerializedHtmlDataForCurrentPageWithLocalLinks( + const std::vector<std::wstring>& links, + const std::vector<std::wstring>& local_paths, + const std::wstring& local_directory_name) { + Send(new ViewMsg_GetSerializedHtmlDataForCurrentPageWithLocalLinks( + routing_id_, links, local_paths, local_directory_name)); +} + +void RenderViewHost::OnReceivedSerializedHtmlData(const GURL& frame_url, + const std::string& data, + int32 status) { + RenderViewHostDelegate::Save* save_delegate = delegate_->GetSaveDelegate(); + if (save_delegate) + save_delegate->OnReceivedSerializedHtmlData(frame_url, data, status); +} + +void RenderViewHost::OnMsgShouldCloseACK(bool proceed) { + StopHangMonitorTimeout(); + DCHECK(is_waiting_for_unload_ack_); + is_waiting_for_unload_ack_ = false; + delegate_->ShouldClosePage(proceed); +} + +void RenderViewHost::OnUnloadListenerChanged(bool has_listener) { + has_unload_listener_ = has_listener; +} + +void RenderViewHost::OnQueryFormFieldAutofill(const std::wstring& field_name, + const std::wstring& user_text, + int64 node_id, + int request_id) { + delegate_->GetAutofillSuggestions(field_name, user_text, node_id, request_id); +} + +void RenderViewHost::AutofillSuggestionsReturned( + const std::vector<std::wstring>& suggestions, + int64 node_id, int request_id, int default_suggestion_index) { + Send(new ViewMsg_AutofillSuggestions(routing_id_, node_id, + request_id, suggestions, -1)); + // Default index -1 means no default suggestion. +} + +void RenderViewHost::NotifyRendererUnresponsive() { + // If the debugger is attached, we're going to be unresponsive anytime it's + // stopped at a breakpoint. + if (!debugger_attached_) { + delegate_->RendererUnresponsive(this, is_waiting_for_unload_ack_); + } +} + +void RenderViewHost::NotifyRendererResponsive() { + delegate_->RendererResponsive(this); +} + +void RenderViewHost::OnDebugDisconnect() { + if (debugger_attached_) { + debugger_attached_ = false; + g_browser_process->debugger_wrapper()->OnDebugDisconnect(); + } +} + +#ifdef CHROME_PERSONALIZATION +void RenderViewHost::RaisePersonalizationEvent(std::string event_name, + std::string event_arg) { + Send(new ViewMsg_PersonalizationEvent(routing_id_, + event_name, + event_arg)); +} +#endif + +void RenderViewHost::ForwardMessageFromExternalHost( + const std::string& target, const std::string& message) { + Send(new ViewMsg_HandleMessageFromExternalHost(routing_id_, target, message)); +} diff --git a/chrome/browser/renderer_host/render_view_host.h b/chrome/browser/renderer_host/render_view_host.h new file mode 100644 index 0000000..4ae497f --- /dev/null +++ b/chrome/browser/renderer_host/render_view_host.h @@ -0,0 +1,628 @@ +// 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. + +#ifndef CHROME_BROWSER_RENDER_VIEW_HOST_H__ +#define CHROME_BROWSER_RENDER_VIEW_HOST_H__ + +#include <string> +#include <vector> + +#include "base/scoped_ptr.h" +#include "chrome/browser/renderer_host/render_view_host_delegate.h" +#include "chrome/browser/renderer_host/render_widget_host.h" +#include "chrome/common/page_zoom.h" +#ifdef CHROME_PERSONALIZATION +#include "chrome/personalization/personalization.h" +#endif +#include "webkit/glue/password_form_dom_manager.h" +#include "webkit/glue/autofill_form.h" + +enum ConsoleMessageLevel; +class NavigationEntry; +class RenderViewHostDelegate; +class SiteInstance; +class SkBitmap; +struct ViewHostMsg_ContextMenu_Params; +struct ViewHostMsg_DidPrintPage_Params; +class ViewMsg_Navigate; +struct ViewMsg_Navigate_Params; +struct ViewMsg_Print_Params; +struct ViewMsg_PrintPages_Params; +struct WebDropData; +struct WebPreferences; +enum WindowOpenDisposition; + +namespace base { +class WaitableEvent; +} + +namespace gfx { +class Point; +} + +namespace net { +enum LoadState; +} + +namespace webkit_glue { +struct WebApplicationInfo; +} + +// +// RenderViewHost +// +// A RenderViewHost is responsible for creating and talking to a RenderView +// object in a child process. It exposes a high level API to users, for things +// like loading pages, adjusting the display and other browser functionality, +// which it translates into IPC messages sent over the IPC channel with the +// RenderView. It responds to all IPC messages sent by that RenderView and +// cracks them, calling a delegate object back with higher level types where +// possible. +// +// The intent of this class is to provide a view-agnostic communication +// conduit with a renderer. This is so we can build HTML views not only as +// TabContents (see WebContents for an example) but also as views, etc. +// +// The exact API of this object needs to be more thoroughly designed. Right +// now it mimics what WebContents exposed, which is a fairly large API and may +// contain things that are not relevant to a common subset of views. See also +// the comment in render_view_host_delegate.h about the size and scope of the +// delegate API. +// +// Right now, the concept of page navigation (both top level and frame) exists +// in the WebContents still, so if you instantiate one of these elsewhere, you +// will not be able to traverse pages back and forward. We need to determine +// if we want to bring that and other functionality down into this object so +// it can be shared by others. +// +class RenderViewHost : public RenderWidgetHost { + public: + // Returns the RenderViewHost given its ID and the ID of its render process. + // Returns NULL if the IDs do not correspond to a live RenderViewHost. + static RenderViewHost* FromID(int render_process_id, int render_view_id); + + // routing_id could be a valid route id, or it could be MSG_ROUTING_NONE, in + // which case RenderWidgetHost will create a new one. modal_dialog_event is + // the event that's set when showing a modal dialog so that the renderer and + // plugin processes know to pump messages. An existing event can be passed + // in, otherwise if it's NULL a new event will be created. + explicit RenderViewHost(SiteInstance* instance, + RenderViewHostDelegate* delegate, + int routing_id, + base::WaitableEvent* modal_dialog_event); + virtual ~RenderViewHost(); + + SiteInstance* site_instance() const { return instance_; } + RenderViewHostDelegate* delegate() const { return delegate_; } + + // Set up the RenderView child process. + virtual bool CreateRenderView(); + // Returns true if the RenderView is active and has not crashed. + virtual bool IsRenderViewLive() const; + // Create a new RenderViewHost but recycle an existing RenderView child + // process. + virtual void Init(); + + // Load the specified entry, optionally reloading. + virtual void NavigateToEntry(const NavigationEntry& entry, bool is_reload); + + // Load the specified URL. + void NavigateToURL(const GURL& url); + + // Loads the specified html (must be UTF8) in the main frame. If + // |new_navigation| is true, it simulates a navigation to |display_url|. + // |security_info| is the security state that will be reported when the page + // load commits. It is useful for mocking SSL errors. Provide an empty + // string if no secure connection state should be simulated. + // Note that if |new_navigation| is false, |display_url| and |security_info| + // are not used. + virtual void LoadAlternateHTMLString(const std::string& html_text, + bool new_navigation, + const GURL& display_url, + const std::string& security_info); + + // Suspends (or unsuspends) any navigation messages from being sent from this + // RenderViewHost. This is called when a pending RenderViewHost is created + // for a cross-site navigation, because we must suspend any navigations until + // we hear back from the old renderer's onbeforeunload handler. Note that it + // is important that only one navigation event happen after calling this + // method with |suspend| equal to true. If |suspend| is false and there is + // a suspended_nav_message_, this will send the message. + void SetNavigationsSuspended(bool suspend); + + // Causes the renderer to invoke the onbeforeunload event handler. The + // result will be returned via ViewMsg_ShouldClose. + virtual void FirePageBeforeUnload(); + + // Close the page after the page has responded that it can be closed via + // ViewMsg_ShouldClose. This is where the page itself is closed. The + // unload handler is triggered here, which can block with a dialog, but cannot + // cancel the close of the page. + void FirePageUnload(); + + // Close the page ignoring whether it has unload events registers. + // This is called after the beforeunload and unload events have fired + // and the user has agreed to continue with closing the page. + static void ClosePageIgnoringUnloadEvents(int render_process_host_id, + int request_id); + + // Causes the renderer to close the current page, including running its + // onunload event handler. A ClosePage_ACK message will be sent to the + // ResourceDispatcherHost when it is finished. |new_render_process_host_id| + // and |new_request_id| will help the ResourceDispatcherHost identify which + // response is associated with this event. + virtual void ClosePage(int new_render_process_host_id, + int new_request_id); + + // Sets whether this RenderViewHost has an outstanding cross-site request, + // for which another renderer will need to run an onunload event handler. + // This is called before the first navigation event for this RenderViewHost, + // and again after the corresponding OnCrossSiteResponse. + void SetHasPendingCrossSiteRequest(bool has_pending_request, int request_id); + + // Returns the request_id for the pending cross-site request. + // This is just needed in case the unload of the current page + // hangs, in which case we need to swap to the pending RenderViewHost. + int GetPendingRequestId(); + + // Called by ResourceDispatcherHost when a response for a pending cross-site + // request is received. The ResourceDispatcherHost will pause the response + // until the onunload handler of the previous renderer is run. + void OnCrossSiteResponse(int new_render_process_host_id, int new_request_id); + + // Stops the current load. + void Stop(); + + + // Retrieves the number of printed pages that would result for the current web + // page and the specified settings. The response is a + // ViewHostMsg_DidGetPrintedPagesCount. + bool GetPrintedPagesCount(const ViewMsg_Print_Params& params); + + // Asks the renderer to "render" printed pages. + bool PrintPages(const ViewMsg_PrintPages_Params& params); + + // Start looking for a string within the content of the page, with the + // specified options. + void StartFinding(int request_id, + const std::wstring& search_string, + bool forward, + bool match_case, + bool find_next); + + // Cancel a pending find operation. If |clear_selection| is true, it will also + // clear the selection on the focused frame. + void StopFinding(bool clear_selection); + + // Change the zoom level of a page. + void Zoom(PageZoom::Function function); + + // Change the encoding of the page. + void SetPageEncoding(const std::wstring& encoding); + + // Change the alternate error page URL. An empty GURL disables the use of + // alternate error pages. + void SetAlternateErrorPageURL(const GURL& url); + + // Fill out a form within the page with the specified data. + void FillForm(const FormData& form_data); + + // Fill out a password form and trigger DOM autocomplete in the case + // of multiple matching logins. + void FillPasswordForm(const PasswordFormDomManager::FillData& form_data); + + // D&d drop target messages that get sent to WebKit. + void DragTargetDragEnter(const WebDropData& drop_data, + const gfx::Point& client_pt, + const gfx::Point& screen_pt); + void DragTargetDragOver(const gfx::Point& client_pt, + const gfx::Point& screen_pt); + void DragTargetDragLeave(); + void DragTargetDrop(const gfx::Point& client_pt, + const gfx::Point& screen_pt); + + // Tell the RenderView to reserve a range of page ids of the given size. + void ReservePageIDRange(int size); + + // Runs some javascript within the context of a frame in the page. + void ExecuteJavascriptInWebFrame(const std::wstring& frame_xpath, + const std::wstring& jscript); + + // Logs a message to the console of a frame in the page. + void AddMessageToConsole(const std::wstring& frame_xpath, + const std::wstring& msg, + ConsoleMessageLevel level); + + // Send command to the debugger + void DebugCommand(const std::wstring& cmd); + + // Attach to the V8 instance for debugging + void DebugAttach(); + + // Detach from the V8 instance for debugging + void DebugDetach(); + + // Cause the V8 debugger to trigger a debug break. If the force flag is set + // force a debug break even if no JS code is running (this actually causes a + // simple JS script to be executed). + void DebugBreak(bool force); + + // Edit operations. + void Undo(); + void Redo(); + void Cut(); + void Copy(); + void Paste(); + void Replace(const std::wstring& text); + void ToggleSpellCheck(); + void AddToDictionary(const std::wstring& word); + void Delete(); + void SelectAll(); + + // Downloads an image notifying the delegate appropriately. The returned + // integer uniquely identifies the download for the lifetime of the browser. + int DownloadImage(const GURL& url, int image_size); + + // Requests application info for the specified page. This is an asynchronous + // request. The delegate is notified by way of OnDidGetApplicationInfo when + // the data is available. + void GetApplicationInfo(int32 page_id); + + // Captures a thumbnail representation of the page. + void CaptureThumbnail(); + + // Notifies the RenderView that the JavaScript message that was shown was + // closed by the user. + void JavaScriptMessageBoxClosed(IPC::Message* reply_msg, + bool success, + const std::wstring& prompt); + + // Notifies the RenderView that the modal html dialog has been closed. + void ModalHTMLDialogClosed(IPC::Message* reply_msg, + const std::string& json_retval); + + // Copies the image at the specified point. + void CopyImageAt(int x, int y); + + // Inspects the element at the specified point using the Web Inspector. + void InspectElementAt(int x, int y); + + // Show the JavaScript console. + void ShowJavaScriptConsole(); + + // Notifies the renderer that a drop occurred. This is necessary because the + // render may be the one that started the drag. + void DragSourceEndedAt( + int client_x, int client_y, int screen_x, int screen_y); + + // Notifies the renderer that a drag and drop operation is in progress, with + // droppable items positioned over the renderer's view. + void DragSourceMovedTo( + int client_x, int client_y, int screen_x, int screen_y); + + // Notifies the renderer that we're done with the drag and drop operation. + // This allows the renderer to reset some state. + void DragSourceSystemDragEnded(); + + // Tell the render view to expose DOM automation bindings so that the js + // content can send JSON-encoded data back to automation in the parent + // process. + void AllowDomAutomationBindings(); + + // Tell the render view to allow the javascript access to + // the external host via automation. + void AllowExternalHostBindings(); + + // Tell the render view to expose DOM bindings so that the JS content + // can send JSON-encoded data back to the browser process. + // This is used for HTML-based UI. + // Must be called before CreateRenderView(). + void AllowDOMUIBindings(); + + // Sets a property with the given name and value on the DOM UI binding object. + // Must call AllowDOMUIBindings() on this renderer first. + void SetDOMUIProperty(const std::string& name, const std::string& value); + + // Fill in a ViewMsg_Navigate_Params struct from a NavigationEntry. + static void MakeNavigateParams(const NavigationEntry& entry, + bool reload, + ViewMsg_Navigate_Params* params); + + // Overridden from RenderWidgetHost: We are hosting a web page. + virtual bool IsRenderView() { return true; } + virtual bool CanBlur() const; + + // IPC::Channel::Listener + virtual void OnMessageReceived(const IPC::Message& msg); + + // Override the RenderWidgetHost's Shutdown method. + virtual void Shutdown(); + + // Tells the renderer view to focus the first (last if reverse is true) node. + void SetInitialFocus(bool reverse); + + // Update render view specific (WebKit) preferences. + void UpdateWebPreferences(const WebPreferences& prefs); + + // Request the Renderer to ask the default plugin to start installation of + // missing plugin. Called by PluginInstaller. + void InstallMissingPlugin(); + + // Get all savable resource links from current webpage, include main + // frame and sub-frame. + void GetAllSavableResourceLinksForCurrentPage(const GURL& page_url); + + // Get html data by serializing all frames of current page with lists + // which contain all resource links that have local copy. + // The parameter links contain original URLs of all saved links. + // The parameter local_paths contain corresponding local file paths of + // all saved links, which matched with vector:links one by one. + // The parameter local_directory_name is relative path of directory which + // contain all saved auxiliary files included all sub frames and resouces. + void GetSerializedHtmlDataForCurrentPageWithLocalLinks( + const std::vector<std::wstring>& links, + const std::vector<std::wstring>& local_paths, + const std::wstring& local_directory_name); + + // Notifies the RenderViewHost that a file has been chosen by the user from + // an Open File dialog for the form. + void FileSelected(const std::wstring& path); + + // Notifies the Listener that many files have been chosen by the user from + // an Open File dialog for the form. + void MultiFilesSelected(const std::vector<std::wstring>& files); + + // Notifies the RenderViewHost that its load state changed. + void LoadStateChanged(const GURL& url, net::LoadState load_state); + + // Does the associated view have an onunload or onbeforeunload handler? + bool HasUnloadListener() { return has_unload_listener_; } + + // If the associated view can be terminated without any side effects + bool CanTerminate() const; + + // Clears the has_unload_listener_ bit since the unload handler has fired + // and we're necessarily leaving the page. + void UnloadListenerHasFired() { has_unload_listener_ = false; } + +#ifdef CHROME_PERSONALIZATION + // Tells the RenderView to raise an personalization event with the given name + // and argument. + void RaisePersonalizationEvent(std::string event_name, std::string event_arg); + + HostPersonalization personalization() { + return personalization_; + } +#endif + + // Forward a message from external host to chrome renderer. + void ForwardMessageFromExternalHost(const std::string& target, + const std::string& message); + + // Message the renderer that we should be counted as a new document and not + // as a popup. + void DisassociateFromPopupCount(); + + // Notifies the Renderer that we've either displayed or hidden the popup + // notification. + void PopupNotificationVisibilityChanged(bool visible); + + // Called by the AutofillManager when the list of suggestions is ready. + void AutofillSuggestionsReturned(const std::vector<std::wstring>& suggestions, + int64 node_id, + int request_id, + int default_suggestion_index); + + protected: + // Overridden from RenderWidgetHost: + virtual void UnhandledInputEvent(const WebInputEvent& event); + virtual void ForwardKeyboardEvent(const WebKeyboardEvent& key_event); + + // IPC message handlers: + void OnMsgCreateWindow(int route_id, HANDLE modal_dialog_event); + void OnMsgCreateWidget(int route_id, bool activatable); + void OnMsgShowView(int route_id, + WindowOpenDisposition disposition, + const gfx::Rect& initial_pos, + bool user_gesture); + void OnMsgShowWidget(int route_id, const gfx::Rect& initial_pos); + void OnMsgRunModal(IPC::Message* reply_msg); + void OnMsgRendererReady(); + void OnMsgRendererGone(); + void OnMsgNavigate(const IPC::Message& msg); + void OnMsgUpdateState(int32 page_id, + const std::string& state); + void OnMsgUpdateTitle(int32 page_id, const std::wstring& title); + void OnMsgUpdateEncoding(const std::wstring& encoding); + void OnMsgUpdateTargetURL(int32 page_id, const GURL& url); + void OnMsgThumbnail(const IPC::Message& msg); + void OnMsgClose(); + void OnMsgRequestMove(const gfx::Rect& pos); + void OnMsgDidRedirectProvisionalLoad(int32 page_id, + const GURL& source_url, + const GURL& target_url); + void OnMsgDidStartLoading(int32 page_id); + void OnMsgDidStopLoading(int32 page_id); + void OnMsgDidLoadResourceFromMemoryCache(const GURL& url, + const std::string& security_info); + void OnMsgDidStartProvisionalLoadForFrame(bool main_frame, + const GURL& url); + void OnMsgDidFailProvisionalLoadWithError(bool main_frame, + int error_code, + const GURL& url, + bool showing_repost_interstitial); + void OnMsgFindReply(int request_id, + int number_of_matches, + const gfx::Rect& selection_rect, + int active_match_ordinal, + bool final_update); + void OnMsgUpdateFavIconURL(int32 page_id, const GURL& icon_url); + void OnMsgDidDownloadImage(int id, + const GURL& image_url, + bool errored, + const SkBitmap& image_data); + void OnMsgContextMenu(const ViewHostMsg_ContextMenu_Params& params); + void OnMsgOpenURL(const GURL& url, const GURL& referrer, + WindowOpenDisposition disposition); + void OnMsgDomOperationResponse(const std::string& json_string, + int automation_id); + void OnMsgDOMUISend(const std::string& message, + const std::string& content); + void OnMsgForwardMessageToExternalHost(const std::string& receiver, + const std::string& message); +#ifdef CHROME_PERSONALIZATION + void OnPersonalizationEvent(const std::string& message, + const std::string& content); +#endif + void OnMsgGoToEntryAtOffset(int offset); + void OnMsgSetTooltipText(const std::wstring& tooltip_text); + void OnMsgRunFileChooser(bool multiple_files, + const std::wstring& title, + const std::wstring& default_file, + const std::wstring& filter); + void OnMsgRunJavaScriptMessage(const std::wstring& message, + const std::wstring& default_prompt, + const int flags, + IPC::Message* reply_msg); + void OnMsgRunBeforeUnloadConfirm(const std::wstring& message, + IPC::Message* reply_msg); + void OnMsgShowModalHTMLDialog(const GURL& url, int width, int height, + const std::string& json_arguments, + IPC::Message* reply_msg); + void OnMsgPasswordFormsSeen(const std::vector<PasswordForm>& forms); + void OnMsgAutofillFormSubmitted(const AutofillForm& forms); + void OnMsgStartDragging(const WebDropData& drop_data); + void OnUpdateDragCursor(bool is_drop_target); + void OnTakeFocus(bool reverse); + void OnMsgPageHasOSDD(int32 page_id, const GURL& doc_url, bool autodetected); + void OnMsgInspectElementReply(int num_resources); + void DidPrintPage(const ViewHostMsg_DidPrintPage_Params& params); + void OnDebugMessage(const std::string& message); + void OnAddMessageToConsole(const std::wstring& message, + int32 line_no, + const std::wstring& source_id); + void OnDebuggerOutput(const std::wstring& output); + void DidDebugAttach(); + void OnUserMetricsRecordAction(const std::wstring& action); + void OnMissingPluginStatus(int status); + void OnMessageReceived(IPC::Message* msg) { } + + void OnReceivedSavableResourceLinksForCurrentPage( + const std::vector<GURL>& resources_list, + const std::vector<GURL>& referrers_list, + const std::vector<GURL>& frames_list); + + void OnReceivedSerializedHtmlData(const GURL& frame_url, + const std::string& data, + int32 status); + + void OnDidGetApplicationInfo(int32 page_id, + const webkit_glue::WebApplicationInfo& info); + void OnMsgShouldCloseACK(bool proceed); + void OnUnloadListenerChanged(bool has_handler); + void OnQueryFormFieldAutofill(const std::wstring& field_name, + const std::wstring& user_text, + int64 node_id, + int request_id); + virtual void NotifyRendererUnresponsive(); + virtual void NotifyRendererResponsive(); + + // Helper function to send a navigation message. If a cross-site request is + // in progress, we may be suspended while waiting for the onbeforeunload + // handler, so this function might buffer the message rather than sending it. + void DoNavigate(ViewMsg_Navigate* nav_message); + + private: + friend class TestRenderViewHost; + + void UpdateBackForwardListCount(); + + void OnDebugDisconnect(); + + // The SiteInstance associated with this RenderViewHost. All pages drawn + // in this RenderViewHost are part of this SiteInstance. Should not change + // over time. + scoped_refptr<SiteInstance> instance_; + + // Our delegate, which wants to know about changes in the RenderView. + RenderViewHostDelegate* delegate_; + +#ifdef CHROME_PERSONALIZATION + HostPersonalization personalization_; +#endif + + // true if a renderer has once been valid. We use this flag to display a sad + // tab only when we lose our renderer and not if a paint occurs during + // initialization. + bool renderer_initialized_; + + // true if we are currently waiting for a response for drag context + // information. + bool waiting_for_drag_context_response_; + + // is the debugger attached to us or not + bool debugger_attached_; + + // True if we've been told to set up the the Javascript bindings for + // sending messages back to the browser. + bool enable_dom_ui_bindings_; + + // The request_id for the pending cross-site request. Set to -1 if + // there is a pending request, but we have not yet started the unload + // for the current page. Set to the request_id value of the pending + // request once we have gotten the some data for the pending page + // and thus started the unload process. + int pending_request_id_; + + // True if javascript access to the external host (through + // automation) is allowed. + bool enable_external_host_bindings_; + + // Handle to an event that's set when the page is showing a modal dialog box + // (or equivalent constrained window). The renderer and plugin processes + // check this to know if they should pump messages/tasks then. + scoped_ptr<base::WaitableEvent> modal_dialog_event_; + + // Multiple dialog boxes can be shown before the first one is finished, + // so we keep a counter to know when we can reset the modal dialog event. + int modal_dialog_count_; + + // Whether we should buffer outgoing Navigate messages rather than sending + // them. This will be true when a RenderViewHost is created for a cross-site + // request, until we hear back from the onbeforeunload handler of the old + // RenderViewHost. + bool navigations_suspended_; + + // We only buffer a suspended navigation message while we a pending RVH for a + // WebContents. There will only ever be one suspended navigation, because + // WebContents will destroy the pending RVH and create a new one if a second + // navigation occurs. + scoped_ptr<ViewMsg_Navigate> suspended_nav_message_; + + // If we were asked to RunModal, then this will hold the reply_msg that we + // must return to the renderer to unblock it. + IPC::Message* run_modal_reply_msg_; + + bool has_unload_listener_; + + bool is_waiting_for_unload_ack_; + + bool are_javascript_messages_suppressed_; + + DISALLOW_EVIL_CONSTRUCTORS(RenderViewHost); +}; + +// Factory for creating RenderViewHosts. Useful for unit tests. +class RenderViewHostFactory { + public: + virtual ~RenderViewHostFactory() {} + + virtual RenderViewHost* CreateRenderViewHost( + SiteInstance* instance, + RenderViewHostDelegate* delegate, + int routing_id, + base::WaitableEvent* modal_dialog_event) = 0; +}; + +#endif // CHROME_BROWSER_RENDER_VIEW_HOST_H__ diff --git a/chrome/browser/renderer_host/render_view_host_delegate.h b/chrome/browser/renderer_host/render_view_host_delegate.h new file mode 100644 index 0000000..764e8ca --- /dev/null +++ b/chrome/browser/renderer_host/render_view_host_delegate.h @@ -0,0 +1,396 @@ +// 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. + +#ifndef CHROME_BROWSER_RENDERER_HOST_RENDER_VIEW_HOST_DELEGATE_H_ +#define CHROME_BROWSER_RENDERER_HOST_RENDER_VIEW_HOST_DELEGATE_H_ + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "chrome/browser/autofill_manager.h" +#include "chrome/common/render_messages.h" +#include "webkit/glue/webpreferences.h" + +class NavigationEntry; +class Profile; +class RenderProcessHost; +class RenderViewHost; +class SkBitmap; +class WebContents; +struct WebDropData; +enum WindowOpenDisposition; + +namespace base { +class WaitableEvent; +} + +namespace IPC { +class Message; +} + +namespace gfx { +class Rect; +} + +namespace net { +enum LoadState; +} + +// +// RenderViewHostDelegate +// +// An interface implemented by an object interested in knowing about the state +// of the RenderViewHost. +// +// This interface currently encompasses every type of message that was +// previously being sent by WebContents itself. Some of these notifications +// may not be relevant to all users of RenderViewHost and we should consider +// exposing a more generic Send function on RenderViewHost and a response +// listener here to serve that need. +// +class RenderViewHostDelegate { + public: + class View { + public: + // The page is trying to open a new page (e.g. a popup window). The + // window should be created associated with the given route, but it should + // not be shown yet. That should happen in response to ShowCreatedWindow. + // + // Note: this is not called "CreateWindow" because that will clash with + // the Windows function which is actually a #define. + // + // NOTE: this takes ownership of @modal_dialog_event + virtual void CreateNewWindow(int route_id, + base::WaitableEvent* modal_dialog_event) = 0; + + // The page is trying to open a new widget (e.g. a select popup). The + // widget should be created associated with the given route, but it should + // not be shown yet. That should happen in response to ShowCreatedWidget. + // If |activatable| is false, the widget cannot be activated or get focus. + virtual void CreateNewWidget(int route_id, bool activatable) = 0; + + // Show a previously created page with the specified disposition and bounds. + // The window is identified by the route_id passed to CreateNewWindow. + // + // Note: this is not called "ShowWindow" because that will clash with + // the Windows function which is actually a #define. + virtual void ShowCreatedWindow(int route_id, + WindowOpenDisposition disposition, + const gfx::Rect& initial_pos, + bool user_gesture) = 0; + + // Show the newly created widget with the specified bounds. + // The widget is identified by the route_id passed to CreateNewWidget. + virtual void ShowCreatedWidget(int route_id, + const gfx::Rect& initial_pos) = 0; + + // A context menu should be shown, to be built using the context information + // provided in the supplied params. + virtual void ShowContextMenu( + const ViewHostMsg_ContextMenu_Params& params) = 0; + + // The user started dragging content of the specified type within the + // RenderView. Contextual information about the dragged content is supplied + // by WebDropData. + virtual void StartDragging(const WebDropData& drop_data) = 0; + + // The page wants to update the mouse cursor during a drag & drop operation. + // |is_drop_target| is true if the mouse is over a valid drop target. + virtual void UpdateDragCursor(bool is_drop_target) = 0; + + // Callback to inform the browser it should take back focus. If reverse is + // true, it means the focus was retrieved by doing a Shift-Tab. + virtual void TakeFocus(bool reverse) = 0; + + // Callback to inform the browser that the renderer did not process the + // specified events. This gives an opportunity to the browser to process the + // event (used for keyboard shortcuts). + virtual void HandleKeyboardEvent(const WebKeyboardEvent& event) = 0; + + // A find operation in the current page completed. + virtual void OnFindReply(int request_id, + int number_of_matches, + const gfx::Rect& selection_rect, + int active_match_ordinal, + bool final_update) = 0; + }; + + // Interface for saving web pages. + class Save { + public: + // Notification that we get when we receive all savable links of + // sub-resources for the current page, their referrers and list of frames + // (include main frame and sub frames). + virtual void OnReceivedSavableResourceLinksForCurrentPage( + const std::vector<GURL>& resources_list, + const std::vector<GURL>& referrers_list, + const std::vector<GURL>& frames_list) = 0; + + // Notification that we get when we receive serialized html content data of + // a specified web page from render process. The parameter frame_url + // specifies what frame the data belongs. The parameter data contains the + // available data for sending. The parameter status indicates the + // serialization status, See + // webkit_glue::DomSerializerDelegate::PageSavingSerializationStatus for + // the detail meaning of status. + virtual void OnReceivedSerializedHtmlData(const GURL& frame_url, + const std::string& data, + int32 status) = 0; + }; + + // Returns the current delegate associated with a feature. May be NULL. + virtual View* GetViewDelegate() const { return NULL; } + virtual Save* GetSaveDelegate() const { return NULL; } + + // Retrieves the profile to be used. + virtual Profile* GetProfile() const = 0; + + // The RenderView is being constructed (message sent to the renderer process + // to construct a RenderView). Now is a good time to send other setup events + // to the RenderView. This precedes any other commands to the RenderView. + virtual void RendererCreated(RenderViewHost* render_view_host) { } + + // The RenderView has been constructed. + virtual void RendererReady(RenderViewHost* render_view_host) { } + + // The RenderView died somehow (crashed or was killed by the user). + virtual void RendererGone(RenderViewHost* render_view_host) { } + + // The RenderView was navigated to a different page. + virtual void DidNavigate(RenderViewHost* render_view_host, + const ViewHostMsg_FrameNavigate_Params& params) { } + + // The state for the page changed and should be updated. + virtual void UpdateState(RenderViewHost* render_view_host, + int32 page_id, + const std::string& state) { } + + // The page's title was changed and should be updated. + virtual void UpdateTitle(RenderViewHost* render_view_host, + int32 page_id, + const std::wstring& title) { } + + // The page's encoding was changed and should be updated. + virtual void UpdateEncoding(RenderViewHost* render_view_host, + const std::wstring& encoding) { } + + // The destination URL has changed should be updated + virtual void UpdateTargetURL(int32 page_id, const GURL& url) { } + + // The thumbnail representation of the page changed and should be updated. + virtual void UpdateThumbnail(const GURL& url, + const SkBitmap& bitmap, + const ThumbnailScore& score) { } + + // The page is trying to close the RenderView's representation in the client. + virtual void Close(RenderViewHost* render_view_host) { } + + // The page is trying to move the RenderView's representation in the client. + virtual void RequestMove(const gfx::Rect& new_bounds) { } + + // The RenderView began loading a new page. + virtual void DidStartLoading(RenderViewHost* render_view_host, + int32 page_id) { } + + // The RenderView stopped loading a page. + virtual void DidStopLoading(RenderViewHost* render_view_host, + int32 page_id) { } + + // The RenderView is starting a provisional load. + virtual void DidStartProvisionalLoadForFrame(RenderViewHost* render_view_host, + bool is_main_frame, + const GURL& url) { } + + // Sent when a provisional load is redirected. + virtual void DidRedirectProvisionalLoad(int32 page_id, + const GURL& source_url, + const GURL& target_url) { } + + // The RenderView loaded a resource from an in-memory cache. + // |security_info| contains the security info if this resource was originally + // loaded over a secure connection. + virtual void DidLoadResourceFromMemoryCache(const GURL& url, + const std::string& security_info) { } + + // The RenderView failed a provisional load with an error. + virtual void DidFailProvisionalLoadWithError( + RenderViewHost* render_view_host, + bool is_main_frame, + int error_code, + const GURL& url, + bool showing_repost_interstitial) { } + + // The URL for the FavIcon of a page has changed. + virtual void UpdateFavIconURL(RenderViewHost* render_view_host, + int32 page_id, const GURL& icon_url) { } + + // An image that was requested to be downloaded by DownloadImage has + // completed. + virtual void DidDownloadImage(RenderViewHost* render_view_host, + int id, + const GURL& image_url, + bool errored, + const SkBitmap& image) { } + + // The page wants to open a URL with the specified disposition. + virtual void RequestOpenURL(const GURL& url, + const GURL& referrer, + WindowOpenDisposition disposition) { } + + // A DOM automation operation completed. The result of the operation is + // expressed in a json string. + virtual void DomOperationResponse(const std::string& json_string, + int automation_id) { } + + // A message was sent from HTML-based UI. + // By default we ignore such messages. + virtual void ProcessDOMUIMessage(const std::string& message, + const std::string& content) { } + + // A message for external host. By default we ignore such messages. + // |receiver| can be a receiving script and |message| is any + // arbitrary string that makes sense to the receiver. + virtual void ProcessExternalHostMessage(const std::string& receiver, + const std::string& message) { } + + // Navigate to the history entry for the given offset from the current + // position within the NavigationController. Makes no change if offset is + // not valid. + virtual void GoToEntryAtOffset(int offset) { } + + // The page requests the size of the back and forward lists + // within the NavigationController. + virtual void GetHistoryListCount(int* back_list_count, + int* forward_list_count) { } + + // A file chooser should be shown. + virtual void RunFileChooser(bool multiple_files, + const std::wstring& title, + const std::wstring& default_file, + const std::wstring& filter) { } + + // A javascript message, confirmation or prompt should be shown. + virtual void RunJavaScriptMessage(const std::wstring& message, + const std::wstring& default_prompt, + const int flags, + IPC::Message* reply_msg, + bool* did_suppress_message) { } + + virtual void RunBeforeUnloadConfirm(const std::wstring& message, + IPC::Message* reply_msg) { } + + // Display this RenderViewHost in a modal fashion. + virtual void RunModal(IPC::Message* reply_msg) { } + + virtual void ShowModalHTMLDialog(const GURL& url, int width, int height, + const std::string& json_arguments, + IPC::Message* reply_msg) { } + + // Password forms have been detected in the page. + virtual void PasswordFormsSeen(const std::vector<PasswordForm>& forms) { } + + // Forms fillable by autofill have been detected in the page. + virtual void AutofillFormSubmitted(const AutofillForm& form) { } + + // Called to retrieve a list of suggestions from the web database given + // the name of the field |field_name| and what the user has already typed in + // the field |user_text|. Appeals to the database thead to perform the query. + // When the database thread is finished, the autofill manager retrieves the + // calling RenderViewHost and then passes the vector of suggestions to + // RenderViewHost::AutofillSuggestionsReturned. + virtual void GetAutofillSuggestions(const std::wstring& field_name, + const std::wstring& user_text, + int64 node_id, + int request_id) { } + + // Notification that the page has an OpenSearch description document. + virtual void PageHasOSDD(RenderViewHost* render_view_host, + int32 page_id, const GURL& doc_url, + bool autodetected) { } + + // Notification that the inspect element window has been opened + virtual void InspectElementReply(int num_resources) { } + + // Notification that the render view has calculated the number of printed + // pages. + virtual void DidGetPrintedPagesCount(int cookie, int number_pages) { + NOTREACHED(); + } + + // Notification that the render view is done rendering one printed page. This + // call is synchronous, the renderer is waiting on us because of the EMF + // memory mapped data. + virtual void DidPrintPage(const ViewHostMsg_DidPrintPage_Params& params) { + NOTREACHED(); + } + + // |url| is assigned to a server that can provide alternate error pages. If + // unchanged, just use the error pages built into our webkit. + virtual GURL GetAlternateErrorPageURL() const { + return GURL(); + } + + // Returns a WebPreferences object that will be used by the renderer + // associated with the owning render view host. + virtual WebPreferences GetWebkitPrefs() { + NOTREACHED(); + return WebPreferences(); + } + + // Notification when default plugin updates status of the missing plugin. + virtual void OnMissingPluginStatus(int status) { } + + // Notification from the renderer that a plugin instance has crashed. + virtual void OnCrashedPlugin(const FilePath& plugin_path) { } + + // Notification from the renderer that JS runs out of memory. + virtual void OnJSOutOfMemory() { } + + // Notification whether we should close the page, after an explicit call to + // AttemptToClosePage. This is called before a cross-site request or before + // a tab/window is closed, to allow the appropriate renderer to approve or + // deny the request. |proceed| indicates whether the user chose to proceed. + virtual void ShouldClosePage(bool proceed) { } + + // Called by ResourceDispatcherHost when a response for a pending cross-site + // request is received. The ResourceDispatcherHost will pause the response + // until the onunload handler of the previous renderer is run. + virtual void OnCrossSiteResponse(int new_render_process_host_id, + int new_request_id) { } + + // Whether this object can be blurred through a javascript + // obj.blur() call. ConstrainedWindows shouldn't be able to be + // blurred. + virtual bool CanBlur() const { return true; } + + // Notification that the renderer has become unresponsive. The + // delegate can use this notification to show a warning to the user. + virtual void RendererUnresponsive(RenderViewHost* render_view_host, + bool is_during_unload) { } + + // Notification that a previously unresponsive renderer has become + // responsive again. The delegate can use this notification to end the + // warning shown to the user. + virtual void RendererResponsive(RenderViewHost* render_view_host) { } + + // Notification that the RenderViewHost's load state changed. + virtual void LoadStateChanged(const GURL& url, net::LoadState load_state) { } + + // Notification that a request for install info has completed. + virtual void OnDidGetApplicationInfo( + int32 page_id, + const webkit_glue::WebApplicationInfo& app_info) { } + + // Notification the user has pressed enter or space while focus was on the + // page. This is used to avoid uninitiated user downloads (aka carpet + // bombing), see DownloadRequestManager for details. + virtual void OnEnterOrSpace() { } + + // If this view can be terminated without any side effects + virtual bool CanTerminate() const { return true; } +}; + +#endif // CHROME_BROWSER_RENDERER_HOST_RENDER_VIEW_HOST_DELEGATE_H_ + diff --git a/chrome/browser/renderer_host/render_widget_helper.cc b/chrome/browser/renderer_host/render_widget_helper.cc new file mode 100644 index 0000000..8d85d92 --- /dev/null +++ b/chrome/browser/renderer_host/render_widget_helper.cc @@ -0,0 +1,235 @@ +// 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 "chrome/browser/renderer_host/render_widget_helper.h" + +#include "base/thread.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/renderer_host/render_process_host.h" +#include "chrome/browser/renderer_host/resource_dispatcher_host.h" + +using base::TimeDelta; +using base::TimeTicks; + +// A Task used with InvokeLater that we hold a pointer to in pending_paints_. +// Instances are deleted by MessageLoop after it calls their Run method. +class RenderWidgetHelper::PaintMsgProxy : public Task { + public: + explicit PaintMsgProxy(RenderWidgetHelper* h, const IPC::Message& m) + : helper(h), + message(m), + cancelled(false) { + } + + ~PaintMsgProxy() { + // If the paint message was never dispatched, then we need to let the + // helper know that we are going away. + if (!cancelled && helper) + helper->OnDiscardPaintMsg(this); + } + + virtual void Run() { + if (!cancelled) { + helper->OnDispatchPaintMsg(this); + helper = NULL; + } + } + + scoped_refptr<RenderWidgetHelper> helper; + IPC::Message message; + bool cancelled; // If true, then the message will not be dispatched. + + DISALLOW_EVIL_CONSTRUCTORS(PaintMsgProxy); +}; + +RenderWidgetHelper::RenderWidgetHelper(int render_process_id) + : render_process_id_(render_process_id), + ui_loop_(MessageLoop::current()), + event_(CreateEvent(NULL, FALSE /* auto-reset */, FALSE, NULL)), + block_popups_(false) { +} + +RenderWidgetHelper::~RenderWidgetHelper() { + // The elements of pending_paints_ each hold an owning reference back to this + // object, so we should not be destroyed unless pending_paints_ is empty! + DCHECK(pending_paints_.empty()); + + CloseHandle(event_); +} + +int RenderWidgetHelper::GetNextRoutingID() { + return next_routing_id_.GetNext() + 1; +} + +void RenderWidgetHelper::CancelResourceRequests(int render_widget_id) { + if (g_browser_process->io_thread()) + g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE, + NewRunnableMethod(this, + &RenderWidgetHelper::OnCancelResourceRequests, + g_browser_process->resource_dispatcher_host(), + render_widget_id)); +} + +void RenderWidgetHelper::CrossSiteClosePageACK(int new_render_process_host_id, + int new_request_id) { + if (g_browser_process->io_thread()) + g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE, + NewRunnableMethod(this, + &RenderWidgetHelper::OnCrossSiteClosePageACK, + g_browser_process->resource_dispatcher_host(), + new_render_process_host_id, + new_request_id)); +} + +bool RenderWidgetHelper::WaitForPaintMsg(int render_widget_id, + const TimeDelta& max_delay, + IPC::Message* msg) { + TimeTicks time_start = TimeTicks::Now(); + + for (;;) { + PaintMsgProxy* proxy = NULL; + { + AutoLock lock(pending_paints_lock_); + + PaintMsgProxyMap::iterator it = pending_paints_.find(render_widget_id); + if (it != pending_paints_.end()) { + proxy = it->second; + + // Flag the proxy as cancelled so that when it is run as a task it will + // do nothing. + proxy->cancelled = true; + + pending_paints_.erase(it); + } + } + + if (proxy) { + *msg = proxy->message; + DCHECK(msg->routing_id() == render_widget_id); + return true; + } + + // Calculate the maximum amount of time that we are willing to sleep. + TimeDelta max_sleep_time = + max_delay - (TimeTicks::Now() - time_start); + if (max_sleep_time <= TimeDelta::FromMilliseconds(0)) + break; + + WaitForSingleObject(event_, + static_cast<DWORD>(max_sleep_time.InMilliseconds())); + } + + return false; +} + +void RenderWidgetHelper::DidReceivePaintMsg(const IPC::Message& msg) { + int render_widget_id = msg.routing_id(); + + PaintMsgProxy* proxy; + { + AutoLock lock(pending_paints_lock_); + + PaintMsgProxyMap::value_type new_value(render_widget_id, NULL); + + // We expect only a single PaintRect message at a time. Optimize for the + // case that we don't already have an entry by using the 'insert' method. + std::pair<PaintMsgProxyMap::iterator, bool> result = + pending_paints_.insert(new_value); + if (!result.second) { + NOTREACHED() << "Unexpected PaintRect message!"; + return; + } + + result.first->second = (proxy = new PaintMsgProxy(this, msg)); + } + + // Notify anyone waiting on the UI thread that there is a new entry in the + // proxy map. If they don't find the entry they are looking for, then they + // will just continue waiting. + SetEvent(event_); + + // The proxy will be deleted when it is run as a task. + ui_loop_->PostTask(FROM_HERE, proxy); +} + +void RenderWidgetHelper::OnDiscardPaintMsg(PaintMsgProxy* proxy) { + const IPC::Message& msg = proxy->message; + + // Remove the proxy from the map now that we are going to handle it normally. + { + AutoLock lock(pending_paints_lock_); + + PaintMsgProxyMap::iterator it = pending_paints_.find(msg.routing_id()); + DCHECK(it != pending_paints_.end()); + DCHECK(it->second == proxy); + + pending_paints_.erase(it); + } +} + +void RenderWidgetHelper::OnDispatchPaintMsg(PaintMsgProxy* proxy) { + OnDiscardPaintMsg(proxy); + + // It is reasonable for the host to no longer exist. + RenderProcessHost* host = RenderProcessHost::FromID(render_process_id_); + if (host) + host->OnMessageReceived(proxy->message); +} + +void RenderWidgetHelper::OnCancelResourceRequests( + ResourceDispatcherHost* dispatcher, + int render_widget_id) { + dispatcher->CancelRequestsForRenderView(render_process_id_, render_widget_id); +} + +void RenderWidgetHelper::OnCrossSiteClosePageACK( + ResourceDispatcherHost* dispatcher, + int new_render_process_host_id, + int new_request_id) { + dispatcher->OnClosePageACK(new_render_process_host_id, new_request_id); +} + +void RenderWidgetHelper::CreateNewWindow(int opener_id, + bool user_gesture, + int* route_id, + HANDLE* modal_dialog_event, + HANDLE render_process) { + if (!user_gesture && block_popups_) { + *route_id = MSG_ROUTING_NONE; + *modal_dialog_event = NULL; + return; + } + + *route_id = GetNextRoutingID(); + HANDLE event = CreateEvent(NULL, TRUE, FALSE, NULL); + BOOL result = DuplicateHandle(GetCurrentProcess(), + event, + render_process, + modal_dialog_event, + SYNCHRONIZE, + FALSE, + 0); + DCHECK(result) << "Couldn't duplicate modal dialog event for the renderer."; + + // The easiest way to reach RenderViewHost is just to send a routed message. + ViewHostMsg_CreateWindowWithRoute msg(opener_id, *route_id, event); + ui_loop_->PostTask(FROM_HERE, NewRunnableMethod( + this, &RenderWidgetHelper::OnSimulateReceivedMessage, msg)); +} + +void RenderWidgetHelper::CreateNewWidget(int opener_id, + bool activatable, + int* route_id) { + *route_id = GetNextRoutingID(); + ViewHostMsg_CreateWidgetWithRoute msg(opener_id, *route_id, activatable); + ui_loop_->PostTask(FROM_HERE, NewRunnableMethod( + this, &RenderWidgetHelper::OnSimulateReceivedMessage, msg)); +} + +void RenderWidgetHelper::OnSimulateReceivedMessage( + const IPC::Message& message) { + RenderProcessHost* host = RenderProcessHost::FromID(render_process_id_); + if (host) + host->OnMessageReceived(message); +} diff --git a/chrome/browser/renderer_host/render_widget_helper.h b/chrome/browser/renderer_host/render_widget_helper.h new file mode 100644 index 0000000..aca3644 --- /dev/null +++ b/chrome/browser/renderer_host/render_widget_helper.h @@ -0,0 +1,159 @@ + +// 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. + +#ifndef CHROME_BROWSER_RENDEDER_HOST_RENDER_WIDGET_HELPER_H_ +#define CHROME_BROWSER_RENDEDER_HOST_RENDER_WIDGET_HELPER_H_ + +#include "base/atomic_sequence_num.h" +#include "base/hash_tables.h" +#include "base/ref_counted.h" +#include "base/lock.h" + +namespace IPC { +class Message; +} + +namespace base { +class TimeDelta; +} + +class MessageLoop; +class ResourceDispatcherHost; + +// Instantiated per RenderProcessHost to provide various optimizations on +// behalf of a RenderWidgetHost. This class bridges between the IO thread +// where the RenderProcessHost's MessageFilter lives and the UI thread where +// the RenderWidgetHost lives. +// +// +// OPTIMIZED RESIZE +// +// RenderWidgetHelper is used to implement optimized resize. When the +// RenderWidgetHost is resized, it sends a Resize message to its RenderWidget +// counterpart in the renderer process. The RenderWidget generates a +// PaintRect message in response to the Resize message, and it sets the +// IS_RESIZE_ACK flag in the PaintRect message to true. +// +// Back in the browser process, when the RenderProcessHost's MessageFilter +// sees a PaintRect message, it directs it to the RenderWidgetHelper by +// calling the DidReceivePaintMsg method. That method stores the data for +// the PaintRect message in a map, where it can be directly accessed by the +// RenderWidgetHost on the UI thread during a call to RenderWidgetHost's +// GetBackingStore method. +// +// When the RenderWidgetHost's GetBackingStore method is called, it first +// checks to see if it is waiting for a resize ack. If it is, then it calls +// the RenderWidgetHelper's WaitForPaintMsg to check if there is already a +// resulting PaintRect message (or to wait a short amount of time for one to +// arrive). The main goal of this mechanism is to short-cut the usual way in +// which IPC messages are proxied over to the UI thread via InvokeLater. +// This approach is necessary since window resize is followed up immediately +// by a request to repaint the window. +// +// +// OPTIMIZED TAB SWITCHING +// +// When a RenderWidgetHost is in a background tab, it is flagged as hidden. +// This causes the corresponding RenderWidget to stop sending PaintRect +// messages. The RenderWidgetHost also discards its backingstore when it is +// hidden, which helps free up memory. As a result, when a RenderWidgetHost +// is restored, it can be momentarily without a backingstore. (Restoring a +// RenderWidgetHost results in a WasRestored message being sent to the +// RenderWidget, which triggers a full PaintRect message.) This can lead to +// an observed rendering glitch as the WebContents will just have to fill +// white overtop the RenderWidgetHost until the RenderWidgetHost receives a +// PaintRect message to refresh its backingstore. +// +// To avoid this 'white flash', the RenderWidgetHost again makes use of the +// RenderWidgetHelper's WaitForPaintMsg method. When the RenderWidgetHost's +// GetBackingStore method is called, it will call WaitForPaintMsg if it has +// no backingstore. +// +class RenderWidgetHelper : + public base::RefCountedThreadSafe<RenderWidgetHelper> { + public: + RenderWidgetHelper(int render_process_id); + ~RenderWidgetHelper(); + + // Gets the next available routing id. This is thread safe. + int GetNextRoutingID(); + + // Sets whether popup blocking is enabled or not. + void set_block_popups(bool block) { block_popups_ = block; } + + + // UI THREAD ONLY ----------------------------------------------------------- + + // These three functions provide the backend implementation of the + // corresponding functions in RenderProcessHost. See those declarations + // for documentation. + void CancelResourceRequests(int render_widget_id); + void CrossSiteClosePageACK(int new_render_process_host_id, + int new_request_id); + bool WaitForPaintMsg(int render_widget_id, + const base::TimeDelta& max_delay, + IPC::Message* msg); + + + // IO THREAD ONLY ----------------------------------------------------------- + + // Called on the IO thread when a PaintRect message is received. + void DidReceivePaintMsg(const IPC::Message& msg); + + MessageLoop* ui_loop() { return ui_loop_; } + + void CreateNewWindow(int opener_id, bool user_gesture, int* route_id, + HANDLE* modal_dialog_event, HANDLE render_process); + void CreateNewWidget(int opener_id, bool activatable, int* route_id); + + private: + // A class used to proxy a paint message. PaintMsgProxy objects are created + // on the IO thread and destroyed on the UI thread. + class PaintMsgProxy; + friend class PaintMsgProxy; + + // Map from render_widget_id to live PaintMsgProxy instance. + typedef base::hash_map<int, PaintMsgProxy*> PaintMsgProxyMap; + + // Called on the UI thread to discard a paint message. + void OnDiscardPaintMsg(PaintMsgProxy* proxy); + + // Called on the UI thread to dispatch a paint message if necessary. + void OnDispatchPaintMsg(PaintMsgProxy* proxy); + + // Called on the UI thread to send a message to the RenderProcessHost. + void OnSimulateReceivedMessage(const IPC::Message& message); + + // Called on the IO thread to cancel resource requests for the render widget. + void OnCancelResourceRequests(ResourceDispatcherHost* dispatcher, + int render_widget_id); + + // Called on the IO thread to resume a cross-site response. + void OnCrossSiteClosePageACK(ResourceDispatcherHost* dispatcher, + int new_render_process_host_id, + int new_request_id); + + // A map of live paint messages. Must hold pending_paints_lock_ to access. + // The PaintMsgProxy objects are not owned by this map. (See PaintMsgProxy + // for details about how the lifetime of instances are managed.) + PaintMsgProxyMap pending_paints_; + Lock pending_paints_lock_; + + int render_process_id_; + MessageLoop* ui_loop_; + + // Event used to implement WaitForPaintMsg. + HANDLE event_; + + // The next routing id to use. + base::AtomicSequenceNumber next_routing_id_; + + // Whether popup blocking is enabled or not. + bool block_popups_; + + DISALLOW_COPY_AND_ASSIGN(RenderWidgetHelper); +}; + +#endif // CHROME_BROWSER_RENDEDER_HOST_RENDER_WIDGET_HELPER_H_ diff --git a/chrome/browser/renderer_host/render_widget_host.cc b/chrome/browser/renderer_host/render_widget_host.cc new file mode 100644 index 0000000..7a77a02 --- /dev/null +++ b/chrome/browser/renderer_host/render_widget_host.cc @@ -0,0 +1,849 @@ +// 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 "chrome/browser/renderer_host/render_widget_host.h" + +#include "base/gfx/gdi_util.h" +#include "base/message_loop.h" +#include "chrome/app/chrome_dll_resource.h" +#include "chrome/browser/renderer_host/render_process_host.h" +#include "chrome/browser/renderer_host/render_widget_helper.h" +#include "chrome/browser/renderer_host/render_widget_host_view.h" +#include "chrome/common/mru_cache.h" +#include "chrome/common/notification_service.h" +#include "chrome/common/win_util.h" +#include "chrome/views/view.h" +#include "webkit/glue/webcursor.h" +#include "webkit/glue/webinputevent.h" + +using base::Time; +using base::TimeDelta; +using base::TimeTicks; + +// How long to (synchronously) wait for the renderer to respond with a +// PaintRect message, when our backing-store is invalid, before giving up and +// returning a null or incorrectly sized backing-store from GetBackingStore. +// This timeout impacts the "choppiness" of our window resize perf. +static const int kPaintMsgTimeoutMS = 40; + +// How long to wait before we consider a renderer hung. +static const int kHungRendererDelayMs = 20000; + +/////////////////////////////////////////////////////////////////////////////// +// RenderWidget::BackingStore + +RenderWidgetHost::BackingStore::BackingStore(const gfx::Size& size) + : size_(size), + backing_store_dib_(NULL), + original_bitmap_(NULL) { + HDC screen_dc = ::GetDC(NULL); + hdc_ = CreateCompatibleDC(screen_dc); + ReleaseDC(NULL, screen_dc); +} + +RenderWidgetHost::BackingStore::~BackingStore() { + DCHECK(hdc_); + + DeleteDC(hdc_); + + if (backing_store_dib_) { + DeleteObject(backing_store_dib_); + backing_store_dib_ = NULL; + } +} + +bool RenderWidgetHost::BackingStore::Refresh(HANDLE process, + HANDLE bitmap_section, + const gfx::Rect& bitmap_rect) { + // The bitmap received is valid only in the renderer process. + HANDLE valid_bitmap = + win_util::GetSectionFromProcess(bitmap_section, process, false); + if (!valid_bitmap) + return false; + + if (!backing_store_dib_) { + backing_store_dib_ = CreateDIB(hdc_, size_.width(), size_.height(), true, + NULL); + DCHECK(backing_store_dib_ != NULL); + original_bitmap_ = SelectObject(hdc_, backing_store_dib_); + } + + // TODO(darin): protect against integer overflow + DWORD size = 4 * bitmap_rect.width() * bitmap_rect.height(); + void* backing_store_data = MapViewOfFile(valid_bitmap, FILE_MAP_READ, 0, 0, + size); + // These values are shared with gfx::PlatformDevice + BITMAPINFOHEADER hdr; + gfx::CreateBitmapHeader(bitmap_rect.width(), bitmap_rect.height(), &hdr); + // Account for a bitmap_rect that exceeds the bounds of our view + gfx::Rect view_rect(0, 0, size_.width(), size_.height()); + gfx::Rect paint_rect = view_rect.Intersect(bitmap_rect); + + StretchDIBits(hdc_, + paint_rect.x(), + paint_rect.y(), + paint_rect.width(), + paint_rect.height(), + 0, 0, // source x,y + paint_rect.width(), + paint_rect.height(), + backing_store_data, + reinterpret_cast<BITMAPINFO*>(&hdr), + DIB_RGB_COLORS, + SRCCOPY); + + UnmapViewOfFile(backing_store_data); + CloseHandle(valid_bitmap); + return true; +} + +HANDLE RenderWidgetHost::BackingStore::CreateDIB(HDC dc, int width, int height, + bool use_system_color_depth, + HANDLE section) { + BITMAPINFOHEADER hdr; + + if (use_system_color_depth) { + HDC screen_dc = ::GetDC(NULL); + int color_depth = GetDeviceCaps(screen_dc, BITSPIXEL); + ::ReleaseDC(NULL, screen_dc); + + // Color depths less than 16 bpp require a palette to be specified in the + // BITMAPINFO structure passed to CreateDIBSection. Instead of creating + // the palette, we specify the desired color depth as 16 which allows the + // OS to come up with an approximation. Tested this with 8bpp. + if (color_depth < 16) + color_depth = 16; + + gfx::CreateBitmapHeaderWithColorDepth(width, height, color_depth, &hdr); + } else { + gfx::CreateBitmapHeader(width, height, &hdr); + } + void* data = NULL; + HANDLE dib = + CreateDIBSection(hdc_, reinterpret_cast<BITMAPINFO*>(&hdr), + 0, &data, section, 0); + return dib; +} + + +/////////////////////////////////////////////////////////////////////////////// +// RenderWidgetHost::BackingStoreManager + +// This class manages backing stores in the browsr. Every RenderWidgetHost +// is associated with a backing store which it requests from this class. +// The hosts don't maintain any references to the backing stores. +// These backing stores are maintained in a cache which can be trimmed as +// needed. +class RenderWidgetHost::BackingStoreManager { + public: + // Returns a backing store which matches the desired dimensions. + // Parameters: + // host + // A pointer to the RenderWidgetHost. + // backing_store_rect + // The desired backing store dimensions. + // Returns a pointer to the backing store on success, NULL on failure. + static BackingStore* GetBackingStore(RenderWidgetHost* host, + const gfx::Size& desired_size) { + BackingStore* backing_store = Lookup(host); + if (backing_store) { + // If we already have a backing store, then make sure it is the correct + // size. + if (backing_store->size() == desired_size) + return backing_store; + backing_store = NULL; + } + + return backing_store; + } + + // Returns a backing store which is fully ready for consumption, + // i.e. the bitmap from the renderer has been copied into the + // backing store dc, or the bitmap in the backing store dc references + // the renderer bitmap. + // Parameters: + // host + // A pointer to the RenderWidgetHost. + // backing_store_rect + // The desired backing store dimensions. + // process_handle + // The renderer process handle. + // bitmap_section + // The bitmap section from the renderer. + // bitmap_rect + // The rect to be painted into the backing store + // needs_full_paint + // Set if we need to send out a request to paint the view + // to the renderer. + static BackingStore* PrepareBackingStore(RenderWidgetHost* host, + const gfx::Rect& backing_store_rect, + HANDLE process_handle, + HANDLE bitmap_section, + const gfx::Rect& bitmap_rect, + bool* needs_full_paint) { + BackingStore* backing_store = GetBackingStore(host, + backing_store_rect.size()); + if (!backing_store) { + // We need to get Webkit to generate a new paint here, as we + // don't have a previous snapshot. + if (bitmap_rect != backing_store_rect) { + DCHECK(needs_full_paint != NULL); + *needs_full_paint = true; + } + backing_store = CreateBackingStore(host, backing_store_rect); + } + + DCHECK(backing_store != NULL); + backing_store->Refresh(process_handle, bitmap_section, bitmap_rect); + return backing_store; + } + + // Returns a matching backing store for the host. + // Returns NULL if we fail to find one. + static BackingStore* Lookup(RenderWidgetHost* host) { + if (cache_) { + BackingStoreCache::iterator it = cache_->Peek(host); + if (it != cache_->end()) + return it->second; + } + return NULL; + } + + // Removes the backing store for the host. + static void RemoveBackingStore(RenderWidgetHost* host) { + if (!cache_) + return; + + BackingStoreCache::iterator it = cache_->Peek(host); + if (it == cache_->end()) + return; + + cache_->Erase(it); + + if (cache_->empty()) { + delete cache_; + cache_ = NULL; + } + } + + private: + // Not intended for instantiation. + ~BackingStoreManager(); + + typedef OwningMRUCache<RenderWidgetHost*, BackingStore*> BackingStoreCache; + static BackingStoreCache* cache_; + + // Returns the size of the backing store cache. + // TODO(iyengar) Make this dynamic, i.e. based on the available resources + // on the machine. + static int GetBackingStoreCacheSize() { + const int kMaxSize = 5; + return kMaxSize; + } + + // Creates the backing store for the host based on the dimensions passed in. + // Removes the existing backing store if there is one. + static BackingStore* CreateBackingStore( + RenderWidgetHost* host, const gfx::Rect& backing_store_rect) { + RemoveBackingStore(host); + + BackingStore* backing_store = new BackingStore(backing_store_rect.size()); + int backing_store_cache_size = GetBackingStoreCacheSize(); + if (backing_store_cache_size > 0) { + if (!cache_) + cache_ = new BackingStoreCache(backing_store_cache_size); + cache_->Put(host, backing_store); + } + return backing_store; + } + + DISALLOW_EVIL_CONSTRUCTORS(BackingStoreManager); +}; + +RenderWidgetHost::BackingStoreManager::BackingStoreCache* + RenderWidgetHost::BackingStoreManager::cache_ = NULL; + + +/////////////////////////////////////////////////////////////////////////////// +// RenderWidgetHost + +RenderWidgetHost::RenderWidgetHost(RenderProcessHost* process, + int routing_id) + : process_(process), + routing_id_(routing_id), + resize_ack_pending_(false), + mouse_move_pending_(false), + view_(NULL), + is_loading_(false), + is_hidden_(false), + suppress_view_updating_(false), + needs_repainting_on_restore_(false), + is_unresponsive_(false), + view_being_painted_(false), + repaint_ack_pending_(false) { + if (routing_id_ == MSG_ROUTING_NONE) + routing_id_ = process_->GetNextRoutingID(); + + process_->Attach(this, routing_id_); + // Because the widget initializes as is_hidden_ == false, + // tell the process host that we're alive. + process_->WidgetRestored(); +} + +RenderWidgetHost::~RenderWidgetHost() { + // Clear our current or cached backing store if either remains. + BackingStoreManager::RemoveBackingStore(this); + + process_->Release(routing_id_); +} + +void RenderWidgetHost::Init() { + DCHECK(process_->channel()); + + // Send the ack along with the information on placement. + HWND plugin_hwnd = view_->GetPluginHWND(); + Send(new ViewMsg_CreatingNew_ACK(routing_id_, plugin_hwnd)); + + WasResized(); +} + +/////////////////////////////////////////////////////////////////////////////// +// RenderWidgetHost, protected: + +IPC_DEFINE_MESSAGE_MAP(RenderWidgetHost) + IPC_MESSAGE_HANDLER(ViewHostMsg_RendererReady, OnMsgRendererReady) + IPC_MESSAGE_HANDLER(ViewHostMsg_RendererGone, OnMsgRendererGone) + IPC_MESSAGE_HANDLER(ViewHostMsg_Close, OnMsgClose) + IPC_MESSAGE_HANDLER(ViewHostMsg_RequestMove, OnMsgRequestMove) + IPC_MESSAGE_HANDLER(ViewHostMsg_PaintRect, OnMsgPaintRect) + IPC_MESSAGE_HANDLER(ViewHostMsg_ScrollRect, OnMsgScrollRect) + IPC_MESSAGE_HANDLER(ViewHostMsg_HandleInputEvent_ACK, OnMsgInputEventAck) + IPC_MESSAGE_HANDLER(ViewHostMsg_Focus, OnMsgFocus) + IPC_MESSAGE_HANDLER(ViewHostMsg_Blur, OnMsgBlur) + IPC_MESSAGE_HANDLER(ViewHostMsg_SetCursor, OnMsgSetCursor) + IPC_MESSAGE_HANDLER(ViewHostMsg_ImeUpdateStatus, OnMsgImeUpdateStatus) + IPC_MESSAGE_UNHANDLED_ERROR() +IPC_END_MESSAGE_MAP() + +void RenderWidgetHost::OnMsgRendererReady() { + WasResized(); +} + +void RenderWidgetHost::OnMsgRendererGone() { + // TODO(evanm): This synchronously ends up calling "delete this". + // Is that really what we want in response to this message? I'm matching + // previous behavior of the code here. + Destroy(); +} + +void RenderWidgetHost::OnMsgClose() { + Shutdown(); +} + +void RenderWidgetHost::OnMsgRequestMove(const gfx::Rect& pos) { + // Note that we ignore the position. + view_->SetSize(pos.size()); +} + +void RenderWidgetHost::OnMsgPaintRect( + const ViewHostMsg_PaintRect_Params& params) { + TimeTicks paint_start = TimeTicks::Now(); + + // Update our knowledge of the RenderWidget's size. + current_size_ = params.view_size; + + bool is_resize_ack = + ViewHostMsg_PaintRect_Flags::is_resize_ack(params.flags); + + // resize_ack_pending_ needs to be cleared before we call DidPaintRect, since + // that will end up reaching GetBackingStore. + if (is_resize_ack) { + DCHECK(resize_ack_pending_); + resize_ack_pending_ = false; + } + + bool is_repaint_ack = + ViewHostMsg_PaintRect_Flags::is_repaint_ack(params.flags); + if (is_repaint_ack) { + repaint_ack_pending_ = false; + TimeDelta delta = TimeTicks::Now() - repaint_start_time_; + UMA_HISTOGRAM_TIMES(L"MPArch.RWH_RepaintDelta", delta); + } + + DCHECK(params.bitmap); + DCHECK(!params.bitmap_rect.IsEmpty()); + DCHECK(!params.view_size.IsEmpty()); + + PaintRect(params.bitmap, params.bitmap_rect, params.view_size); + + // ACK early so we can prefetch the next PaintRect if there is a next one. + Send(new ViewMsg_PaintRect_ACK(routing_id_)); + + // TODO(darin): This should really be done by the view_! + MovePluginWindows(params.plugin_window_moves); + + // The view might be destroyed already. Check for this case. + if (view_ && !suppress_view_updating_) { + view_being_painted_ = true; + view_->DidPaintRect(params.bitmap_rect); + view_being_painted_ = false; + } + + if (paint_observer_.get()) + paint_observer_->RenderWidgetHostDidPaint(this); + + // If we got a resize ack, then perhaps we have another resize to send? + if (is_resize_ack && view_) { + gfx::Rect view_bounds = view_->GetViewBounds(); + if (current_size_.width() != view_bounds.width() || + current_size_.height() != view_bounds.height()) { + WasResized(); + } + } + + // Log the time delta for processing a paint message. + TimeDelta delta = TimeTicks::Now() - paint_start; + UMA_HISTOGRAM_TIMES(L"MPArch.RWH_OnMsgPaintRect", delta); +} + +void RenderWidgetHost::OnMsgScrollRect( + const ViewHostMsg_ScrollRect_Params& params) { + TimeTicks scroll_start = TimeTicks::Now(); + + DCHECK(!params.view_size.IsEmpty()); + + ScrollRect(params.bitmap, params.bitmap_rect, params.dx, params.dy, + params.clip_rect, params.view_size); + + // ACK early so we can prefetch the next ScrollRect if there is a next one. + Send(new ViewMsg_ScrollRect_ACK(routing_id_)); + + // TODO(darin): This should really be done by the view_! + MovePluginWindows(params.plugin_window_moves); + + // The view might be destroyed already. Check for this case + if (view_) { + view_being_painted_ = true; + view_->DidScrollRect(params.clip_rect, params.dx, params.dy); + view_being_painted_ = false; + } + + // Log the time delta for processing a scroll message. + TimeDelta delta = TimeTicks::Now() - scroll_start; + UMA_HISTOGRAM_TIMES(L"MPArch.RWH_OnMsgScrollRect", delta); +} + +void RenderWidgetHost::MovePluginWindows( + const std::vector<WebPluginGeometry>& plugin_window_moves) { + if (plugin_window_moves.empty()) + return; + + HDWP defer_window_pos_info = + ::BeginDeferWindowPos(static_cast<int>(plugin_window_moves.size())); + + if (!defer_window_pos_info) { + NOTREACHED(); + return; + } + + for (size_t i = 0; i < plugin_window_moves.size(); ++i) { + unsigned long flags = 0; + const WebPluginGeometry& move = plugin_window_moves[i]; + + if (move.visible) + flags |= SWP_SHOWWINDOW; + else + flags |= SWP_HIDEWINDOW; + + HRGN hrgn = ::CreateRectRgn(move.clip_rect.x(), + move.clip_rect.y(), + move.clip_rect.right(), + move.clip_rect.bottom()); + gfx::SubtractRectanglesFromRegion(hrgn, move.cutout_rects); + + // Note: System will own the hrgn after we call SetWindowRgn, + // so we don't need to call DeleteObject(hrgn) + ::SetWindowRgn(move.window, hrgn, !move.clip_rect.IsEmpty()); + + defer_window_pos_info = ::DeferWindowPos(defer_window_pos_info, + move.window, NULL, + move.window_rect.x(), + move.window_rect.y(), + move.window_rect.width(), + move.window_rect.height(), flags); + if (!defer_window_pos_info) { + return; + } + } + + ::EndDeferWindowPos(defer_window_pos_info); +} + +void RenderWidgetHost::OnMsgInputEventAck(const IPC::Message& message) { + // Log the time delta for processing an input event. + TimeDelta delta = TimeTicks::Now() - input_event_start_time_; + UMA_HISTOGRAM_TIMES(L"MPArch.RWH_InputEventDelta", delta); + + // Cancel pending hung renderer checks since the renderer is responsive. + StopHangMonitorTimeout(); + + void* iter = NULL; + int type = 0; + bool r = message.ReadInt(&iter, &type); + DCHECK(r); + + if (type == WebInputEvent::MOUSE_MOVE) { + mouse_move_pending_ = false; + + // now, we can send the next mouse move event + if (next_mouse_move_.get()) { + DCHECK(next_mouse_move_->type == WebInputEvent::MOUSE_MOVE); + ForwardMouseEvent(*next_mouse_move_); + } + } + + const char* data = NULL; + int length = 0; + if (message.ReadData(&iter, &data, &length)) { + const WebInputEvent* input_event = + reinterpret_cast<const WebInputEvent*>(data); + UnhandledInputEvent(*input_event); + } +} + +void RenderWidgetHost::OnMsgFocus() { + // Only the user can focus a RenderWidgetHost. + NOTREACHED(); +} + +void RenderWidgetHost::OnMsgBlur() { + if (view_) { + view_->Blur(); + } +} + +void RenderWidgetHost::OnMsgSetCursor(const WebCursor& cursor) { + if (!view_) { + return; + } + view_->UpdateCursor(cursor); +} + +void RenderWidgetHost::OnMsgImeUpdateStatus(ViewHostMsg_ImeControl control, + const gfx::Rect& caret_rect) { + if (view_) { + view_->IMEUpdateStatus(control, caret_rect); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void RenderWidgetHost::WasHidden() { + is_hidden_ = true; + + // Don't bother reporting hung state when we aren't the active tab. + StopHangMonitorTimeout(); + + // If we have a renderer, then inform it that we are being hidden so it can + // reduce its resource utilization. + Send(new ViewMsg_WasHidden(routing_id_)); + + // TODO(darin): what about constrained windows? it doesn't look like they + // see a message when their parent is hidden. maybe there is something more + // generic we can do at the TabContents API level instead of relying on + // Windows messages. + + // Tell the RenderProcessHost we were hidden. + process_->WidgetHidden(); +} + +void RenderWidgetHost::WasRestored() { + // When we create the widget, it is created as *not* hidden. + if (!is_hidden_) + return; + is_hidden_ = false; + + BackingStore* backing_store = BackingStoreManager::Lookup(this); + // If we already have a backing store for this widget, then we don't need to + // repaint on restore _unless_ we know that our backing store is invalid. + bool needs_repainting; + if (needs_repainting_on_restore_ || !backing_store) { + needs_repainting = true; + needs_repainting_on_restore_ = false; + } else { + needs_repainting = false; + } + Send(new ViewMsg_WasRestored(routing_id_, needs_repainting)); + + process_->WidgetRestored(); +} + +void RenderWidgetHost::WasResized() { + if (resize_ack_pending_ || !process_->channel() || !view_) + return; + + gfx::Rect view_bounds = view_->GetViewBounds(); + gfx::Size new_size(view_bounds.width(), view_bounds.height()); + + // Avoid asking the RenderWidget to resize to its current size, since it + // won't send us a PaintRect message in that case. + if (new_size == current_size_) + return; + + // We don't expect to receive an ACK when the requested size is empty. + if (!new_size.IsEmpty()) + resize_ack_pending_ = true; + + if (!Send(new ViewMsg_Resize(routing_id_, new_size))) + resize_ack_pending_ = false; +} + +void RenderWidgetHost::ForwardMouseEvent(const WebMouseEvent& mouse_event) { + // Avoid spamming the renderer with mouse move events. It is important + // to note that WM_MOUSEMOVE events are anyways synthetic, but since our + // thread is able to rapidly consume WM_MOUSEMOVE events, we may get way + // more WM_MOUSEMOVE events than we wish to send to the renderer. + if (mouse_event.type == WebInputEvent::MOUSE_MOVE) { + if (mouse_move_pending_) { + next_mouse_move_.reset(new WebMouseEvent(mouse_event)); + return; + } + mouse_move_pending_ = true; + } + + ForwardInputEvent(mouse_event, sizeof(WebMouseEvent)); +} + +void RenderWidgetHost::ForwardKeyboardEvent( + const WebKeyboardEvent& key_event) { + ForwardInputEvent(key_event, sizeof(WebKeyboardEvent)); +} + +void RenderWidgetHost::ForwardWheelEvent( + const WebMouseWheelEvent& wheel_event) { + ForwardInputEvent(wheel_event, sizeof(WebMouseWheelEvent)); +} + +void RenderWidgetHost::ForwardInputEvent(const WebInputEvent& input_event, + int event_size) { + if (!process_->channel()) + return; + + IPC::Message* message = new ViewMsg_HandleInputEvent(routing_id_); + message->WriteData( + reinterpret_cast<const char*>(&input_event), event_size); + input_event_start_time_ = TimeTicks::Now(); + Send(message); + + // any input event cancels a pending mouse move event + next_mouse_move_.reset(); + + StartHangMonitorTimeout(TimeDelta::FromMilliseconds(kHungRendererDelayMs)); +} + +void RenderWidgetHost::Shutdown() { + if (process_->channel()) { + // Tell the renderer object to close. + process_->ReportExpectingClose(routing_id_); + bool rv = Send(new ViewMsg_Close(routing_id_)); + DCHECK(rv); + } + + Destroy(); +} + +void RenderWidgetHost::Focus() { + Send(new ViewMsg_SetFocus(routing_id_, true)); +} + +void RenderWidgetHost::Blur() { + Send(new ViewMsg_SetFocus(routing_id_, false)); +} + +void RenderWidgetHost::LostCapture() { + Send(new ViewMsg_MouseCaptureLost(routing_id_)); +} + +void RenderWidgetHost::ViewDestroyed() { + // TODO(evanm): tracking this may no longer be necessary; + // eliminate this function if so. + view_ = NULL; +} + +void RenderWidgetHost::Destroy() { + NotificationService::current()->Notify( + NOTIFY_RENDER_WIDGET_HOST_DESTROYED, + Source<RenderWidgetHost>(this), + NotificationService::NoDetails()); + + // Tell the view to die. + // Note that in the process of the view shutting down, it can call a ton + // of other messages on us. So if you do any other deinitialization here, + // do it after this call to view_->Destroy(). + if (view_) + view_->Destroy(); + + delete this; +} + +void RenderWidgetHost::CheckRendererIsUnresponsive() { + // If we received a call to StopHangMonitorTimeout. + if (time_when_considered_hung_.is_null()) + return; + + // If we have not waited long enough, then wait some more. + Time now = Time::Now(); + if (now < time_when_considered_hung_) { + StartHangMonitorTimeout(time_when_considered_hung_ - now); + return; + } + + // OK, looks like we have a hung renderer! + NotificationService::current()->Notify(NOTIFY_RENDERER_PROCESS_HANG, + Source<RenderWidgetHost>(this), + NotificationService::NoDetails()); + is_unresponsive_ = true; + NotifyRendererUnresponsive(); +} + +void RenderWidgetHost::RendererIsResponsive() { + if (is_unresponsive_) { + is_unresponsive_ = false; + NotifyRendererResponsive(); + } +} + +bool RenderWidgetHost::Send(IPC::Message* msg) { + return process_->Send(msg); +} + +void RenderWidgetHost::SetIsLoading(bool is_loading) { + is_loading_ = is_loading; + if (!view_) + return; + view_->SetIsLoading(is_loading); +} + +RenderWidgetHost::BackingStore* RenderWidgetHost::GetBackingStore() { + // We should not be asked to paint while we are hidden. If we are hidden, + // then it means that our consumer failed to call WasRestored. + DCHECK(!is_hidden_) << "GetBackingStore called while hidden!"; + + // We might have a cached backing store that we can reuse! + BackingStore* backing_store = + BackingStoreManager::GetBackingStore(this, current_size_); + // If we fail to find a backing store in the cache, send out a request + // to the renderer to paint the view if required. + if (!backing_store && !repaint_ack_pending_ && !resize_ack_pending_ && + !view_being_painted_) { + repaint_start_time_ = TimeTicks::Now(); + repaint_ack_pending_ = true; + Send(new ViewMsg_Repaint(routing_id_, current_size_)); + } + + // When we have asked the RenderWidget to resize, and we are still waiting on + // a response, block for a little while to see if we can't get a response + // before returning the old (incorrectly sized) backing store. + if (resize_ack_pending_ || !backing_store) { + IPC::Message msg; + TimeDelta max_delay = TimeDelta::FromMilliseconds(kPaintMsgTimeoutMS); + if (process_->WaitForPaintMsg(routing_id_, max_delay, &msg)) { + suppress_view_updating_ = true; + ViewHostMsg_PaintRect::Dispatch( + &msg, this, &RenderWidgetHost::OnMsgPaintRect); + suppress_view_updating_ = false; + backing_store = BackingStoreManager::GetBackingStore(this, current_size_); + } + } + + return backing_store; +} + +void RenderWidgetHost::PaintRect(HANDLE bitmap, const gfx::Rect& bitmap_rect, + const gfx::Size& view_size) { + if (is_hidden_) { + needs_repainting_on_restore_ = true; + return; + } + + // We use the view size according to the render view, which may not be + // quite the same as the size of our window. + gfx::Rect view_rect(0, 0, view_size.width(), view_size.height()); + + bool needs_full_paint = false; + BackingStore* backing_store = + BackingStoreManager::PrepareBackingStore(this, view_rect, + process_->process().handle(), + bitmap, bitmap_rect, + &needs_full_paint); + DCHECK(backing_store != NULL); + if (needs_full_paint) { + repaint_start_time_ = TimeTicks::Now(); + repaint_ack_pending_ = true; + Send(new ViewMsg_Repaint(routing_id_, view_size)); + } +} + +void RenderWidgetHost::ScrollRect(HANDLE bitmap, const gfx::Rect& bitmap_rect, + int dx, int dy, const gfx::Rect& clip_rect, + const gfx::Size& view_size) { + if (is_hidden_) { + needs_repainting_on_restore_ = true; + return; + } + + // TODO(darin): do we need to do something else if our backing store is not + // the same size as the advertised view? maybe we just assume there is a + // full paint on its way? + BackingStore* backing_store = BackingStoreManager::Lookup(this); + if (!backing_store || (backing_store->size() != view_size)) + return; + + RECT damaged_rect, r = clip_rect.ToRECT(); + ScrollDC(backing_store->dc(), dx, dy, NULL, &r, NULL, &damaged_rect); + + // TODO(darin): this doesn't work if dx and dy are both non-zero! + DCHECK(dx == 0 || dy == 0); + + // We expect that damaged_rect should equal bitmap_rect. + DCHECK(gfx::Rect(damaged_rect) == bitmap_rect); + + backing_store->Refresh(process_->process().handle(), bitmap, bitmap_rect); +} + +void RenderWidgetHost::RestartHangMonitorTimeout() { + StartHangMonitorTimeout(TimeDelta::FromMilliseconds(kHungRendererDelayMs)); +} + +void RenderWidgetHost::StopHangMonitorTimeout() { + time_when_considered_hung_ = Time(); + RendererIsResponsive(); + + // We do not bother to stop the hung_renderer_timer_ here in case it will be + // started again shortly, which happens to be the common use case. +} + +void RenderWidgetHost::StartHangMonitorTimeout(TimeDelta delay) { + time_when_considered_hung_ = Time::Now() + delay; + + // If we already have a timer that will expire at or before the given delay, + // then we have nothing more to do now. + if (hung_renderer_timer_.IsRunning() && + hung_renderer_timer_.GetCurrentDelay() <= delay) + return; + + // Either the timer is not yet running, or we need to adjust the timer to + // fire sooner. + hung_renderer_timer_.Stop(); + hung_renderer_timer_.Start(delay, this, + &RenderWidgetHost::CheckRendererIsUnresponsive); +} + +void RenderWidgetHost::RendererExited() { + BackingStoreManager::RemoveBackingStore(this); +} + +void RenderWidgetHost::SystemThemeChanged() { + Send(new ViewMsg_ThemeChanged(routing_id_)); +} diff --git a/chrome/browser/renderer_host/render_widget_host.h b/chrome/browser/renderer_host/render_widget_host.h new file mode 100644 index 0000000..d683e84 --- /dev/null +++ b/chrome/browser/renderer_host/render_widget_host.h @@ -0,0 +1,369 @@ +// 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. + +#ifndef CHROME_BROWSER_RENDERER_HOST_RENDER_WIDGET_HOST_H_ +#define CHROME_BROWSER_RENDERER_HOST_RENDER_WIDGET_HOST_H_ + +#include <windows.h> + +#include <vector> + +#include "base/gfx/size.h" +#include "base/timer.h" +#include "chrome/common/ipc_channel.h" + +namespace gfx { +class Rect; +} + +class BackingStore; +class PaintObserver; +class RenderProcessHost; +class RenderWidgetHostView; +class WebInputEvent; +class WebKeyboardEvent; +class WebMouseEvent; +class WebMouseWheelEvent; +class WebCursor; +enum ViewHostMsg_ImeControl; +struct ViewHostMsg_PaintRect_Params; +struct ViewHostMsg_ScrollRect_Params; +struct WebPluginGeometry; + +// This class manages the browser side of a browser<->renderer HWND connection. +// The HWND lives in the browser process, and windows events are sent over +// IPC to the corresponding object in the renderer. The renderer paints into +// shared memory, which we transfer to a backing store and blit to the screen +// when Windows sends us a WM_PAINT message. +// +// How Shutdown Works +// +// There are two situations in which this object, a RenderWidgetHost, can be +// instantiated: +// +// 1. By a WebContents as the communication conduit for a rendered web page. +// The WebContents instantiates a derived class: RenderViewHost. +// 2. By a WebContents as the communication conduit for a select widget. The +// WebContents instantiates the RenderWidgetHost directly. +// +// For every WebContents there are several objects in play that need to be +// properly destroyed or cleaned up when certain events occur. +// +// - WebContents - the TabContents itself, and its associated HWND. +// - RenderViewHost - representing the communication conduit with the child +// process. +// - RenderWidgetHostView - the view of the web page content, message handler, +// and plugin root. +// +// Normally, the WebContents contains a child RenderWidgetHostView that renders +// the contents of the loaded page. It has a WS_CLIPCHILDREN style so that it +// does no painting of its own. +// +// The lifetime of the RenderWidgetHostView is tied to the render process. If +// the render process dies, the RenderWidgetHostView goes away and all +// references to it must become NULL. If the WebContents finds itself without a +// RenderWidgetHostView, it paints Sad Tab instead. +// +// RenderViewHost (a RenderWidgetHost subclass) is the conduit used to +// communicate with the RenderView and is owned by the WebContents. If the +// render process crashes, the RenderViewHost remains and restarts the render +// process if needed to continue navigation. +// +// The WebContents is itself owned by the NavigationController in which it +// resides. +// +// Some examples of how shutdown works: +// +// When a tab is closed (either by the user, the web page calling window.close, +// etc) the TabStrip destroys the associated NavigationController, which calls +// Destroy on each TabContents it owns. +// +// For a WebContents, its Destroy method tells the RenderViewHost to +// shut down the render process and die. +// +// When the render process is destroyed it destroys the View: the +// RenderWidgetHostView, which destroys its HWND and deletes that object. +// +// For select popups, the situation is a little different. The RenderWidgetHost +// associated with the select popup owns the view and itself (is responsible +// for destroying itself when the view is closed). The WebContents's only +// responsibility is to select popups is to create them when it is told to. When +// the View is destroyed via an IPC message (for when WebCore destroys the +// popup, e.g. if the user selects one of the options), or because +// WM_CANCELMODE is received by the view, the View schedules the destruction of +// the render process. However in this case since there's no WebContents +// container, when the render process is destroyed, the RenderWidgetHost just +// deletes itself, which is safe because no one else should have any references +// to it (the WebContents does not). +// +// It should be noted that the RenderViewHost, not the RenderWidgetHost, +// handles IPC messages relating to the render process going away, since the +// way a RenderViewHost (WebContents) handles the process dying is different to +// the way a select popup does. As such the RenderWidgetHostView handles these +// messages for select popups. This placement is more out of convenience than +// anything else. When the view is live, these messages are forwarded to it by +// the RenderWidgetHost's IPC message map. +// +class RenderWidgetHost : public IPC::Channel::Listener { + public: + // routing_id can be MSG_ROUTING_NONE, in which case the next available + // routing id is taken from the RenderProcessHost. + RenderWidgetHost(RenderProcessHost* process, int routing_id); + virtual ~RenderWidgetHost(); + + // Gets/Sets the View of this RenderWidgetHost. Can be NULL, e.g. if the + // RenderWidget is being destroyed or the render process crashed. You should + // never cache this pointer since it can become NULL if the renderer crashes, + // instead you should always ask for it using the accessor. + void set_view(RenderWidgetHostView* view) { view_ = view; } + RenderWidgetHostView* view() const { return view_; } + + RenderProcessHost* process() const { return process_; } + int routing_id() const { return routing_id_; } + + // Manual RTTI FTW. We are not hosting a web page. + virtual bool IsRenderView() { return false; } + + // Called when a renderer object already been created for this host, and we + // just need to be attached to it. Used for window.open, <select> dropdown + // menus, and other times when the renderer initiates creating an object. + virtual void Init(); + + // IPC::Channel::Listener + virtual void OnMessageReceived(const IPC::Message& msg); + + // Sends a message to the corresponding object in the renderer. + bool Send(IPC::Message* msg); + + // Called to notify the RenderWidget that it has been hidden or restored from + // having been hidden. + void WasHidden(); + void WasRestored(); + + // Called to notify the RenderWidget that it has been resized. + void WasResized(); + + // Tells the renderer to die and then calls Destroy(). + virtual void Shutdown(); + + void Focus(); + void Blur(); + void LostCapture(); + + // Notifies the RenderWidgetHost that the View was destroyed. + void ViewDestroyed(); + + // Indicates if the page has finished loading. + virtual void SetIsLoading(bool is_loading); + + // Represents a device-dependent drawing surface used to hold the rendering + // of a RenderWidgetHost. + class BackingStore; + + // Manages a set of backing stores. + class BackingStoreManager; + + // Get access to the widget's backing store. If a resize is in progress, + // then the current size of the backing store may be less than the size of + // the widget's view. This method returns NULL if the backing store could + // not be created. + BackingStore* GetBackingStore(); + + // An interface that gets called when paints happen. + // Used in performance tests. + class PaintObserver; + // Set the PaintObserver on this object. Takes ownership. + void SetPaintObserver(PaintObserver* paint_observer) { + paint_observer_.reset(paint_observer); + } + + // Checks to see if we can give up focus to this widget through a + // javascript call. + virtual bool CanBlur() const { return true; } + + // Restart the active hang monitor timeout. Clears all existing timeouts and + // starts with a new one. This can be because the renderer has become + // active, the tab is being hidden, or the user has chosen to wait some more + // to give the tab a chance to become active and we don't want to display a + // warning too soon. + void RestartHangMonitorTimeout(); + + // Stops all existing hang monitor timeouts and assumes the renderer is + // responsive. + void StopHangMonitorTimeout(); + + // Starts a hang monitor timeout. If there's already a hang monitor timeout + // the new one will only fire if it has a shorter delay than the time + // left on the existing timeouts. + void StartHangMonitorTimeout(base::TimeDelta delay); + + // Called when we receive a notification indicating that the renderer + // process has gone. + void RendererExited(); + + // Called when the system theme changes. At this time all existing native + // theme handles are invalid and the renderer must obtain new ones and + // repaint. + void SystemThemeChanged(); + + protected: + // Called when we an InputEvent was not processed by the renderer. + virtual void UnhandledInputEvent(const WebInputEvent& event) { } + + // IPC message handlers + void OnMsgRendererReady(); + void OnMsgRendererGone(); + void OnMsgClose(); + void OnMsgRequestMove(const gfx::Rect& pos); + void OnMsgResizeAck(); + void OnMsgPaintRect(const ViewHostMsg_PaintRect_Params& params); + void OnMsgScrollRect(const ViewHostMsg_ScrollRect_Params& params); + void OnMsgInputEventAck(const IPC::Message& message); + void OnMsgFocus(); + void OnMsgBlur(); + void OnMsgSetCursor(const WebCursor& cursor); + void OnMsgImeUpdateStatus(ViewHostMsg_ImeControl control, + const gfx::Rect& caret_rect); + + void MovePluginWindows( + const std::vector<WebPluginGeometry>& plugin_window_moves); + + // TODO(beng): (Cleanup) remove this friendship once we expose a clean API to + // RenderWidgetHost Views. This exists only to give RenderWidgetHostView + // access to Forward*Event. + friend class RenderWidgetHostViewWin; + + void ForwardMouseEvent(const WebMouseEvent& mouse_event); + virtual void ForwardKeyboardEvent(const WebKeyboardEvent& key_event); + void ForwardWheelEvent(const WebMouseWheelEvent& wheel_event); + void ForwardInputEvent(const WebInputEvent& input_event, int event_size); + + // Called to paint a region of the backing store + void PaintRect(HANDLE bitmap, const gfx::Rect& bitmap_rect, + const gfx::Size& view_size); + + // Called to scroll a region of the backing store + void ScrollRect(HANDLE bitmap, const gfx::Rect& bitmap_rect, int dx, int dy, + const gfx::Rect& clip_rect, const gfx::Size& view_size); + + // Tell this object to destroy itself. + void Destroy(); + + // Callbacks for notification when the renderer becomes unresponsive to user + // input events, and subsequently responsive again. The delegate can use + // these notifications to show a warning. + void CheckRendererIsUnresponsive(); + virtual void NotifyRendererUnresponsive() {} + void RendererIsResponsive(); + virtual void NotifyRendererResponsive() {} + + // Created during construction but initialized during Init*(). Therefore, it + // is guaranteed never to be NULL, but its channel may be NULL if the + // renderer crashed, so you must always check that. + RenderProcessHost* process_; + + // The ID of the corresponding object in the Renderer Instance. + int routing_id_; + + bool resize_ack_pending_; // True when waiting for RESIZE_ACK. + gfx::Size current_size_; // The current size of the RenderWidget. + + // True if a mouse move event was sent to the render view and we are waiting + // for a corresponding ViewHostMsg_HandleInputEvent_ACK message. + bool mouse_move_pending_; + + // The next mouse move event to send (only non-null while mouse_move_pending_ + // is true). + scoped_ptr<WebMouseEvent> next_mouse_move_; + + // The View associated with the RenderViewHost. The lifetime of this object + // is associated with the lifetime of the Render process. If the Renderer + // crashes, its View is destroyed and this pointer becomes NULL, even though + // render_view_host_ lives on to load another URL (creating a new View while + // doing so). + RenderWidgetHostView* view_; + + // The time when an input event was sent to the RenderWidget. + base::TimeTicks input_event_start_time_; + + // Indicates whether a page is loading or not. + bool is_loading_; + // Indicates whether a page is hidden or not. + bool is_hidden_; + // If true, then we should not ask our view to repaint when our backingstore + // is updated. + bool suppress_view_updating_; + + // If true, then we should repaint when restoring even if we have a + // backingstore. This flag is set to true if we receive a paint message + // while is_hidden_ to true. Even though we tell the render widget to hide + // itself, a paint message could already be in flight at that point. + bool needs_repainting_on_restore_; + + // The following value indicates a time in the future when we would consider + // the renderer hung if it does not generate an appropriate response message. + base::Time time_when_considered_hung_; + + // This timer runs to check if time_when_considered_hung_ has past. + base::OneShotTimer<RenderWidgetHost> hung_renderer_timer_; + + // This is true if the renderer is currently unresponsive. + bool is_unresponsive_; + + // Optional observer that listens for notifications of painting. + scoped_ptr<PaintObserver> paint_observer_; + + // Set when we call DidPaintRect/DidScrollRect on the view. + bool view_being_painted_; + + // Set if we are waiting for a repaint ack for the view. + bool repaint_ack_pending_; + + // Used for UMA histogram logging to measure the time for a repaint view + // operation to finish. + base::TimeTicks repaint_start_time_; + + DISALLOW_EVIL_CONSTRUCTORS(RenderWidgetHost); +}; + +class RenderWidgetHost::BackingStore { + public: + BackingStore(const gfx::Size& size); + ~BackingStore(); + + HDC dc() { return hdc_; } + const gfx::Size& size() { return size_; } + + // Paints the bitmap from the renderer onto the backing store. + bool Refresh(HANDLE process, HANDLE bitmap_section, + const gfx::Rect& bitmap_rect); + + private: + // Creates a dib conforming to the height/width/section parameters passed + // in. The use_os_color_depth parameter controls whether we use the color + // depth to create an appropriate dib or not. + HANDLE CreateDIB(HDC dc, int width, int height, bool use_os_color_depth, + HANDLE section); + + // The backing store dc. + HDC hdc_; + // The size of the backing store. + gfx::Size size_; + // Handle to the backing store dib. + HANDLE backing_store_dib_; + // Handle to the original bitmap in the dc. + HANDLE original_bitmap_; + + DISALLOW_COPY_AND_ASSIGN(BackingStore); +}; + +class RenderWidgetHost::PaintObserver { + public: + virtual ~PaintObserver() {} + + // Called each time the RenderWidgetHost paints. + virtual void RenderWidgetHostDidPaint(RenderWidgetHost* rwh) = 0; +}; + +#endif // #ifndef CHROME_BROWSER_RENDERER_HOST_RENDER_WIDGET_HOST_H_ diff --git a/chrome/browser/renderer_host/render_widget_host_view.h b/chrome/browser/renderer_host/render_widget_host_view.h new file mode 100644 index 0000000..8e6152f --- /dev/null +++ b/chrome/browser/renderer_host/render_widget_host_view.h @@ -0,0 +1,121 @@ +// 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. + +#ifndef CHROME_BROWSER_RENDERER_HOST_RENDER_WIDGET_HOST_VIEW_H_ +#define CHROME_BROWSER_RENDERER_HOST_RENDER_WIDGET_HOST_VIEW_H_ + +#include <windows.h> + +#include "base/shared_memory.h" +#include "chrome/common/render_messages.h" + +namespace gfx { +class Rect; +class Size; +} +namespace IPC { +class Message; +} + +class RenderProcessHost; +class RenderWidgetHost; +class WebCursor; + +/////////////////////////////////////////////////////////////////////////////// +// +// RenderWidgetHostView +// +// RenderWidgetHostView is an interface implemented by an object that acts as +// the "View" portion of a RenderWidgetHost. The RenderWidgetHost and its +// associated RenderProcessHost own the "Model" in this case which is the +// child renderer process. The View is responsible for receiving events from +// the surrounding environment and passing them to the RenderWidgetHost, and +// for actually displaying the content of the RenderWidgetHost when it +// changes. +// +/////////////////////////////////////////////////////////////////////////////// +class RenderWidgetHostView { + public: + // Platform-specific creator. Use this to construct new RenderWidgetHostViews + // rather than using RenderWidgetHostViewWin & friends. + // + // The RenderWidgetHost must already be created (because we can't know if it's + // going to be a regular RenderWidgetHost or a RenderViewHost (a subclass). + static RenderWidgetHostView* CreateViewForWidget(RenderWidgetHost* widget); + + // Returns the associated RenderWidgetHost. + virtual RenderWidgetHost* GetRenderWidgetHost() const = 0; + + // Notifies the View that it has become visible. + virtual void DidBecomeSelected() = 0; + + // Notifies the View that it has been hidden. + virtual void WasHidden() = 0; + + // Tells the View to size itself to the specified size. + virtual void SetSize(const gfx::Size& size) = 0; + + // Retrieves the HWND used to contain plugin HWNDs. + virtual HWND GetPluginHWND() = 0; + + // Sends the specified mouse event to the renderer. + virtual void ForwardMouseEventToRenderer(UINT message, + WPARAM wparam, + LPARAM lparam) = 0; + + // Actually set/take focus to/from the associated View component. + virtual void Focus() = 0; + virtual void Blur() = 0; + + // Returns true if the View currently has the focus. + virtual bool HasFocus() = 0; + + // Shows/hides the view. + virtual void Show() = 0; + virtual void Hide() = 0; + + // Retrieve the bounds of the View, in screen coordinates. + virtual gfx::Rect GetViewBounds() const = 0; + + // Sets the cursor to the one associated with the specified cursor_type + virtual void UpdateCursor(const WebCursor& cursor) = 0; + + // Updates the displayed cursor to the current one. + virtual void UpdateCursorIfOverSelf() = 0; + + // Indicates whether the page has finished loading. + virtual void SetIsLoading(bool is_loading) = 0; + + // Enable or disable IME for the view. + virtual void IMEUpdateStatus(ViewHostMsg_ImeControl control, + const gfx::Rect& caret_rect) = 0; + + // Informs the view that a portion of the widget's backing store was painted. + virtual void DidPaintRect(const gfx::Rect& rect) = 0; + + // Informs the view that a portion of the widget's backing store was scrolled + // by dx pixels horizontally and dy pixels vertically. + virtual void DidScrollRect( + const gfx::Rect& rect, int dx, int dy) = 0; + + // Notifies the View that the renderer has ceased to exist. + virtual void RendererGone() = 0; + + // Tells the View to destroy itself. + virtual void Destroy() = 0; + + // Tells the View that the tooltip text for the current mouse position over + // the page has changed. + virtual void SetTooltipText(const std::wstring& tooltip_text) = 0; + + protected: + // Interface class only, do not construct. + RenderWidgetHostView() {} + + private: + DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostView); +}; + +#endif // CHROME_BROWSER_RENDERER_HOST_RENDER_WIDGET_HOST_VIEW_H_ + diff --git a/chrome/browser/renderer_host/render_widget_host_view_win.cc b/chrome/browser/renderer_host/render_widget_host_view_win.cc new file mode 100644 index 0000000..f628809 --- /dev/null +++ b/chrome/browser/renderer_host/render_widget_host_view_win.cc @@ -0,0 +1,925 @@ +// 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 "chrome/browser/renderer_host/render_widget_host_view_win.h" + +#include "base/command_line.h" +#include "base/gfx/gdi_util.h" +#include "base/gfx/rect.h" +#include "base/histogram.h" +#include "base/win_util.h" +#include "chrome/browser/browser_accessibility.h" +#include "chrome/browser/browser_accessibility_manager.h" +#include "chrome/browser/browser_trial.h" +#include "chrome/browser/renderer_host/render_process_host.h" +// TODO(beng): (Cleanup) we should not need to include this file... see comment +// in |DidBecomeSelected|. +#include "chrome/browser/renderer_host/render_view_host.h" +#include "chrome/browser/renderer_host/render_widget_host.h" +#include "chrome/common/chrome_constants.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/l10n_util.h" +#include "chrome/common/plugin_messages.h" +#include "chrome/common/win_util.h" +// Included for views::kReflectedMessage - TODO(beng): move this to win_util.h! +#include "chrome/views/widget_win.h" +#include "webkit/glue/plugins/plugin_constants_win.h" +#include "webkit/glue/plugins/webplugin_delegate_impl.h" +#include "webkit/glue/webcursor.h" + +using base::TimeDelta; +using base::TimeTicks; + +namespace { + +// Tooltips will wrap after this width. Yes, wrap. Imagine that! +const int kTooltipMaxWidthPixels = 300; + +// Maximum number of characters we allow in a tooltip. +const int kMaxTooltipLength = 1024; + +// A callback function for EnumThreadWindows to enumerate and dismiss +// any owned popop windows +BOOL CALLBACK DismissOwnedPopups(HWND window, LPARAM arg) { + const HWND toplevel_hwnd = reinterpret_cast<HWND>(arg); + + if (::IsWindowVisible(window)) { + const HWND owner = ::GetWindow(window, GW_OWNER); + if (toplevel_hwnd == owner) { + ::PostMessage(window, WM_CANCELMODE, 0, 0); + } + } + + return TRUE; +} + +} // namespace + +// RenderWidgetHostView -------------------------------------------------------- + +// static +RenderWidgetHostView* RenderWidgetHostView::CreateViewForWidget( + RenderWidgetHost* widget) { + return new RenderWidgetHostViewWin(widget); +} + +/////////////////////////////////////////////////////////////////////////////// +// RenderWidgetHostViewWin, public: + +RenderWidgetHostViewWin::RenderWidgetHostViewWin(RenderWidgetHost* widget) + : render_widget_host_(widget), + track_mouse_leave_(false), + ime_notification_(false), + is_hidden_(false), + close_on_deactivate_(false), + tooltip_hwnd_(NULL), + tooltip_showing_(false), + shutdown_factory_(this), + parent_hwnd_(NULL), + is_loading_(false), + activatable_(true) { + render_widget_host_->set_view(this); + renderer_accessible_ = + CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableRendererAccessibility); +} + +RenderWidgetHostViewWin::~RenderWidgetHostViewWin() { + ResetTooltip(); +} + +/////////////////////////////////////////////////////////////////////////////// +// RenderWidgetHostViewWin, RenderWidgetHostView implementation: + +RenderWidgetHost* RenderWidgetHostViewWin::GetRenderWidgetHost() const { + return render_widget_host_; +} + +void RenderWidgetHostViewWin::DidBecomeSelected() { + if (!is_hidden_) + return; + + is_hidden_ = false; + EnsureTooltip(); + render_widget_host_->WasRestored(); +} + +void RenderWidgetHostViewWin::WasHidden() { + if (is_hidden_) + return; + + // If we receive any more paint messages while we are hidden, we want to + // ignore them so we don't re-allocate the backing store. We will paint + // everything again when we become selected again. + is_hidden_ = true; + + ResetTooltip(); + + // If we have a renderer, then inform it that we are being hidden so it can + // reduce its resource utilization. + render_widget_host_->WasHidden(); + + // TODO(darin): what about constrained windows? it doesn't look like they + // see a message when their parent is hidden. maybe there is something more + // generic we can do at the TabContents API level instead of relying on + // Windows messages. +} + +void RenderWidgetHostViewWin::SetSize(const gfx::Size& size) { + if (is_hidden_) + return; + + // No SWP_NOREDRAW as autofill popups can resize and the underneath window + // should redraw in that case. + UINT swp_flags = SWP_NOSENDCHANGING | SWP_NOOWNERZORDER | SWP_NOCOPYBITS | + SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_DEFERERASE; + SetWindowPos(NULL, 0, 0, size.width(), size.height(), swp_flags); + render_widget_host_->WasResized(); + EnsureTooltip(); +} + +HWND RenderWidgetHostViewWin::GetPluginHWND() { + return m_hWnd; +} + +void RenderWidgetHostViewWin::ForwardMouseEventToRenderer(UINT message, + WPARAM wparam, + LPARAM lparam) { + WebMouseEvent event(m_hWnd, message, wparam, lparam); + switch (event.type) { + case WebInputEvent::MOUSE_MOVE: + TrackMouseLeave(true); + break; + case WebInputEvent::MOUSE_LEAVE: + TrackMouseLeave(false); + break; + case WebInputEvent::MOUSE_DOWN: + SetCapture(); + break; + case WebInputEvent::MOUSE_UP: + if (GetCapture() == m_hWnd) + ReleaseCapture(); + break; + } + + render_widget_host_->ForwardMouseEvent(event); + + if (activatable_ && event.type == WebInputEvent::MOUSE_DOWN) { + // This is a temporary workaround for bug 765011 to get focus when the + // mouse is clicked. This happens after the mouse down event is sent to + // the renderer because normally Windows does a WM_SETFOCUS after + // WM_LBUTTONDOWN. + SetFocus(); + } +} + +void RenderWidgetHostViewWin::Focus() { + if (IsWindow()) + SetFocus(); +} + +void RenderWidgetHostViewWin::Blur() { + views::FocusManager* focus_manager = + views::FocusManager::GetFocusManager(GetParent()); + // We don't have a FocusManager if we are hidden. + if (focus_manager && render_widget_host_->CanBlur()) + focus_manager->ClearFocus(); +} + +bool RenderWidgetHostViewWin::HasFocus() { + return ::GetFocus() == m_hWnd; +} + +void RenderWidgetHostViewWin::Show() { + DCHECK(parent_hwnd_); + SetParent(parent_hwnd_); + ShowWindow(SW_SHOW); + + DidBecomeSelected(); +} + +void RenderWidgetHostViewWin::Hide() { + if (::GetFocus() == m_hWnd) + ::SetFocus(NULL); + ShowWindow(SW_HIDE); + parent_hwnd_ = GetParent(); + // Orphan the window so we stop receiving messages. + SetParent(NULL); + + WasHidden(); +} + +gfx::Rect RenderWidgetHostViewWin::GetViewBounds() const { + CRect window_rect; + GetWindowRect(&window_rect); + return gfx::Rect(window_rect); +} + +void RenderWidgetHostViewWin::UpdateCursor(const WebCursor& cursor) { + current_cursor_ = cursor; + UpdateCursorIfOverSelf(); +} + +void RenderWidgetHostViewWin::UpdateCursorIfOverSelf() { + static HCURSOR kCursorArrow = LoadCursor(NULL, IDC_ARROW); + static HCURSOR kCursorAppStarting = LoadCursor(NULL, IDC_APPSTARTING); + static HINSTANCE module_handle = + GetModuleHandle(chrome::kBrowserResourcesDll); + + // We cannot pass in NULL as the module handle as this would only work for + // standard win32 cursors. We can also receive cursor types which are defined + // as webkit resources. We need to specify the module handle of chrome.dll + // while loading these cursors. + HCURSOR display_cursor = current_cursor_.GetCursor(module_handle); + + // If a page is in the loading state, we want to show the Arrow+Hourglass + // cursor only when the current cursor is the ARROW cursor. In all other + // cases we should continue to display the current cursor. + if (is_loading_ && display_cursor == kCursorArrow) + display_cursor = kCursorAppStarting; + + // If the mouse is over our HWND, then update the cursor state immediately. + CPoint pt; + GetCursorPos(&pt); + if (WindowFromPoint(pt) == m_hWnd) + SetCursor(display_cursor); +} + +void RenderWidgetHostViewWin::SetIsLoading(bool is_loading) { + is_loading_ = is_loading; + UpdateCursorIfOverSelf(); +} + +void RenderWidgetHostViewWin::IMEUpdateStatus(ViewHostMsg_ImeControl control, + const gfx::Rect& caret_rect) { + if (control == IME_DISABLE) { + ime_input_.DisableIME(m_hWnd); + } else { + ime_input_.EnableIME(m_hWnd, caret_rect, + control == IME_COMPLETE_COMPOSITION); + } +} + +BOOL CALLBACK EnumChildProc(HWND hwnd, LPARAM lparam) { + if (!WebPluginDelegateImpl::IsPluginDelegateWindow(hwnd)) + return TRUE; + + gfx::Rect* rect = reinterpret_cast<gfx::Rect*>(lparam); + static UINT msg = RegisterWindowMessage(kPaintMessageName); + WPARAM wparam = rect->x() << 16 | rect->y(); + lparam = rect->width() << 16 | rect->height(); + + // SendMessage gets the message across much quicker than PostMessage, since it + // doesn't get queued. When the plugin thread calls PeekMessage or other + // Win32 APIs, sent messages are dispatched automatically. + SendNotifyMessage(hwnd, msg, wparam, lparam); + + return TRUE; +} + +void RenderWidgetHostViewWin::Redraw(const gfx::Rect& rect) { + // Paint the invalid region synchronously. Our caller will not paint again + // until we return, so by painting to the screen here, we ensure effective + // rate-limiting of backing store updates. This helps a lot on pages that + // have animations or fairly expensive layout (e.g., google maps). + // + // We paint this window synchronously, however child windows (i.e. plugins) + // are painted asynchronously. By avoiding synchronous cross-process window + // message dispatching we allow scrolling to be smooth, and also avoid the + // browser process locking up if the plugin process is hung. + // + RedrawWindow( + &rect.ToRECT(), NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_NOCHILDREN); + + // Send the invalid rect in screen coordinates. + gfx::Rect screen_rect = GetViewBounds(); + gfx::Rect invalid_screen_rect = rect; + invalid_screen_rect.Offset(screen_rect.x(), screen_rect.y()); + + LPARAM lparam = reinterpret_cast<LPARAM>(&invalid_screen_rect); + EnumChildWindows(m_hWnd, EnumChildProc, lparam); +} + +void RenderWidgetHostViewWin::DidPaintRect(const gfx::Rect& rect) { + if (is_hidden_) + return; + + Redraw(rect); +} + +void RenderWidgetHostViewWin::DidScrollRect( + const gfx::Rect& rect, int dx, int dy) { + if (is_hidden_) + return; + + // We need to pass in SW_INVALIDATE to ScrollWindowEx. The MSDN + // documentation states that it only applies to the HRGN argument, which is + // wrong. Not passing in this flag does not invalidate the region which was + // scrolled from, thus causing painting issues. + RECT clip_rect = rect.ToRECT(); + ScrollWindowEx(dx, dy, NULL, &clip_rect, NULL, NULL, SW_INVALIDATE); + + RECT invalid_rect = {0}; + GetUpdateRect(&invalid_rect); + Redraw(gfx::Rect(invalid_rect)); +} + +void RenderWidgetHostViewWin::RendererGone() { + // TODO(darin): keep this around, and draw sad-tab into it. + UpdateCursorIfOverSelf(); + DestroyWindow(); +} + +void RenderWidgetHostViewWin::Destroy() { + // We've been told to destroy. + // By clearing close_on_deactivate_, we prevent further deactivations + // (caused by windows messages resulting from the DestroyWindow) from + // triggering further destructions. The deletion of this is handled by + // OnFinalMessage(); + close_on_deactivate_ = false; + DestroyWindow(); +} + +void RenderWidgetHostViewWin::SetTooltipText(const std::wstring& tooltip_text) { + if (tooltip_text != tooltip_text_) { + tooltip_text_ = tooltip_text; + + // Clamp the tooltip length to kMaxTooltipLength so that we don't + // accidentally DOS the user with a mega tooltip (since Windows doesn't seem + // to do this itself). + if (tooltip_text_.length() > kMaxTooltipLength) + tooltip_text_ = tooltip_text_.substr(0, kMaxTooltipLength); + + // Need to check if the tooltip is already showing so that we don't + // immediately show the tooltip with no delay when we move the mouse from + // a region with no tooltip to a region with a tooltip. + if (::IsWindow(tooltip_hwnd_) && tooltip_showing_) { + ::SendMessage(tooltip_hwnd_, TTM_POP, 0, 0); + ::SendMessage(tooltip_hwnd_, TTM_POPUP, 0, 0); + } + } else { + // Make sure the tooltip gets closed after TTN_POP gets sent. For some + // reason this doesn't happen automatically, so moving the mouse around + // within the same link/image/etc doesn't cause the tooltip to re-appear. + if (!tooltip_showing_) { + if (::IsWindow(tooltip_hwnd_)) + ::SendMessage(tooltip_hwnd_, TTM_POP, 0, 0); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// RenderWidgetHostViewWin, private: + +LRESULT RenderWidgetHostViewWin::OnCreate(CREATESTRUCT* create_struct) { + // Call the WM_INPUTLANGCHANGE message handler to initialize the input locale + // of a browser process. + OnInputLangChange(0, 0); + return 0; +} + +void RenderWidgetHostViewWin::OnActivate(UINT action, BOOL minimized, + HWND window) { + // If the container is a popup, clicking elsewhere on screen should close the + // popup. + if (close_on_deactivate_ && action == WA_INACTIVE) { + // Send a windows message so that any derived classes + // will get a change to override the default handling + SendMessage(WM_CANCELMODE); + } +} + +void RenderWidgetHostViewWin::OnDestroy() { + ResetTooltip(); + TrackMouseLeave(false); +} + +void RenderWidgetHostViewWin::OnPaint(HDC dc) { + DCHECK(render_widget_host_->process()->channel()); + + CPaintDC paint_dc(m_hWnd); + HBRUSH white_brush = reinterpret_cast<HBRUSH>(GetStockObject(WHITE_BRUSH)); + + RenderWidgetHost::BackingStore* backing_store = + render_widget_host_->GetBackingStore(); + + if (backing_store) { + gfx::Rect damaged_rect(paint_dc.m_ps.rcPaint); + + gfx::Rect bitmap_rect( + 0, 0, backing_store->size().width(), backing_store->size().height()); + + gfx::Rect paint_rect = bitmap_rect.Intersect(damaged_rect); + if (!paint_rect.IsEmpty()) { + BitBlt(paint_dc.m_hDC, + paint_rect.x(), + paint_rect.y(), + paint_rect.width(), + paint_rect.height(), + backing_store->dc(), + paint_rect.x(), + paint_rect.y(), + SRCCOPY); + } + + // Fill the remaining portion of the damaged_rect with white + if (damaged_rect.right() > bitmap_rect.right()) { + RECT r; + r.left = std::max(bitmap_rect.right(), damaged_rect.x()); + r.right = damaged_rect.right(); + r.top = damaged_rect.y(); + r.bottom = std::min(bitmap_rect.bottom(), damaged_rect.bottom()); + paint_dc.FillRect(&r, white_brush); + } + if (damaged_rect.bottom() > bitmap_rect.bottom()) { + RECT r; + r.left = damaged_rect.x(); + r.right = damaged_rect.right(); + r.top = std::max(bitmap_rect.bottom(), damaged_rect.y()); + r.bottom = damaged_rect.bottom(); + paint_dc.FillRect(&r, white_brush); + } + if (!whiteout_start_time_.is_null()) { + TimeDelta whiteout_duration = TimeTicks::Now() - whiteout_start_time_; + + // If field trial is active, report results in special histogram. + static scoped_refptr<FieldTrial> trial( + FieldTrialList::Find(BrowserTrial::kMemoryModelFieldTrial)); + if (trial.get()) { + if (trial->boolean_value()) + UMA_HISTOGRAM_TIMES(L"MPArch.RWHH_WhiteoutDuration_trial_high_memory", + whiteout_duration); + else + UMA_HISTOGRAM_TIMES(L"MPArch.RWHH_WhiteoutDuration_trial_med_memory", + whiteout_duration); + } else { + UMA_HISTOGRAM_TIMES(L"MPArch.RWHH_WhiteoutDuration", whiteout_duration); + } + + // Reset the start time to 0 so that we start recording again the next + // time the backing store is NULL... + whiteout_start_time_ = TimeTicks(); + } + } else { + paint_dc.FillRect(&paint_dc.m_ps.rcPaint, white_brush); + if (whiteout_start_time_.is_null()) + whiteout_start_time_ = TimeTicks::Now(); + } +} + +void RenderWidgetHostViewWin::OnNCPaint(HRGN update_region) { + // Do nothing. This suppresses the resize corner that Windows would + // otherwise draw for us. +} + +LRESULT RenderWidgetHostViewWin::OnEraseBkgnd(HDC dc) { + return 1; +} + +LRESULT RenderWidgetHostViewWin::OnSetCursor(HWND window, UINT hittest_code, + UINT mouse_message_id) { + UpdateCursorIfOverSelf(); + return 0; +} + +void RenderWidgetHostViewWin::OnSetFocus(HWND window) { + render_widget_host_->Focus(); +} + +void RenderWidgetHostViewWin::OnKillFocus(HWND window) { + render_widget_host_->Blur(); +} + +void RenderWidgetHostViewWin::OnCaptureChanged(HWND window) { + render_widget_host_->LostCapture(); +} + +void RenderWidgetHostViewWin::OnCancelMode() { + render_widget_host_->LostCapture(); + + if (close_on_deactivate_ && shutdown_factory_.empty()) { + // Dismiss popups and menus. We do this asynchronously to avoid changing + // activation within this callstack, which may interfere with another window + // being activated. We can synchronously hide the window, but we need to + // not change activation while doing so. + SetWindowPos(NULL, 0, 0, 0, 0, + SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOMOVE | + SWP_NOREPOSITION | SWP_NOSIZE | SWP_NOZORDER); + MessageLoop::current()->PostTask(FROM_HERE, + shutdown_factory_.NewRunnableMethod( + &RenderWidgetHostViewWin::ShutdownHost)); + } +} + +void RenderWidgetHostViewWin::OnInputLangChange(DWORD character_set, + HKL input_language_id) { + // Send the given Locale ID to the ImeInput object and retrieves whether + // or not the current input context has IMEs. + // If the current input context has IMEs, a browser process has to send a + // request to a renderer process that it needs status messages about + // the focused edit control from the renderer process. + // On the other hand, if the current input context does not have IMEs, the + // browser process also has to send a request to the renderer process that + // it does not need the status messages any longer. + // To minimize the number of this notification request, we should check if + // the browser process is actually retrieving the status messages (this + // state is stored in ime_notification_) and send a request only if the + // browser process has to update this status, its details are listed below: + // * If a browser process is not retrieving the status messages, + // (i.e. ime_notification_ == false), + // send this request only if the input context does have IMEs, + // (i.e. ime_status == true); + // When it successfully sends the request, toggle its notification status, + // (i.e.ime_notification_ = !ime_notification_ = true). + // * If a browser process is retrieving the status messages + // (i.e. ime_notification_ == true), + // send this request only if the input context does not have IMEs, + // (i.e. ime_status == false). + // When it successfully sends the request, toggle its notification status, + // (i.e.ime_notification_ = !ime_notification_ = false). + // To analyze the above actions, we can optimize them into the ones + // listed below: + // 1 Sending a request only if ime_status_ != ime_notification_, and; + // 2 Copying ime_status to ime_notification_ if it sends the request + // successfully (because Action 1 shows ime_status = !ime_notification_.) + bool ime_status = ime_input_.SetInputLanguage(); + if (ime_status != ime_notification_) { + if (Send(new ViewMsg_ImeSetInputMode(render_widget_host_->routing_id(), + ime_status))) { + ime_notification_ = ime_status; + } + } +} + +void RenderWidgetHostViewWin::OnThemeChanged() { + render_widget_host_->SystemThemeChanged(); +} + +LRESULT RenderWidgetHostViewWin::OnNotify(int w_param, NMHDR* header) { + if (tooltip_hwnd_ == NULL) + return 0; + + switch (header->code) { + case TTN_GETDISPINFO: { + NMTTDISPINFOW* tooltip_info = reinterpret_cast<NMTTDISPINFOW*>(header); + tooltip_info->szText[0] = L'\0'; + tooltip_info->lpszText = const_cast<wchar_t*>(tooltip_text_.c_str()); + ::SendMessage( + tooltip_hwnd_, TTM_SETMAXTIPWIDTH, 0, kTooltipMaxWidthPixels); + SetMsgHandled(TRUE); + break; + } + case TTN_POP: + tooltip_showing_ = false; + SetMsgHandled(TRUE); + break; + case TTN_SHOW: + tooltip_showing_ = true; + SetMsgHandled(TRUE); + break; + } + return 0; +} + +LRESULT RenderWidgetHostViewWin::OnImeSetContext( + UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { + // We need status messages about the focused input control from a + // renderer process when: + // * the current input context has IMEs, and; + // * an application is activated. + // This seems to tell we should also check if the current input context has + // IMEs before sending a request, however, this WM_IME_SETCONTEXT is + // fortunately sent to an application only while the input context has IMEs. + // Therefore, we just start/stop status messages according to the activation + // status of this application without checks. + bool activated = (wparam == TRUE); + if (Send(new ViewMsg_ImeSetInputMode( + render_widget_host_->routing_id(), activated))) { + ime_notification_ = activated; + } + + if (ime_notification_) + ime_input_.CreateImeWindow(m_hWnd); + + ime_input_.CleanupComposition(m_hWnd); + ime_input_.SetImeWindowStyle(m_hWnd, message, wparam, lparam, &handled); + return 0; +} + +LRESULT RenderWidgetHostViewWin::OnImeStartComposition( + UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { + // Reset the composition status and create IME windows. + ime_input_.CreateImeWindow(m_hWnd); + ime_input_.ResetComposition(m_hWnd); + // We have to prevent WTL from calling ::DefWindowProc() because the function + // calls ::ImmSetCompositionWindow() and ::ImmSetCandidateWindow() to + // over-write the position of IME windows. + handled = TRUE; + return 0; +} + +LRESULT RenderWidgetHostViewWin::OnImeComposition( + UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { + // At first, update the position of the IME window. + ime_input_.UpdateImeWindow(m_hWnd); + + // Retrieve the result string and its attributes of the ongoing composition + // and send it to a renderer process. + ImeComposition composition; + if (ime_input_.GetResult(m_hWnd, lparam, &composition)) { + Send(new ViewMsg_ImeSetComposition(render_widget_host_->routing_id(), + composition.string_type, + composition.cursor_position, + composition.target_start, + composition.target_end, + composition.ime_string)); + ime_input_.ResetComposition(m_hWnd); + // Fall though and try reading the composition string. + // Japanese IMEs send a message containing both GCS_RESULTSTR and + // GCS_COMPSTR, which means an ongoing composition has been finished + // by the start of another composition. + } + // Retrieve the composition string and its attributes of the ongoing + // composition and send it to a renderer process. + if (ime_input_.GetComposition(m_hWnd, lparam, &composition)) { + Send(new ViewMsg_ImeSetComposition(render_widget_host_->routing_id(), + composition.string_type, + composition.cursor_position, + composition.target_start, + composition.target_end, + composition.ime_string)); + } + // We have to prevent WTL from calling ::DefWindowProc() because we do not + // want for the IMM (Input Method Manager) to send WM_IME_CHAR messages. + handled = TRUE; + return 0; +} + +LRESULT RenderWidgetHostViewWin::OnImeEndComposition( + UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { + if (ime_input_.is_composing()) { + // A composition has been ended while there is an ongoing composition, + // i.e. the ongoing composition has been canceled. + // We need to reset the composition status both of the ImeInput object and + // of the renderer process. + std::wstring empty_string; + Send(new ViewMsg_ImeSetComposition(render_widget_host_->routing_id(), + 0, -1, -1, -1, empty_string)); + ime_input_.ResetComposition(m_hWnd); + } + ime_input_.DestroyImeWindow(m_hWnd); + // Let WTL call ::DefWindowProc() and release its resources. + handled = FALSE; + return 0; +} + +LRESULT RenderWidgetHostViewWin::OnMouseEvent(UINT message, WPARAM wparam, + LPARAM lparam, BOOL& handled) { + handled = TRUE; + + if (::IsWindow(tooltip_hwnd_)) { + // Forward mouse events through to the tooltip window + MSG msg; + msg.hwnd = m_hWnd; + msg.message = message; + msg.wParam = wparam; + msg.lParam = lparam; + SendMessage(tooltip_hwnd_, TTM_RELAYEVENT, NULL, + reinterpret_cast<LPARAM>(&msg)); + } + + // TODO(jcampan): I am not sure if we should forward the message to the + // WebContents first in the case of popups. If we do, we would need to + // convert the click from the popup window coordinates to the WebContents' + // window coordinates. For now we don't forward the message in that case to + // address bug #907474. + // Note: GetParent() on popup windows returns the top window and not the + // parent the window was created with (the parent and the owner of the popup + // is the first non-child view of the view that was specified to the create + // call). So the WebContents window would have to be specified to the + // RenderViewHostHWND as there is no way to retrieve it from the HWND. + if (!close_on_deactivate_) { // Don't forward if the container is a popup. + switch (message) { + case WM_LBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_MOUSEMOVE: + case WM_MOUSELEAVE: + case WM_RBUTTONDOWN: { + // Give the WebContents first crack at the message. It may want to + // prevent forwarding to the renderer if some higher level browser + // functionality is invoked. + if (SendMessage(GetParent(), message, wparam, lparam) != 0) + return 1; + } + } + } + + ForwardMouseEventToRenderer(message, wparam, lparam); + return 0; +} + +LRESULT RenderWidgetHostViewWin::OnKeyEvent(UINT message, WPARAM wparam, + LPARAM lparam, BOOL& handled) { + handled = TRUE; + + // If we are a pop-up, forward tab related messages to our parent HWND, so + // that we are dismissed appropriately and so that the focus advance in our + // parent. + // TODO(jcampan): http://b/issue?id=1192881 Could be abstracted in the + // FocusManager. + if (close_on_deactivate_ && + (((message == WM_KEYDOWN || message == WM_KEYUP) && (wparam == VK_TAB)) || + (message == WM_CHAR && wparam == L'\t'))) { + DCHECK(parent_hwnd_); + // First close the pop-up. + SendMessage(WM_CANCELMODE); + // Then move the focus by forwarding the tab key to the parent. + return ::SendMessage(parent_hwnd_, message, wparam, lparam); + } + + render_widget_host_->ForwardKeyboardEvent( + WebKeyboardEvent(m_hWnd, message, wparam, lparam)); + return 0; +} + +LRESULT RenderWidgetHostViewWin::OnWheelEvent(UINT message, WPARAM wparam, + LPARAM lparam, BOOL& handled) { + // Workaround for Thinkpad mousewheel driver. We get mouse wheel/scroll + // messages even if we are not in the foreground. So here we check if + // we have any owned popup windows in the foreground and dismiss them. + if (m_hWnd != GetForegroundWindow()) { + HWND toplevel_hwnd = ::GetAncestor(m_hWnd, GA_ROOT); + EnumThreadWindows( + GetCurrentThreadId(), + DismissOwnedPopups, + reinterpret_cast<LPARAM>(toplevel_hwnd)); + } + + // This is a bit of a hack, but will work for now since we don't want to + // pollute this object with WebContents-specific functionality... + bool handled_by_webcontents = false; + if (GetParent()) { + // Use a special reflected message to break recursion. If we send + // WM_MOUSEWHEEL, the focus manager subclass of web contents will + // route it back here. + MSG new_message = {0}; + new_message.hwnd = m_hWnd; + new_message.message = message; + new_message.wParam = wparam; + new_message.lParam = lparam; + + handled_by_webcontents = + !!::SendMessage(GetParent(), views::kReflectedMessage, 0, + reinterpret_cast<LPARAM>(&new_message)); + } + + if (!handled_by_webcontents) { + render_widget_host_->ForwardWheelEvent( + WebMouseWheelEvent(m_hWnd, message, wparam, lparam)); + } + handled = TRUE; + return 0; +} + +LRESULT RenderWidgetHostViewWin::OnMouseActivate(UINT, WPARAM, LPARAM, + BOOL& handled) { + if (!activatable_) + return MA_NOACTIVATE; + + HWND focus_window = GetFocus(); + if (!::IsWindow(focus_window) || !IsChild(focus_window)) { + // We handle WM_MOUSEACTIVATE to set focus to the underlying plugin + // child window. This is to ensure that keyboard events are received + // by the plugin. The correct way to fix this would be send over + // an event to the renderer which would then eventually send over + // a setFocus call to the plugin widget. This would ensure that + // the renderer (webkit) knows about the plugin widget receiving + // focus. + // TODO(iyengar) Do the right thing as per the above comment. + POINT cursor_pos = {0}; + ::GetCursorPos(&cursor_pos); + ::ScreenToClient(m_hWnd, &cursor_pos); + HWND child_window = ::RealChildWindowFromPoint(m_hWnd, cursor_pos); + if (::IsWindow(child_window)) { + ::SetFocus(child_window); + return MA_NOACTIVATE; + } + } + handled = FALSE; + return MA_ACTIVATE; +} + +LRESULT RenderWidgetHostViewWin::OnGetObject(UINT message, WPARAM wparam, + LPARAM lparam, BOOL& handled) { + LRESULT reference_result = static_cast<LRESULT>(0L); + // TODO(jcampan): http://b/issue?id=1432077 Disabling accessibility in the + // renderer is a temporary work-around until that bug is fixed. + if (!renderer_accessible_) + return reference_result; + + // Accessibility readers will send an OBJID_CLIENT message. + if (OBJID_CLIENT == lparam) { + // If our MSAA DOM root is already created, reuse that pointer. Otherwise, + // create a new one. + if (!browser_accessibility_root_) { + CComObject<BrowserAccessibility>* accessibility = NULL; + + if (!SUCCEEDED(CComObject<BrowserAccessibility>::CreateInstance( + &accessibility)) || !accessibility) { + // Return with failure. + return static_cast<LRESULT>(0L); + } + + CComPtr<IAccessible> accessibility_comptr(accessibility); + + // Root id is always 0, to distinguish this particular instance when + // mapping to the render-side IAccessible. + accessibility->set_iaccessible_id(0); + + // Set the unique member variables of this particular process. + accessibility->set_instance_id( + BrowserAccessibilityManager::GetInstance()-> + SetMembers(accessibility, m_hWnd, render_widget_host_)); + + // All is well, assign the temp instance to the class smart pointer. + browser_accessibility_root_.Attach(accessibility_comptr.Detach()); + + if (!browser_accessibility_root_) { + // Paranoia check. Return with failure. + NOTREACHED(); + return static_cast<LRESULT>(0L); + } + + // Notify that an instance of IAccessible was allocated for m_hWnd. + ::NotifyWinEvent(EVENT_OBJECT_CREATE, m_hWnd, OBJID_CLIENT, + CHILDID_SELF); + } + + // Create a reference to ViewAccessibility that MSAA will marshall + // to the client. + reference_result = LresultFromObject(IID_IAccessible, wparam, + static_cast<IAccessible*>(browser_accessibility_root_)); + } + return reference_result; +} + +void RenderWidgetHostViewWin::OnFinalMessage(HWND window) { + render_widget_host_->ViewDestroyed(); + delete this; +} + +void RenderWidgetHostViewWin::TrackMouseLeave(bool track) { + if (track == track_mouse_leave_) + return; + track_mouse_leave_ = track; + + DCHECK(m_hWnd); + + TRACKMOUSEEVENT tme; + tme.cbSize = sizeof(TRACKMOUSEEVENT); + tme.dwFlags = TME_LEAVE; + if (!track_mouse_leave_) + tme.dwFlags |= TME_CANCEL; + tme.hwndTrack = m_hWnd; + + TrackMouseEvent(&tme); +} + +bool RenderWidgetHostViewWin::Send(IPC::Message* message) { + return render_widget_host_->Send(message); +} + +void RenderWidgetHostViewWin::EnsureTooltip() { + UINT message = TTM_NEWTOOLRECT; + + TOOLINFO ti; + ti.cbSize = sizeof(ti); + ti.hwnd = m_hWnd; + ti.uId = 0; + if (!::IsWindow(tooltip_hwnd_)) { + message = TTM_ADDTOOL; + tooltip_hwnd_ = CreateWindowEx( + WS_EX_TRANSPARENT | l10n_util::GetExtendedTooltipStyles(), + TOOLTIPS_CLASS, NULL, TTS_NOPREFIX, 0, 0, 0, 0, m_hWnd, NULL, + NULL, NULL); + ti.uFlags = TTF_TRANSPARENT; + ti.lpszText = LPSTR_TEXTCALLBACK; + } + + CRect cr; + GetClientRect(&ti.rect); + SendMessage(tooltip_hwnd_, message, NULL, reinterpret_cast<LPARAM>(&ti)); +} + +void RenderWidgetHostViewWin::ResetTooltip() { + if (::IsWindow(tooltip_hwnd_)) + ::DestroyWindow(tooltip_hwnd_); + tooltip_hwnd_ = NULL; +} + +void RenderWidgetHostViewWin::ShutdownHost() { + shutdown_factory_.RevokeAll(); + render_widget_host_->Shutdown(); + // Do not touch any members at this point, |this| has been deleted. +} diff --git a/chrome/browser/renderer_host/render_widget_host_view_win.h b/chrome/browser/renderer_host/render_widget_host_view_win.h new file mode 100644 index 0000000..a735ab0 --- /dev/null +++ b/chrome/browser/renderer_host/render_widget_host_view_win.h @@ -0,0 +1,283 @@ +// 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. + +#ifndef CHROME_BROWSER_RENDERER_HOST_RENDER_WIDGET_HOST_VIEW_WIN_H_ +#define CHROME_BROWSER_RENDERER_HOST_RENDER_WIDGET_HOST_VIEW_WIN_H_ + +#include <atlbase.h> +#include <atlapp.h> +#include <atlcrack.h> +#include <atlmisc.h> + +#include "base/basictypes.h" +#include "base/gfx/size.h" +#include "base/scoped_handle.h" +#include "base/shared_memory.h" +#include "base/task.h" +#include "chrome/browser/ime_input.h" +#include "chrome/browser/renderer_host/render_widget_host_view.h" +#include "chrome/common/render_messages.h" +#include "chrome/views/focus_manager.h" + +namespace gfx { +class Rect; +} +namespace IPC { +class Message; +} +class RenderWidgetHost; +class WebMouseEvent; +class WebCursor; + +typedef CWinTraits<WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0> + RenderWidgetHostHWNDTraits; + +static const wchar_t* const kRenderWidgetHostHWNDClass = + L"Chrome_RenderWidgetHostHWND"; + +/////////////////////////////////////////////////////////////////////////////// +// RenderWidgetHostViewWin +// +// An object representing the "View" of a rendered web page. This object is +// responsible for displaying the content of the web page, receiving windows +// messages, and containing plugins HWNDs. It is the implementation of the +// RenderWidgetHostView that the cross-platform RenderWidgetHost object uses +// to display the data. +// +// Comment excerpted from render_widget_host.h: +// +// "The lifetime of the RenderWidgetHostHWND is tied to the render process. +// If the render process dies, the RenderWidgetHostHWND goes away and all +// references to it must become NULL." +// +class RenderWidgetHostViewWin : + public CWindowImpl<RenderWidgetHostViewWin, + CWindow, + RenderWidgetHostHWNDTraits>, + public RenderWidgetHostView { + public: + // The view will associate itself with the given widget. + explicit RenderWidgetHostViewWin(RenderWidgetHost* widget); + virtual ~RenderWidgetHostViewWin(); + + void set_close_on_deactivate(bool close_on_deactivate) { + close_on_deactivate_ = close_on_deactivate; + } + + void set_activatable(bool activatable) { + activatable_ = activatable; + } + bool activatable() const { return activatable_; } + + void set_parent_hwnd(HWND parent) { parent_hwnd_ = parent; } + + DECLARE_WND_CLASS_EX(kRenderWidgetHostHWNDClass, CS_DBLCLKS, 0); + + BEGIN_MSG_MAP(RenderWidgetHostHWND) + MSG_WM_CREATE(OnCreate) + MSG_WM_ACTIVATE(OnActivate) + MSG_WM_DESTROY(OnDestroy) + MSG_WM_PAINT(OnPaint) + MSG_WM_NCPAINT(OnNCPaint) + MSG_WM_ERASEBKGND(OnEraseBkgnd) + MSG_WM_SETCURSOR(OnSetCursor) + MSG_WM_SETFOCUS(OnSetFocus) + MSG_WM_KILLFOCUS(OnKillFocus) + MSG_WM_CAPTURECHANGED(OnCaptureChanged) + MSG_WM_CANCELMODE(OnCancelMode) + MSG_WM_INPUTLANGCHANGE(OnInputLangChange) + MSG_WM_THEMECHANGED(OnThemeChanged) + MSG_WM_NOTIFY(OnNotify) + MESSAGE_HANDLER(WM_IME_SETCONTEXT, OnImeSetContext) + MESSAGE_HANDLER(WM_IME_STARTCOMPOSITION, OnImeStartComposition) + MESSAGE_HANDLER(WM_IME_COMPOSITION, OnImeComposition) + MESSAGE_HANDLER(WM_IME_ENDCOMPOSITION, OnImeEndComposition) + MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseEvent) + MESSAGE_HANDLER(WM_MOUSELEAVE, OnMouseEvent) + MESSAGE_HANDLER(WM_LBUTTONDOWN, OnMouseEvent) + MESSAGE_HANDLER(WM_MBUTTONDOWN, OnMouseEvent) + MESSAGE_HANDLER(WM_RBUTTONDOWN, OnMouseEvent) + MESSAGE_HANDLER(WM_LBUTTONUP, OnMouseEvent) + MESSAGE_HANDLER(WM_MBUTTONUP, OnMouseEvent) + MESSAGE_HANDLER(WM_RBUTTONUP, OnMouseEvent) + MESSAGE_HANDLER(WM_LBUTTONDBLCLK, OnMouseEvent) + MESSAGE_HANDLER(WM_MBUTTONDBLCLK, OnMouseEvent) + MESSAGE_HANDLER(WM_RBUTTONDBLCLK, OnMouseEvent) + MESSAGE_HANDLER(WM_SYSKEYDOWN, OnKeyEvent) + MESSAGE_HANDLER(WM_SYSKEYUP, OnKeyEvent) + MESSAGE_HANDLER(WM_KEYDOWN, OnKeyEvent) + MESSAGE_HANDLER(WM_KEYUP, OnKeyEvent) + MESSAGE_HANDLER(WM_MOUSEWHEEL, OnWheelEvent) + MESSAGE_HANDLER(WM_MOUSEHWHEEL, OnWheelEvent) + MESSAGE_HANDLER(WM_HSCROLL, OnWheelEvent) + MESSAGE_HANDLER(WM_VSCROLL, OnWheelEvent) + MESSAGE_HANDLER(WM_CHAR, OnKeyEvent) + MESSAGE_HANDLER(WM_SYSCHAR, OnKeyEvent) + MESSAGE_HANDLER(WM_IME_CHAR, OnKeyEvent) + MESSAGE_HANDLER(WM_MOUSEACTIVATE, OnMouseActivate) + MESSAGE_HANDLER(WM_GETOBJECT, OnGetObject) + END_MSG_MAP() + + // Implementation of RenderWidgetHostView: + virtual RenderWidgetHost* GetRenderWidgetHost() const; + virtual void DidBecomeSelected(); + virtual void WasHidden(); + virtual void SetSize(const gfx::Size& size); + virtual HWND GetPluginHWND(); + virtual void ForwardMouseEventToRenderer(UINT message, + WPARAM wparam, + LPARAM lparam); + virtual void Focus(); + virtual void Blur(); + virtual bool HasFocus(); + virtual void Show(); + virtual void Hide(); + virtual gfx::Rect GetViewBounds() const; + virtual void UpdateCursor(const WebCursor& cursor); + virtual void UpdateCursorIfOverSelf(); + virtual void SetIsLoading(bool is_loading); + virtual void IMEUpdateStatus(ViewHostMsg_ImeControl control, + const gfx::Rect& caret_rect); + virtual void DidPaintRect(const gfx::Rect& rect); + virtual void DidScrollRect(const gfx::Rect& rect, int dx, int dy); + virtual void RendererGone(); + virtual void Destroy(); + virtual void SetTooltipText(const std::wstring& tooltip_text); + + protected: + // Windows Message Handlers + LRESULT OnCreate(CREATESTRUCT* create_struct); + void OnActivate(UINT, BOOL, HWND); + void OnDestroy(); + void OnPaint(HDC dc); + void OnNCPaint(HRGN update_region); + LRESULT OnEraseBkgnd(HDC dc); + LRESULT OnSetCursor(HWND window, UINT hittest_code, UINT mouse_message_id); + void OnSetFocus(HWND window); + void OnKillFocus(HWND window); + void OnCaptureChanged(HWND window); + void OnCancelMode(); + void OnInputLangChange(DWORD character_set, HKL input_language_id); + void OnThemeChanged(); + LRESULT OnNotify(int w_param, NMHDR* header); + LRESULT OnImeSetContext( + UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled); + LRESULT OnImeStartComposition( + UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled); + LRESULT OnImeComposition( + UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled); + LRESULT OnImeEndComposition( + UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled); + LRESULT OnMouseEvent( + UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled); + LRESULT OnKeyEvent( + UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled); + LRESULT OnWheelEvent( + UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled); + LRESULT OnMouseActivate(UINT, WPARAM, LPARAM, BOOL& handled); + // Handle MSAA requests for accessibility information. + LRESULT OnGetObject(UINT message, WPARAM wparam, LPARAM lparam, + BOOL& handled); + // Handle vertical scrolling + LRESULT OnVScroll(int code, short position, HWND scrollbar_control); + // Handle horizontal scrolling + LRESULT OnHScroll(int code, short position, HWND scrollbar_control); + + void OnFinalMessage(HWND window); + + private: + // Tells Windows that we want to hear about mouse exit messages. + void TrackMouseLeave(bool start_tracking); + + // Sends a message to the RenderView in the renderer process. + bool Send(IPC::Message* message); + + // Set the tooltip region to the size of the window, creating the tooltip + // hwnd if it has not been created yet. + void EnsureTooltip(); + + // Tooltips become invalid when the root ancestor changes. When the View + // becomes hidden, this method is called to reset the tooltip. + void ResetTooltip(); + + // Synthesize mouse wheel event. + LRESULT SynthesizeMouseWheel(bool is_vertical, int scroll_code, + short scroll_position); + + // Shuts down the render_widget_host_. This is a separate function so we can + // invoke it from the message loop. + void ShutdownHost(); + + // Redraws the window synchronously, and any child windows (i.e. plugins) + // asynchronously. + void Redraw(const gfx::Rect& invalid_rect); + + // The associated Model. + RenderWidgetHost* render_widget_host_; + + // The cursor for the page. This is passed up from the renderer. + WebCursor current_cursor_; + + // Indicates if the page is loading. + bool is_loading_; + + // true if we are currently tracking WM_MOUSEEXIT messages. + bool track_mouse_leave_; + + // Wrapper class for IME input. + // (See "chrome/browser/ime_input.h" for its details.) + ImeInput ime_input_; + + // Represents whether or not this browser process is receiving status + // messages about the focused edit control from a renderer process. + bool ime_notification_; + + // true if the View is not visible. + bool is_hidden_; + + // true if the View should be closed when its HWND is deactivated (used to + // support SELECT popups which are closed when they are deactivated). + bool close_on_deactivate_; + + // Tooltips + // The text to be shown in the tooltip, supplied by the renderer. + std::wstring tooltip_text_; + // The tooltip control hwnd + HWND tooltip_hwnd_; + // Whether or not a tooltip is currently visible. We use this to track + // whether or not we want to force-close the tooltip when we receive mouse + // move notifications from the renderer. See comment in OnMsgSetTooltipText. + bool tooltip_showing_; + + // Factory used to safely scope delayed calls to ShutdownHost(). + ScopedRunnableMethodFactory<RenderWidgetHostViewWin> shutdown_factory_; + + // Our parent HWND. We keep a reference to it as we SetParent(NULL) when + // hidden to prevent getting messages (Paint, Resize...), and we reattach + // when shown again. + HWND parent_hwnd_; + + // Instance of accessibility information for the root of the MSAA + // tree representation of the WebKit render tree. + CComPtr<IAccessible> browser_accessibility_root_; + + // The time at which this view started displaying white pixels as a result of + // not having anything to paint (empty backing store from renderer). This + // value returns true for is_null() if we are not recording whiteout times. + base::TimeTicks whiteout_start_time_; + + // Whether the window can be activated. Autocomplete popup windows for example + // cannot be activated. Default is true. + bool activatable_; + + // Whether the renderer is made accessible. + // TODO(jcampan): http://b/issue?id=1432077 This is a temporary work-around + // until that bug is fixed. + bool renderer_accessible_; + + DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewWin); +}; + +#endif // CHROME_BROWSER_RENDERER_HOST_RENDER_WIDGET_HOST_VIEW_WIN_H_ + diff --git a/chrome/browser/renderer_host/renderer_security_policy.cc b/chrome/browser/renderer_host/renderer_security_policy.cc new file mode 100644 index 0000000..ccea740 --- /dev/null +++ b/chrome/browser/renderer_host/renderer_security_policy.cc @@ -0,0 +1,285 @@ +// 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 "chrome/browser/renderer_host/renderer_security_policy.h" + +#include "base/logging.h" +#include "base/string_util.h" +#ifdef CHROME_PERSONALIZATION +#include "chrome/personalization/personalization.h" +#endif +#include "googleurl/src/gurl.h" +#include "net/url_request/url_request.h" + +// The SecurityState class is used to maintain per-renderer security state +// information. +class RendererSecurityPolicy::SecurityState { + public: + SecurityState() : has_dom_ui_bindings_(false) { } + + // Grant permission to request URLs with the specified scheme. + void GrantScheme(const std::string& scheme) { + scheme_policy_[scheme] = true; + } + + // Revoke permission to request URLs with the specified scheme. + void RevokeScheme(const std::string& scheme) { + scheme_policy_[scheme] = false; + } + + // Grant permission to upload the specified file to the web. + void GrantUploadFile(const std::wstring& file) { + uploadable_files_.insert(file); + } + + void GrantDOMUIBindings() { + has_dom_ui_bindings_ = true; + } + + // Determine whether permission has been granted to request url. + // Schemes that have not been granted default to being denied. + bool CanRequestURL(const GURL& url) { + SchemeMap::const_iterator judgment(scheme_policy_.find(url.scheme())); + + if (judgment == scheme_policy_.end()) + return false; // Unmentioned schemes are disallowed. + + return judgment->second; + } + + // Determine whether permission has been granted to upload file. + // Files that have not been granted default to being denied. + bool CanUploadFile(const std::wstring& file) { + return uploadable_files_.find(file) != uploadable_files_.end(); + } + + bool has_dom_ui_bindings() const { return has_dom_ui_bindings_; } + + private: + typedef std::map<std::string, bool> SchemeMap; + typedef std::set<std::wstring> FileSet; + + // Maps URL schemes to whether permission has been granted or revoked: + // |true| means the scheme has been granted. + // |false| means the scheme has been revoked. + // If a scheme is not present in the map, then it has never been granted + // or revoked. + SchemeMap scheme_policy_; + + // The set of files the renderer is permited to upload to the web. + FileSet uploadable_files_; + + bool has_dom_ui_bindings_; + + DISALLOW_COPY_AND_ASSIGN(SecurityState); +}; + +RendererSecurityPolicy::RendererSecurityPolicy() { + // We know about these schemes and believe them to be safe. + RegisterWebSafeScheme("http"); + RegisterWebSafeScheme("https"); + RegisterWebSafeScheme("ftp"); + RegisterWebSafeScheme("data"); + RegisterWebSafeScheme("feed"); + + // We know about the following psuedo schemes and treat them specially. + RegisterPseudoScheme("about"); + RegisterPseudoScheme("javascript"); + RegisterPseudoScheme("view-source"); +} + +// static +RendererSecurityPolicy* RendererSecurityPolicy::GetInstance() { + return Singleton<RendererSecurityPolicy>::get(); +} + +void RendererSecurityPolicy::Add(int renderer_id) { + AutoLock lock(lock_); + if (security_state_.count(renderer_id) != 0) { + NOTREACHED() << "Add renderers at most once."; + return; + } + + security_state_[renderer_id] = new SecurityState(); +} + +void RendererSecurityPolicy::Remove(int renderer_id) { + AutoLock lock(lock_); + if (security_state_.count(renderer_id) != 1) { + NOTREACHED() << "Remove renderers at most once."; + return; + } + + delete security_state_[renderer_id]; + security_state_.erase(renderer_id); +} + +void RendererSecurityPolicy::RegisterWebSafeScheme(const std::string& scheme) { + AutoLock lock(lock_); + DCHECK(web_safe_schemes_.count(scheme) == 0) << "Add schemes at most once."; + DCHECK(pseudo_schemes_.count(scheme) == 0) << "Web-safe implies not psuedo."; + + web_safe_schemes_.insert(scheme); +} + +bool RendererSecurityPolicy::IsWebSafeScheme(const std::string& scheme) { + AutoLock lock(lock_); + + return (web_safe_schemes_.find(scheme) != web_safe_schemes_.end()); +} + +void RendererSecurityPolicy::RegisterPseudoScheme(const std::string& scheme) { + AutoLock lock(lock_); + DCHECK(pseudo_schemes_.count(scheme) == 0) << "Add schemes at most once."; + DCHECK(web_safe_schemes_.count(scheme) == 0) << "Psuedo implies not web-safe."; + + pseudo_schemes_.insert(scheme); +} + +bool RendererSecurityPolicy::IsPseudoScheme(const std::string& scheme) { + AutoLock lock(lock_); + + return (pseudo_schemes_.find(scheme) != pseudo_schemes_.end()); +} + +void RendererSecurityPolicy::GrantRequestURL(int renderer_id, const GURL& url) { + + if (!url.is_valid()) + return; // Can't grant the capability to request invalid URLs. + + if (IsWebSafeScheme(url.scheme())) + return; // The scheme has already been white-listed for every renderer. + + if (IsPseudoScheme(url.scheme())) { + // The view-source scheme is a special case of a pseudo URL that eventually + // results in requesting its embedded URL. + if (url.SchemeIs("view-source")) { + // URLs with the view-source scheme typically look like: + // view-source:http://www.google.com/a + // In order to request these URLs, the renderer needs to be able to request + // the embedded URL. + GrantRequestURL(renderer_id, GURL(url.path())); + } + + return; // Can't grant the capability to request pseudo schemes. + } + + { + AutoLock lock(lock_); + SecurityStateMap::iterator state = security_state_.find(renderer_id); + if (state == security_state_.end()) + return; + + // If the renderer has been commanded to request a scheme, then we grant + // it the capability to request URLs of that scheme. + state->second->GrantScheme(url.scheme()); + } +} + +void RendererSecurityPolicy::GrantUploadFile(int renderer_id, + const std::wstring& file) { + AutoLock lock(lock_); + + SecurityStateMap::iterator state = security_state_.find(renderer_id); + if (state == security_state_.end()) + return; + + state->second->GrantUploadFile(file); +} + +void RendererSecurityPolicy::GrantInspectElement(int renderer_id) { + AutoLock lock(lock_); + + SecurityStateMap::iterator state = security_state_.find(renderer_id); + if (state == security_state_.end()) + return; + + // The inspector is served from a chrome: URL. In order to run the + // inspector, the renderer needs to be able to load chrome URLs. + state->second->GrantScheme("chrome"); +} + +void RendererSecurityPolicy::GrantDOMUIBindings(int renderer_id) { + AutoLock lock(lock_); + + SecurityStateMap::iterator state = security_state_.find(renderer_id); + if (state == security_state_.end()) + return; + + state->second->GrantDOMUIBindings(); + + // DOM UI bindings need the ability to request chrome URLs. + state->second->GrantScheme("chrome"); + + // DOM UI pages can contain links to file:// URLs. + state->second->GrantScheme("file"); +} + +bool RendererSecurityPolicy::CanRequestURL(int renderer_id, const GURL& url) { + if (!url.is_valid()) + return false; // Can't request invalid URLs. + + if (IsWebSafeScheme(url.scheme())) + return true; // The scheme has been white-listed for every renderer. + + if (IsPseudoScheme(url.scheme())) { + // There are a number of special cases for pseudo schemes. + + if (url.SchemeIs("view-source")) { + // A view-source URL is allowed if the renderer is permitted to request + // the embedded URL. + return CanRequestURL(renderer_id, GURL(url.path())); + } + + if (LowerCaseEqualsASCII(url.spec(), "about:blank")) + return true; // Every renderer can request <about:blank>. + + // URLs like <about:memory> and <about:crash> shouldn't be requestable by + // any renderer. Also, this case covers <javascript:...>, which should be + // handled internally by the renderer and not kicked up to the browser. + return false; + } + +#ifdef CHROME_PERSONALIZATION + if (url.SchemeIs(kPersonalizationScheme)) + return true; +#endif + + if (!URLRequest::IsHandledURL(url)) + return true; // This URL request is destined for ShellExecute. + + { + AutoLock lock(lock_); + + SecurityStateMap::iterator state = security_state_.find(renderer_id); + if (state == security_state_.end()) + return false; + + // Otherwise, we consult the renderer's security state to see if it is + // allowed to request the URL. + return state->second->CanRequestURL(url); + } +} + +bool RendererSecurityPolicy::CanUploadFile(int renderer_id, + const std::wstring& file) { + AutoLock lock(lock_); + + SecurityStateMap::iterator state = security_state_.find(renderer_id); + if (state == security_state_.end()) + return false; + + return state->second->CanUploadFile(file); +} + +bool RendererSecurityPolicy::HasDOMUIBindings(int renderer_id) { + AutoLock lock(lock_); + + SecurityStateMap::iterator state = security_state_.find(renderer_id); + if (state == security_state_.end()) + return false; + + return state->second->has_dom_ui_bindings(); +} + diff --git a/chrome/browser/renderer_host/renderer_security_policy.h b/chrome/browser/renderer_host/renderer_security_policy.h new file mode 100644 index 0000000..646f783 --- /dev/null +++ b/chrome/browser/renderer_host/renderer_security_policy.h @@ -0,0 +1,122 @@ +// 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. + +#ifndef CHROME_BROWSER_RENDERER_HOST_RENDERER_SECURITY_POLICY_H_ +#define CHROME_BROWSER_RENDERER_HOST_RENDERER_SECURITY_POLICY_H_ + +#include <string> +#include <map> +#include <set> + +#include "base/basictypes.h" +#include "base/lock.h" +#include "base/singleton.h" + +class GURL; + +// The RendererSecurityPolicy class is used to grant and revoke security +// capabilities for renderers. For example, it restricts whether a renderer +// is permmitted to loaded file:// URLs based on whether the renderer has ever +// been commanded to load file:// URLs by the browser. +// +// RendererSecurityPolicy is a singleton that may be used on any thread. +// +class RendererSecurityPolicy { + public: + // There is one global RendererSecurityPolicy object for the entire browser + // processes. The object returned by this method may be accessed on any + // thread. + static RendererSecurityPolicy* GetInstance(); + + // Web-safe schemes can be requested by any renderer. Once a web-safe scheme + // has been registered, any renderer processes can request URLs with that + // scheme. There is no mechanism for revoking web-safe schemes. + void RegisterWebSafeScheme(const std::string& scheme); + + // Returns true iff |scheme| has been registered as a web-safe scheme. + bool IsWebSafeScheme(const std::string& scheme); + + // Pseudo schemes are treated differently than other schemes because they + // cannot be requested like normal URLs. There is no mechanism for revoking + // pseudo schemes. + void RegisterPseudoScheme(const std::string& scheme); + + // Returns true iff |scheme| has been registered as pseudo scheme. + bool IsPseudoScheme(const std::string& scheme); + + // Upon creation, render processes should register themselves by calling this + // this method exactly once. + void Add(int renderer_id); + + // Upon destruction, render processess should unregister themselves by caling + // this method exactly once. + void Remove(int renderer_id); + + // Whenever the browser processes commands the renderer to request a URL, it + // should call this method to grant the renderer process the capability to + // request the URL. + void GrantRequestURL(int renderer_id, const GURL& url); + + // Whenever the user picks a file from a <input type="file"> element, the + // browser should call this function to grant the renderer the capability to + // upload the file to the web. + void GrantUploadFile(int renderer_id, const std::wstring& file); + + // Whenever the browser processes commands the renderer to run web inspector, + // it should call this method to grant the renderer process the capability to + // run the inspector. + void GrantInspectElement(int renderer_id); + + // Grant this renderer the ability to use DOM UI Bindings. + void GrantDOMUIBindings(int renderer_id); + + // Before servicing a renderer's request for a URL, the browser should call + // this method to determine whether the renderer has the capability to + // request the URL. + bool CanRequestURL(int renderer_id, const GURL& url); + + // Before servicing a renderer's request to upload a file to the web, the + // browser should call this method to determine whether the renderer has the + // capability to upload the requested file. + bool CanUploadFile(int renderer_id, const std::wstring& file); + + // Returns true of the specified renderer_id has been granted DOMUIBindings. + // The browser should check this property before assuming the renderer is + // allowed to use DOMUIBindings. + bool HasDOMUIBindings(int renderer_id); + + private: + class SecurityState; + + typedef std::set<std::string> SchemeSet; + typedef std::map<int, SecurityState*> SecurityStateMap; + + // Obtain an instance of RendererSecurityPolicy via GetInstance(). + RendererSecurityPolicy(); + friend struct DefaultSingletonTraits<RendererSecurityPolicy>; + + // You must acquire this lock before reading or writing any members of this + // class. You must not block while holding this lock. + Lock lock_; + + // These schemes are white-listed for all renderers. This set is protected + // by |lock_|. + SchemeSet web_safe_schemes_; + + // These schemes do not actually represent retrievable URLs. For example, + // the the URLs in the "about" scheme are aliases to other URLs. This set is + // protected by |lock_|. + SchemeSet pseudo_schemes_; + + // This map holds a SecurityState for each renderer process. The key for the + // map is the ID of the RenderProcessHost. The SecurityState objects are + // owned by this object and are protected by |lock_|. References to them must + // not escape this class. + SecurityStateMap security_state_; + + DISALLOW_COPY_AND_ASSIGN(RendererSecurityPolicy); +}; + +#endif // CHROME_BROWSER_RENDERER_HOST_RENDERER_SECURITY_POLICY_H_ + diff --git a/chrome/browser/renderer_host/renderer_security_policy_unittest.cc b/chrome/browser/renderer_host/renderer_security_policy_unittest.cc new file mode 100644 index 0000000..ae3ac52 --- /dev/null +++ b/chrome/browser/renderer_host/renderer_security_policy_unittest.cc @@ -0,0 +1,260 @@ +// 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 <string> + +#include "base/basictypes.h" +#include "chrome/browser/renderer_host/renderer_security_policy.h" +#include "net/url_request/url_request.h" +#include "net/url_request/url_request_test_job.h" +#include "testing/gtest/include/gtest/gtest.h" + +class RendererSecurityPolicyTest : public testing::Test { +protected: + // testing::Test + virtual void SetUp() { + // In the real world, "chrome" is a handled scheme. + URLRequest::RegisterProtocolFactory("chrome", + &URLRequestTestJob::Factory); + } + virtual void TearDown() { + URLRequest::RegisterProtocolFactory("chrome", NULL); + } +}; + +static int kRendererID = 42; + +TEST_F(RendererSecurityPolicyTest, IsWebSafeSchemeTest) { + RendererSecurityPolicy* p = RendererSecurityPolicy::GetInstance(); + + EXPECT_TRUE(p->IsWebSafeScheme("http")); + EXPECT_TRUE(p->IsWebSafeScheme("https")); + EXPECT_TRUE(p->IsWebSafeScheme("ftp")); + EXPECT_TRUE(p->IsWebSafeScheme("data")); + EXPECT_TRUE(p->IsWebSafeScheme("feed")); + + EXPECT_FALSE(p->IsWebSafeScheme("registered-web-safe-scheme")); + p->RegisterWebSafeScheme("registered-web-safe-scheme"); + EXPECT_TRUE(p->IsWebSafeScheme("registered-web-safe-scheme")); +} + +TEST_F(RendererSecurityPolicyTest, IsPseudoSchemeTest) { + RendererSecurityPolicy* p = RendererSecurityPolicy::GetInstance(); + + EXPECT_TRUE(p->IsPseudoScheme("about")); + EXPECT_TRUE(p->IsPseudoScheme("javascript")); + EXPECT_TRUE(p->IsPseudoScheme("view-source")); + + EXPECT_FALSE(p->IsPseudoScheme("registered-psuedo-scheme")); + p->RegisterPseudoScheme("registered-psuedo-scheme"); + EXPECT_TRUE(p->IsPseudoScheme("registered-psuedo-scheme")); +} + +TEST_F(RendererSecurityPolicyTest, StandardSchemesTest) { + RendererSecurityPolicy* p = RendererSecurityPolicy::GetInstance(); + + p->Add(kRendererID); + + // Safe + EXPECT_TRUE(p->CanRequestURL(kRendererID, GURL("http://www.google.com/"))); + EXPECT_TRUE(p->CanRequestURL(kRendererID, GURL("https://www.paypal.com/"))); + EXPECT_TRUE(p->CanRequestURL(kRendererID, GURL("ftp://ftp.gnu.org/"))); + EXPECT_TRUE(p->CanRequestURL(kRendererID, GURL("data:text/html,<b>Hi</b>"))); + EXPECT_TRUE(p->CanRequestURL(kRendererID, + GURL("view-source:http://www.google.com/"))); + + // Dangerous + EXPECT_FALSE(p->CanRequestURL(kRendererID, + GURL("file:///etc/passwd"))); + EXPECT_FALSE(p->CanRequestURL(kRendererID, + GURL("view-cache:http://www.google.com/"))); + EXPECT_FALSE(p->CanRequestURL(kRendererID, + GURL("chrome://foo/bar"))); + + p->Remove(kRendererID); +} + +TEST_F(RendererSecurityPolicyTest, AboutTest) { + RendererSecurityPolicy* p = RendererSecurityPolicy::GetInstance(); + + p->Add(kRendererID); + + EXPECT_TRUE(p->CanRequestURL(kRendererID, GURL("about:blank"))); + EXPECT_TRUE(p->CanRequestURL(kRendererID, GURL("about:BlAnK"))); + EXPECT_TRUE(p->CanRequestURL(kRendererID, GURL("aBouT:BlAnK"))); + EXPECT_TRUE(p->CanRequestURL(kRendererID, GURL("aBouT:blank"))); + + EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("about:memory"))); + EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("about:crash"))); + EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("about:cache"))); + EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("about:hang"))); + + EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("aBoUt:memory"))); + EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("about:CrASh"))); + EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("abOuT:cAChe"))); + + p->GrantRequestURL(kRendererID, GURL("about:memory")); + EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("about:memory"))); + + p->GrantRequestURL(kRendererID, GURL("about:crash")); + EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("about:crash"))); + + p->GrantRequestURL(kRendererID, GURL("about:cache")); + EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("about:cache"))); + + p->GrantRequestURL(kRendererID, GURL("about:hang")); + EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("about:hang"))); + + p->Remove(kRendererID); +} + +TEST_F(RendererSecurityPolicyTest, JavaScriptTest) { + RendererSecurityPolicy* p = RendererSecurityPolicy::GetInstance(); + + p->Add(kRendererID); + + EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("javascript:alert('xss')"))); + p->GrantRequestURL(kRendererID, GURL("javascript:alert('xss')")); + EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("javascript:alert('xss')"))); + + p->Remove(kRendererID); +} + +TEST_F(RendererSecurityPolicyTest, RegisterWebSafeSchemeTest) { + RendererSecurityPolicy* p = RendererSecurityPolicy::GetInstance(); + + p->Add(kRendererID); + + // Currently, "asdf" is destined for ShellExecute, so it is allowed. + EXPECT_TRUE(p->CanRequestURL(kRendererID, GURL("asdf:rockers"))); + + // Once we register a ProtocolFactory for "asdf", we default to deny. + URLRequest::RegisterProtocolFactory("asdf", &URLRequestTestJob::Factory); + EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("asdf:rockers"))); + + // We can allow new schemes by adding them to the whitelist. + p->RegisterWebSafeScheme("asdf"); + EXPECT_TRUE(p->CanRequestURL(kRendererID, GURL("asdf:rockers"))); + + // Cleanup. + URLRequest::RegisterProtocolFactory("asdf", NULL); + EXPECT_TRUE(p->CanRequestURL(kRendererID, GURL("asdf:rockers"))); + + p->Remove(kRendererID); +} + +TEST_F(RendererSecurityPolicyTest, CanServiceCommandsTest) { + RendererSecurityPolicy* p = RendererSecurityPolicy::GetInstance(); + + p->Add(kRendererID); + + EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("file:///etc/passwd"))); + p->GrantRequestURL(kRendererID, GURL("file:///etc/passwd")); + EXPECT_TRUE(p->CanRequestURL(kRendererID, GURL("file:///etc/passwd"))); + + // We should forget our state if we repeat a renderer id. + p->Remove(kRendererID); + p->Add(kRendererID); + EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("file:///etc/passwd"))); + p->Remove(kRendererID); +} + +TEST_F(RendererSecurityPolicyTest, ViewSource) { + RendererSecurityPolicy* p = RendererSecurityPolicy::GetInstance(); + + p->Add(kRendererID); + + // View source is determined by the embedded scheme. + EXPECT_TRUE(p->CanRequestURL(kRendererID, + GURL("view-source:http://www.google.com/"))); + EXPECT_FALSE(p->CanRequestURL(kRendererID, + GURL("view-source:file:///etc/passwd"))); + EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("file:///etc/passwd"))); + + p->GrantRequestURL(kRendererID, GURL("view-source:file:///etc/passwd")); + // View source needs to be able to request the embedded scheme. + EXPECT_TRUE(p->CanRequestURL(kRendererID, + GURL("view-source:file:///etc/passwd"))); + EXPECT_TRUE(p->CanRequestURL(kRendererID, GURL("file:///etc/passwd"))); + + p->Remove(kRendererID); +} + +TEST_F(RendererSecurityPolicyTest, CanUploadFiles) { + RendererSecurityPolicy* p = RendererSecurityPolicy::GetInstance(); + + p->Add(kRendererID); + + EXPECT_FALSE(p->CanUploadFile(kRendererID, L"/etc/passwd")); + p->GrantUploadFile(kRendererID, L"/etc/passwd"); + EXPECT_TRUE(p->CanUploadFile(kRendererID, L"/etc/passwd")); + EXPECT_FALSE(p->CanUploadFile(kRendererID, L"/etc/shadow")); + + p->Remove(kRendererID); + p->Add(kRendererID); + + EXPECT_FALSE(p->CanUploadFile(kRendererID, L"/etc/passwd")); + EXPECT_FALSE(p->CanUploadFile(kRendererID, L"/etc/shadow")); + + p->Remove(kRendererID); +} + +TEST_F(RendererSecurityPolicyTest, CanServiceInspectElement) { + RendererSecurityPolicy* p = RendererSecurityPolicy::GetInstance(); + + GURL url("chrome://inspector/inspector.html"); + + p->Add(kRendererID); + + EXPECT_FALSE(p->CanRequestURL(kRendererID, url)); + p->GrantInspectElement(kRendererID); + EXPECT_TRUE(p->CanRequestURL(kRendererID, url)); + + p->Remove(kRendererID); +} + +TEST_F(RendererSecurityPolicyTest, CanServiceDOMUIBindings) { + RendererSecurityPolicy* p = RendererSecurityPolicy::GetInstance(); + + GURL url("chrome://thumb/http://www.google.com/"); + + p->Add(kRendererID); + + EXPECT_FALSE(p->HasDOMUIBindings(kRendererID)); + EXPECT_FALSE(p->CanRequestURL(kRendererID, url)); + p->GrantDOMUIBindings(kRendererID); + EXPECT_TRUE(p->HasDOMUIBindings(kRendererID)); + EXPECT_TRUE(p->CanRequestURL(kRendererID, url)); + + p->Remove(kRendererID); +} + +TEST_F(RendererSecurityPolicyTest, RemoveRace) { + RendererSecurityPolicy* p = RendererSecurityPolicy::GetInstance(); + + GURL url("file:///etc/passwd"); + std::wstring file(L"/etc/passwd"); + + p->Add(kRendererID); + + p->GrantRequestURL(kRendererID, url); + p->GrantUploadFile(kRendererID, file); + p->GrantDOMUIBindings(kRendererID); + + EXPECT_TRUE(p->CanRequestURL(kRendererID, url)); + EXPECT_TRUE(p->CanUploadFile(kRendererID, file)); + EXPECT_TRUE(p->HasDOMUIBindings(kRendererID)); + + p->Remove(kRendererID); + + // Renderers are added and removed on the UI thread, but the policy can be + // queried on the IO thread. The RendererSecurityPolicy needs to be prepared + // to answer policy questions about renderers who no longer exist. + + // In this case, we default to secure behavior. + EXPECT_FALSE(p->CanRequestURL(kRendererID, url)); + EXPECT_FALSE(p->CanUploadFile(kRendererID, file)); + EXPECT_FALSE(p->HasDOMUIBindings(kRendererID)); +} + diff --git a/chrome/browser/renderer_host/resource_dispatcher_host.cc b/chrome/browser/renderer_host/resource_dispatcher_host.cc index 7c74420..261d1c1 100644 --- a/chrome/browser/renderer_host/resource_dispatcher_host.cc +++ b/chrome/browser/renderer_host/resource_dispatcher_host.cc @@ -20,16 +20,16 @@ #include "chrome/browser/external_protocol_handler.h" #include "chrome/browser/login_prompt.h" #include "chrome/browser/plugin_service.h" -#include "chrome/browser/render_view_host.h" -#include "chrome/browser/render_view_host_delegate.h" +#include "chrome/browser/renderer_host/render_view_host.h" +#include "chrome/browser/renderer_host/render_view_host_delegate.h" #include "chrome/browser/renderer_host/async_resource_handler.h" #include "chrome/browser/renderer_host/buffered_resource_handler.h" #include "chrome/browser/renderer_host/cross_site_resource_handler.h" #include "chrome/browser/renderer_host/download_resource_handler.h" +#include "chrome/browser/renderer_host/renderer_security_policy.h" #include "chrome/browser/renderer_host/safe_browsing_resource_handler.h" #include "chrome/browser/renderer_host/save_file_resource_handler.h" #include "chrome/browser/renderer_host/sync_resource_handler.h" -#include "chrome/browser/renderer_security_policy.h" #include "chrome/browser/resource_request_details.h" #include "chrome/browser/safe_browsing/safe_browsing_service.h" #include "chrome/browser/tab_contents/tab_util.h" diff --git a/chrome/browser/renderer_host/test_render_view_host.cc b/chrome/browser/renderer_host/test_render_view_host.cc index 7c47b53..1c77bb8 100644 --- a/chrome/browser/renderer_host/test_render_view_host.cc +++ b/chrome/browser/renderer_host/test_render_view_host.cc @@ -73,8 +73,10 @@ void RenderViewHostTestHarness::SetUp() { } void RenderViewHostTestHarness::TearDown() { - contents_->CloseContents(); - contents_ = NULL; + if (contents_) { + contents_->CloseContents(); + contents_ = NULL; + } controller_ = NULL; // Make sure that we flush any messages related to WebContents destruction diff --git a/chrome/browser/renderer_host/test_render_view_host.h b/chrome/browser/renderer_host/test_render_view_host.h index ba7c7ea..be7acfe 100644 --- a/chrome/browser/renderer_host/test_render_view_host.h +++ b/chrome/browser/renderer_host/test_render_view_host.h @@ -9,9 +9,9 @@ #include "base/message_loop.h" #include "chrome/browser/tab_contents/navigation_controller.h" #include "chrome/browser/tab_contents/test_web_contents.h" -#include "chrome/browser/render_view_host.h" #include "chrome/browser/renderer_host/mock_render_process_host.h" -#include "chrome/browser/render_widget_host_view.h" +#include "chrome/browser/renderer_host/render_view_host.h" +#include "chrome/browser/renderer_host/render_widget_host_view.h" #include "chrome/test/testing_profile.h" #include "testing/gtest/include/gtest/gtest.h" @@ -183,6 +183,12 @@ class RenderViewHostTestHarness : public testing::Test { return profile_.get(); } + // Marks the contents as already cleaned up. If a test calls CloseContents, + // then our cleanup code shouldn't run. This function makes sure that happens. + void ContentsCleanedUp() { + contents_ = NULL; + } + protected: // testing::Test virtual void SetUp(); |