external_tab_container_win.cc revision dc0f95d653279beabeb9817299e2902918ba123e
1// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/external_tab_container_win.h" 6 7#include <string> 8 9#include "base/debug/trace_event.h" 10#include "base/i18n/rtl.h" 11#include "base/logging.h" 12#include "base/utf_string_conversions.h" 13#include "base/win/win_util.h" 14#include "chrome/app/chrome_command_ids.h" 15#include "chrome/app/chrome_dll_resource.h" 16#include "chrome/browser/automation/automation_provider.h" 17#include "chrome/browser/automation/automation_extension_function.h" 18#include "chrome/browser/browser_window.h" 19#include "chrome/browser/debugger/devtools_manager.h" 20#include "chrome/browser/debugger/devtools_toggle_action.h" 21#include "chrome/browser/google/google_util.h" 22#include "chrome/browser/history/history_types.h" 23#include "chrome/browser/load_notification_details.h" 24#include "chrome/browser/page_info_window.h" 25#include "chrome/browser/profiles/profile.h" 26#include "chrome/browser/ui/browser.h" 27#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" 28#include "chrome/browser/ui/views/browser_dialogs.h" 29#include "chrome/browser/ui/views/page_info_bubble_view.h" 30#include "chrome/browser/ui/views/tab_contents/render_view_context_menu_views.h" 31#include "chrome/browser/ui/views/tab_contents/tab_contents_container.h" 32#include "chrome/common/automation_messages.h" 33#include "chrome/common/bindings_policy.h" 34#include "chrome/common/chrome_constants.h" 35#include "chrome/common/render_messages.h" 36#include "chrome/common/render_messages_params.h" 37#include "chrome/common/native_web_keyboard_event.h" 38#include "chrome/common/notification_service.h" 39#include "chrome/common/page_transition_types.h" 40#include "chrome/common/url_constants.h" 41#include "content/browser/renderer_host/render_process_host.h" 42#include "content/browser/renderer_host/render_view_host.h" 43#include "content/browser/renderer_host/resource_dispatcher_host_request_info.h" 44#include "content/browser/tab_contents/provisional_load_details.h" 45#include "grit/generated_resources.h" 46#include "grit/locale_settings.h" 47#include "ui/base/l10n/l10n_util.h" 48#include "ui/base/view_prop.h" 49#include "views/layout/grid_layout.h" 50#include "views/widget/root_view.h" 51#include "views/window/window.h" 52 53using ui::ViewProp; 54 55static const char kWindowObjectKey[] = "ChromeWindowObject"; 56 57// This class overrides the LinkActivated function in the PageInfoBubbleView 58// class and routes the help center link navigation to the host browser. 59class ExternalTabPageInfoBubbleView : public PageInfoBubbleView { 60 public: 61 ExternalTabPageInfoBubbleView(ExternalTabContainer* container, 62 gfx::NativeWindow parent_window, 63 Profile* profile, 64 const GURL& url, 65 const NavigationEntry::SSLStatus& ssl, 66 bool show_history) 67 : PageInfoBubbleView(parent_window, profile, url, ssl, show_history), 68 container_(container) { 69 DVLOG(1) << __FUNCTION__; 70 } 71 virtual ~ExternalTabPageInfoBubbleView() { 72 DVLOG(1) << __FUNCTION__; 73 } 74 // LinkController methods: 75 virtual void LinkActivated(views::Link* source, int event_flags) { 76 GURL url = google_util::AppendGoogleLocaleParam( 77 GURL(chrome::kPageInfoHelpCenterURL)); 78 container_->OpenURLFromTab(container_->tab_contents(), url, GURL(), 79 NEW_FOREGROUND_TAB, PageTransition::LINK); 80 } 81 private: 82 scoped_refptr<ExternalTabContainer> container_; 83}; 84 85base::LazyInstance<ExternalTabContainer::PendingTabs> 86 ExternalTabContainer::pending_tabs_(base::LINKER_INITIALIZED); 87 88ExternalTabContainer::ExternalTabContainer( 89 AutomationProvider* automation, AutomationResourceMessageFilter* filter) 90 : automation_(automation), 91 tab_contents_container_(NULL), 92 tab_handle_(0), 93 ignore_next_load_notification_(false), 94 automation_resource_message_filter_(filter), 95 load_requests_via_automation_(false), 96 handle_top_level_requests_(false), 97 external_method_factory_(this), 98 enabled_extension_automation_(false), 99 pending_(false), 100 infobars_enabled_(true), 101 focus_manager_(NULL), 102 external_tab_view_(NULL), 103 unload_reply_message_(NULL), 104 route_all_top_level_navigations_(false) { 105} 106 107ExternalTabContainer::~ExternalTabContainer() { 108 Uninitialize(); 109} 110 111TabContents* ExternalTabContainer::tab_contents() const { 112 return tab_contents_.get() ? tab_contents_->tab_contents() : NULL; 113} 114 115bool ExternalTabContainer::Init(Profile* profile, 116 HWND parent, 117 const gfx::Rect& bounds, 118 DWORD style, 119 bool load_requests_via_automation, 120 bool handle_top_level_requests, 121 TabContentsWrapper* existing_contents, 122 const GURL& initial_url, 123 const GURL& referrer, 124 bool infobars_enabled, 125 bool route_all_top_level_navigations) { 126 if (IsWindow()) { 127 NOTREACHED(); 128 return false; 129 } 130 131 load_requests_via_automation_ = load_requests_via_automation; 132 handle_top_level_requests_ = handle_top_level_requests; 133 infobars_enabled_ = infobars_enabled; 134 route_all_top_level_navigations_ = route_all_top_level_navigations; 135 136 set_window_style(WS_POPUP | WS_CLIPCHILDREN); 137 views::WidgetWin::Init(NULL, bounds); 138 if (!IsWindow()) { 139 NOTREACHED(); 140 return false; 141 } 142 143 // TODO(jcampan): limit focus traversal to contents. 144 145 prop_.reset(new ViewProp(GetNativeView(), kWindowObjectKey, this)); 146 147 if (existing_contents) { 148 tab_contents_.reset(existing_contents); 149 tab_contents_->controller().set_profile(profile); 150 } else { 151 TabContents* new_contents = new TabContents(profile, NULL, MSG_ROUTING_NONE, 152 NULL, NULL); 153 tab_contents_.reset(new TabContentsWrapper(new_contents)); 154 } 155 156 tab_contents_->tab_contents()->set_delegate(this); 157 158 tab_contents_->tab_contents()-> 159 GetMutableRendererPrefs()->browser_handles_top_level_requests = 160 handle_top_level_requests; 161 162 if (!existing_contents) { 163 tab_contents_->render_view_host()->AllowBindings( 164 BindingsPolicy::EXTERNAL_HOST); 165 } 166 167 NavigationController* controller = &tab_contents_->controller(); 168 registrar_.Add(this, NotificationType::NAV_ENTRY_COMMITTED, 169 Source<NavigationController>(controller)); 170 registrar_.Add(this, NotificationType::FAIL_PROVISIONAL_LOAD_WITH_ERROR, 171 Source<NavigationController>(controller)); 172 registrar_.Add(this, NotificationType::LOAD_STOP, 173 Source<NavigationController>(controller)); 174 registrar_.Add(this, NotificationType::RENDER_VIEW_HOST_CREATED_FOR_TAB, 175 Source<TabContents>(tab_contents_->tab_contents())); 176 registrar_.Add(this, NotificationType::RENDER_VIEW_HOST_DELETED, 177 Source<TabContents>(tab_contents_->tab_contents())); 178 179 NotificationService::current()->Notify( 180 NotificationType::EXTERNAL_TAB_CREATED, 181 Source<NavigationController>(controller), 182 NotificationService::NoDetails()); 183 184 // Start loading initial URL 185 if (!initial_url.is_empty()) { 186 // Navigate out of context since we don't have a 'tab_handle_' yet. 187 MessageLoop::current()->PostTask( 188 FROM_HERE, 189 external_method_factory_.NewRunnableMethod( 190 &ExternalTabContainer::Navigate, initial_url, referrer)); 191 } 192 193 // We need WS_POPUP to be on the window during initialization, but 194 // once initialized we apply the requested style which may or may not 195 // include the popup bit. 196 // Note that it's important to do this before we call SetParent since 197 // during the SetParent call we will otherwise get a WA_ACTIVATE call 198 // that causes us to steal the current focus. 199 SetWindowLong(GWL_STYLE, (GetWindowLong(GWL_STYLE) & ~WS_POPUP) | style); 200 201 // Now apply the parenting and style 202 if (parent) 203 SetParent(GetNativeView(), parent); 204 205 ::ShowWindow(tab_contents_->tab_contents()->GetNativeView(), SW_SHOWNA); 206 207 LoadAccelerators(); 208 SetupExternalTabView(); 209 return true; 210} 211 212void ExternalTabContainer::Uninitialize() { 213 if (enabled_extension_automation_) { 214 AutomationExtensionFunction::Disable(); 215 } 216 217 registrar_.RemoveAll(); 218 if (tab_contents_.get()) { 219 RenderViewHost* rvh = tab_contents_->render_view_host(); 220 if (rvh) { 221 if (DevToolsManager::GetInstance()) 222 DevToolsManager::GetInstance()->UnregisterDevToolsClientHostFor(rvh); 223 224 UnregisterRenderViewHost(rvh); 225 } 226 227 if (GetRootView()) { 228 GetRootView()->RemoveAllChildViews(true); 229 } 230 231 NotificationService::current()->Notify( 232 NotificationType::EXTERNAL_TAB_CLOSED, 233 Source<NavigationController>(&tab_contents_->controller()), 234 Details<ExternalTabContainer>(this)); 235 236 tab_contents_.reset(NULL); 237 } 238 239 if (focus_manager_) { 240 focus_manager_->UnregisterAccelerators(this); 241 focus_manager_ = NULL; 242 } 243 244 external_tab_view_ = NULL; 245 request_context_ = NULL; 246 tab_contents_container_ = NULL; 247} 248 249bool ExternalTabContainer::Reinitialize( 250 AutomationProvider* automation_provider, 251 AutomationResourceMessageFilter* filter, 252 gfx::NativeWindow parent_window) { 253 if (!automation_provider || !filter) { 254 NOTREACHED(); 255 return false; 256 } 257 258 automation_ = automation_provider; 259 automation_resource_message_filter_ = filter; 260 // Wait for the automation channel to be initialized before resuming pending 261 // render views and sending in the navigation state. 262 MessageLoop::current()->PostTask( 263 FROM_HERE, 264 external_method_factory_.NewRunnableMethod( 265 &ExternalTabContainer::OnReinitialize)); 266 267 if (parent_window) 268 SetParent(GetNativeView(), parent_window); 269 return true; 270} 271 272void ExternalTabContainer::SetTabHandle(int handle) { 273 tab_handle_ = handle; 274} 275 276void ExternalTabContainer::ProcessUnhandledAccelerator(const MSG& msg) { 277 NativeWebKeyboardEvent keyboard_event(msg.hwnd, msg.message, msg.wParam, 278 msg.lParam); 279 unhandled_keyboard_event_handler_.HandleKeyboardEvent(keyboard_event, 280 focus_manager_); 281} 282 283void ExternalTabContainer::FocusThroughTabTraversal( 284 bool reverse, bool restore_focus_to_view) { 285 DCHECK(tab_contents_.get()); 286 if (tab_contents_.get()) 287 tab_contents_->tab_contents()->Focus(); 288 289 // The tab_contents_ member can get destroyed in the context of the call to 290 // TabContentsViewWin::Focus() above. This method eventually calls SetFocus 291 // on the native window, which could end up dispatching messages like 292 // WM_DESTROY for the external tab. 293 if (tab_contents_.get() && restore_focus_to_view) 294 tab_contents_->tab_contents()->FocusThroughTabTraversal(reverse); 295} 296 297// static 298bool ExternalTabContainer::IsExternalTabContainer(HWND window) { 299 return ViewProp::GetValue(window, kWindowObjectKey) != NULL; 300} 301 302// static 303ExternalTabContainer* ExternalTabContainer::GetContainerForTab( 304 HWND tab_window) { 305 HWND parent_window = ::GetParent(tab_window); 306 if (!::IsWindow(parent_window)) { 307 return NULL; 308 } 309 if (!IsExternalTabContainer(parent_window)) { 310 return NULL; 311 } 312 ExternalTabContainer* container = reinterpret_cast<ExternalTabContainer*>( 313 ViewProp::GetValue(parent_window, kWindowObjectKey)); 314 return container; 315} 316 317// static 318ExternalTabContainer* 319 ExternalTabContainer::GetExternalContainerFromNativeWindow( 320 gfx::NativeView native_window) { 321 ExternalTabContainer* tab_container = NULL; 322 if (native_window) { 323 tab_container = reinterpret_cast<ExternalTabContainer*>( 324 ViewProp::GetValue(native_window, kWindowObjectKey)); 325 } 326 return tab_container; 327} 328//////////////////////////////////////////////////////////////////////////////// 329// ExternalTabContainer, TabContentsDelegate implementation: 330 331void ExternalTabContainer::OpenURLFromTab(TabContents* source, 332 const GURL& url, 333 const GURL& referrer, 334 WindowOpenDisposition disposition, 335 PageTransition::Type transition) { 336 if (pending()) { 337 PendingTopLevelNavigation url_request; 338 url_request.disposition = disposition; 339 url_request.transition = transition; 340 url_request.url = url; 341 url_request.referrer = referrer; 342 343 pending_open_url_requests_.push_back(url_request); 344 return; 345 } 346 347 switch (disposition) { 348 case CURRENT_TAB: 349 case SINGLETON_TAB: 350 case NEW_FOREGROUND_TAB: 351 case NEW_BACKGROUND_TAB: 352 case NEW_POPUP: 353 case NEW_WINDOW: 354 case SAVE_TO_DISK: 355 if (automation_) { 356 automation_->Send(new AutomationMsg_OpenURL(tab_handle_, 357 url, referrer, 358 disposition)); 359 // TODO(ananta) 360 // We should populate other fields in the 361 // ViewHostMsg_FrameNavigate_Params structure. Another option could be 362 // to refactor the UpdateHistoryForNavigation function in TabContents. 363 ViewHostMsg_FrameNavigate_Params params; 364 params.referrer = referrer; 365 params.url = url; 366 params.page_id = -1; 367 params.transition = PageTransition::LINK; 368 369 NavigationController::LoadCommittedDetails details; 370 details.did_replace_entry = false; 371 372 scoped_refptr<history::HistoryAddPageArgs> add_page_args( 373 tab_contents_->tab_contents()-> 374 CreateHistoryAddPageArgs(url, details, params)); 375 tab_contents_->tab_contents()-> 376 UpdateHistoryForNavigation(add_page_args); 377 } 378 break; 379 default: 380 NOTREACHED(); 381 break; 382 } 383} 384 385void ExternalTabContainer::NavigationStateChanged(const TabContents* source, 386 unsigned changed_flags) { 387 if (automation_) { 388 NavigationInfo nav_info; 389 if (InitNavigationInfo(&nav_info, NavigationType::NAV_IGNORE, 0)) 390 automation_->Send(new AutomationMsg_NavigationStateChanged( 391 tab_handle_, changed_flags, nav_info)); 392 } 393} 394 395void ExternalTabContainer::AddNewContents(TabContents* source, 396 TabContents* new_contents, 397 WindowOpenDisposition disposition, 398 const gfx::Rect& initial_pos, 399 bool user_gesture) { 400 if (!automation_) { 401 DCHECK(pending_); 402 LOG(ERROR) << "Invalid automation provider. Dropping new contents notify"; 403 delete new_contents; 404 return; 405 } 406 407 scoped_refptr<ExternalTabContainer> new_container; 408 // If the host is a browser like IE8, then the URL being navigated to in the 409 // new tab contents could potentially navigate back to Chrome from a new 410 // IE process. We support full tab mode only for IE and hence we use that as 411 // a determining factor in whether the new ExternalTabContainer instance is 412 // created as pending or not. 413 if (!route_all_top_level_navigations_) { 414 new_container = new ExternalTabContainer(NULL, NULL); 415 } else { 416 // Reuse the same tab handle here as the new container instance is a dummy 417 // instance which does not have an automation client connected at the other 418 // end. 419 new_container = new TemporaryPopupExternalTabContainer( 420 automation_, automation_resource_message_filter_.get()); 421 new_container->SetTabHandle(tab_handle_); 422 } 423 424 // Make sure that ExternalTabContainer instance is initialized with 425 // an unwrapped Profile. 426 scoped_ptr<TabContentsWrapper> wrapper(new TabContentsWrapper(new_contents)); 427 bool result = new_container->Init( 428 new_contents->profile()->GetOriginalProfile(), 429 NULL, 430 initial_pos, 431 WS_CHILD, 432 load_requests_via_automation_, 433 handle_top_level_requests_, 434 wrapper.get(), 435 GURL(), 436 GURL(), 437 true, 438 route_all_top_level_navigations_); 439 440 if (result) { 441 wrapper.release(); // Ownership has been transferred. 442 if (route_all_top_level_navigations_) { 443 return; 444 } 445 uintptr_t cookie = reinterpret_cast<uintptr_t>(new_container.get()); 446 pending_tabs_.Get()[cookie] = new_container; 447 new_container->set_pending(true); 448 AttachExternalTabParams attach_params_; 449 attach_params_.cookie = static_cast<uint64>(cookie); 450 attach_params_.dimensions = initial_pos; 451 attach_params_.user_gesture = user_gesture; 452 attach_params_.disposition = disposition; 453 attach_params_.profile_name = WideToUTF8( 454 tab_contents()->profile()->GetPath().DirName().BaseName().value()); 455 automation_->Send(new AutomationMsg_AttachExternalTab( 456 tab_handle_, attach_params_)); 457 } else { 458 NOTREACHED(); 459 } 460} 461 462void ExternalTabContainer::TabContentsCreated(TabContents* new_contents) { 463 RenderViewHost* rvh = new_contents->render_view_host(); 464 DCHECK(rvh != NULL); 465 466 // Register this render view as a pending render view, i.e. any network 467 // requests initiated by this render view would be serviced when the 468 // external host connects to the new external tab instance. 469 RegisterRenderViewHostForAutomation(rvh, true); 470} 471 472bool ExternalTabContainer::infobars_enabled() { 473 return infobars_enabled_; 474} 475 476void ExternalTabContainer::ActivateContents(TabContents* contents) { 477} 478 479void ExternalTabContainer::DeactivateContents(TabContents* contents) { 480} 481 482void ExternalTabContainer::LoadingStateChanged(TabContents* source) { 483} 484 485void ExternalTabContainer::CloseContents(TabContents* source) { 486 if (!automation_) 487 return; 488 489 if (unload_reply_message_) { 490 AutomationMsg_RunUnloadHandlers::WriteReplyParams(unload_reply_message_, 491 true); 492 automation_->Send(unload_reply_message_); 493 unload_reply_message_ = NULL; 494 } else { 495 automation_->Send(new AutomationMsg_CloseExternalTab(tab_handle_)); 496 } 497} 498 499void ExternalTabContainer::MoveContents(TabContents* source, 500 const gfx::Rect& pos) { 501} 502 503void ExternalTabContainer::UpdateTargetURL(TabContents* source, 504 const GURL& url) { 505 if (automation_) { 506 std::wstring url_string = CA2W(url.spec().c_str()); 507 automation_->Send( 508 new AutomationMsg_UpdateTargetUrl(tab_handle_, url_string)); 509 } 510} 511 512void ExternalTabContainer::ContentsZoomChange(bool zoom_in) { 513} 514 515void ExternalTabContainer::ToolbarSizeChanged(TabContents* source, 516 bool finished) { 517} 518 519void ExternalTabContainer::ForwardMessageToExternalHost( 520 const std::string& message, const std::string& origin, 521 const std::string& target) { 522 if (automation_) { 523 automation_->Send(new AutomationMsg_ForwardMessageToExternalHost( 524 tab_handle_, message, origin, target)); 525 } 526} 527 528bool ExternalTabContainer::IsExternalTabContainer() const { 529 return true; 530} 531 532gfx::NativeWindow ExternalTabContainer::GetFrameNativeWindow() { 533 return hwnd(); 534} 535 536bool ExternalTabContainer::TakeFocus(bool reverse) { 537 if (automation_) { 538 automation_->Send(new AutomationMsg_TabbedOut(tab_handle_, 539 base::win::IsShiftPressed())); 540 } 541 542 return true; 543} 544 545bool ExternalTabContainer::CanDownload(int request_id) { 546 if (load_requests_via_automation_) { 547 if (automation_) { 548 // In case the host needs to show UI that needs to take the focus. 549 ::AllowSetForegroundWindow(ASFW_ANY); 550 551 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, 552 NewRunnableMethod(automation_resource_message_filter_.get(), 553 &AutomationResourceMessageFilter::SendDownloadRequestToHost, 554 0, tab_handle_, request_id)); 555 } 556 } else { 557 DLOG(WARNING) << "Downloads are only supported with host browser network " 558 "stack enabled."; 559 } 560 561 // Never allow downloads. 562 return false; 563} 564 565void ExternalTabContainer::ShowPageInfo(Profile* profile, 566 const GURL& url, 567 const NavigationEntry::SSLStatus& ssl, 568 bool show_history) { 569 POINT cursor_pos = {0}; 570 GetCursorPos(&cursor_pos); 571 572 gfx::Rect bounds; 573 bounds.set_origin(gfx::Point(cursor_pos)); 574 575 PageInfoBubbleView* page_info_bubble = 576 new ExternalTabPageInfoBubbleView(this, NULL, profile, url, 577 ssl, show_history); 578 InfoBubble* info_bubble = 579 InfoBubble::Show(this, bounds, 580 BubbleBorder::TOP_LEFT, 581 page_info_bubble, page_info_bubble); 582 page_info_bubble->set_info_bubble(info_bubble); 583} 584 585void ExternalTabContainer::RegisterRenderViewHostForAutomation( 586 RenderViewHost* render_view_host, bool pending_view) { 587 if (render_view_host) { 588 AutomationResourceMessageFilter::RegisterRenderView( 589 render_view_host->process()->id(), 590 render_view_host->routing_id(), 591 tab_handle(), 592 automation_resource_message_filter_, 593 pending_view); 594 } 595} 596 597 598void ExternalTabContainer::RegisterRenderViewHost( 599 RenderViewHost* render_view_host) { 600 // RenderViewHost instances that are to be associated with this 601 // ExternalTabContainer should share the same resource request automation 602 // settings. 603 RegisterRenderViewHostForAutomation( 604 render_view_host, 605 false); // Network requests should not be handled later. 606} 607 608void ExternalTabContainer::UnregisterRenderViewHost( 609 RenderViewHost* render_view_host) { 610 // Undo the resource automation registration performed in 611 // ExternalTabContainer::RegisterRenderViewHost. 612 if (render_view_host) { 613 AutomationResourceMessageFilter::UnRegisterRenderView( 614 render_view_host->process()->id(), 615 render_view_host->routing_id()); 616 } 617} 618 619bool ExternalTabContainer::HandleContextMenu(const ContextMenuParams& params) { 620 if (!automation_) { 621 NOTREACHED(); 622 return false; 623 } 624 external_context_menu_.reset( 625 new RenderViewContextMenuViews(tab_contents(), params)); 626 external_context_menu_->SetExternal(); 627 external_context_menu_->Init(); 628 external_context_menu_->UpdateMenuItemStates(); 629 630 POINT screen_pt = { params.x, params.y }; 631 MapWindowPoints(GetNativeView(), HWND_DESKTOP, &screen_pt, 1); 632 633 MiniContextMenuParams ipc_params( 634 screen_pt.x, 635 screen_pt.y, 636 params.link_url, 637 params.unfiltered_link_url, 638 params.src_url, 639 params.page_url, 640 params.frame_url); 641 642 bool rtl = base::i18n::IsRTL(); 643 automation_->Send( 644 new AutomationMsg_ForwardContextMenuToExternalHost(tab_handle_, 645 external_context_menu_->GetMenuHandle(), 646 rtl ? TPM_RIGHTALIGN : TPM_LEFTALIGN, ipc_params)); 647 648 return true; 649} 650 651bool ExternalTabContainer::ExecuteContextMenuCommand(int command) { 652 if (!external_context_menu_.get()) { 653 NOTREACHED(); 654 return false; 655 } 656 657 switch (command) { 658 case IDS_CONTENT_CONTEXT_SAVEAUDIOAS: 659 case IDS_CONTENT_CONTEXT_SAVEVIDEOAS: 660 case IDS_CONTENT_CONTEXT_SAVEIMAGEAS: 661 case IDS_CONTENT_CONTEXT_SAVELINKAS: { 662 NOTREACHED(); // Should be handled in host. 663 break; 664 } 665 } 666 667 external_context_menu_->ExecuteCommand(command); 668 return true; 669} 670 671bool ExternalTabContainer::PreHandleKeyboardEvent( 672 const NativeWebKeyboardEvent& event, bool* is_keyboard_shortcut) { 673 return false; 674} 675 676void ExternalTabContainer::HandleKeyboardEvent( 677 const NativeWebKeyboardEvent& event) { 678 ProcessUnhandledKeyStroke(event.os_event.hwnd, event.os_event.message, 679 event.os_event.wParam, event.os_event.lParam); 680} 681 682void ExternalTabContainer::ShowHtmlDialog(HtmlDialogUIDelegate* delegate, 683 gfx::NativeWindow parent_window) { 684 if (!browser_.get()) { 685 browser_.reset(Browser::CreateForType(Browser::TYPE_POPUP, 686 tab_contents_->profile())); 687 } 688 689 gfx::NativeWindow parent = parent_window ? parent_window 690 : GetParent(); 691 browser_->window()->ShowHTMLDialog(delegate, parent); 692} 693 694void ExternalTabContainer::BeforeUnloadFired(TabContents* tab, 695 bool proceed, 696 bool* proceed_to_fire_unload) { 697 *proceed_to_fire_unload = true; 698 699 if (!automation_) { 700 delete unload_reply_message_; 701 unload_reply_message_ = NULL; 702 return; 703 } 704 705 if (!proceed) { 706 DCHECK(unload_reply_message_); 707 AutomationMsg_RunUnloadHandlers::WriteReplyParams(unload_reply_message_, 708 false); 709 automation_->Send(unload_reply_message_); 710 unload_reply_message_ = NULL; 711 *proceed_to_fire_unload = false; 712 } 713} 714 715void ExternalTabContainer::ShowRepostFormWarningDialog( 716 TabContents* tab_contents) { 717 browser::ShowRepostFormWarningDialog(GetNativeView(), tab_contents); 718} 719 720//////////////////////////////////////////////////////////////////////////////// 721// ExternalTabContainer, NotificationObserver implementation: 722 723void ExternalTabContainer::Observe(NotificationType type, 724 const NotificationSource& source, 725 const NotificationDetails& details) { 726 if (!automation_) 727 return; 728 729 static const int kHttpClientErrorStart = 400; 730 static const int kHttpServerErrorEnd = 510; 731 732 switch (type.value) { 733 case NotificationType::LOAD_STOP: { 734 const LoadNotificationDetails* load = 735 Details<LoadNotificationDetails>(details).ptr(); 736 if (load != NULL && PageTransition::IsMainFrame(load->origin())) { 737 TRACE_EVENT_END("ExternalTabContainer::Navigate", 0, 738 load->url().spec()); 739 automation_->Send(new AutomationMsg_TabLoaded(tab_handle_, 740 load->url())); 741 } 742 break; 743 } 744 case NotificationType::NAV_ENTRY_COMMITTED: { 745 if (ignore_next_load_notification_) { 746 ignore_next_load_notification_ = false; 747 return; 748 } 749 750 const NavigationController::LoadCommittedDetails* commit = 751 Details<NavigationController::LoadCommittedDetails>(details).ptr(); 752 753 if (commit->http_status_code >= kHttpClientErrorStart && 754 commit->http_status_code <= kHttpServerErrorEnd) { 755 automation_->Send(new AutomationMsg_NavigationFailed( 756 tab_handle_, commit->http_status_code, commit->entry->url())); 757 758 ignore_next_load_notification_ = true; 759 } else { 760 NavigationInfo navigation_info; 761 // When the previous entry index is invalid, it will be -1, which 762 // will still make the computation come out right (navigating to the 763 // 0th entry will be +1). 764 if (InitNavigationInfo(&navigation_info, commit->type, 765 commit->previous_entry_index - 766 tab_contents_->controller().last_committed_entry_index())) 767 automation_->Send(new AutomationMsg_DidNavigate(tab_handle_, 768 navigation_info)); 769 } 770 break; 771 } 772 case NotificationType::FAIL_PROVISIONAL_LOAD_WITH_ERROR: { 773 const ProvisionalLoadDetails* load_details = 774 Details<ProvisionalLoadDetails>(details).ptr(); 775 automation_->Send(new AutomationMsg_NavigationFailed( 776 tab_handle_, load_details->error_code(), load_details->url())); 777 778 ignore_next_load_notification_ = true; 779 break; 780 } 781 case NotificationType::RENDER_VIEW_HOST_CREATED_FOR_TAB: { 782 if (load_requests_via_automation_) { 783 RenderViewHost* rvh = Details<RenderViewHost>(details).ptr(); 784 RegisterRenderViewHostForAutomation(rvh, false); 785 } 786 break; 787 } 788 case NotificationType::RENDER_VIEW_HOST_DELETED: { 789 if (load_requests_via_automation_) { 790 RenderViewHost* rvh = Details<RenderViewHost>(details).ptr(); 791 UnregisterRenderViewHost(rvh); 792 } 793 break; 794 } 795 default: 796 NOTREACHED(); 797 } 798} 799 800//////////////////////////////////////////////////////////////////////////////// 801// ExternalTabContainer, views::WidgetWin overrides: 802 803LRESULT ExternalTabContainer::OnCreate(LPCREATESTRUCT create_struct) { 804 LRESULT result = views::WidgetWin::OnCreate(create_struct); 805 if (result == 0) { 806 // Grab a reference here which will be released in OnFinalMessage 807 AddRef(); 808 } 809 return result; 810} 811 812void ExternalTabContainer::OnDestroy() { 813 prop_.reset(); 814 Uninitialize(); 815 WidgetWin::OnDestroy(); 816 if (browser_.get()) { 817 ::DestroyWindow(browser_->window()->GetNativeHandle()); 818 } 819} 820 821void ExternalTabContainer::OnFinalMessage(HWND window) { 822 // Release the reference which we grabbed in WM_CREATE. 823 Release(); 824} 825 826void ExternalTabContainer::RunUnloadHandlers(IPC::Message* reply_message) { 827 if (!automation_) { 828 delete reply_message; 829 return; 830 } 831 832 // If we have a pending unload message, then just respond back to this 833 // request and continue processing the previous unload message. 834 if (unload_reply_message_) { 835 AutomationMsg_RunUnloadHandlers::WriteReplyParams(reply_message, true); 836 automation_->Send(reply_message); 837 return; 838 } 839 if (tab_contents_.get() && 840 Browser::RunUnloadEventsHelper(tab_contents_->tab_contents())) { 841 unload_reply_message_ = reply_message; 842 } else { 843 AutomationMsg_RunUnloadHandlers::WriteReplyParams(reply_message, true); 844 automation_->Send(reply_message); 845 } 846} 847 848//////////////////////////////////////////////////////////////////////////////// 849// ExternalTabContainer, private: 850bool ExternalTabContainer::ProcessUnhandledKeyStroke(HWND window, 851 UINT message, 852 WPARAM wparam, 853 LPARAM lparam) { 854 if (!automation_) { 855 return false; 856 } 857 if ((wparam == VK_TAB) && !base::win::IsCtrlPressed()) { 858 // Tabs are handled separately (except if this is Ctrl-Tab or 859 // Ctrl-Shift-Tab) 860 return false; 861 } 862 863 // Send this keystroke to the external host as it could be processed as an 864 // accelerator there. If the host does not handle this accelerator, it will 865 // reflect the accelerator back to us via the ProcessUnhandledAccelerator 866 // method. 867 MSG msg = {0}; 868 msg.hwnd = window; 869 msg.message = message; 870 msg.wParam = wparam; 871 msg.lParam = lparam; 872 automation_->Send(new AutomationMsg_HandleAccelerator(tab_handle_, msg)); 873 return true; 874} 875 876bool ExternalTabContainer::InitNavigationInfo(NavigationInfo* nav_info, 877 NavigationType::Type nav_type, 878 int relative_offset) { 879 DCHECK(nav_info); 880 NavigationEntry* entry = tab_contents_->controller().GetActiveEntry(); 881 // If this is very early in the game then we may not have an entry. 882 if (!entry) 883 return false; 884 885 nav_info->navigation_type = nav_type; 886 nav_info->relative_offset = relative_offset; 887 nav_info->navigation_index = 888 tab_contents_->controller().GetCurrentEntryIndex(); 889 nav_info->url = entry->url(); 890 nav_info->referrer = entry->referrer(); 891 nav_info->title = UTF16ToWideHack(entry->title()); 892 if (nav_info->title.empty()) 893 nav_info->title = UTF8ToWide(nav_info->url.spec()); 894 895 nav_info->security_style = entry->ssl().security_style(); 896 nav_info->displayed_insecure_content = 897 entry->ssl().displayed_insecure_content(); 898 nav_info->ran_insecure_content = entry->ssl().ran_insecure_content(); 899 return true; 900} 901 902scoped_refptr<ExternalTabContainer> ExternalTabContainer::RemovePendingTab( 903 uintptr_t cookie) { 904 ExternalTabContainer::PendingTabs& pending_tabs = pending_tabs_.Get(); 905 PendingTabs::iterator index = pending_tabs.find(cookie); 906 if (index != pending_tabs.end()) { 907 scoped_refptr<ExternalTabContainer> container = (*index).second; 908 pending_tabs.erase(index); 909 return container; 910 } 911 912 NOTREACHED() << "Failed to find ExternalTabContainer for cookie: " 913 << cookie; 914 return NULL; 915} 916 917void ExternalTabContainer::SetEnableExtensionAutomation( 918 const std::vector<std::string>& functions_enabled) { 919 if (!functions_enabled.empty()) { 920 if (!tab_contents_.get()) { 921 NOTREACHED() << "Being invoked via tab so should have TabContents"; 922 return; 923 } 924 925 AutomationExtensionFunction::Enable(tab_contents_->tab_contents(), 926 functions_enabled); 927 enabled_extension_automation_ = true; 928 } else { 929 AutomationExtensionFunction::Disable(); 930 enabled_extension_automation_ = false; 931 } 932} 933 934void ExternalTabContainer::InfoBarContainerSizeChanged(bool is_animating) { 935 if (external_tab_view_) 936 external_tab_view_->Layout(); 937} 938 939// ExternalTabContainer instances do not have a window. 940views::Window* ExternalTabContainer::GetWindow() { 941 return NULL; 942} 943 944bool ExternalTabContainer::AcceleratorPressed( 945 const views::Accelerator& accelerator) { 946 std::map<views::Accelerator, int>::const_iterator iter = 947 accelerator_table_.find(accelerator); 948 DCHECK(iter != accelerator_table_.end()); 949 950 if (!tab_contents_.get() || !tab_contents_->render_view_host()) { 951 NOTREACHED(); 952 return false; 953 } 954 955 int command_id = iter->second; 956 switch (command_id) { 957 case IDC_ZOOM_PLUS: 958 tab_contents_->render_view_host()->Zoom(PageZoom::ZOOM_IN); 959 break; 960 case IDC_ZOOM_NORMAL: 961 tab_contents_->render_view_host()->Zoom(PageZoom::RESET); 962 break; 963 case IDC_ZOOM_MINUS: 964 tab_contents_->render_view_host()->Zoom(PageZoom::ZOOM_OUT); 965 break; 966 case IDC_DEV_TOOLS: 967 DevToolsManager::GetInstance()->ToggleDevToolsWindow( 968 tab_contents_->render_view_host(), DEVTOOLS_TOGGLE_ACTION_NONE); 969 break; 970 case IDC_DEV_TOOLS_CONSOLE: 971 DevToolsManager::GetInstance()->ToggleDevToolsWindow( 972 tab_contents_->render_view_host(), 973 DEVTOOLS_TOGGLE_ACTION_SHOW_CONSOLE); 974 break; 975 case IDC_DEV_TOOLS_INSPECT: 976 DevToolsManager::GetInstance()->ToggleDevToolsWindow( 977 tab_contents_->render_view_host(), 978 DEVTOOLS_TOGGLE_ACTION_INSPECT); 979 break; 980 default: 981 NOTREACHED() << "Unsupported accelerator: " << command_id; 982 return false; 983 } 984 return true; 985} 986 987void ExternalTabContainer::Navigate(const GURL& url, const GURL& referrer) { 988 if (!tab_contents_.get()) { 989 NOTREACHED(); 990 return; 991 } 992 993 TRACE_EVENT_BEGIN("ExternalTabContainer::Navigate", 0, url.spec()); 994 995 tab_contents_->controller().LoadURL(url, referrer, 996 PageTransition::START_PAGE); 997} 998 999bool ExternalTabContainer::OnGoToEntryOffset(int offset) { 1000 if (load_requests_via_automation_) { 1001 automation_->Send(new AutomationMsg_RequestGoToHistoryEntryOffset( 1002 tab_handle_, offset)); 1003 return false; 1004 } 1005 1006 return true; 1007} 1008 1009void ExternalTabContainer::LoadAccelerators() { 1010 HACCEL accelerator_table = AtlLoadAccelerators(IDR_CHROMEFRAME); 1011 DCHECK(accelerator_table); 1012 1013 // We have to copy the table to access its contents. 1014 int count = CopyAcceleratorTable(accelerator_table, 0, 0); 1015 if (count == 0) { 1016 // Nothing to do in that case. 1017 return; 1018 } 1019 1020 scoped_ptr<ACCEL> scoped_accelerators(new ACCEL[count]); 1021 ACCEL* accelerators = scoped_accelerators.get(); 1022 DCHECK(accelerators != NULL); 1023 1024 CopyAcceleratorTable(accelerator_table, accelerators, count); 1025 1026 focus_manager_ = GetFocusManager(); 1027 DCHECK(focus_manager_); 1028 1029 // Let's fill our own accelerator table. 1030 for (int i = 0; i < count; ++i) { 1031 bool alt_down = (accelerators[i].fVirt & FALT) == FALT; 1032 bool ctrl_down = (accelerators[i].fVirt & FCONTROL) == FCONTROL; 1033 bool shift_down = (accelerators[i].fVirt & FSHIFT) == FSHIFT; 1034 views::Accelerator accelerator( 1035 static_cast<ui::KeyboardCode>(accelerators[i].key), 1036 shift_down, ctrl_down, alt_down); 1037 accelerator_table_[accelerator] = accelerators[i].cmd; 1038 1039 // Also register with the focus manager. 1040 if (focus_manager_) 1041 focus_manager_->RegisterAccelerator(accelerator, this); 1042 } 1043} 1044 1045void ExternalTabContainer::OnReinitialize() { 1046 if (load_requests_via_automation_) { 1047 RenderViewHost* rvh = tab_contents_->render_view_host(); 1048 if (rvh) { 1049 AutomationResourceMessageFilter::ResumePendingRenderView( 1050 rvh->process()->id(), rvh->routing_id(), 1051 tab_handle_, automation_resource_message_filter_); 1052 } 1053 } 1054 1055 NavigationStateChanged(tab_contents(), 0); 1056 ServicePendingOpenURLRequests(); 1057} 1058 1059void ExternalTabContainer::ServicePendingOpenURLRequests() { 1060 DCHECK(pending()); 1061 1062 set_pending(false); 1063 1064 for (size_t index = 0; index < pending_open_url_requests_.size(); 1065 ++index) { 1066 const PendingTopLevelNavigation& url_request = 1067 pending_open_url_requests_[index]; 1068 OpenURLFromTab(tab_contents(), url_request.url, url_request.referrer, 1069 url_request.disposition, url_request.transition); 1070 } 1071 pending_open_url_requests_.clear(); 1072} 1073 1074void ExternalTabContainer::SetupExternalTabView() { 1075 // Create a TabContentsContainer to handle focus cycling using Tab and 1076 // Shift-Tab. 1077 tab_contents_container_ = new TabContentsContainer; 1078 1079 // The views created here will be destroyed when the ExternalTabContainer 1080 // widget is torn down. 1081 external_tab_view_ = new views::View(); 1082 1083 InfoBarContainer* info_bar_container = new InfoBarContainer(this); 1084 info_bar_container->ChangeTabContents(tab_contents()); 1085 1086 views::GridLayout* layout = new views::GridLayout(external_tab_view_); 1087 // Give this column an identifier of 0. 1088 views::ColumnSet* columns = layout->AddColumnSet(0); 1089 columns->AddColumn(views::GridLayout::FILL, 1090 views::GridLayout::FILL, 1091 1, 1092 views::GridLayout::USE_PREF, 1093 0, 1094 0); 1095 1096 external_tab_view_->SetLayoutManager(layout); 1097 1098 layout->StartRow(0, 0); 1099 layout->AddView(info_bar_container); 1100 layout->StartRow(1, 0); 1101 layout->AddView(tab_contents_container_); 1102 SetContentsView(external_tab_view_); 1103 // Note that SetTabContents must be called after AddChildView is called 1104 tab_contents_container_->ChangeTabContents(tab_contents()); 1105} 1106 1107TemporaryPopupExternalTabContainer::TemporaryPopupExternalTabContainer( 1108 AutomationProvider* automation, 1109 AutomationResourceMessageFilter* filter) 1110 : ExternalTabContainer(automation, filter) { 1111} 1112 1113TemporaryPopupExternalTabContainer::~TemporaryPopupExternalTabContainer() { 1114 DVLOG(1) << __FUNCTION__; 1115} 1116 1117void TemporaryPopupExternalTabContainer::OpenURLFromTab( 1118 TabContents* source, const GURL& url, const GURL& referrer, 1119 WindowOpenDisposition disposition, PageTransition::Type transition) { 1120 if (!automation_) 1121 return; 1122 1123 if (disposition == CURRENT_TAB) { 1124 DCHECK(route_all_top_level_navigations_); 1125 disposition = NEW_FOREGROUND_TAB; 1126 } 1127 ExternalTabContainer::OpenURLFromTab(source, url, referrer, disposition, 1128 transition); 1129 // support only one navigation for a dummy tab before it is killed. 1130 ::DestroyWindow(GetNativeView()); 1131} 1132