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
53using ui::ViewProp;
54
55static const char kWindowObjectKey[] = "ChromeWindowObject";
56
57// This class overrides the LinkActivated function in the PageInfoBubbleView
58// class and routes the help center link navigation to the host browser.
59class ExternalTabPageInfoBubbleView : public PageInfoBubbleView {
60 public:
61  ExternalTabPageInfoBubbleView(ExternalTabContainer* container,
62                                gfx::NativeWindow parent_window,
63                                Profile* profile,
64                                const GURL& url,
65                                const NavigationEntry::SSLStatus& ssl,
66                                bool show_history)
67      : PageInfoBubbleView(parent_window, profile, url, ssl, show_history),
68        container_(container) {
69    DVLOG(1) << __FUNCTION__;
70  }
71  virtual ~ExternalTabPageInfoBubbleView() {
72    DVLOG(1) << __FUNCTION__;
73  }
74  // LinkController methods:
75  virtual void LinkActivated(views::Link* source, int event_flags) {
76    GURL url = google_util::AppendGoogleLocaleParam(
77        GURL(chrome::kPageInfoHelpCenterURL));
78    container_->OpenURLFromTab(container_->tab_contents(), url, GURL(),
79                               NEW_FOREGROUND_TAB, PageTransition::LINK);
80  }
81 private:
82  scoped_refptr<ExternalTabContainer> container_;
83};
84
85base::LazyInstance<ExternalTabContainer::PendingTabs>
86    ExternalTabContainer::pending_tabs_(base::LINKER_INITIALIZED);
87
88ExternalTabContainer::ExternalTabContainer(
89    AutomationProvider* automation, AutomationResourceMessageFilter* filter)
90    : automation_(automation),
91      tab_contents_container_(NULL),
92      tab_handle_(0),
93      ignore_next_load_notification_(false),
94      automation_resource_message_filter_(filter),
95      load_requests_via_automation_(false),
96      handle_top_level_requests_(false),
97      external_method_factory_(this),
98      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
107ExternalTabContainer::~ExternalTabContainer() {
108  Uninitialize();
109}
110
111TabContents* ExternalTabContainer::tab_contents() const {
112  return tab_contents_.get() ? tab_contents_->tab_contents() : NULL;
113}
114
115bool ExternalTabContainer::Init(Profile* profile,
116                                HWND parent,
117                                const gfx::Rect& bounds,
118                                DWORD style,
119                                bool load_requests_via_automation,
120                                bool handle_top_level_requests,
121                                TabContentsWrapper* existing_contents,
122                                const GURL& initial_url,
123                                const GURL& referrer,
124                                bool infobars_enabled,
125                                bool route_all_top_level_navigations) {
126  if (IsWindow()) {
127    NOTREACHED();
128    return false;
129  }
130
131  load_requests_via_automation_ = load_requests_via_automation;
132  handle_top_level_requests_ = handle_top_level_requests;
133  infobars_enabled_ = infobars_enabled;
134  route_all_top_level_navigations_ = route_all_top_level_navigations;
135
136  set_window_style(WS_POPUP | WS_CLIPCHILDREN);
137  views::WidgetWin::Init(NULL, bounds);
138  if (!IsWindow()) {
139    NOTREACHED();
140    return false;
141  }
142
143  // TODO(jcampan): limit focus traversal to contents.
144
145  prop_.reset(new ViewProp(GetNativeView(), kWindowObjectKey, this));
146
147  if (existing_contents) {
148    tab_contents_.reset(existing_contents);
149    tab_contents_->controller().set_profile(profile);
150  } else {
151    TabContents* new_contents = new TabContents(profile, NULL, MSG_ROUTING_NONE,
152                                                NULL, NULL);
153    tab_contents_.reset(new TabContentsWrapper(new_contents));
154  }
155
156  tab_contents_->tab_contents()->set_delegate(this);
157
158  tab_contents_->tab_contents()->
159      GetMutableRendererPrefs()->browser_handles_top_level_requests =
160          handle_top_level_requests;
161
162  if (!existing_contents) {
163    tab_contents_->render_view_host()->AllowBindings(
164        BindingsPolicy::EXTERNAL_HOST);
165  }
166
167  NavigationController* controller = &tab_contents_->controller();
168  registrar_.Add(this, NotificationType::NAV_ENTRY_COMMITTED,
169                 Source<NavigationController>(controller));
170  registrar_.Add(this, NotificationType::FAIL_PROVISIONAL_LOAD_WITH_ERROR,
171                 Source<NavigationController>(controller));
172  registrar_.Add(this, NotificationType::LOAD_STOP,
173                 Source<NavigationController>(controller));
174  registrar_.Add(this, NotificationType::RENDER_VIEW_HOST_CREATED_FOR_TAB,
175                 Source<TabContents>(tab_contents_->tab_contents()));
176  registrar_.Add(this, NotificationType::RENDER_VIEW_HOST_DELETED,
177                 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
212void 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
239bool 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
262void ExternalTabContainer::SetTabHandle(int handle) {
263  tab_handle_ = handle;
264}
265
266void 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
273void 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
288bool ExternalTabContainer::IsExternalTabContainer(HWND window) {
289  return ViewProp::GetValue(window, kWindowObjectKey) != NULL;
290}
291
292// static
293ExternalTabContainer* 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
308ExternalTabContainer*
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
321void 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
375void 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
385void 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
453void 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
463bool ExternalTabContainer::infobars_enabled() {
464  return infobars_enabled_;
465}
466
467void ExternalTabContainer::ActivateContents(TabContents* contents) {
468}
469
470void ExternalTabContainer::DeactivateContents(TabContents* contents) {
471}
472
473void ExternalTabContainer::LoadingStateChanged(TabContents* source) {
474}
475
476void 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
490void 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
496bool ExternalTabContainer::IsPopup(const TabContents* source) const {
497  return is_popup_window_;
498}
499
500void 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
509void ExternalTabContainer::ContentsZoomChange(bool zoom_in) {
510}
511
512void 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
521bool ExternalTabContainer::IsExternalTabContainer() const {
522  return true;
523}
524
525gfx::NativeWindow ExternalTabContainer::GetFrameNativeWindow() {
526  return hwnd();
527}
528
529bool 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
538bool 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
558void 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
576void 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
588void 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
598void 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
609bool 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
641bool 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
661bool ExternalTabContainer::PreHandleKeyboardEvent(
662    const NativeWebKeyboardEvent& event, bool* is_keyboard_shortcut) {
663  return false;
664}
665
666void 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
672void 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
683void 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
708void ExternalTabContainer::ShowRepostFormWarningDialog(
709    TabContents* tab_contents) {
710  browser::ShowRepostFormWarningDialog(GetNativeView(), tab_contents);
711}
712
713////////////////////////////////////////////////////////////////////////////////
714// ExternalTabContainer, NotificationObserver implementation:
715
716void 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
796LRESULT 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
805void ExternalTabContainer::OnDestroy() {
806  prop_.reset();
807  Uninitialize();
808  WidgetWin::OnDestroy();
809  if (browser_.get()) {
810    ::DestroyWindow(browser_->window()->GetNativeHandle());
811  }
812}
813
814void ExternalTabContainer::OnFinalMessage(HWND window) {
815  // Release the reference which we grabbed in WM_CREATE.
816  Release();
817}
818
819void 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:
846bool 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
872bool 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
898scoped_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
913SkColor ExternalTabContainer::GetInfoBarSeparatorColor() const {
914  return ResourceBundle::toolbar_separator_color;
915}
916
917void ExternalTabContainer::InfoBarContainerStateChanged(bool is_animating) {
918  if (external_tab_view_)
919    external_tab_view_->Layout();
920}
921
922bool ExternalTabContainer::DrawInfoBarArrows(int* x) const {
923  return false;
924}
925
926// ExternalTabContainer instances do not have a window.
927views::Window* ExternalTabContainer::GetWindow() {
928  return NULL;
929}
930
931bool 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
974void 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
986bool 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
996void 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
1032void 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
1046void 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
1061void 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
1094TemporaryPopupExternalTabContainer::TemporaryPopupExternalTabContainer(
1095    AutomationProvider* automation,
1096    AutomationResourceMessageFilter* filter)
1097    : ExternalTabContainer(automation, filter) {
1098}
1099
1100TemporaryPopupExternalTabContainer::~TemporaryPopupExternalTabContainer() {
1101  DVLOG(1) << __FUNCTION__;
1102}
1103
1104void 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