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