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