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