| // Copyright (c) 2011 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 "content/browser/renderer_host/render_view_host.h" |
| |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/command_line.h" |
| #include "base/i18n/rtl.h" |
| #include "base/json/json_reader.h" |
| #include "base/string_util.h" |
| #include "base/time.h" |
| #include "base/utf_string_conversions.h" |
| #include "base/values.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "content/browser/browser_message_filter.h" |
| #include "content/browser/child_process_security_policy.h" |
| #include "content/browser/content_browser_client.h" |
| #include "content/browser/cross_site_request_manager.h" |
| #include "content/browser/host_zoom_map.h" |
| #include "content/browser/in_process_webkit/session_storage_namespace.h" |
| #include "content/browser/renderer_host/render_process_host.h" |
| #include "content/browser/renderer_host/render_view_host_delegate.h" |
| #include "content/browser/renderer_host/render_view_host_observer.h" |
| #include "content/browser/renderer_host/render_widget_host.h" |
| #include "content/browser/renderer_host/render_widget_host_view.h" |
| #include "content/browser/site_instance.h" |
| #include "content/browser/user_metrics.h" |
| #include "content/common/bindings_policy.h" |
| #include "content/common/content_constants.h" |
| #include "content/common/content_notification_types.h" |
| #include "content/common/desktop_notification_messages.h" |
| #include "content/common/drag_messages.h" |
| #include "content/common/native_web_keyboard_event.h" |
| #include "content/common/notification_details.h" |
| #include "content/common/notification_service.h" |
| #include "content/common/result_codes.h" |
| #include "content/common/swapped_out_messages.h" |
| #include "content/common/url_constants.h" |
| #include "content/common/view_messages.h" |
| #include "net/base/net_util.h" |
| #include "net/url_request/url_request_context_getter.h" |
| #include "third_party/skia/include/core/SkBitmap.h" |
| #include "ui/gfx/native_widget_types.h" |
| #include "webkit/glue/context_menu.h" |
| #include "webkit/glue/webaccessibility.h" |
| #include "webkit/glue/webdropdata.h" |
| |
| using base::TimeDelta; |
| using WebKit::WebConsoleMessage; |
| using WebKit::WebDragOperation; |
| using WebKit::WebDragOperationNone; |
| using WebKit::WebDragOperationsMask; |
| using WebKit::WebInputEvent; |
| using WebKit::WebMediaPlayerAction; |
| using WebKit::WebTextDirection; |
| |
| namespace { |
| |
| // 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, |
| SessionStorageNamespace* session_storage) |
| : RenderWidgetHost(instance->GetProcess(), routing_id), |
| instance_(instance), |
| delegate_(delegate), |
| waiting_for_drag_context_response_(false), |
| enabled_bindings_(0), |
| pending_request_id_(-1), |
| navigations_suspended_(false), |
| suspended_nav_message_(NULL), |
| is_swapped_out_(false), |
| run_modal_reply_msg_(NULL), |
| is_waiting_for_beforeunload_ack_(false), |
| is_waiting_for_unload_ack_(false), |
| unload_ack_is_for_cross_site_transition_(false), |
| are_javascript_messages_suppressed_(false), |
| sudden_termination_allowed_(false), |
| session_storage_namespace_(session_storage), |
| save_accessibility_tree_for_testing_(false), |
| render_view_termination_status_(base::TERMINATION_STATUS_STILL_RUNNING) { |
| if (!session_storage_namespace_) { |
| session_storage_namespace_ = |
| new SessionStorageNamespace(process()->profile()->GetWebKitContext()); |
| } |
| |
| DCHECK(instance_); |
| CHECK(delegate_); // http://crbug.com/82827 |
| |
| process()->EnableSendQueue(); |
| |
| content::GetContentClient()->browser()->RenderViewHostCreated(this); |
| |
| NotificationService::current()->Notify( |
| content::NOTIFICATION_RENDER_VIEW_HOST_CREATED, |
| Source<RenderViewHost>(this), |
| NotificationService::NoDetails()); |
| } |
| |
| RenderViewHost::~RenderViewHost() { |
| FOR_EACH_OBSERVER( |
| RenderViewHostObserver, observers_, RenderViewHostDestruction()); |
| |
| NotificationService::current()->Notify( |
| content::NOTIFICATION_RENDER_VIEW_HOST_DELETED, |
| Source<RenderViewHost>(this), |
| NotificationService::NoDetails()); |
| |
| delegate()->RenderViewDeleted(this); |
| |
| // Be sure to clean up any leftover state from cross-site requests. |
| CrossSiteRequestManager::GetInstance()->SetHasPendingCrossSiteRequest( |
| process()->id(), routing_id(), false); |
| } |
| |
| bool RenderViewHost::CreateRenderView(const string16& frame_name) { |
| 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(renderer_accessible())) |
| return false; |
| DCHECK(process()->HasConnection()); |
| DCHECK(process()->profile()); |
| |
| if (BindingsPolicy::is_web_ui_enabled(enabled_bindings_)) { |
| ChildProcessSecurityPolicy::GetInstance()->GrantWebUIBindings( |
| process()->id()); |
| } |
| |
| if (BindingsPolicy::is_extension_enabled(enabled_bindings_)) { |
| ChildProcessSecurityPolicy::GetInstance()->GrantExtensionBindings( |
| process()->id()); |
| } |
| |
| renderer_initialized_ = true; |
| |
| process()->SetCompositingSurface(routing_id(), |
| GetCompositingSurface()); |
| |
| ViewMsg_New_Params params; |
| params.parent_window = GetNativeViewId(); |
| params.renderer_preferences = |
| delegate_->GetRendererPrefs(process()->profile()); |
| params.web_preferences = delegate_->GetWebkitPrefs(); |
| params.view_id = routing_id(); |
| params.session_storage_namespace_id = session_storage_namespace_->id(); |
| params.frame_name = frame_name; |
| Send(new ViewMsg_New(params)); |
| |
| // 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(), enabled_bindings_)); |
| // Let our delegate know that we created a RenderView. |
| delegate_->RenderViewCreated(this); |
| |
| FOR_EACH_OBSERVER( |
| RenderViewHostObserver, observers_, RenderViewHostInitialized()); |
| |
| return true; |
| } |
| |
| bool RenderViewHost::IsRenderViewLive() const { |
| return process()->HasConnection() && renderer_initialized_; |
| } |
| |
| void RenderViewHost::SyncRendererPrefs() { |
| Send(new ViewMsg_SetRendererPrefs(routing_id(), |
| delegate_->GetRendererPrefs( |
| process()->profile()))); |
| } |
| |
| void RenderViewHost::Navigate(const ViewMsg_Navigate_Params& params) { |
| ChildProcessSecurityPolicy::GetInstance()->GrantRequestURL( |
| process()->id(), params.url); |
| |
| ViewMsg_Navigate* nav_message = new ViewMsg_Navigate(routing_id(), params); |
| |
| // 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, TabContents will cancel this pending RVH |
| // create a new pending RVH. |
| DCHECK(!suspended_nav_message_.get()); |
| suspended_nav_message_.reset(nav_message); |
| } else { |
| // Unset this, otherwise if true and the hang monitor fires we'll |
| // incorrectly close the tab. |
| is_waiting_for_unload_ack_ = false; |
| |
| // Unset this, in case we never finished committing a previous RVH swap. |
| // Otherwise we'll filter out the messages for this navigation. |
| is_swapped_out_ = false; |
| |
| Send(nav_message); |
| } |
| |
| // Force the throbber to start. We do this because WebKit's "started |
| // loading" message will be received asynchronously from the UI of the |
| // browser. But we want to keep the throbber in sync with what's happening |
| // in the UI. For example, we want to start throbbing immediately when the |
| // user naivgates even if the renderer is delayed. There is also an issue |
| // with the throbber starting because the WebUI (which controls whether the |
| // favicon is displayed) happens synchronously. If the start loading |
| // messages was asynchronous, then the default favicon would flash in. |
| // |
| // WebKit doesn't send throb notifications for JavaScript URLs, so we |
| // don't want to either. |
| if (!params.url.SchemeIs(chrome::kJavaScriptScheme)) |
| delegate_->DidStartLoading(); |
| |
| FOR_EACH_OBSERVER( |
| RenderViewHostObserver, observers_, Navigate(params)); |
| } |
| |
| void RenderViewHost::NavigateToURL(const GURL& url) { |
| ViewMsg_Navigate_Params params; |
| params.page_id = -1; |
| params.pending_history_list_offset = -1; |
| params.current_history_list_offset = -1; |
| params.current_history_list_length = 0; |
| params.url = url; |
| params.transition = PageTransition::LINK; |
| params.navigation_type = ViewMsg_Navigate_Type::NORMAL; |
| Navigate(params); |
| } |
| |
| void RenderViewHost::SetNavigationsSuspended(bool suspend) { |
| // This should only be called to toggle the state. |
| DCHECK(navigations_suspended_ != suspend); |
| |
| navigations_suspended_ = suspend; |
| if (!suspend && suspended_nav_message_.get()) { |
| // There's a navigation message waiting to be sent. Now that we're not |
| // suspended anymore, resume navigation by sending it. If we were swapped |
| // out, we should also stop filtering out the IPC messages now. |
| is_swapped_out_ = false; |
| Send(suspended_nav_message_.release()); |
| } |
| } |
| |
| void RenderViewHost::CancelSuspendedNavigations() { |
| // Clear any state if a pending navigation is canceled or pre-empted. |
| if (suspended_nav_message_.get()) |
| suspended_nav_message_.reset(); |
| navigations_suspended_ = false; |
| } |
| |
| void RenderViewHost::FirePageBeforeUnload(bool for_cross_site_transition) { |
| if (!IsRenderViewLive()) { |
| // This RenderViewHost doesn't have a live renderer, so just skip running |
| // the onbeforeunload handler. |
| is_waiting_for_beforeunload_ack_ = true; // Checked by OnMsgShouldCloseACK. |
| unload_ack_is_for_cross_site_transition_ = for_cross_site_transition; |
| 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 then the browser close |
| // button), and we only send the message once. |
| if (is_waiting_for_beforeunload_ack_) { |
| // Some of our close messages could be for the tab, others for cross-site |
| // transitions. We always want to think it's for closing the tab if any |
| // of the messages were, since otherwise it might be impossible to close |
| // (if there was a cross-site "close" request pending when the user clicked |
| // the close button). We want to keep the "for cross site" flag only if |
| // both the old and the new ones are also for cross site. |
| unload_ack_is_for_cross_site_transition_ = |
| unload_ack_is_for_cross_site_transition_ && for_cross_site_transition; |
| } else { |
| // Start the hang monitor in case the renderer hangs in the beforeunload |
| // handler. |
| is_waiting_for_beforeunload_ack_ = true; |
| unload_ack_is_for_cross_site_transition_ = for_cross_site_transition; |
| StartHangMonitorTimeout(TimeDelta::FromMilliseconds(kUnloadTimeoutMS)); |
| Send(new ViewMsg_ShouldClose(routing_id())); |
| } |
| } |
| |
| void RenderViewHost::SwapOut(int new_render_process_host_id, |
| int new_request_id) { |
| // Start filtering IPC messages to avoid confusing the delegate. This will |
| // prevent any dialogs from appearing during unload handlers, but we've |
| // already decided to silence them in crbug.com/68780. We will set it back |
| // to false in SetNavigationsSuspended if we swap back in. |
| is_swapped_out_ = true; |
| |
| // This will be set back to false in OnSwapOutACK, just before we replace |
| // this RVH with the pending RVH. |
| is_waiting_for_unload_ack_ = true; |
| // Start the hang monitor in case the renderer hangs in the unload handler. |
| StartHangMonitorTimeout(TimeDelta::FromMilliseconds(kUnloadTimeoutMS)); |
| |
| ViewMsg_SwapOut_Params params; |
| params.closing_process_id = process()->id(); |
| params.closing_route_id = routing_id(); |
| params.new_render_process_host_id = new_render_process_host_id; |
| params.new_request_id = new_request_id; |
| if (IsRenderViewLive()) { |
| Send(new ViewMsg_SwapOut(routing_id(), params)); |
| } else { |
| // This RenderViewHost doesn't have a live renderer, so just skip the unload |
| // event. We must notify the ResourceDispatcherHost on the IO thread, |
| // which we will do through the RenderProcessHost's widget helper. |
| process()->CrossSiteSwapOutACK(params); |
| } |
| } |
| |
| void RenderViewHost::OnSwapOutACK() { |
| // Stop the hang monitor now that the unload handler has finished. |
| StopHangMonitorTimeout(); |
| is_waiting_for_unload_ack_ = false; |
| } |
| |
| void RenderViewHost::WasSwappedOut() { |
| // Don't bother reporting hung state anymore. |
| StopHangMonitorTimeout(); |
| |
| // Inform the renderer that it can exit if no one else is using it. |
| Send(new ViewMsg_WasSwappedOut(routing_id())); |
| } |
| |
| void RenderViewHost::ClosePage() { |
| // 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()) { |
| // TODO(creis): Should this be moved to Shutdown? It may not be called for |
| // RenderViewHosts that have been swapped out. |
| NotificationService::current()->Notify( |
| content::NOTIFICATION_RENDER_VIEW_HOST_WILL_CLOSE_RENDER_VIEW, |
| Source<RenderViewHost>(this), |
| NotificationService::NoDetails()); |
| |
| Send(new ViewMsg_ClosePage(routing_id())); |
| } else { |
| // This RenderViewHost doesn't have a live renderer, so just skip the unload |
| // event and close the page. |
| ClosePageIgnoringUnloadEvents(); |
| } |
| } |
| |
| void RenderViewHost::ClosePageIgnoringUnloadEvents() { |
| StopHangMonitorTimeout(); |
| is_waiting_for_beforeunload_ack_ = false; |
| is_waiting_for_unload_ack_ = false; |
| |
| sudden_termination_allowed_ = true; |
| delegate_->Close(this); |
| } |
| |
| void RenderViewHost::SetHasPendingCrossSiteRequest(bool has_pending_request, |
| int request_id) { |
| CrossSiteRequestManager::GetInstance()->SetHasPendingCrossSiteRequest( |
| process()->id(), routing_id(), has_pending_request); |
| pending_request_id_ = request_id; |
| } |
| |
| int RenderViewHost::GetPendingRequestId() { |
| return pending_request_id_; |
| } |
| |
| void RenderViewHost::DragTargetDragEnter( |
| const WebDropData& drop_data, |
| const gfx::Point& client_pt, |
| const gfx::Point& screen_pt, |
| WebDragOperationsMask operations_allowed) { |
| // Grant the renderer the ability to load the drop_data. |
| ChildProcessSecurityPolicy* policy = |
| ChildProcessSecurityPolicy::GetInstance(); |
| policy->GrantRequestURL(process()->id(), drop_data.url); |
| for (std::vector<string16>::const_iterator iter(drop_data.filenames.begin()); |
| iter != drop_data.filenames.end(); ++iter) { |
| FilePath path = FilePath::FromWStringHack(UTF16ToWideHack(*iter)); |
| policy->GrantRequestURL(process()->id(), |
| net::FilePathToFileURL(path)); |
| policy->GrantReadFile(process()->id(), path); |
| |
| // Allow dragged directories to be enumerated by the child process. |
| // Note that we can't tell a file from a directory at this point. |
| policy->GrantReadDirectory(process()->id(), path); |
| } |
| Send(new DragMsg_TargetDragEnter(routing_id(), drop_data, client_pt, |
| screen_pt, operations_allowed)); |
| } |
| |
| void RenderViewHost::DragTargetDragOver( |
| const gfx::Point& client_pt, const gfx::Point& screen_pt, |
| WebDragOperationsMask operations_allowed) { |
| Send(new DragMsg_TargetDragOver(routing_id(), client_pt, screen_pt, |
| operations_allowed)); |
| } |
| |
| void RenderViewHost::DragTargetDragLeave() { |
| Send(new DragMsg_TargetDragLeave(routing_id())); |
| } |
| |
| void RenderViewHost::DragTargetDrop( |
| const gfx::Point& client_pt, const gfx::Point& screen_pt) { |
| Send(new DragMsg_TargetDrop(routing_id(), client_pt, screen_pt)); |
| } |
| |
| void RenderViewHost::DesktopNotificationPermissionRequestDone( |
| int callback_context) { |
| Send(new DesktopNotificationMsg_PermissionRequestDone( |
| routing_id(), callback_context)); |
| } |
| |
| void RenderViewHost::DesktopNotificationPostDisplay(int callback_context) { |
| Send(new DesktopNotificationMsg_PostDisplay(routing_id(), callback_context)); |
| } |
| |
| void RenderViewHost::DesktopNotificationPostError(int notification_id, |
| const string16& message) { |
| Send(new DesktopNotificationMsg_PostError( |
| routing_id(), notification_id, message)); |
| } |
| |
| void RenderViewHost::DesktopNotificationPostClose(int notification_id, |
| bool by_user) { |
| Send(new DesktopNotificationMsg_PostClose( |
| routing_id(), notification_id, by_user)); |
| } |
| |
| void RenderViewHost::DesktopNotificationPostClick(int notification_id) { |
| Send(new DesktopNotificationMsg_PostClick(routing_id(), notification_id)); |
| } |
| |
| void RenderViewHost::ExecuteJavascriptInWebFrame( |
| const string16& frame_xpath, |
| const string16& jscript) { |
| Send(new ViewMsg_ScriptEvalRequest(routing_id(), frame_xpath, jscript, |
| 0, false)); |
| } |
| |
| int RenderViewHost::ExecuteJavascriptInWebFrameNotifyResult( |
| const string16& frame_xpath, |
| const string16& jscript) { |
| static int next_id = 1; |
| Send(new ViewMsg_ScriptEvalRequest(routing_id(), frame_xpath, jscript, |
| next_id, true)); |
| return next_id++; |
| } |
| |
| void RenderViewHost::Undo() { |
| Send(new ViewMsg_Undo(routing_id())); |
| UserMetrics::RecordAction(UserMetricsAction("Undo")); |
| } |
| |
| void RenderViewHost::Redo() { |
| Send(new ViewMsg_Redo(routing_id())); |
| UserMetrics::RecordAction(UserMetricsAction("Redo")); |
| } |
| |
| void RenderViewHost::Cut() { |
| Send(new ViewMsg_Cut(routing_id())); |
| UserMetrics::RecordAction(UserMetricsAction("Cut")); |
| } |
| |
| void RenderViewHost::Copy() { |
| Send(new ViewMsg_Copy(routing_id())); |
| UserMetrics::RecordAction(UserMetricsAction("Copy")); |
| } |
| |
| void RenderViewHost::CopyToFindPboard() { |
| #if defined(OS_MACOSX) |
| // Windows/Linux don't have the concept of a find pasteboard. |
| Send(new ViewMsg_CopyToFindPboard(routing_id())); |
| UserMetrics::RecordAction(UserMetricsAction("CopyToFindPboard")); |
| #endif |
| } |
| |
| void RenderViewHost::Paste() { |
| Send(new ViewMsg_Paste(routing_id())); |
| UserMetrics::RecordAction(UserMetricsAction("Paste")); |
| } |
| |
| void RenderViewHost::Delete() { |
| Send(new ViewMsg_Delete(routing_id())); |
| UserMetrics::RecordAction(UserMetricsAction("DeleteSelection")); |
| } |
| |
| void RenderViewHost::SelectAll() { |
| Send(new ViewMsg_SelectAll(routing_id())); |
| UserMetrics::RecordAction(UserMetricsAction("SelectAll")); |
| } |
| |
| void RenderViewHost::JavaScriptDialogClosed(IPC::Message* reply_msg, |
| bool success, |
| const string16& user_input) { |
| process()->set_ignore_input_events(false); |
| bool is_waiting = |
| is_waiting_for_beforeunload_ack_ || is_waiting_for_unload_ack_; |
| if (is_waiting) |
| StartHangMonitorTimeout(TimeDelta::FromMilliseconds(kUnloadTimeoutMS)); |
| |
| ViewHostMsg_RunJavaScriptMessage::WriteReplyParams(reply_msg, |
| success, user_input); |
| Send(reply_msg); |
| |
| // If we are waiting for an unload or beforeunload ack and the user has |
| // suppressed messages, kill the tab immediately; a page that's spamming |
| // alerts in onbeforeunload is presumably malicious, so there's no point in |
| // continuing to run its script and dragging out the process. |
| // This must be done after sending the reply since RenderView can't close |
| // correctly while waiting for a response. |
| if (is_waiting && are_javascript_messages_suppressed_) |
| delegate_->RendererUnresponsive(this, is_waiting); |
| } |
| |
| void RenderViewHost::DragSourceEndedAt( |
| int client_x, int client_y, int screen_x, int screen_y, |
| WebDragOperation operation) { |
| Send(new DragMsg_SourceEndedOrMoved( |
| routing_id(), |
| gfx::Point(client_x, client_y), |
| gfx::Point(screen_x, screen_y), |
| true, operation)); |
| } |
| |
| void RenderViewHost::DragSourceMovedTo( |
| int client_x, int client_y, int screen_x, int screen_y) { |
| Send(new DragMsg_SourceEndedOrMoved( |
| routing_id(), |
| gfx::Point(client_x, client_y), |
| gfx::Point(screen_x, screen_y), |
| false, WebDragOperationNone)); |
| } |
| |
| void RenderViewHost::DragSourceSystemDragEnded() { |
| Send(new DragMsg_SourceSystemDragEnded(routing_id())); |
| } |
| |
| void RenderViewHost::AllowBindings(int bindings_flags) { |
| DCHECK(!renderer_initialized_); |
| enabled_bindings_ |= bindings_flags; |
| } |
| |
| void RenderViewHost::SetWebUIProperty(const std::string& name, |
| const std::string& value) { |
| DCHECK(BindingsPolicy::is_web_ui_enabled(enabled_bindings_)); |
| Send(new ViewMsg_SetWebUIProperty(routing_id(), name, value)); |
| } |
| |
| void RenderViewHost::GotFocus() { |
| RenderWidgetHost::GotFocus(); // Notifies the renderer it got focus. |
| |
| RenderViewHostDelegate::View* view = delegate_->GetViewDelegate(); |
| if (view) |
| view->GotFocus(); |
| } |
| |
| void RenderViewHost::LostCapture() { |
| RenderWidgetHost::LostCapture(); |
| delegate_->LostCapture(); |
| } |
| |
| void RenderViewHost::SetInitialFocus(bool reverse) { |
| Send(new ViewMsg_SetInitialFocus(routing_id(), reverse)); |
| } |
| |
| void RenderViewHost::FilesSelectedInChooser( |
| const std::vector<FilePath>& files) { |
| // Grant the security access requested to the given files. |
| for (std::vector<FilePath>::const_iterator file = files.begin(); |
| file != files.end(); ++file) { |
| ChildProcessSecurityPolicy::GetInstance()->GrantReadFile( |
| process()->id(), *file); |
| } |
| Send(new ViewMsg_RunFileChooserResponse(routing_id(), files)); |
| } |
| |
| void RenderViewHost::DirectoryEnumerationFinished( |
| int request_id, |
| const std::vector<FilePath>& files) { |
| // Grant the security access requested to the given files. |
| for (std::vector<FilePath>::const_iterator file = files.begin(); |
| file != files.end(); ++file) { |
| ChildProcessSecurityPolicy::GetInstance()->GrantReadFile( |
| process()->id(), *file); |
| } |
| Send(new ViewMsg_EnumerateDirectoryResponse(routing_id(), |
| request_id, |
| files)); |
| } |
| |
| void RenderViewHost::LoadStateChanged(const GURL& url, |
| net::LoadState load_state, |
| uint64 upload_position, |
| uint64 upload_size) { |
| delegate_->LoadStateChanged(url, load_state, upload_position, upload_size); |
| } |
| |
| bool RenderViewHost::SuddenTerminationAllowed() const { |
| return sudden_termination_allowed_ || process()->sudden_termination_allowed(); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // RenderViewHost, IPC message handlers: |
| |
| bool RenderViewHost::OnMessageReceived(const IPC::Message& msg) { |
| if (!BrowserMessageFilter::CheckCanDispatchOnUI(msg, this)) |
| return true; |
| |
| // Filter out most IPC messages if this renderer is swapped out. |
| // We still want to certain ACKs to keep our state consistent. |
| if (is_swapped_out_) |
| if (!content::SwappedOutMessages::CanHandleWhileSwappedOut(msg)) |
| return true; |
| |
| ObserverListBase<RenderViewHostObserver>::Iterator it(observers_); |
| RenderViewHostObserver* observer; |
| while ((observer = it.GetNext()) != NULL) { |
| if (observer->OnMessageReceived(msg)) |
| return true; |
| } |
| |
| if (delegate_->OnMessageReceived(msg)) |
| return true; |
| |
| bool handled = true; |
| bool msg_is_ok = true; |
| IPC_BEGIN_MESSAGE_MAP_EX(RenderViewHost, msg, msg_is_ok) |
| IPC_MESSAGE_HANDLER(ViewHostMsg_ShowView, OnMsgShowView) |
| IPC_MESSAGE_HANDLER(ViewHostMsg_ShowWidget, OnMsgShowWidget) |
| IPC_MESSAGE_HANDLER(ViewHostMsg_ShowFullscreenWidget, |
| OnMsgShowFullscreenWidget) |
| IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_RunModal, OnMsgRunModal) |
| IPC_MESSAGE_HANDLER(ViewHostMsg_RenderViewReady, OnMsgRenderViewReady) |
| IPC_MESSAGE_HANDLER(ViewHostMsg_RenderViewGone, OnMsgRenderViewGone) |
| 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(ViewHostMsg_UpdateInspectorSetting, |
| OnUpdateInspectorSetting) |
| 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_DidChangeLoadProgress, |
| OnMsgDidChangeLoadProgress) |
| IPC_MESSAGE_HANDLER(ViewHostMsg_DocumentAvailableInMainFrame, |
| OnMsgDocumentAvailableInMainFrame) |
| IPC_MESSAGE_HANDLER(ViewHostMsg_DocumentOnLoadCompletedInMainFrame, |
| OnMsgDocumentOnLoadCompletedInMainFrame) |
| IPC_MESSAGE_HANDLER(ViewHostMsg_ContextMenu, OnMsgContextMenu) |
| IPC_MESSAGE_HANDLER(ViewHostMsg_OpenURL, OnMsgOpenURL) |
| IPC_MESSAGE_HANDLER(ViewHostMsg_DidContentsPreferredSizeChange, |
| OnMsgDidContentsPreferredSizeChange) |
| IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_RunJavaScriptMessage, |
| OnMsgRunJavaScriptMessage) |
| IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_RunBeforeUnloadConfirm, |
| OnMsgRunBeforeUnloadConfirm) |
| IPC_MESSAGE_HANDLER(DragHostMsg_StartDragging, OnMsgStartDragging) |
| IPC_MESSAGE_HANDLER(DragHostMsg_UpdateDragCursor, OnUpdateDragCursor) |
| IPC_MESSAGE_HANDLER(DragHostMsg_TargetDrop_ACK, OnTargetDropACK) |
| IPC_MESSAGE_HANDLER(ViewHostMsg_TakeFocus, OnTakeFocus) |
| IPC_MESSAGE_HANDLER(ViewHostMsg_AddMessageToConsole, OnAddMessageToConsole) |
| IPC_MESSAGE_HANDLER(ViewHostMsg_ShouldClose_ACK, OnMsgShouldCloseACK) |
| IPC_MESSAGE_HANDLER(ViewHostMsg_ClosePage_ACK, OnMsgClosePageACK) |
| IPC_MESSAGE_HANDLER(ViewHostMsg_SelectionChanged, OnMsgSelectionChanged) |
| IPC_MESSAGE_HANDLER(ViewHostMsg_AccessibilityNotifications, |
| OnAccessibilityNotifications) |
| IPC_MESSAGE_HANDLER(ViewHostMsg_ScriptEvalResponse, OnScriptEvalResponse) |
| IPC_MESSAGE_HANDLER(ViewHostMsg_DidZoomURL, OnDidZoomURL) |
| IPC_MESSAGE_HANDLER(DesktopNotificationHostMsg_RequestPermission, |
| OnRequestDesktopNotificationPermission) |
| IPC_MESSAGE_HANDLER(DesktopNotificationHostMsg_Show, |
| OnShowDesktopNotification) |
| IPC_MESSAGE_HANDLER(DesktopNotificationHostMsg_Cancel, |
| OnCancelDesktopNotification) |
| #if defined(OS_MACOSX) |
| IPC_MESSAGE_HANDLER(ViewHostMsg_ShowPopup, OnMsgShowPopup) |
| #endif |
| // Have the super handle all other messages. |
| IPC_MESSAGE_UNHANDLED(handled = RenderWidgetHost::OnMessageReceived(msg)) |
| // NOTE: Do not add a message handler that just calls the delegate! |
| // Dispatch the message directly there instead. |
| IPC_END_MESSAGE_MAP_EX() |
| |
| if (!msg_is_ok) { |
| // The message had a handler, but its de-serialization failed. |
| // Kill the renderer. |
| UserMetrics::RecordAction(UserMetricsAction("BadMessageTerminate_RVH")); |
| process()->ReceivedBadMessage(); |
| } |
| |
| return handled; |
| } |
| |
| void RenderViewHost::Shutdown() { |
| // If we are being run modally (see RunModal), then we need to cleanup. |
| if (run_modal_reply_msg_) { |
| Send(run_modal_reply_msg_); |
| run_modal_reply_msg_ = NULL; |
| } |
| |
| RenderWidgetHost::Shutdown(); |
| } |
| |
| bool RenderViewHost::IsRenderView() const { |
| return true; |
| } |
| |
| void RenderViewHost::CreateNewWindow( |
| int route_id, |
| const ViewHostMsg_CreateWindow_Params& params) { |
| RenderViewHostDelegate::View* view = delegate_->GetViewDelegate(); |
| if (!view) |
| return; |
| |
| view->CreateNewWindow(route_id, params); |
| } |
| |
| void RenderViewHost::CreateNewWidget(int route_id, |
| WebKit::WebPopupType popup_type) { |
| RenderViewHostDelegate::View* view = delegate_->GetViewDelegate(); |
| if (view) |
| view->CreateNewWidget(route_id, popup_type); |
| } |
| |
| void RenderViewHost::CreateNewFullscreenWidget(int route_id) { |
| RenderViewHostDelegate::View* view = delegate_->GetViewDelegate(); |
| if (view) |
| view->CreateNewFullscreenWidget(route_id); |
| } |
| |
| void RenderViewHost::OnMsgShowView(int route_id, |
| WindowOpenDisposition disposition, |
| const gfx::Rect& initial_pos, |
| bool user_gesture) { |
| RenderViewHostDelegate::View* view = delegate_->GetViewDelegate(); |
| if (view) { |
| if (!is_swapped_out_) |
| view->ShowCreatedWindow(route_id, disposition, initial_pos, user_gesture); |
| Send(new ViewMsg_Move_ACK(route_id)); |
| } |
| } |
| |
| void RenderViewHost::OnMsgShowWidget(int route_id, |
| const gfx::Rect& initial_pos) { |
| RenderViewHostDelegate::View* view = delegate_->GetViewDelegate(); |
| if (view) { |
| if (!is_swapped_out_) |
| view->ShowCreatedWidget(route_id, initial_pos); |
| Send(new ViewMsg_Move_ACK(route_id)); |
| } |
| } |
| |
| void RenderViewHost::OnMsgShowFullscreenWidget(int route_id) { |
| RenderViewHostDelegate::View* view = delegate_->GetViewDelegate(); |
| if (view) { |
| if (!is_swapped_out_) |
| view->ShowCreatedFullscreenWidget(route_id); |
| Send(new ViewMsg_Move_ACK(route_id)); |
| } |
| } |
| |
| void RenderViewHost::OnMsgRunModal(IPC::Message* reply_msg) { |
| DCHECK(!run_modal_reply_msg_); |
| 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::OnMsgRenderViewReady() { |
| render_view_termination_status_ = base::TERMINATION_STATUS_STILL_RUNNING; |
| WasResized(); |
| delegate_->RenderViewReady(this); |
| } |
| |
| void RenderViewHost::OnMsgRenderViewGone(int status, int exit_code) { |
| // Keep the termination status so we can get at it later when we |
| // need to know why it died. |
| render_view_termination_status_ = |
| static_cast<base::TerminationStatus>(status); |
| |
| // Our base class RenderWidgetHost needs to reset some stuff. |
| RendererExited(render_view_termination_status_, exit_code); |
| |
| delegate_->RenderViewGone(this, |
| static_cast<base::TerminationStatus>(status), |
| exit_code); |
| } |
| |
| // 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; |
| |
| // If we're waiting for a cross-site beforeunload ack from this renderer and |
| // we receive a Navigate message from the main frame, then the renderer was |
| // navigating already and sent it before hearing the ViewMsg_Stop message. |
| // We do not want to cancel the pending navigation in this case, since the |
| // old page will soon be stopped. Instead, treat this as a beforeunload ack |
| // to allow the pending navigation to continue. |
| if (is_waiting_for_beforeunload_ack_ && |
| unload_ack_is_for_cross_site_transition_ && |
| PageTransition::IsMainFrame(validated_params.transition)) { |
| OnMsgShouldCloseACK(true); |
| return; |
| } |
| |
| // If we're waiting for an unload ack from this renderer and we receive a |
| // Navigate message, then the renderer was navigating before it received the |
| // unload request. It will either respond to the unload request soon or our |
| // timer will expire. Either way, we should ignore this message, because we |
| // have already committed to closing this renderer. |
| if (is_waiting_for_unload_ack_) |
| return; |
| |
| const int renderer_id = process()->id(); |
| ChildProcessSecurityPolicy* policy = |
| ChildProcessSecurityPolicy::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); |
| } |
| |
| void RenderViewHost::OnMsgUpdateState(int32 page_id, |
| const std::string& state) { |
| delegate_->UpdateState(this, page_id, state); |
| } |
| |
| void RenderViewHost::OnMsgUpdateTitle(int32 page_id, |
| const string16& title) { |
| if (title.length() > content::kMaxTitleChars) { |
| NOTREACHED() << "Renderer sent too many characters in title."; |
| return; |
| } |
| delegate_->UpdateTitle(this, page_id, title); |
| } |
| |
| void RenderViewHost::OnMsgUpdateEncoding(const std::string& encoding_name) { |
| delegate_->UpdateEncoding(this, encoding_name); |
| } |
| |
| void RenderViewHost::OnMsgUpdateTargetURL(int32 page_id, |
| const GURL& url) { |
| if (!is_swapped_out_) |
| 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::OnUpdateInspectorSetting( |
| const std::string& key, const std::string& value) { |
| content::GetContentClient()->browser()->UpdateInspectorSetting( |
| this, key, value); |
| } |
| |
| void RenderViewHost::OnMsgClose() { |
| // If the renderer is telling us to close, it has already run the unload |
| // events, and we can take the fast path. |
| ClosePageIgnoringUnloadEvents(); |
| } |
| |
| void RenderViewHost::OnMsgRequestMove(const gfx::Rect& pos) { |
| if (!is_swapped_out_) |
| delegate_->RequestMove(pos); |
| Send(new ViewMsg_Move_ACK(routing_id())); |
| } |
| |
| void RenderViewHost::OnMsgDidStartLoading() { |
| delegate_->DidStartLoading(); |
| } |
| |
| void RenderViewHost::OnMsgDidStopLoading() { |
| delegate_->DidStopLoading(); |
| } |
| |
| void RenderViewHost::OnMsgDidChangeLoadProgress(double load_progress) { |
| delegate_->DidChangeLoadProgress(load_progress); |
| } |
| |
| void RenderViewHost::OnMsgDocumentAvailableInMainFrame() { |
| delegate_->DocumentAvailableInMainFrame(this); |
| } |
| |
| void RenderViewHost::OnMsgDocumentOnLoadCompletedInMainFrame(int32 page_id) { |
| delegate_->DocumentOnLoadCompletedInMainFrame(this, page_id); |
| } |
| |
| void RenderViewHost::OnMsgContextMenu(const ContextMenuParams& 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. |
| ContextMenuParams validated_params(params); |
| int renderer_id = process()->id(); |
| ChildProcessSecurityPolicy* policy = |
| ChildProcessSecurityPolicy::GetInstance(); |
| |
| // We don't validate |unfiltered_link_url| so that this field can be used |
| // when users want to copy the original link URL. |
| FilterURL(policy, renderer_id, &validated_params.link_url); |
| FilterURL(policy, renderer_id, &validated_params.src_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(ChildProcessSecurityPolicy::GetInstance(), |
| process()->id(), &validated_url); |
| |
| delegate_->RequestOpenURL(validated_url, referrer, disposition); |
| } |
| |
| void RenderViewHost::OnMsgDidContentsPreferredSizeChange( |
| const gfx::Size& new_size) { |
| RenderViewHostDelegate::View* view = delegate_->GetViewDelegate(); |
| if (!view) |
| return; |
| view->UpdatePreferredSize(new_size); |
| } |
| |
| void RenderViewHost::OnMsgSelectionChanged(const std::string& text, |
| const ui::Range& range) { |
| if (view()) |
| view()->SelectionChanged(text, range); |
| } |
| |
| void RenderViewHost::OnMsgRunJavaScriptMessage( |
| const string16& message, |
| const string16& default_prompt, |
| const GURL& frame_url, |
| const int flags, |
| IPC::Message* reply_msg) { |
| // While a JS message dialog is showing, tabs in the same process shouldn't |
| // process input events. |
| process()->set_ignore_input_events(true); |
| StopHangMonitorTimeout(); |
| delegate_->RunJavaScriptMessage(this, message, default_prompt, frame_url, |
| flags, reply_msg, |
| &are_javascript_messages_suppressed_); |
| } |
| |
| void RenderViewHost::OnMsgRunBeforeUnloadConfirm(const GURL& frame_url, |
| const string16& message, |
| IPC::Message* reply_msg) { |
| // While a JS before unload dialog is showing, tabs in the same process |
| // shouldn't process input events. |
| process()->set_ignore_input_events(true); |
| StopHangMonitorTimeout(); |
| delegate_->RunBeforeUnloadConfirm(this, message, reply_msg); |
| } |
| |
| void RenderViewHost::OnMsgStartDragging( |
| const WebDropData& drop_data, |
| WebDragOperationsMask drag_operations_mask, |
| const SkBitmap& image, |
| const gfx::Point& image_offset) { |
| RenderViewHostDelegate::View* view = delegate_->GetViewDelegate(); |
| if (!view) |
| return; |
| |
| GURL drag_url = drop_data.url; |
| GURL html_base_url = drop_data.html_base_url; |
| |
| ChildProcessSecurityPolicy* policy = |
| ChildProcessSecurityPolicy::GetInstance(); |
| |
| // Allow drag of Javascript URLs to enable bookmarklet drag to bookmark bar. |
| if (!drag_url.SchemeIs(chrome::kJavaScriptScheme)) |
| FilterURL(policy, process()->id(), &drag_url); |
| FilterURL(policy, process()->id(), &html_base_url); |
| |
| if (drag_url != drop_data.url || html_base_url != drop_data.html_base_url) { |
| WebDropData drop_data_copy = drop_data; |
| drop_data_copy.url = drag_url; |
| drop_data_copy.html_base_url = html_base_url; |
| view->StartDragging(drop_data_copy, drag_operations_mask, image, |
| image_offset); |
| } else { |
| view->StartDragging(drop_data, drag_operations_mask, image, image_offset); |
| } |
| } |
| |
| void RenderViewHost::OnUpdateDragCursor(WebDragOperation current_op) { |
| RenderViewHostDelegate::View* view = delegate_->GetViewDelegate(); |
| if (view) |
| view->UpdateDragCursor(current_op); |
| } |
| |
| void RenderViewHost::OnTargetDropACK() { |
| NotificationService::current()->Notify( |
| content::NOTIFICATION_RENDER_VIEW_HOST_DID_RECEIVE_DRAG_TARGET_DROP_ACK, |
| Source<RenderViewHost>(this), |
| NotificationService::NoDetails()); |
| } |
| |
| void RenderViewHost::OnTakeFocus(bool reverse) { |
| RenderViewHostDelegate::View* view = delegate_->GetViewDelegate(); |
| if (view) |
| view->TakeFocus(reverse); |
| } |
| |
| void RenderViewHost::OnAddMessageToConsole(int32 level, |
| const std::wstring& message, |
| int32 line_no, |
| const std::wstring& source_id) { |
| // Pass through log level only on WebUI pages to limit console spew. |
| int32 resolved_level = |
| BindingsPolicy::is_web_ui_enabled(enabled_bindings_) ? level : 0; |
| |
| logging::LogMessage("CONSOLE", line_no, resolved_level).stream() << "\"" << |
| message << "\", source: " << source_id << " (" << line_no << ")"; |
| } |
| |
| void RenderViewHost::AddObserver(RenderViewHostObserver* observer) { |
| observers_.AddObserver(observer); |
| } |
| |
| void RenderViewHost::RemoveObserver(RenderViewHostObserver* observer) { |
| observers_.RemoveObserver(observer); |
| } |
| |
| bool RenderViewHost::PreHandleKeyboardEvent( |
| const NativeWebKeyboardEvent& event, bool* is_keyboard_shortcut) { |
| return delegate_->PreHandleKeyboardEvent(event, is_keyboard_shortcut); |
| } |
| |
| void RenderViewHost::UnhandledKeyboardEvent( |
| const NativeWebKeyboardEvent& event) { |
| delegate_->HandleKeyboardEvent(event); |
| } |
| |
| void RenderViewHost::OnUserGesture() { |
| delegate_->OnUserGesture(); |
| } |
| |
| void RenderViewHost::OnMsgShouldCloseACK(bool proceed) { |
| StopHangMonitorTimeout(); |
| // If this renderer navigated while the beforeunload request was in flight, we |
| // may have cleared this state in OnMsgNavigate, in which case we can ignore |
| // this message. |
| if (!is_waiting_for_beforeunload_ack_ || is_swapped_out_) |
| return; |
| |
| is_waiting_for_beforeunload_ack_ = false; |
| |
| RenderViewHostDelegate::RendererManagement* management_delegate = |
| delegate_->GetRendererManagementDelegate(); |
| if (management_delegate) { |
| management_delegate->ShouldClosePage( |
| unload_ack_is_for_cross_site_transition_, proceed); |
| } |
| |
| // If canceled, notify the delegate to cancel its pending navigation entry. |
| if (!proceed) |
| delegate_->DidCancelLoading(); |
| } |
| |
| void RenderViewHost::OnMsgClosePageACK() { |
| ClosePageIgnoringUnloadEvents(); |
| } |
| |
| void RenderViewHost::NotifyRendererUnresponsive() { |
| delegate_->RendererUnresponsive( |
| this, is_waiting_for_beforeunload_ack_ || is_waiting_for_unload_ack_); |
| } |
| |
| void RenderViewHost::NotifyRendererResponsive() { |
| delegate_->RendererResponsive(this); |
| } |
| |
| void RenderViewHost::OnMsgFocus() { |
| delegate_->Activate(); |
| } |
| |
| void RenderViewHost::OnMsgBlur() { |
| delegate_->Deactivate(); |
| } |
| |
| void RenderViewHost::ForwardMouseEvent( |
| const WebKit::WebMouseEvent& mouse_event) { |
| |
| // We make a copy of the mouse event because |
| // RenderWidgetHost::ForwardMouseEvent will delete |mouse_event|. |
| WebKit::WebMouseEvent event_copy(mouse_event); |
| RenderWidgetHost::ForwardMouseEvent(event_copy); |
| |
| switch (event_copy.type) { |
| case WebInputEvent::MouseMove: |
| delegate_->HandleMouseMove(); |
| break; |
| case WebInputEvent::MouseLeave: |
| delegate_->HandleMouseLeave(); |
| break; |
| case WebInputEvent::MouseDown: |
| delegate_->HandleMouseDown(); |
| break; |
| case WebInputEvent::MouseWheel: |
| if (ignore_input_events()) |
| delegate_->OnIgnoredUIEvent(); |
| break; |
| case WebInputEvent::MouseUp: |
| delegate_->HandleMouseUp(); |
| default: |
| // For now, we don't care about the rest. |
| break; |
| } |
| } |
| |
| void RenderViewHost::OnMouseActivate() { |
| delegate_->HandleMouseActivate(); |
| } |
| |
| void RenderViewHost::ForwardKeyboardEvent( |
| const NativeWebKeyboardEvent& key_event) { |
| if (ignore_input_events()) { |
| if (key_event.type == WebInputEvent::RawKeyDown) |
| delegate_->OnIgnoredUIEvent(); |
| return; |
| } |
| RenderWidgetHost::ForwardKeyboardEvent(key_event); |
| } |
| |
| #if defined(OS_MACOSX) |
| void RenderViewHost::DidSelectPopupMenuItem(int selected_index) { |
| Send(new ViewMsg_SelectPopupMenuItem(routing_id(), selected_index)); |
| } |
| |
| void RenderViewHost::DidCancelPopupMenu() { |
| Send(new ViewMsg_SelectPopupMenuItem(routing_id(), -1)); |
| } |
| #endif |
| |
| void RenderViewHost::FilterURL(ChildProcessSecurityPolicy* policy, |
| int renderer_id, |
| GURL* url) { |
| if (!url->is_valid()) |
| return; // We don't need to block invalid URLs. |
| |
| if (url->SchemeIs(chrome::kAboutScheme)) { |
| // The renderer treats all URLs in the about: scheme as being about:blank. |
| // Canonicalize about: URLs to about:blank. |
| *url = GURL(chrome::kAboutBlankURL); |
| } |
| |
| 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. |
| VLOG(1) << "Blocked URL " << url->spec(); |
| *url = GURL(); |
| } |
| } |
| |
| void RenderViewHost::OnAccessibilityNotifications( |
| const std::vector<ViewHostMsg_AccessibilityNotification_Params>& params) { |
| if (view() && !is_swapped_out_) |
| view()->OnAccessibilityNotifications(params); |
| |
| if (!params.empty()) { |
| for (unsigned i = 0; i < params.size(); i++) { |
| const ViewHostMsg_AccessibilityNotification_Params& param = params[i]; |
| |
| if (param.notification_type == |
| ViewHostMsg_AccessibilityNotification_Type:: |
| NOTIFICATION_TYPE_LOAD_COMPLETE) { |
| if (save_accessibility_tree_for_testing_) |
| accessibility_tree_ = param.acc_obj; |
| } |
| } |
| |
| NotificationService::current()->Notify( |
| content::NOTIFICATION_RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED, |
| Source<RenderViewHost>(this), |
| NotificationService::NoDetails()); |
| } |
| |
| Send(new ViewMsg_AccessibilityNotifications_ACK(routing_id())); |
| } |
| |
| void RenderViewHost::OnScriptEvalResponse(int id, const ListValue& result) { |
| Value* result_value; |
| if (!result.Get(0, &result_value)) { |
| // Programming error or rogue renderer. |
| NOTREACHED() << "Got bad arguments for OnScriptEvalResponse"; |
| return; |
| } |
| std::pair<int, Value*> details(id, result_value); |
| NotificationService::current()->Notify( |
| content::NOTIFICATION_EXECUTE_JAVASCRIPT_RESULT, |
| Source<RenderViewHost>(this), |
| Details<std::pair<int, Value*> >(&details)); |
| } |
| |
| void RenderViewHost::OnDidZoomURL(double zoom_level, |
| bool remember, |
| const GURL& url) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| HostZoomMap* host_zoom_map = process()->profile()->GetHostZoomMap(); |
| if (remember) { |
| host_zoom_map->SetZoomLevel(net::GetHostOrSpecFromURL(url), zoom_level); |
| // Notify renderers from this profile. |
| for (RenderProcessHost::iterator i(RenderProcessHost::AllHostsIterator()); |
| !i.IsAtEnd(); i.Advance()) { |
| RenderProcessHost* render_process_host = i.GetCurrentValue(); |
| if (render_process_host->profile() == process()->profile()) { |
| render_process_host->Send( |
| new ViewMsg_SetZoomLevelForCurrentURL(url, zoom_level)); |
| } |
| } |
| } else { |
| host_zoom_map->SetTemporaryZoomLevel( |
| process()->id(), routing_id(), zoom_level); |
| } |
| } |
| |
| void RenderViewHost::OnRequestDesktopNotificationPermission( |
| const GURL& source_origin, int callback_context) { |
| content::GetContentClient()->browser()->RequestDesktopNotificationPermission( |
| source_origin, callback_context, process()->id(), routing_id()); |
| } |
| |
| void RenderViewHost::OnShowDesktopNotification( |
| const DesktopNotificationHostMsg_Show_Params& params) { |
| // Disallow HTML notifications from unwanted schemes. javascript: |
| // in particular allows unwanted cross-domain access. |
| GURL url = params.contents_url; |
| if (params.is_html && |
| !url.SchemeIs(chrome::kHttpScheme) && |
| !url.SchemeIs(chrome::kHttpsScheme) && |
| !url.SchemeIs(chrome::kExtensionScheme) && |
| !url.SchemeIs(chrome::kDataScheme)) { |
| return; |
| } |
| |
| content::GetContentClient()->browser()->ShowDesktopNotification( |
| params, process()->id(), routing_id(), false); |
| } |
| |
| void RenderViewHost::OnCancelDesktopNotification(int notification_id) { |
| content::GetContentClient()->browser()->CancelDesktopNotification( |
| process()->id(), routing_id(), notification_id); |
| } |
| |
| #if defined(OS_MACOSX) |
| void RenderViewHost::OnMsgShowPopup( |
| const ViewHostMsg_ShowPopup_Params& params) { |
| RenderViewHostDelegate::View* view = delegate_->GetViewDelegate(); |
| if (view) { |
| view->ShowPopupMenu(params.bounds, |
| params.item_height, |
| params.item_font_size, |
| params.selected_item, |
| params.popup_items, |
| params.right_aligned); |
| } |
| } |
| #endif |