Home | History | Annotate | Download | only in browser
      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