| // 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 "chrome/browser/external_tab_container_win.h" |
| |
| #include <string> |
| |
| #include "base/debug/trace_event.h" |
| #include "base/i18n/rtl.h" |
| #include "base/logging.h" |
| #include "base/utf_string_conversions.h" |
| #include "base/win/win_util.h" |
| #include "chrome/app/chrome_command_ids.h" |
| #include "chrome/app/chrome_dll_resource.h" |
| #include "chrome/browser/automation/automation_provider.h" |
| #include "chrome/browser/debugger/devtools_manager.h" |
| #include "chrome/browser/debugger/devtools_toggle_action.h" |
| #include "chrome/browser/google/google_util.h" |
| #include "chrome/browser/history/history_types.h" |
| #include "chrome/browser/load_notification_details.h" |
| #include "chrome/browser/page_info_window.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/browser_window.h" |
| #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" |
| #include "chrome/browser/ui/views/browser_dialogs.h" |
| #include "chrome/browser/ui/views/infobars/infobar_container_view.h" |
| #include "chrome/browser/ui/views/page_info_bubble_view.h" |
| #include "chrome/browser/ui/views/tab_contents/render_view_context_menu_views.h" |
| #include "chrome/browser/ui/views/tab_contents/tab_contents_container.h" |
| #include "chrome/common/automation_messages.h" |
| #include "chrome/common/chrome_constants.h" |
| #include "chrome/common/url_constants.h" |
| #include "content/browser/renderer_host/render_process_host.h" |
| #include "content/browser/renderer_host/render_view_host.h" |
| #include "content/browser/renderer_host/resource_dispatcher_host_request_info.h" |
| #include "content/browser/tab_contents/provisional_load_details.h" |
| #include "content/common/bindings_policy.h" |
| #include "content/common/native_web_keyboard_event.h" |
| #include "content/common/notification_service.h" |
| #include "content/common/page_transition_types.h" |
| #include "content/common/view_messages.h" |
| #include "grit/generated_resources.h" |
| #include "grit/locale_settings.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/base/resource/resource_bundle.h" |
| #include "ui/base/view_prop.h" |
| #include "views/layout/grid_layout.h" |
| #include "views/widget/root_view.h" |
| #include "views/window/window.h" |
| |
| using ui::ViewProp; |
| |
| static const char kWindowObjectKey[] = "ChromeWindowObject"; |
| |
| // This class overrides the LinkActivated function in the PageInfoBubbleView |
| // class and routes the help center link navigation to the host browser. |
| class ExternalTabPageInfoBubbleView : public PageInfoBubbleView { |
| public: |
| ExternalTabPageInfoBubbleView(ExternalTabContainer* container, |
| gfx::NativeWindow parent_window, |
| Profile* profile, |
| const GURL& url, |
| const NavigationEntry::SSLStatus& ssl, |
| bool show_history) |
| : PageInfoBubbleView(parent_window, profile, url, ssl, show_history), |
| container_(container) { |
| DVLOG(1) << __FUNCTION__; |
| } |
| virtual ~ExternalTabPageInfoBubbleView() { |
| DVLOG(1) << __FUNCTION__; |
| } |
| // LinkController methods: |
| virtual void LinkActivated(views::Link* source, int event_flags) { |
| GURL url = google_util::AppendGoogleLocaleParam( |
| GURL(chrome::kPageInfoHelpCenterURL)); |
| container_->OpenURLFromTab(container_->tab_contents(), url, GURL(), |
| NEW_FOREGROUND_TAB, PageTransition::LINK); |
| } |
| private: |
| scoped_refptr<ExternalTabContainer> container_; |
| }; |
| |
| base::LazyInstance<ExternalTabContainer::PendingTabs> |
| ExternalTabContainer::pending_tabs_(base::LINKER_INITIALIZED); |
| |
| ExternalTabContainer::ExternalTabContainer( |
| AutomationProvider* automation, AutomationResourceMessageFilter* filter) |
| : automation_(automation), |
| tab_contents_container_(NULL), |
| tab_handle_(0), |
| ignore_next_load_notification_(false), |
| automation_resource_message_filter_(filter), |
| load_requests_via_automation_(false), |
| handle_top_level_requests_(false), |
| external_method_factory_(this), |
| pending_(false), |
| infobars_enabled_(true), |
| focus_manager_(NULL), |
| external_tab_view_(NULL), |
| unload_reply_message_(NULL), |
| route_all_top_level_navigations_(false), |
| is_popup_window_(false) { |
| } |
| |
| ExternalTabContainer::~ExternalTabContainer() { |
| Uninitialize(); |
| } |
| |
| TabContents* ExternalTabContainer::tab_contents() const { |
| return tab_contents_.get() ? tab_contents_->tab_contents() : NULL; |
| } |
| |
| bool ExternalTabContainer::Init(Profile* profile, |
| HWND parent, |
| const gfx::Rect& bounds, |
| DWORD style, |
| bool load_requests_via_automation, |
| bool handle_top_level_requests, |
| TabContentsWrapper* existing_contents, |
| const GURL& initial_url, |
| const GURL& referrer, |
| bool infobars_enabled, |
| bool route_all_top_level_navigations) { |
| if (IsWindow()) { |
| NOTREACHED(); |
| return false; |
| } |
| |
| load_requests_via_automation_ = load_requests_via_automation; |
| handle_top_level_requests_ = handle_top_level_requests; |
| infobars_enabled_ = infobars_enabled; |
| route_all_top_level_navigations_ = route_all_top_level_navigations; |
| |
| set_window_style(WS_POPUP | WS_CLIPCHILDREN); |
| views::WidgetWin::Init(NULL, bounds); |
| if (!IsWindow()) { |
| NOTREACHED(); |
| return false; |
| } |
| |
| // TODO(jcampan): limit focus traversal to contents. |
| |
| prop_.reset(new ViewProp(GetNativeView(), kWindowObjectKey, this)); |
| |
| if (existing_contents) { |
| tab_contents_.reset(existing_contents); |
| tab_contents_->controller().set_profile(profile); |
| } else { |
| TabContents* new_contents = new TabContents(profile, NULL, MSG_ROUTING_NONE, |
| NULL, NULL); |
| tab_contents_.reset(new TabContentsWrapper(new_contents)); |
| } |
| |
| tab_contents_->tab_contents()->set_delegate(this); |
| |
| tab_contents_->tab_contents()-> |
| GetMutableRendererPrefs()->browser_handles_top_level_requests = |
| handle_top_level_requests; |
| |
| if (!existing_contents) { |
| tab_contents_->render_view_host()->AllowBindings( |
| BindingsPolicy::EXTERNAL_HOST); |
| } |
| |
| NavigationController* controller = &tab_contents_->controller(); |
| registrar_.Add(this, NotificationType::NAV_ENTRY_COMMITTED, |
| Source<NavigationController>(controller)); |
| registrar_.Add(this, NotificationType::FAIL_PROVISIONAL_LOAD_WITH_ERROR, |
| Source<NavigationController>(controller)); |
| registrar_.Add(this, NotificationType::LOAD_STOP, |
| Source<NavigationController>(controller)); |
| registrar_.Add(this, NotificationType::RENDER_VIEW_HOST_CREATED_FOR_TAB, |
| Source<TabContents>(tab_contents_->tab_contents())); |
| registrar_.Add(this, NotificationType::RENDER_VIEW_HOST_DELETED, |
| NotificationService::AllSources()); |
| |
| NotificationService::current()->Notify( |
| NotificationType::EXTERNAL_TAB_CREATED, |
| Source<NavigationController>(controller), |
| NotificationService::NoDetails()); |
| |
| // Start loading initial URL |
| if (!initial_url.is_empty()) { |
| // Navigate out of context since we don't have a 'tab_handle_' yet. |
| MessageLoop::current()->PostTask( |
| FROM_HERE, |
| external_method_factory_.NewRunnableMethod( |
| &ExternalTabContainer::Navigate, initial_url, referrer)); |
| } |
| |
| // We need WS_POPUP to be on the window during initialization, but |
| // once initialized we apply the requested style which may or may not |
| // include the popup bit. |
| // Note that it's important to do this before we call SetParent since |
| // during the SetParent call we will otherwise get a WA_ACTIVATE call |
| // that causes us to steal the current focus. |
| SetWindowLong(GWL_STYLE, (GetWindowLong(GWL_STYLE) & ~WS_POPUP) | style); |
| |
| // Now apply the parenting and style |
| if (parent) |
| SetParent(GetNativeView(), parent); |
| |
| ::ShowWindow(tab_contents_->tab_contents()->GetNativeView(), SW_SHOWNA); |
| |
| LoadAccelerators(); |
| SetupExternalTabView(); |
| return true; |
| } |
| |
| void ExternalTabContainer::Uninitialize() { |
| registrar_.RemoveAll(); |
| if (tab_contents_.get()) { |
| UnregisterRenderViewHost(tab_contents_->render_view_host()); |
| |
| if (GetRootView()) { |
| GetRootView()->RemoveAllChildViews(true); |
| } |
| |
| NotificationService::current()->Notify( |
| NotificationType::EXTERNAL_TAB_CLOSED, |
| Source<NavigationController>(&tab_contents_->controller()), |
| Details<ExternalTabContainer>(this)); |
| |
| tab_contents_.reset(NULL); |
| } |
| |
| if (focus_manager_) { |
| focus_manager_->UnregisterAccelerators(this); |
| focus_manager_ = NULL; |
| } |
| |
| external_tab_view_ = NULL; |
| request_context_ = NULL; |
| tab_contents_container_ = NULL; |
| } |
| |
| bool ExternalTabContainer::Reinitialize( |
| AutomationProvider* automation_provider, |
| AutomationResourceMessageFilter* filter, |
| gfx::NativeWindow parent_window) { |
| if (!automation_provider || !filter) { |
| NOTREACHED(); |
| return false; |
| } |
| |
| automation_ = automation_provider; |
| automation_resource_message_filter_ = filter; |
| // Wait for the automation channel to be initialized before resuming pending |
| // render views and sending in the navigation state. |
| MessageLoop::current()->PostTask( |
| FROM_HERE, |
| external_method_factory_.NewRunnableMethod( |
| &ExternalTabContainer::OnReinitialize)); |
| |
| if (parent_window) |
| SetParent(GetNativeView(), parent_window); |
| return true; |
| } |
| |
| void ExternalTabContainer::SetTabHandle(int handle) { |
| tab_handle_ = handle; |
| } |
| |
| void ExternalTabContainer::ProcessUnhandledAccelerator(const MSG& msg) { |
| NativeWebKeyboardEvent keyboard_event(msg.hwnd, msg.message, msg.wParam, |
| msg.lParam); |
| unhandled_keyboard_event_handler_.HandleKeyboardEvent(keyboard_event, |
| focus_manager_); |
| } |
| |
| void ExternalTabContainer::FocusThroughTabTraversal( |
| bool reverse, bool restore_focus_to_view) { |
| DCHECK(tab_contents_.get()); |
| if (tab_contents_.get()) |
| tab_contents_->tab_contents()->Focus(); |
| |
| // The tab_contents_ member can get destroyed in the context of the call to |
| // TabContentsViewViews::Focus() above. This method eventually calls SetFocus |
| // on the native window, which could end up dispatching messages like |
| // WM_DESTROY for the external tab. |
| if (tab_contents_.get() && restore_focus_to_view) |
| tab_contents_->tab_contents()->FocusThroughTabTraversal(reverse); |
| } |
| |
| // static |
| bool ExternalTabContainer::IsExternalTabContainer(HWND window) { |
| return ViewProp::GetValue(window, kWindowObjectKey) != NULL; |
| } |
| |
| // static |
| ExternalTabContainer* ExternalTabContainer::GetContainerForTab( |
| HWND tab_window) { |
| HWND parent_window = ::GetParent(tab_window); |
| if (!::IsWindow(parent_window)) { |
| return NULL; |
| } |
| if (!IsExternalTabContainer(parent_window)) { |
| return NULL; |
| } |
| ExternalTabContainer* container = reinterpret_cast<ExternalTabContainer*>( |
| ViewProp::GetValue(parent_window, kWindowObjectKey)); |
| return container; |
| } |
| |
| // static |
| ExternalTabContainer* |
| ExternalTabContainer::GetExternalContainerFromNativeWindow( |
| gfx::NativeView native_window) { |
| ExternalTabContainer* tab_container = NULL; |
| if (native_window) { |
| tab_container = reinterpret_cast<ExternalTabContainer*>( |
| ViewProp::GetValue(native_window, kWindowObjectKey)); |
| } |
| return tab_container; |
| } |
| //////////////////////////////////////////////////////////////////////////////// |
| // ExternalTabContainer, TabContentsDelegate implementation: |
| |
| void ExternalTabContainer::OpenURLFromTab(TabContents* source, |
| const GURL& url, |
| const GURL& referrer, |
| WindowOpenDisposition disposition, |
| PageTransition::Type transition) { |
| if (pending()) { |
| PendingTopLevelNavigation url_request; |
| url_request.disposition = disposition; |
| url_request.transition = transition; |
| url_request.url = url; |
| url_request.referrer = referrer; |
| |
| pending_open_url_requests_.push_back(url_request); |
| return; |
| } |
| |
| switch (disposition) { |
| case CURRENT_TAB: |
| case SINGLETON_TAB: |
| case NEW_FOREGROUND_TAB: |
| case NEW_BACKGROUND_TAB: |
| case NEW_POPUP: |
| case NEW_WINDOW: |
| case SAVE_TO_DISK: |
| if (automation_) { |
| automation_->Send(new AutomationMsg_OpenURL(tab_handle_, |
| url, referrer, |
| disposition)); |
| // TODO(ananta) |
| // We should populate other fields in the |
| // ViewHostMsg_FrameNavigate_Params structure. Another option could be |
| // to refactor the UpdateHistoryForNavigation function in TabContents. |
| ViewHostMsg_FrameNavigate_Params params; |
| params.referrer = referrer; |
| params.url = url; |
| params.page_id = -1; |
| params.transition = PageTransition::LINK; |
| |
| NavigationController::LoadCommittedDetails details; |
| details.did_replace_entry = false; |
| |
| scoped_refptr<history::HistoryAddPageArgs> add_page_args( |
| tab_contents_->tab_contents()-> |
| CreateHistoryAddPageArgs(url, details, params)); |
| tab_contents_->tab_contents()-> |
| UpdateHistoryForNavigation(add_page_args); |
| } |
| break; |
| default: |
| NOTREACHED(); |
| break; |
| } |
| } |
| |
| void ExternalTabContainer::NavigationStateChanged(const TabContents* source, |
| unsigned changed_flags) { |
| if (automation_) { |
| NavigationInfo nav_info; |
| if (InitNavigationInfo(&nav_info, NavigationType::NAV_IGNORE, 0)) |
| automation_->Send(new AutomationMsg_NavigationStateChanged( |
| tab_handle_, changed_flags, nav_info)); |
| } |
| } |
| |
| void ExternalTabContainer::AddNewContents(TabContents* source, |
| TabContents* new_contents, |
| WindowOpenDisposition disposition, |
| const gfx::Rect& initial_pos, |
| bool user_gesture) { |
| if (!automation_) { |
| DCHECK(pending_); |
| LOG(ERROR) << "Invalid automation provider. Dropping new contents notify"; |
| delete new_contents; |
| return; |
| } |
| |
| scoped_refptr<ExternalTabContainer> new_container; |
| // If the host is a browser like IE8, then the URL being navigated to in the |
| // new tab contents could potentially navigate back to Chrome from a new |
| // IE process. We support full tab mode only for IE and hence we use that as |
| // a determining factor in whether the new ExternalTabContainer instance is |
| // created as pending or not. |
| if (!route_all_top_level_navigations_) { |
| new_container = new ExternalTabContainer(NULL, NULL); |
| } else { |
| // Reuse the same tab handle here as the new container instance is a dummy |
| // instance which does not have an automation client connected at the other |
| // end. |
| new_container = new TemporaryPopupExternalTabContainer( |
| automation_, automation_resource_message_filter_.get()); |
| new_container->SetTabHandle(tab_handle_); |
| } |
| |
| // Make sure that ExternalTabContainer instance is initialized with |
| // an unwrapped Profile. |
| scoped_ptr<TabContentsWrapper> wrapper(new TabContentsWrapper(new_contents)); |
| bool result = new_container->Init( |
| new_contents->profile()->GetOriginalProfile(), |
| NULL, |
| initial_pos, |
| WS_CHILD, |
| load_requests_via_automation_, |
| handle_top_level_requests_, |
| wrapper.get(), |
| GURL(), |
| GURL(), |
| true, |
| route_all_top_level_navigations_); |
| |
| if (result) { |
| wrapper.release(); // Ownership has been transferred. |
| if (route_all_top_level_navigations_) { |
| return; |
| } |
| uintptr_t cookie = reinterpret_cast<uintptr_t>(new_container.get()); |
| pending_tabs_.Get()[cookie] = new_container; |
| new_container->set_pending(true); |
| new_container->set_is_popup_window(disposition == NEW_POPUP); |
| AttachExternalTabParams attach_params_; |
| attach_params_.cookie = static_cast<uint64>(cookie); |
| attach_params_.dimensions = initial_pos; |
| attach_params_.user_gesture = user_gesture; |
| attach_params_.disposition = disposition; |
| attach_params_.profile_name = WideToUTF8( |
| tab_contents()->profile()->GetPath().DirName().BaseName().value()); |
| automation_->Send(new AutomationMsg_AttachExternalTab( |
| tab_handle_, attach_params_)); |
| } else { |
| NOTREACHED(); |
| } |
| } |
| |
| void ExternalTabContainer::TabContentsCreated(TabContents* new_contents) { |
| RenderViewHost* rvh = new_contents->render_view_host(); |
| DCHECK(rvh != NULL); |
| |
| // Register this render view as a pending render view, i.e. any network |
| // requests initiated by this render view would be serviced when the |
| // external host connects to the new external tab instance. |
| RegisterRenderViewHostForAutomation(rvh, true); |
| } |
| |
| bool ExternalTabContainer::infobars_enabled() { |
| return infobars_enabled_; |
| } |
| |
| void ExternalTabContainer::ActivateContents(TabContents* contents) { |
| } |
| |
| void ExternalTabContainer::DeactivateContents(TabContents* contents) { |
| } |
| |
| void ExternalTabContainer::LoadingStateChanged(TabContents* source) { |
| } |
| |
| void ExternalTabContainer::CloseContents(TabContents* source) { |
| if (!automation_) |
| return; |
| |
| if (unload_reply_message_) { |
| AutomationMsg_RunUnloadHandlers::WriteReplyParams(unload_reply_message_, |
| true); |
| automation_->Send(unload_reply_message_); |
| unload_reply_message_ = NULL; |
| } else { |
| automation_->Send(new AutomationMsg_CloseExternalTab(tab_handle_)); |
| } |
| } |
| |
| void ExternalTabContainer::MoveContents(TabContents* source, |
| const gfx::Rect& pos) { |
| if (automation_ && is_popup_window_) |
| automation_->Send(new AutomationMsg_MoveWindow(tab_handle_, pos)); |
| } |
| |
| bool ExternalTabContainer::IsPopup(const TabContents* source) const { |
| return is_popup_window_; |
| } |
| |
| void ExternalTabContainer::UpdateTargetURL(TabContents* source, |
| const GURL& url) { |
| if (automation_) { |
| std::wstring url_string = CA2W(url.spec().c_str()); |
| automation_->Send( |
| new AutomationMsg_UpdateTargetUrl(tab_handle_, url_string)); |
| } |
| } |
| |
| void ExternalTabContainer::ContentsZoomChange(bool zoom_in) { |
| } |
| |
| void ExternalTabContainer::ForwardMessageToExternalHost( |
| const std::string& message, const std::string& origin, |
| const std::string& target) { |
| if (automation_) { |
| automation_->Send(new AutomationMsg_ForwardMessageToExternalHost( |
| tab_handle_, message, origin, target)); |
| } |
| } |
| |
| bool ExternalTabContainer::IsExternalTabContainer() const { |
| return true; |
| } |
| |
| gfx::NativeWindow ExternalTabContainer::GetFrameNativeWindow() { |
| return hwnd(); |
| } |
| |
| bool ExternalTabContainer::TakeFocus(bool reverse) { |
| if (automation_) { |
| automation_->Send(new AutomationMsg_TabbedOut(tab_handle_, |
| base::win::IsShiftPressed())); |
| } |
| |
| return true; |
| } |
| |
| bool ExternalTabContainer::CanDownload(int request_id) { |
| if (load_requests_via_automation_) { |
| if (automation_) { |
| // In case the host needs to show UI that needs to take the focus. |
| ::AllowSetForegroundWindow(ASFW_ANY); |
| |
| BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, |
| NewRunnableMethod(automation_resource_message_filter_.get(), |
| &AutomationResourceMessageFilter::SendDownloadRequestToHost, |
| 0, tab_handle_, request_id)); |
| } |
| } else { |
| DLOG(WARNING) << "Downloads are only supported with host browser network " |
| "stack enabled."; |
| } |
| |
| // Never allow downloads. |
| return false; |
| } |
| |
| void ExternalTabContainer::ShowPageInfo(Profile* profile, |
| const GURL& url, |
| const NavigationEntry::SSLStatus& ssl, |
| bool show_history) { |
| POINT cursor_pos = {0}; |
| GetCursorPos(&cursor_pos); |
| |
| gfx::Rect bounds; |
| bounds.set_origin(gfx::Point(cursor_pos)); |
| |
| PageInfoBubbleView* page_info_bubble = |
| new ExternalTabPageInfoBubbleView(this, NULL, profile, url, |
| ssl, show_history); |
| Bubble* bubble = Bubble::Show(this, bounds, BubbleBorder::TOP_LEFT, |
| page_info_bubble, page_info_bubble); |
| page_info_bubble->set_bubble(bubble); |
| } |
| |
| void ExternalTabContainer::RegisterRenderViewHostForAutomation( |
| RenderViewHost* render_view_host, bool pending_view) { |
| if (render_view_host) { |
| AutomationResourceMessageFilter::RegisterRenderView( |
| render_view_host->process()->id(), |
| render_view_host->routing_id(), |
| tab_handle(), |
| automation_resource_message_filter_, |
| pending_view); |
| } |
| } |
| |
| void ExternalTabContainer::RegisterRenderViewHost( |
| RenderViewHost* render_view_host) { |
| // RenderViewHost instances that are to be associated with this |
| // ExternalTabContainer should share the same resource request automation |
| // settings. |
| RegisterRenderViewHostForAutomation( |
| render_view_host, |
| false); // Network requests should not be handled later. |
| } |
| |
| void ExternalTabContainer::UnregisterRenderViewHost( |
| RenderViewHost* render_view_host) { |
| // Undo the resource automation registration performed in |
| // ExternalTabContainer::RegisterRenderViewHost. |
| if (render_view_host) { |
| AutomationResourceMessageFilter::UnRegisterRenderView( |
| render_view_host->process()->id(), |
| render_view_host->routing_id()); |
| } |
| } |
| |
| bool ExternalTabContainer::HandleContextMenu(const ContextMenuParams& params) { |
| if (!automation_) { |
| NOTREACHED(); |
| return false; |
| } |
| external_context_menu_.reset( |
| new RenderViewContextMenuViews(tab_contents(), params)); |
| external_context_menu_->SetExternal(); |
| external_context_menu_->Init(); |
| external_context_menu_->UpdateMenuItemStates(); |
| |
| POINT screen_pt = { params.x, params.y }; |
| MapWindowPoints(GetNativeView(), HWND_DESKTOP, &screen_pt, 1); |
| |
| MiniContextMenuParams ipc_params( |
| screen_pt.x, |
| screen_pt.y, |
| params.link_url, |
| params.unfiltered_link_url, |
| params.src_url, |
| params.page_url, |
| params.frame_url); |
| |
| bool rtl = base::i18n::IsRTL(); |
| automation_->Send( |
| new AutomationMsg_ForwardContextMenuToExternalHost(tab_handle_, |
| external_context_menu_->GetMenuHandle(), |
| rtl ? TPM_RIGHTALIGN : TPM_LEFTALIGN, ipc_params)); |
| |
| return true; |
| } |
| |
| bool ExternalTabContainer::ExecuteContextMenuCommand(int command) { |
| if (!external_context_menu_.get()) { |
| NOTREACHED(); |
| return false; |
| } |
| |
| switch (command) { |
| case IDS_CONTENT_CONTEXT_SAVEAUDIOAS: |
| case IDS_CONTENT_CONTEXT_SAVEVIDEOAS: |
| case IDS_CONTENT_CONTEXT_SAVEIMAGEAS: |
| case IDS_CONTENT_CONTEXT_SAVELINKAS: { |
| NOTREACHED(); // Should be handled in host. |
| break; |
| } |
| } |
| |
| external_context_menu_->ExecuteCommand(command); |
| return true; |
| } |
| |
| bool ExternalTabContainer::PreHandleKeyboardEvent( |
| const NativeWebKeyboardEvent& event, bool* is_keyboard_shortcut) { |
| return false; |
| } |
| |
| void ExternalTabContainer::HandleKeyboardEvent( |
| const NativeWebKeyboardEvent& event) { |
| ProcessUnhandledKeyStroke(event.os_event.hwnd, event.os_event.message, |
| event.os_event.wParam, event.os_event.lParam); |
| } |
| |
| void ExternalTabContainer::ShowHtmlDialog(HtmlDialogUIDelegate* delegate, |
| gfx::NativeWindow parent_window) { |
| if (!browser_.get()) { |
| browser_.reset(Browser::CreateForType(Browser::TYPE_POPUP, |
| tab_contents_->profile())); |
| } |
| |
| gfx::NativeWindow parent = parent_window ? parent_window : GetParent(); |
| browser_->window()->ShowHTMLDialog(delegate, parent); |
| } |
| |
| void ExternalTabContainer::BeforeUnloadFired(TabContents* tab, |
| bool proceed, |
| bool* proceed_to_fire_unload) { |
| *proceed_to_fire_unload = true; |
| |
| if (!automation_) { |
| delete unload_reply_message_; |
| unload_reply_message_ = NULL; |
| return; |
| } |
| |
| if (!unload_reply_message_) { |
| NOTREACHED() << "**** NULL unload reply message pointer."; |
| return; |
| } |
| |
| if (!proceed) { |
| AutomationMsg_RunUnloadHandlers::WriteReplyParams(unload_reply_message_, |
| false); |
| automation_->Send(unload_reply_message_); |
| unload_reply_message_ = NULL; |
| *proceed_to_fire_unload = false; |
| } |
| } |
| |
| void ExternalTabContainer::ShowRepostFormWarningDialog( |
| TabContents* tab_contents) { |
| browser::ShowRepostFormWarningDialog(GetNativeView(), tab_contents); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // ExternalTabContainer, NotificationObserver implementation: |
| |
| void ExternalTabContainer::Observe(NotificationType type, |
| const NotificationSource& source, |
| const NotificationDetails& details) { |
| if (!automation_) |
| return; |
| |
| static const int kHttpClientErrorStart = 400; |
| static const int kHttpServerErrorEnd = 510; |
| |
| switch (type.value) { |
| case NotificationType::LOAD_STOP: { |
| const LoadNotificationDetails* load = |
| Details<LoadNotificationDetails>(details).ptr(); |
| if (load != NULL && PageTransition::IsMainFrame(load->origin())) { |
| TRACE_EVENT_END("ExternalTabContainer::Navigate", 0, |
| load->url().spec()); |
| automation_->Send(new AutomationMsg_TabLoaded(tab_handle_, |
| load->url())); |
| } |
| break; |
| } |
| case NotificationType::NAV_ENTRY_COMMITTED: { |
| if (ignore_next_load_notification_) { |
| ignore_next_load_notification_ = false; |
| return; |
| } |
| |
| const NavigationController::LoadCommittedDetails* commit = |
| Details<NavigationController::LoadCommittedDetails>(details).ptr(); |
| |
| if (commit->http_status_code >= kHttpClientErrorStart && |
| commit->http_status_code <= kHttpServerErrorEnd) { |
| automation_->Send(new AutomationMsg_NavigationFailed( |
| tab_handle_, commit->http_status_code, commit->entry->url())); |
| |
| ignore_next_load_notification_ = true; |
| } else { |
| NavigationInfo navigation_info; |
| // When the previous entry index is invalid, it will be -1, which |
| // will still make the computation come out right (navigating to the |
| // 0th entry will be +1). |
| if (InitNavigationInfo(&navigation_info, commit->type, |
| commit->previous_entry_index - |
| tab_contents_->controller().last_committed_entry_index())) |
| automation_->Send(new AutomationMsg_DidNavigate(tab_handle_, |
| navigation_info)); |
| } |
| break; |
| } |
| case NotificationType::FAIL_PROVISIONAL_LOAD_WITH_ERROR: { |
| const ProvisionalLoadDetails* load_details = |
| Details<ProvisionalLoadDetails>(details).ptr(); |
| automation_->Send(new AutomationMsg_NavigationFailed( |
| tab_handle_, load_details->error_code(), load_details->url())); |
| |
| ignore_next_load_notification_ = true; |
| break; |
| } |
| case NotificationType::RENDER_VIEW_HOST_CREATED_FOR_TAB: { |
| if (load_requests_via_automation_) { |
| RenderViewHost* rvh = Details<RenderViewHost>(details).ptr(); |
| RegisterRenderViewHostForAutomation(rvh, false); |
| } |
| break; |
| } |
| case NotificationType::RENDER_VIEW_HOST_DELETED: { |
| if (load_requests_via_automation_) { |
| RenderViewHost* rvh = Source<RenderViewHost>(source).ptr(); |
| UnregisterRenderViewHost(rvh); |
| } |
| break; |
| } |
| default: |
| NOTREACHED(); |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // ExternalTabContainer, views::WidgetWin overrides: |
| |
| LRESULT ExternalTabContainer::OnCreate(LPCREATESTRUCT create_struct) { |
| LRESULT result = views::WidgetWin::OnCreate(create_struct); |
| if (result == 0) { |
| // Grab a reference here which will be released in OnFinalMessage |
| AddRef(); |
| } |
| return result; |
| } |
| |
| void ExternalTabContainer::OnDestroy() { |
| prop_.reset(); |
| Uninitialize(); |
| WidgetWin::OnDestroy(); |
| if (browser_.get()) { |
| ::DestroyWindow(browser_->window()->GetNativeHandle()); |
| } |
| } |
| |
| void ExternalTabContainer::OnFinalMessage(HWND window) { |
| // Release the reference which we grabbed in WM_CREATE. |
| Release(); |
| } |
| |
| void ExternalTabContainer::RunUnloadHandlers(IPC::Message* reply_message) { |
| if (!automation_) { |
| delete reply_message; |
| return; |
| } |
| |
| // If we have a pending unload message, then just respond back to this |
| // request and continue processing the previous unload message. |
| if (unload_reply_message_) { |
| AutomationMsg_RunUnloadHandlers::WriteReplyParams(reply_message, true); |
| automation_->Send(reply_message); |
| return; |
| } |
| |
| unload_reply_message_ = reply_message; |
| bool wait_for_unload_handlers = |
| tab_contents_.get() && |
| Browser::RunUnloadEventsHelper(tab_contents_->tab_contents()); |
| if (!wait_for_unload_handlers) { |
| AutomationMsg_RunUnloadHandlers::WriteReplyParams(reply_message, true); |
| automation_->Send(reply_message); |
| unload_reply_message_ = NULL; |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // ExternalTabContainer, private: |
| bool ExternalTabContainer::ProcessUnhandledKeyStroke(HWND window, |
| UINT message, |
| WPARAM wparam, |
| LPARAM lparam) { |
| if (!automation_) { |
| return false; |
| } |
| if ((wparam == VK_TAB) && !base::win::IsCtrlPressed()) { |
| // Tabs are handled separately (except if this is Ctrl-Tab or |
| // Ctrl-Shift-Tab) |
| return false; |
| } |
| |
| // Send this keystroke to the external host as it could be processed as an |
| // accelerator there. If the host does not handle this accelerator, it will |
| // reflect the accelerator back to us via the ProcessUnhandledAccelerator |
| // method. |
| MSG msg = {0}; |
| msg.hwnd = window; |
| msg.message = message; |
| msg.wParam = wparam; |
| msg.lParam = lparam; |
| automation_->Send(new AutomationMsg_HandleAccelerator(tab_handle_, msg)); |
| return true; |
| } |
| |
| bool ExternalTabContainer::InitNavigationInfo(NavigationInfo* nav_info, |
| NavigationType::Type nav_type, |
| int relative_offset) { |
| DCHECK(nav_info); |
| NavigationEntry* entry = tab_contents_->controller().GetActiveEntry(); |
| // If this is very early in the game then we may not have an entry. |
| if (!entry) |
| return false; |
| |
| nav_info->navigation_type = nav_type; |
| nav_info->relative_offset = relative_offset; |
| nav_info->navigation_index = |
| tab_contents_->controller().GetCurrentEntryIndex(); |
| nav_info->url = entry->url(); |
| nav_info->referrer = entry->referrer(); |
| nav_info->title = UTF16ToWideHack(entry->title()); |
| if (nav_info->title.empty()) |
| nav_info->title = UTF8ToWide(nav_info->url.spec()); |
| |
| nav_info->security_style = entry->ssl().security_style(); |
| nav_info->displayed_insecure_content = |
| entry->ssl().displayed_insecure_content(); |
| nav_info->ran_insecure_content = entry->ssl().ran_insecure_content(); |
| return true; |
| } |
| |
| scoped_refptr<ExternalTabContainer> ExternalTabContainer::RemovePendingTab( |
| uintptr_t cookie) { |
| ExternalTabContainer::PendingTabs& pending_tabs = pending_tabs_.Get(); |
| PendingTabs::iterator index = pending_tabs.find(cookie); |
| if (index != pending_tabs.end()) { |
| scoped_refptr<ExternalTabContainer> container = (*index).second; |
| pending_tabs.erase(index); |
| return container; |
| } |
| |
| NOTREACHED() << "Failed to find ExternalTabContainer for cookie: " |
| << cookie; |
| return NULL; |
| } |
| |
| SkColor ExternalTabContainer::GetInfoBarSeparatorColor() const { |
| return ResourceBundle::toolbar_separator_color; |
| } |
| |
| void ExternalTabContainer::InfoBarContainerStateChanged(bool is_animating) { |
| if (external_tab_view_) |
| external_tab_view_->Layout(); |
| } |
| |
| bool ExternalTabContainer::DrawInfoBarArrows(int* x) const { |
| return false; |
| } |
| |
| // ExternalTabContainer instances do not have a window. |
| views::Window* ExternalTabContainer::GetWindow() { |
| return NULL; |
| } |
| |
| bool ExternalTabContainer::AcceleratorPressed( |
| const views::Accelerator& accelerator) { |
| std::map<views::Accelerator, int>::const_iterator iter = |
| accelerator_table_.find(accelerator); |
| DCHECK(iter != accelerator_table_.end()); |
| |
| if (!tab_contents_.get() || !tab_contents_->render_view_host()) { |
| NOTREACHED(); |
| return false; |
| } |
| |
| int command_id = iter->second; |
| switch (command_id) { |
| case IDC_ZOOM_PLUS: |
| tab_contents_->render_view_host()->Zoom(PageZoom::ZOOM_IN); |
| break; |
| case IDC_ZOOM_NORMAL: |
| tab_contents_->render_view_host()->Zoom(PageZoom::RESET); |
| break; |
| case IDC_ZOOM_MINUS: |
| tab_contents_->render_view_host()->Zoom(PageZoom::ZOOM_OUT); |
| break; |
| case IDC_DEV_TOOLS: |
| DevToolsManager::GetInstance()->ToggleDevToolsWindow( |
| tab_contents_->render_view_host(), DEVTOOLS_TOGGLE_ACTION_NONE); |
| break; |
| case IDC_DEV_TOOLS_CONSOLE: |
| DevToolsManager::GetInstance()->ToggleDevToolsWindow( |
| tab_contents_->render_view_host(), |
| DEVTOOLS_TOGGLE_ACTION_SHOW_CONSOLE); |
| break; |
| case IDC_DEV_TOOLS_INSPECT: |
| DevToolsManager::GetInstance()->ToggleDevToolsWindow( |
| tab_contents_->render_view_host(), |
| DEVTOOLS_TOGGLE_ACTION_INSPECT); |
| break; |
| default: |
| NOTREACHED() << "Unsupported accelerator: " << command_id; |
| return false; |
| } |
| return true; |
| } |
| |
| void ExternalTabContainer::Navigate(const GURL& url, const GURL& referrer) { |
| if (!tab_contents_.get()) { |
| NOTREACHED(); |
| return; |
| } |
| |
| TRACE_EVENT_BEGIN("ExternalTabContainer::Navigate", 0, url.spec()); |
| |
| tab_contents_->controller().LoadURL(url, referrer, |
| PageTransition::START_PAGE); |
| } |
| |
| bool ExternalTabContainer::OnGoToEntryOffset(int offset) { |
| if (load_requests_via_automation_) { |
| automation_->Send(new AutomationMsg_RequestGoToHistoryEntryOffset( |
| tab_handle_, offset)); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| void ExternalTabContainer::LoadAccelerators() { |
| HACCEL accelerator_table = AtlLoadAccelerators(IDR_CHROMEFRAME); |
| DCHECK(accelerator_table); |
| |
| // We have to copy the table to access its contents. |
| int count = CopyAcceleratorTable(accelerator_table, 0, 0); |
| if (count == 0) { |
| // Nothing to do in that case. |
| return; |
| } |
| |
| scoped_ptr<ACCEL> scoped_accelerators(new ACCEL[count]); |
| ACCEL* accelerators = scoped_accelerators.get(); |
| DCHECK(accelerators != NULL); |
| |
| CopyAcceleratorTable(accelerator_table, accelerators, count); |
| |
| focus_manager_ = GetFocusManager(); |
| DCHECK(focus_manager_); |
| |
| // Let's fill our own accelerator table. |
| for (int i = 0; i < count; ++i) { |
| bool alt_down = (accelerators[i].fVirt & FALT) == FALT; |
| bool ctrl_down = (accelerators[i].fVirt & FCONTROL) == FCONTROL; |
| bool shift_down = (accelerators[i].fVirt & FSHIFT) == FSHIFT; |
| views::Accelerator accelerator( |
| static_cast<ui::KeyboardCode>(accelerators[i].key), |
| shift_down, ctrl_down, alt_down); |
| accelerator_table_[accelerator] = accelerators[i].cmd; |
| |
| // Also register with the focus manager. |
| if (focus_manager_) |
| focus_manager_->RegisterAccelerator(accelerator, this); |
| } |
| } |
| |
| void ExternalTabContainer::OnReinitialize() { |
| if (load_requests_via_automation_) { |
| RenderViewHost* rvh = tab_contents_->render_view_host(); |
| if (rvh) { |
| AutomationResourceMessageFilter::ResumePendingRenderView( |
| rvh->process()->id(), rvh->routing_id(), |
| tab_handle_, automation_resource_message_filter_); |
| } |
| } |
| |
| NavigationStateChanged(tab_contents(), 0); |
| ServicePendingOpenURLRequests(); |
| } |
| |
| void ExternalTabContainer::ServicePendingOpenURLRequests() { |
| DCHECK(pending()); |
| |
| set_pending(false); |
| |
| for (size_t index = 0; index < pending_open_url_requests_.size(); |
| ++index) { |
| const PendingTopLevelNavigation& url_request = |
| pending_open_url_requests_[index]; |
| OpenURLFromTab(tab_contents(), url_request.url, url_request.referrer, |
| url_request.disposition, url_request.transition); |
| } |
| pending_open_url_requests_.clear(); |
| } |
| |
| void ExternalTabContainer::SetupExternalTabView() { |
| // Create a TabContentsContainer to handle focus cycling using Tab and |
| // Shift-Tab. |
| tab_contents_container_ = new TabContentsContainer; |
| |
| // The views created here will be destroyed when the ExternalTabContainer |
| // widget is torn down. |
| external_tab_view_ = new views::View(); |
| |
| InfoBarContainerView* info_bar_container = new InfoBarContainerView(this); |
| info_bar_container->ChangeTabContents(tab_contents()); |
| |
| views::GridLayout* layout = new views::GridLayout(external_tab_view_); |
| // Give this column an identifier of 0. |
| views::ColumnSet* columns = layout->AddColumnSet(0); |
| columns->AddColumn(views::GridLayout::FILL, |
| views::GridLayout::FILL, |
| 1, |
| views::GridLayout::USE_PREF, |
| 0, |
| 0); |
| |
| external_tab_view_->SetLayoutManager(layout); |
| |
| layout->StartRow(0, 0); |
| layout->AddView(info_bar_container); |
| layout->StartRow(1, 0); |
| layout->AddView(tab_contents_container_); |
| SetContentsView(external_tab_view_); |
| // Note that SetTabContents must be called after AddChildView is called |
| tab_contents_container_->ChangeTabContents(tab_contents()); |
| } |
| |
| TemporaryPopupExternalTabContainer::TemporaryPopupExternalTabContainer( |
| AutomationProvider* automation, |
| AutomationResourceMessageFilter* filter) |
| : ExternalTabContainer(automation, filter) { |
| } |
| |
| TemporaryPopupExternalTabContainer::~TemporaryPopupExternalTabContainer() { |
| DVLOG(1) << __FUNCTION__; |
| } |
| |
| void TemporaryPopupExternalTabContainer::OpenURLFromTab( |
| TabContents* source, const GURL& url, const GURL& referrer, |
| WindowOpenDisposition disposition, PageTransition::Type transition) { |
| if (!automation_) |
| return; |
| |
| if (disposition == CURRENT_TAB) { |
| DCHECK(route_all_top_level_navigations_); |
| disposition = NEW_FOREGROUND_TAB; |
| } |
| ExternalTabContainer::OpenURLFromTab(source, url, referrer, disposition, |
| transition); |
| // support only one navigation for a dummy tab before it is killed. |
| ::DestroyWindow(GetNativeView()); |
| } |