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