external_tab_container_win.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
1// Copyright (c) 2012 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/ui/views/external_tab_container_win.h"
6
7#include <string>
8
9#include "base/bind.h"
10#include "base/bind_helpers.h"
11#include "base/debug/trace_event.h"
12#include "base/i18n/rtl.h"
13#include "base/logging.h"
14#include "base/memory/scoped_ptr.h"
15#include "base/string16.h"
16#include "base/time.h"
17#include "base/utf_string_conversions.h"
18#include "base/win/win_util.h"
19#include "chrome/app/chrome_command_ids.h"
20#include "chrome/app/chrome_dll_resource.h"
21#include "chrome/browser/automation/automation_provider.h"
22#include "chrome/browser/debugger/devtools_toggle_action.h"
23#include "chrome/browser/debugger/devtools_window.h"
24#include "chrome/browser/file_select_helper.h"
25#include "chrome/browser/history/history_tab_helper.h"
26#include "chrome/browser/history/history_types.h"
27#include "chrome/browser/infobars/infobar_tab_helper.h"
28#include "chrome/browser/profiles/profile.h"
29#include "chrome/browser/repost_form_warning_controller.h"
30#include "chrome/browser/themes/theme_service.h"
31#include "chrome/browser/ui/app_modal_dialogs/javascript_dialog_creator.h"
32#include "chrome/browser/ui/blocked_content/blocked_content_tab_helper.h"
33#include "chrome/browser/ui/browser.h"
34#include "chrome/browser/ui/browser_window.h"
35#include "chrome/browser/ui/tab_contents/tab_contents.h"
36#include "chrome/browser/ui/tab_modal_confirm_dialog.h"
37#include "chrome/browser/ui/views/infobars/infobar_container_view.h"
38#include "chrome/browser/ui/views/tab_contents/render_view_context_menu_win.h"
39#include "chrome/common/automation_messages.h"
40#include "chrome/common/chrome_constants.h"
41#include "chrome/common/chrome_notification_types.h"
42#include "chrome/common/render_messages.h"
43#include "chrome/common/url_constants.h"
44#include "content/public/browser/load_notification_details.h"
45#include "content/public/browser/native_web_keyboard_event.h"
46#include "content/public/browser/navigation_details.h"
47#include "content/public/browser/navigation_entry.h"
48#include "content/public/browser/notification_service.h"
49#include "content/public/browser/render_process_host.h"
50#include "content/public/browser/render_view_host.h"
51#include "content/public/browser/web_contents.h"
52#include "content/public/browser/web_intents_dispatcher.h"
53#include "content/public/common/bindings_policy.h"
54#include "content/public/common/frame_navigate_params.h"
55#include "content/public/common/page_transition_types.h"
56#include "content/public/common/page_zoom.h"
57#include "content/public/common/renderer_preferences.h"
58#include "content/public/common/ssl_status.h"
59#include "grit/generated_resources.h"
60#include "grit/locale_settings.h"
61#include "third_party/WebKit/Source/Platform/chromium/public/WebReferrerPolicy.h"
62#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebCString.h"
63#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h"
64#include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityPolicy.h"
65#include "ui/base/events/event_utils.h"
66#include "ui/base/l10n/l10n_util.h"
67#include "ui/base/models/menu_model.h"
68#include "ui/base/view_prop.h"
69#include "ui/views/controls/webview/webview.h"
70#include "ui/views/layout/grid_layout.h"
71#include "ui/views/win/hwnd_message_handler.h"
72
73using content::BrowserThread;
74using content::LoadNotificationDetails;
75using content::NativeWebKeyboardEvent;
76using content::NavigationController;
77using content::NavigationEntry;
78using content::OpenURLParams;
79using content::RenderViewHost;
80using content::SSLStatus;
81using content::WebContents;
82using ui::ViewProp;
83using WebKit::WebCString;
84using WebKit::WebReferrerPolicy;
85using WebKit::WebSecurityPolicy;
86using WebKit::WebString;
87
88static const char kWindowObjectKey[] = "ChromeWindowObject";
89
90namespace {
91
92// Convert ui::MenuModel into a serializable form for Chrome Frame
93ContextMenuModel* ConvertMenuModel(const ui::MenuModel* ui_model) {
94  ContextMenuModel* new_model = new ContextMenuModel;
95
96  const int index_base = ui_model->GetFirstItemIndex(NULL);
97  const int item_count = ui_model->GetItemCount();
98  new_model->items.reserve(item_count);
99  for (int i = 0; i < item_count; ++i) {
100    const int index = index_base + i;
101    if (ui_model->IsVisibleAt(index)) {
102      ContextMenuModel::Item item;
103      item.type = ui_model->GetTypeAt(index);
104      item.item_id = ui_model->GetCommandIdAt(index);
105      item.label = ui_model->GetLabelAt(index);
106      item.checked = ui_model->IsItemCheckedAt(index);
107      item.enabled = ui_model->IsEnabledAt(index);
108      if (item.type == ui::MenuModel::TYPE_SUBMENU)
109        item.submenu = ConvertMenuModel(ui_model->GetSubmenuModelAt(index));
110
111      new_model->items.push_back(item);
112    }
113  }
114
115  return new_model;
116}
117
118}  // namespace
119
120base::LazyInstance<ExternalTabContainerWin::PendingTabs>
121    ExternalTabContainerWin::pending_tabs_ = LAZY_INSTANCE_INITIALIZER;
122
123ExternalTabContainerWin::ExternalTabContainerWin(
124    AutomationProvider* automation,
125    AutomationResourceMessageFilter* filter)
126    : views::NativeWidgetWin(new views::Widget),
127      automation_(automation),
128      tab_contents_container_(NULL),
129      tab_handle_(0),
130      ignore_next_load_notification_(false),
131      automation_resource_message_filter_(filter),
132      load_requests_via_automation_(false),
133      handle_top_level_requests_(false),
134      ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)),
135      pending_(false),
136      focus_manager_(NULL),
137      external_tab_view_(NULL),
138      unload_reply_message_(NULL),
139      route_all_top_level_navigations_(false),
140      is_popup_window_(false) {
141}
142
143// static
144scoped_refptr<ExternalTabContainer>
145    ExternalTabContainerWin::RemovePendingExternalTab(uintptr_t cookie) {
146  PendingTabs& pending_tabs = pending_tabs_.Get();
147  PendingTabs::iterator index = pending_tabs.find(cookie);
148  if (index != pending_tabs.end()) {
149    scoped_refptr<ExternalTabContainer> container = (*index).second;
150    pending_tabs.erase(index);
151    return container;
152  }
153
154  NOTREACHED() << "Failed to find ExternalTabContainer for cookie: "
155               << cookie;
156  return NULL;
157}
158
159bool ExternalTabContainerWin::Init(Profile* profile,
160                                   HWND parent,
161                                   const gfx::Rect& bounds,
162                                   DWORD style,
163                                   bool load_requests_via_automation,
164                                   bool handle_top_level_requests,
165                                   content::WebContents* existing_contents,
166                                   const GURL& initial_url,
167                                   const GURL& referrer,
168                                   bool infobars_enabled,
169                                   bool route_all_top_level_navigations) {
170  if (IsWindow(GetNativeView())) {
171    NOTREACHED();
172    return false;
173  }
174
175  load_requests_via_automation_ = load_requests_via_automation;
176  handle_top_level_requests_ = handle_top_level_requests;
177  route_all_top_level_navigations_ = route_all_top_level_navigations;
178
179  GetMessageHandler()->set_window_style(WS_POPUP | WS_CLIPCHILDREN);
180
181  views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
182  params.bounds = bounds;
183  params.native_widget = this;
184  GetWidget()->Init(params);
185  if (!IsWindow(GetNativeView())) {
186    NOTREACHED();
187    return false;
188  }
189
190  // TODO(jcampan): limit focus traversal to contents.
191
192  prop_.reset(new ViewProp(GetNativeView(), kWindowObjectKey, this));
193
194  if (existing_contents) {
195    existing_contents->GetController().SetBrowserContext(profile);
196  } else {
197    existing_contents =
198        WebContents::Create(profile, NULL, MSG_ROUTING_NONE, NULL);
199    existing_contents->GetRenderViewHost()->AllowBindings(
200        content::BINDINGS_POLICY_EXTERNAL_HOST);
201  }
202
203  existing_contents->SetDelegate(this);
204  existing_contents->GetMutableRendererPrefs()->
205      browser_handles_non_local_top_level_requests = handle_top_level_requests;
206
207  NavigationController* controller = &existing_contents->GetController();
208  registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED,
209                 content::Source<NavigationController>(controller));
210  registrar_.Add(this, content::NOTIFICATION_LOAD_STOP,
211                 content::Source<NavigationController>(controller));
212  registrar_.Add(this,
213                 content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED,
214                 content::Source<WebContents>(existing_contents));
215  registrar_.Add(this, content::NOTIFICATION_RENDER_VIEW_HOST_DELETED,
216                 content::NotificationService::AllSources());
217  registrar_.Add(this, content::NOTIFICATION_RENDER_VIEW_HOST_CREATED,
218                 content::NotificationService::AllSources());
219
220  content::WebContentsObserver::Observe(existing_contents);
221
222  // TODO(avi): Rather than create a TabContents, attach desired tab helpers.
223  tab_contents_.reset(
224      TabContents::Factory::CreateTabContents(existing_contents));
225
226  if (!infobars_enabled) {
227    InfoBarTabHelper* infobar_tab_helper =
228        InfoBarTabHelper::FromWebContents(existing_contents);
229    infobar_tab_helper->set_infobars_enabled(false);
230  }
231
232  // Start loading initial URL
233  if (!initial_url.is_empty()) {
234    // Navigate out of context since we don't have a 'tab_handle_' yet.
235    MessageLoop::current()->PostTask(
236        FROM_HERE,
237        base::Bind(&ExternalTabContainerWin::Navigate,
238                   weak_factory_.GetWeakPtr(),
239                   initial_url, referrer));
240  }
241
242  // We need WS_POPUP to be on the window during initialization, but
243  // once initialized we apply the requested style which may or may not
244  // include the popup bit.
245  // Note that it's important to do this before we call SetParent since
246  // during the SetParent call we will otherwise get a WA_ACTIVATE call
247  // that causes us to steal the current focus.
248  SetWindowLong(
249      GetNativeView(), GWL_STYLE,
250      (GetWindowLong(GetNativeView(), GWL_STYLE) & ~WS_POPUP) | style);
251
252  // Now apply the parenting and style
253  if (parent)
254    SetParent(GetNativeView(), parent);
255
256  ::ShowWindow(existing_contents->GetNativeView(), SW_SHOWNA);
257
258  LoadAccelerators();
259  SetupExternalTabView();
260  BlockedContentTabHelper::FromWebContents(existing_contents)->
261      set_delegate(this);
262  return true;
263}
264
265void ExternalTabContainerWin::Uninitialize() {
266  registrar_.RemoveAll();
267  if (tab_contents_.get()) {
268    UnregisterRenderViewHost(
269        tab_contents_->web_contents()->GetRenderViewHost());
270
271    if (GetWidget()->GetRootView())
272      GetWidget()->GetRootView()->RemoveAllChildViews(true);
273
274    content::NotificationService::current()->Notify(
275        chrome::NOTIFICATION_EXTERNAL_TAB_CLOSED,
276        content::Source<NavigationController>(
277            &tab_contents_->web_contents()->GetController()),
278        content::Details<ExternalTabContainer>(this));
279
280    tab_contents_.reset(NULL);
281  }
282
283  if (focus_manager_) {
284    focus_manager_->UnregisterAccelerators(this);
285    focus_manager_ = NULL;
286  }
287
288  external_tab_view_ = NULL;
289  request_context_ = NULL;
290  tab_contents_container_ = NULL;
291}
292
293bool ExternalTabContainerWin::Reinitialize(
294    AutomationProvider* automation_provider,
295    AutomationResourceMessageFilter* filter,
296    gfx::NativeWindow parent_window) {
297  if (!automation_provider || !filter) {
298    NOTREACHED();
299    return false;
300  }
301
302  automation_ = automation_provider;
303  automation_resource_message_filter_ = filter;
304  // Wait for the automation channel to be initialized before resuming pending
305  // render views and sending in the navigation state.
306  MessageLoop::current()->PostTask(
307      FROM_HERE, base::Bind(&ExternalTabContainerWin::OnReinitialize,
308                            weak_factory_.GetWeakPtr()));
309
310  if (parent_window)
311    SetParent(GetNativeView(), parent_window);
312  return true;
313}
314
315WebContents* ExternalTabContainerWin::GetWebContents() const {
316  return tab_contents_.get() ? tab_contents_->web_contents() : NULL;
317}
318
319TabContents* ExternalTabContainerWin::GetTabContents() {
320  return tab_contents_.get();
321}
322
323gfx::NativeView ExternalTabContainerWin::GetExternalTabNativeView() const {
324  return GetNativeView();
325}
326
327void ExternalTabContainerWin::SetTabHandle(int handle) {
328  tab_handle_ = handle;
329}
330
331int ExternalTabContainerWin::GetTabHandle() const {
332  return tab_handle_;
333}
334
335bool ExternalTabContainerWin::ExecuteContextMenuCommand(int command) {
336  if (!external_context_menu_.get()) {
337    NOTREACHED();
338    return false;
339  }
340
341  switch (command) {
342    case IDS_CONTENT_CONTEXT_SAVEAUDIOAS:
343    case IDS_CONTENT_CONTEXT_SAVEVIDEOAS:
344    case IDS_CONTENT_CONTEXT_SAVEIMAGEAS:
345    case IDS_CONTENT_CONTEXT_SAVELINKAS: {
346      NOTREACHED();  // Should be handled in host.
347      break;
348    }
349  }
350
351  external_context_menu_->ExecuteCommand(command);
352  return true;
353}
354
355void ExternalTabContainerWin::RunUnloadHandlers(IPC::Message* reply_message) {
356  if (!automation_) {
357    delete reply_message;
358    return;
359  }
360
361  // If we have a pending unload message, then just respond back to this
362  // request and continue processing the previous unload message.
363  if (unload_reply_message_) {
364     AutomationMsg_RunUnloadHandlers::WriteReplyParams(reply_message, true);
365     automation_->Send(reply_message);
366     return;
367  }
368
369  unload_reply_message_ = reply_message;
370  bool wait_for_unload_handlers =
371      tab_contents_.get() &&
372      Browser::RunUnloadEventsHelper(tab_contents_->web_contents());
373  if (!wait_for_unload_handlers) {
374    AutomationMsg_RunUnloadHandlers::WriteReplyParams(reply_message, true);
375    automation_->Send(reply_message);
376    unload_reply_message_ = NULL;
377  }
378}
379
380void ExternalTabContainerWin::ProcessUnhandledAccelerator(const MSG& msg) {
381  NativeWebKeyboardEvent keyboard_event(msg);
382  unhandled_keyboard_event_handler_.HandleKeyboardEvent(keyboard_event,
383                                                        focus_manager_);
384}
385
386void ExternalTabContainerWin::FocusThroughTabTraversal(
387    bool reverse,
388    bool restore_focus_to_view) {
389  DCHECK(tab_contents_.get());
390  if (tab_contents_.get())
391    tab_contents_->web_contents()->Focus();
392
393  // The tab_contents_ member can get destroyed in the context of the call to
394  // TabContentsViewViews::Focus() above. This method eventually calls SetFocus
395  // on the native window, which could end up dispatching messages like
396  // WM_DESTROY for the external tab.
397  if (tab_contents_.get() && restore_focus_to_view)
398    tab_contents_->web_contents()->FocusThroughTabTraversal(reverse);
399}
400
401// static
402bool ExternalTabContainerWin::IsExternalTabContainer(HWND window) {
403  return ViewProp::GetValue(window, kWindowObjectKey) != NULL;
404}
405
406// static
407ExternalTabContainer*
408    ExternalTabContainerWin::GetExternalContainerFromNativeWindow(
409        gfx::NativeView native_window) {
410  ExternalTabContainer* tab_container = NULL;
411  if (native_window) {
412    tab_container = reinterpret_cast<ExternalTabContainer*>(
413        ViewProp::GetValue(native_window, kWindowObjectKey));
414  }
415  return tab_container;
416}
417////////////////////////////////////////////////////////////////////////////////
418// ExternalTabContainer, content::WebContentsDelegate implementation:
419
420WebContents* ExternalTabContainerWin::OpenURLFromTab(
421    WebContents* source,
422    const OpenURLParams& params) {
423  if (pending()) {
424    pending_open_url_requests_.push_back(params);
425    return NULL;
426  }
427
428  switch (params.disposition) {
429    case CURRENT_TAB:
430    case SINGLETON_TAB:
431    case NEW_FOREGROUND_TAB:
432    case NEW_BACKGROUND_TAB:
433    case NEW_POPUP:
434    case NEW_WINDOW:
435    case SAVE_TO_DISK:
436      if (automation_) {
437        GURL referrer = GURL(WebSecurityPolicy::generateReferrerHeader(
438            params.referrer.policy,
439            params.url,
440            WebString::fromUTF8(params.referrer.url.spec())).utf8());
441        automation_->Send(new AutomationMsg_OpenURL(tab_handle_,
442                                                    params.url,
443                                                    referrer,
444                                                    params.disposition));
445        // TODO(ananta)
446        // We should populate other fields in the
447        // ViewHostMsg_FrameNavigate_Params structure. Another option could be
448        // to refactor the UpdateHistoryForNavigation function in WebContents.
449        content::FrameNavigateParams nav_params;
450        nav_params.referrer = content::Referrer(referrer,
451                                                params.referrer.policy);
452        nav_params.url = params.url;
453        nav_params.page_id = -1;
454        nav_params.transition = content::PAGE_TRANSITION_LINK;
455
456        HistoryTabHelper* history_tab_helper =
457            HistoryTabHelper::FromWebContents(tab_contents_->web_contents());
458        const history::HistoryAddPageArgs& add_page_args =
459            history_tab_helper->CreateHistoryAddPageArgs(
460                params.url, base::Time::Now(),
461                false /* did_replace_entry */, nav_params);
462        history_tab_helper->UpdateHistoryForNavigation(add_page_args);
463
464        return tab_contents_->web_contents();
465      }
466      break;
467    default:
468      NOTREACHED();
469      break;
470  }
471
472  return NULL;
473}
474
475void ExternalTabContainerWin::NavigationStateChanged(const WebContents* source,
476                                                     unsigned changed_flags) {
477  if (automation_) {
478    NavigationInfo nav_info;
479    if (InitNavigationInfo(&nav_info, content::NAVIGATION_TYPE_NAV_IGNORE, 0))
480      automation_->Send(new AutomationMsg_NavigationStateChanged(
481          tab_handle_, changed_flags, nav_info));
482  }
483}
484
485void ExternalTabContainerWin::AddNewContents(WebContents* source,
486                                             WebContents* new_contents,
487                                             WindowOpenDisposition disposition,
488                                             const gfx::Rect& initial_pos,
489                                             bool user_gesture,
490                                             bool* was_blocked) {
491  if (!automation_) {
492    DCHECK(pending_);
493    LOG(ERROR) << "Invalid automation provider. Dropping new contents notify";
494    delete new_contents;
495    return;
496  }
497
498  scoped_refptr<ExternalTabContainerWin> new_container;
499  // If the host is a browser like IE8, then the URL being navigated to in the
500  // new tab contents could potentially navigate back to Chrome from a new
501  // IE process. We support full tab mode only for IE and hence we use that as
502  // a determining factor in whether the new ExternalTabContainer instance is
503  // created as pending or not.
504  if (!route_all_top_level_navigations_) {
505    new_container = new ExternalTabContainerWin(NULL, NULL);
506  } else {
507    // Reuse the same tab handle here as the new container instance is a dummy
508    // instance which does not have an automation client connected at the other
509    // end.
510    new_container = new TemporaryPopupExternalTabContainerWin(
511        automation_, automation_resource_message_filter_.get());
512    new_container->SetTabHandle(tab_handle_);
513  }
514
515  // Make sure that ExternalTabContainer instance is initialized with
516  // an unwrapped Profile.
517  Profile* profile =
518      Profile::FromBrowserContext(new_contents->GetBrowserContext())->
519          GetOriginalProfile();
520  bool result = new_container->Init(profile,
521                                    NULL,
522                                    initial_pos,
523                                    WS_CHILD,
524                                    load_requests_via_automation_,
525                                    handle_top_level_requests_,
526                                    new_contents,
527                                    GURL(),
528                                    GURL(),
529                                    true,
530                                    route_all_top_level_navigations_);
531
532  if (result) {
533    if (route_all_top_level_navigations_) {
534      return;
535    }
536    uintptr_t cookie = reinterpret_cast<uintptr_t>(new_container.get());
537    pending_tabs_.Get()[cookie] = new_container;
538    new_container->set_pending(true);
539    new_container->set_is_popup_window(disposition == NEW_POPUP);
540    AttachExternalTabParams attach_params_;
541    attach_params_.cookie = static_cast<uint64>(cookie);
542    attach_params_.dimensions = initial_pos;
543    attach_params_.user_gesture = user_gesture;
544    attach_params_.disposition = disposition;
545    attach_params_.profile_name = WideToUTF8(
546        profile->GetPath().DirName().BaseName().value());
547    automation_->Send(new AutomationMsg_AttachExternalTab(
548        tab_handle_, attach_params_));
549  } else {
550    NOTREACHED();
551  }
552}
553
554void ExternalTabContainerWin::WebContentsCreated(WebContents* source_contents,
555                                                 int64 source_frame_id,
556                                                 const GURL& target_url,
557                                                 WebContents* new_contents) {
558  if (!load_requests_via_automation_)
559    return;
560
561  RenderViewHost* rvh = new_contents->GetRenderViewHost();
562  DCHECK(rvh != NULL);
563
564  // Register this render view as a pending render view, i.e. any network
565  // requests initiated by this render view would be serviced when the
566  // external host connects to the new external tab instance.
567  RegisterRenderViewHostForAutomation(rvh, true);
568}
569
570void ExternalTabContainerWin::CloseContents(content::WebContents* source) {
571  if (!automation_)
572    return;
573
574  if (unload_reply_message_) {
575    AutomationMsg_RunUnloadHandlers::WriteReplyParams(unload_reply_message_,
576                                                      true);
577    automation_->Send(unload_reply_message_);
578    unload_reply_message_ = NULL;
579  } else {
580    automation_->Send(new AutomationMsg_CloseExternalTab(tab_handle_));
581  }
582}
583
584void ExternalTabContainerWin::MoveContents(WebContents* source,
585                                           const gfx::Rect& pos) {
586  if (automation_ && is_popup_window_)
587    automation_->Send(new AutomationMsg_MoveWindow(tab_handle_, pos));
588}
589
590content::WebContents* ExternalTabContainerWin::GetConstrainingWebContents(
591    content::WebContents* source) {
592  return source;
593}
594
595ExternalTabContainerWin::~ExternalTabContainerWin() {
596  Uninitialize();
597}
598
599bool ExternalTabContainerWin::IsPopupOrPanel(const WebContents* source) const {
600  return is_popup_window_;
601}
602
603void ExternalTabContainerWin::UpdateTargetURL(WebContents* source,
604                                              int32 page_id,
605                                              const GURL& url) {
606  if (automation_) {
607    string16 url_string = CA2W(url.spec().c_str());
608    automation_->Send(
609        new AutomationMsg_UpdateTargetUrl(tab_handle_, url_string));
610  }
611}
612
613void ExternalTabContainerWin::ContentsZoomChange(bool zoom_in) {
614}
615
616bool ExternalTabContainerWin::TakeFocus(content::WebContents* source,
617                                        bool reverse) {
618  if (automation_) {
619    automation_->Send(new AutomationMsg_TabbedOut(tab_handle_,
620        base::win::IsShiftPressed()));
621  }
622
623  return true;
624}
625
626bool ExternalTabContainerWin::CanDownload(RenderViewHost* render_view_host,
627                                          int request_id,
628                                          const std::string& request_method) {
629  if (load_requests_via_automation_) {
630    if (automation_) {
631      // In case the host needs to show UI that needs to take the focus.
632      ::AllowSetForegroundWindow(ASFW_ANY);
633
634      BrowserThread::PostTask(
635          BrowserThread::IO, FROM_HERE,
636          base::Bind(
637             base::IgnoreResult(
638                 &AutomationResourceMessageFilter::SendDownloadRequestToHost),
639             automation_resource_message_filter_.get(), 0, tab_handle_,
640             request_id));
641    }
642  } else {
643    DLOG(WARNING) << "Downloads are only supported with host browser network "
644                     "stack enabled.";
645  }
646
647  // Never allow downloads.
648  return false;
649}
650
651void ExternalTabContainerWin::RegisterRenderViewHostForAutomation(
652    RenderViewHost* render_view_host,
653    bool pending_view) {
654  if (render_view_host) {
655    AutomationResourceMessageFilter::RegisterRenderView(
656        render_view_host->GetProcess()->GetID(),
657        render_view_host->GetRoutingID(),
658        GetTabHandle(),
659        automation_resource_message_filter_,
660        pending_view);
661  }
662}
663
664void ExternalTabContainerWin::RegisterRenderViewHost(
665    RenderViewHost* render_view_host) {
666  // RenderViewHost instances that are to be associated with this
667  // ExternalTabContainer should share the same resource request automation
668  // settings.
669  RegisterRenderViewHostForAutomation(
670      render_view_host,
671      false);  // Network requests should not be handled later.
672}
673
674void ExternalTabContainerWin::UnregisterRenderViewHost(
675    RenderViewHost* render_view_host) {
676  // Undo the resource automation registration performed in
677  // ExternalTabContainerWin::RegisterRenderViewHost.
678  if (render_view_host) {
679    AutomationResourceMessageFilter::UnRegisterRenderView(
680      render_view_host->GetProcess()->GetID(),
681      render_view_host->GetRoutingID());
682  }
683}
684
685content::JavaScriptDialogCreator*
686ExternalTabContainerWin::GetJavaScriptDialogCreator() {
687  return GetJavaScriptDialogCreatorInstance();
688}
689
690bool ExternalTabContainerWin::HandleContextMenu(
691    const content::ContextMenuParams& params) {
692  if (!automation_) {
693    NOTREACHED();
694    return false;
695  }
696  external_context_menu_.reset(RenderViewContextMenuViews::Create(
697      web_contents(), params));
698  static_cast<RenderViewContextMenuWin*>(
699      external_context_menu_.get())->SetExternal();
700  external_context_menu_->Init();
701  external_context_menu_->UpdateMenuItemStates();
702
703  scoped_ptr<ContextMenuModel> context_menu_model(
704    ConvertMenuModel(&external_context_menu_->menu_model()));
705
706  POINT screen_pt = { params.x, params.y };
707  MapWindowPoints(GetNativeView(), HWND_DESKTOP, &screen_pt, 1);
708
709  MiniContextMenuParams ipc_params;
710  ipc_params.screen_x = screen_pt.x;
711  ipc_params.screen_y = screen_pt.y;
712  ipc_params.link_url = params.link_url;
713  ipc_params.unfiltered_link_url = params.unfiltered_link_url;
714  ipc_params.src_url = params.src_url;
715  ipc_params.page_url = params.page_url;
716  ipc_params.keyword_url = params.keyword_url;
717  ipc_params.frame_url = params.frame_url;
718
719  bool rtl = base::i18n::IsRTL();
720  automation_->Send(
721      new AutomationMsg_ForwardContextMenuToExternalHost(tab_handle_,
722          *context_menu_model,
723          rtl ? TPM_RIGHTALIGN : TPM_LEFTALIGN, ipc_params));
724
725  return true;
726}
727
728bool ExternalTabContainerWin::PreHandleKeyboardEvent(
729    content::WebContents* source,
730    const NativeWebKeyboardEvent& event,
731    bool* is_keyboard_shortcut) {
732  return false;
733}
734
735void ExternalTabContainerWin::HandleKeyboardEvent(
736    content::WebContents* source,
737    const NativeWebKeyboardEvent& event) {
738  ProcessUnhandledKeyStroke(event.os_event.hwnd, event.os_event.message,
739                            event.os_event.wParam, event.os_event.lParam);
740}
741
742void ExternalTabContainerWin::BeforeUnloadFired(WebContents* tab,
743                                                bool proceed,
744                                                bool* proceed_to_fire_unload) {
745  *proceed_to_fire_unload = true;
746
747  if (!automation_) {
748    delete unload_reply_message_;
749    unload_reply_message_ = NULL;
750    return;
751  }
752
753  if (!unload_reply_message_) {
754    NOTREACHED() << "**** NULL unload reply message pointer.";
755    return;
756  }
757
758  if (!proceed) {
759    AutomationMsg_RunUnloadHandlers::WriteReplyParams(unload_reply_message_,
760                                                      false);
761    automation_->Send(unload_reply_message_);
762    unload_reply_message_ = NULL;
763    *proceed_to_fire_unload = false;
764  }
765}
766
767void ExternalTabContainerWin::ShowRepostFormWarningDialog(WebContents* source) {
768  TabModalConfirmDialog::Create(new RepostFormWarningController(source),
769                                source);
770}
771
772void ExternalTabContainerWin::RunFileChooser(
773    WebContents* tab,
774    const content::FileChooserParams& params) {
775  FileSelectHelper::RunFileChooser(tab, params);
776}
777
778void ExternalTabContainerWin::EnumerateDirectory(WebContents* tab,
779                                                 int request_id,
780                                                 const FilePath& path) {
781  FileSelectHelper::EnumerateDirectory(tab, request_id, path);
782}
783
784void ExternalTabContainerWin::JSOutOfMemory(WebContents* tab) {
785  Browser::JSOutOfMemoryHelper(tab);
786}
787
788void ExternalTabContainerWin::RegisterProtocolHandler(
789    WebContents* tab,
790    const std::string& protocol,
791    const GURL& url,
792    const string16& title,
793    bool user_gesture) {
794  Browser::RegisterProtocolHandlerHelper(tab, protocol, url, title,
795                                         user_gesture, NULL);
796}
797
798void ExternalTabContainerWin::RegisterIntentHandler(
799    WebContents* tab,
800    const webkit_glue::WebIntentServiceData& data,
801    bool user_gesture) {
802  Browser::RegisterIntentHandlerHelper(tab, data, user_gesture);
803}
804
805void ExternalTabContainerWin::WebIntentDispatch(
806    WebContents* tab,
807    content::WebIntentsDispatcher* intents_dispatcher) {
808  // TODO(binji) How do we want to display the WebIntentPicker bubble if there
809  // is no BrowserWindow?
810  delete intents_dispatcher;
811}
812
813void ExternalTabContainerWin::FindReply(WebContents* tab,
814                                        int request_id,
815                                        int number_of_matches,
816                                        const gfx::Rect& selection_rect,
817                                        int active_match_ordinal,
818                                        bool final_update) {
819  Browser::FindReplyHelper(tab, request_id, number_of_matches, selection_rect,
820                           active_match_ordinal, final_update);
821}
822
823void ExternalTabContainerWin::RequestMediaAccessPermission(
824    content::WebContents* web_contents,
825    const content::MediaStreamRequest* request,
826    const content::MediaResponseCallback& callback) {
827  Browser::RequestMediaAccessPermissionHelper(web_contents, request, callback);
828}
829
830bool ExternalTabContainerWin::OnMessageReceived(const IPC::Message& message) {
831  bool handled = true;
832  IPC_BEGIN_MESSAGE_MAP(ExternalTabContainerWin, message)
833    IPC_MESSAGE_HANDLER(ChromeViewHostMsg_ForwardMessageToExternalHost,
834                        OnForwardMessageToExternalHost)
835    IPC_MESSAGE_UNHANDLED(handled = false)
836  IPC_END_MESSAGE_MAP()
837  return handled;
838}
839
840void ExternalTabContainerWin::DidFailProvisionalLoad(
841    int64 frame_id,
842    bool is_main_frame,
843    const GURL& validated_url,
844    int error_code,
845    const string16& error_description,
846    content::RenderViewHost* render_view_host) {
847  if (automation_) {
848    automation_->Send(new AutomationMsg_NavigationFailed(
849        tab_handle_, error_code, validated_url));
850  }
851  ignore_next_load_notification_ = true;
852}
853
854void ExternalTabContainerWin::OnForwardMessageToExternalHost(
855    const std::string& message,
856    const std::string& origin,
857    const std::string& target) {
858  if (automation_) {
859    automation_->Send(new AutomationMsg_ForwardMessageToExternalHost(
860        tab_handle_, message, origin, target));
861  }
862}
863
864////////////////////////////////////////////////////////////////////////////////
865// ExternalTabContainer, NotificationObserver implementation:
866
867void ExternalTabContainerWin::Observe(
868    int type,
869    const content::NotificationSource& source,
870    const content::NotificationDetails& details) {
871  if (!automation_)
872    return;
873
874  static const int kHttpClientErrorStart = 400;
875  static const int kHttpServerErrorEnd = 510;
876
877  switch (type) {
878    case content::NOTIFICATION_LOAD_STOP: {
879        const LoadNotificationDetails* load =
880            content::Details<LoadNotificationDetails>(details).ptr();
881        if (load && content::PageTransitionIsMainFrame(load->origin)) {
882          TRACE_EVENT_END_ETW("ExternalTabContainerWin::Navigate", 0,
883                              load->url.spec());
884          automation_->Send(new AutomationMsg_TabLoaded(tab_handle_,
885                                                        load->url));
886        }
887        break;
888      }
889    case content::NOTIFICATION_NAV_ENTRY_COMMITTED: {
890      if (ignore_next_load_notification_) {
891        ignore_next_load_notification_ = false;
892        return;
893      }
894
895      const content::LoadCommittedDetails* commit =
896          content::Details<content::LoadCommittedDetails>(details).ptr();
897
898      if (commit->http_status_code >= kHttpClientErrorStart &&
899          commit->http_status_code <= kHttpServerErrorEnd) {
900        automation_->Send(new AutomationMsg_NavigationFailed(
901            tab_handle_, commit->http_status_code, commit->entry->GetURL()));
902
903        ignore_next_load_notification_ = true;
904      } else {
905        NavigationInfo navigation_info;
906        // When the previous entry index is invalid, it will be -1, which
907        // will still make the computation come out right (navigating to the
908        // 0th entry will be +1).
909        if (InitNavigationInfo(&navigation_info, commit->type,
910                commit->previous_entry_index -
911                tab_contents_->web_contents()->
912                    GetController().GetLastCommittedEntryIndex()))
913          automation_->Send(new AutomationMsg_DidNavigate(tab_handle_,
914                                                          navigation_info));
915      }
916      break;
917    }
918    case content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED: {
919      if (load_requests_via_automation_) {
920        RenderViewHost* rvh = content::Details<RenderViewHost>(details).ptr();
921        RegisterRenderViewHostForAutomation(rvh, false);
922      }
923      break;
924    }
925    case content::NOTIFICATION_RENDER_VIEW_HOST_DELETED: {
926      if (load_requests_via_automation_) {
927        RenderViewHost* rvh = content::Source<RenderViewHost>(source).ptr();
928        UnregisterRenderViewHost(rvh);
929      }
930      break;
931    }
932    case content::NOTIFICATION_RENDER_VIEW_HOST_CREATED: {
933      if (load_requests_via_automation_) {
934        RenderViewHost* rvh = content::Source<RenderViewHost>(source).ptr();
935        RegisterRenderViewHostForAutomation(rvh, false);
936      }
937      break;
938    }
939    default:
940      NOTREACHED();
941  }
942}
943
944////////////////////////////////////////////////////////////////////////////////
945// ExternalTabContainer, views::NativeWidgetWin overrides:
946
947bool ExternalTabContainerWin::PreHandleMSG(UINT message,
948                                           WPARAM w_param,
949                                           LPARAM l_param,
950                                           LRESULT* result) {
951  if (message == WM_DESTROY) {
952    prop_.reset();
953    Uninitialize();
954  }
955  return false;
956}
957
958void ExternalTabContainerWin::PostHandleMSG(UINT message,
959                                            WPARAM w_param,
960                                            LPARAM l_param) {
961    // Grab a reference here which will be released in OnFinalMessage
962  if (message == WM_CREATE)
963    AddRef();
964}
965
966void ExternalTabContainerWin::OnFinalMessage(HWND window) {
967  GetWidget()->OnNativeWidgetDestroyed();
968  // Release the reference which we grabbed in WM_CREATE.
969  Release();
970}
971
972////////////////////////////////////////////////////////////////////////////////
973// ExternalTabContainer, private:
974bool ExternalTabContainerWin::ProcessUnhandledKeyStroke(HWND window,
975                                                        UINT message,
976                                                        WPARAM wparam,
977                                                        LPARAM lparam) {
978  if (!automation_) {
979    return false;
980  }
981  if ((wparam == VK_TAB) && !base::win::IsCtrlPressed()) {
982    // Tabs are handled separately (except if this is Ctrl-Tab or
983    // Ctrl-Shift-Tab)
984    return false;
985  }
986
987  // Send this keystroke to the external host as it could be processed as an
988  // accelerator there. If the host does not handle this accelerator, it will
989  // reflect the accelerator back to us via the ProcessUnhandledAccelerator
990  // method.
991  MSG msg = {0};
992  msg.hwnd = window;
993  msg.message = message;
994  msg.wParam = wparam;
995  msg.lParam = lparam;
996  automation_->Send(new AutomationMsg_HandleAccelerator(tab_handle_, msg));
997  return true;
998}
999
1000bool ExternalTabContainerWin::InitNavigationInfo(
1001    NavigationInfo* nav_info,
1002    content::NavigationType nav_type,
1003    int relative_offset) {
1004  DCHECK(nav_info);
1005  NavigationEntry* entry =
1006      tab_contents_->web_contents()->GetController().GetActiveEntry();
1007  // If this is very early in the game then we may not have an entry.
1008  if (!entry)
1009    return false;
1010
1011  nav_info->navigation_type = nav_type;
1012  nav_info->relative_offset = relative_offset;
1013  nav_info->navigation_index =
1014      tab_contents_->web_contents()->GetController().GetCurrentEntryIndex();
1015  nav_info->url = entry->GetURL();
1016  nav_info->referrer = entry->GetReferrer().url;
1017  nav_info->title =  UTF16ToWideHack(entry->GetTitle());
1018  if (nav_info->title.empty())
1019    nav_info->title = UTF8ToWide(nav_info->url.spec());
1020
1021  nav_info->security_style = entry->GetSSL().security_style;
1022  int content_status = entry->GetSSL().content_status;
1023  nav_info->displayed_insecure_content =
1024      !!(content_status & SSLStatus::DISPLAYED_INSECURE_CONTENT);
1025  nav_info->ran_insecure_content =
1026      !!(content_status & SSLStatus::RAN_INSECURE_CONTENT);
1027  return true;
1028}
1029
1030SkColor ExternalTabContainerWin::GetInfoBarSeparatorColor() const {
1031  return ThemeService::GetDefaultColor(ThemeService::COLOR_TOOLBAR_SEPARATOR);
1032}
1033
1034void ExternalTabContainerWin::InfoBarContainerStateChanged(bool is_animating) {
1035  if (external_tab_view_)
1036    external_tab_view_->Layout();
1037}
1038
1039bool ExternalTabContainerWin::DrawInfoBarArrows(int* x) const {
1040  return false;
1041}
1042
1043bool ExternalTabContainerWin::AcceleratorPressed(
1044    const ui::Accelerator& accelerator) {
1045  std::map<ui::Accelerator, int>::const_iterator iter =
1046      accelerator_table_.find(accelerator);
1047  DCHECK(iter != accelerator_table_.end());
1048
1049  if (!tab_contents_.get() ||
1050      !tab_contents_->web_contents()->GetRenderViewHost()) {
1051    NOTREACHED();
1052    return false;
1053  }
1054
1055  RenderViewHost* host = tab_contents_->web_contents()->GetRenderViewHost();
1056  int command_id = iter->second;
1057  switch (command_id) {
1058    case IDC_ZOOM_PLUS:
1059      host->Zoom(content::PAGE_ZOOM_IN);
1060      break;
1061    case IDC_ZOOM_NORMAL:
1062      host->Zoom(content::PAGE_ZOOM_RESET);
1063      break;
1064    case IDC_ZOOM_MINUS:
1065      host->Zoom(content::PAGE_ZOOM_OUT);
1066      break;
1067    case IDC_DEV_TOOLS:
1068      DevToolsWindow::ToggleDevToolsWindow(
1069          tab_contents_->web_contents()->GetRenderViewHost(),
1070          false,
1071          DEVTOOLS_TOGGLE_ACTION_SHOW);
1072      break;
1073    case IDC_DEV_TOOLS_CONSOLE:
1074      DevToolsWindow::ToggleDevToolsWindow(
1075          tab_contents_->web_contents()->GetRenderViewHost(),
1076          false,
1077          DEVTOOLS_TOGGLE_ACTION_SHOW_CONSOLE);
1078      break;
1079    case IDC_DEV_TOOLS_INSPECT:
1080      DevToolsWindow::ToggleDevToolsWindow(
1081          tab_contents_->web_contents()->GetRenderViewHost(),
1082          false,
1083          DEVTOOLS_TOGGLE_ACTION_INSPECT);
1084      break;
1085    case IDC_DEV_TOOLS_TOGGLE:
1086      DevToolsWindow::ToggleDevToolsWindow(
1087          tab_contents_->web_contents()->GetRenderViewHost(),
1088          false,
1089          DEVTOOLS_TOGGLE_ACTION_TOGGLE);
1090      break;
1091    default:
1092      NOTREACHED() << "Unsupported accelerator: " << command_id;
1093      return false;
1094  }
1095  return true;
1096}
1097
1098bool ExternalTabContainerWin::CanHandleAccelerators() const {
1099  return true;
1100}
1101
1102void ExternalTabContainerWin::Navigate(const GURL& url, const GURL& referrer) {
1103  if (!tab_contents_.get()) {
1104    NOTREACHED();
1105    return;
1106  }
1107
1108  TRACE_EVENT_BEGIN_ETW("ExternalTabContainerWin::Navigate", 0, url.spec());
1109
1110  tab_contents_->web_contents()->GetController().LoadURL(
1111      url, content::Referrer(referrer, WebKit::WebReferrerPolicyDefault),
1112      content::PAGE_TRANSITION_AUTO_TOPLEVEL, std::string());
1113}
1114
1115bool ExternalTabContainerWin::OnGoToEntryOffset(int offset) {
1116  if (load_requests_via_automation_) {
1117    if (automation_) {
1118      automation_->Send(new AutomationMsg_RequestGoToHistoryEntryOffset(
1119          tab_handle_, offset));
1120    }
1121    return false;
1122  }
1123
1124  return true;
1125}
1126
1127void ExternalTabContainerWin::LoadAccelerators() {
1128  HACCEL accelerator_table = AtlLoadAccelerators(IDR_CHROMEFRAME);
1129  DCHECK(accelerator_table);
1130
1131  // We have to copy the table to access its contents.
1132  int count = CopyAcceleratorTable(accelerator_table, 0, 0);
1133  if (count == 0) {
1134    // Nothing to do in that case.
1135    return;
1136  }
1137
1138  scoped_array<ACCEL> scoped_accelerators(new ACCEL[count]);
1139  ACCEL* accelerators = scoped_accelerators.get();
1140  DCHECK(accelerators != NULL);
1141
1142  CopyAcceleratorTable(accelerator_table, accelerators, count);
1143
1144  focus_manager_ = GetWidget()->GetFocusManager();
1145  DCHECK(focus_manager_);
1146
1147  // Let's fill our own accelerator table.
1148  for (int i = 0; i < count; ++i) {
1149    ui::Accelerator accelerator(
1150        static_cast<ui::KeyboardCode>(accelerators[i].key),
1151        ui::GetModifiersFromACCEL(accelerators[i]));
1152    accelerator_table_[accelerator] = accelerators[i].cmd;
1153
1154    // Also register with the focus manager.
1155    if (focus_manager_) {
1156      focus_manager_->RegisterAccelerator(
1157          accelerator, ui::AcceleratorManager::kNormalPriority, this);
1158    }
1159  }
1160}
1161
1162void ExternalTabContainerWin::OnReinitialize() {
1163  if (load_requests_via_automation_) {
1164    RenderViewHost* rvh = tab_contents_->web_contents()->GetRenderViewHost();
1165    if (rvh) {
1166      AutomationResourceMessageFilter::ResumePendingRenderView(
1167          rvh->GetProcess()->GetID(), rvh->GetRoutingID(),
1168          tab_handle_, automation_resource_message_filter_);
1169    }
1170  }
1171
1172  NavigationStateChanged(web_contents(), 0);
1173  ServicePendingOpenURLRequests();
1174}
1175
1176void ExternalTabContainerWin::ServicePendingOpenURLRequests() {
1177  DCHECK(pending());
1178
1179  set_pending(false);
1180
1181  for (size_t index = 0; index < pending_open_url_requests_.size();
1182       ++index) {
1183    const OpenURLParams& url_request = pending_open_url_requests_[index];
1184    OpenURLFromTab(web_contents(), url_request);
1185  }
1186  pending_open_url_requests_.clear();
1187}
1188
1189void ExternalTabContainerWin::SetupExternalTabView() {
1190  // Create a TabContentsContainer to handle focus cycling using Tab and
1191  // Shift-Tab.
1192  tab_contents_container_ = new views::WebView(tab_contents_->profile());
1193
1194  // The views created here will be destroyed when the ExternalTabContainer
1195  // widget is torn down.
1196  external_tab_view_ = new views::View();
1197
1198  InfoBarContainerView* info_bar_container =
1199      new InfoBarContainerView(this, NULL);
1200  InfoBarTabHelper* infobar_tab_helper =
1201      InfoBarTabHelper::FromWebContents(tab_contents_->web_contents());
1202  info_bar_container->ChangeTabContents(infobar_tab_helper);
1203
1204  views::GridLayout* layout = new views::GridLayout(external_tab_view_);
1205  // Give this column an identifier of 0.
1206  views::ColumnSet* columns = layout->AddColumnSet(0);
1207  columns->AddColumn(views::GridLayout::FILL,
1208                     views::GridLayout::FILL,
1209                     1,
1210                     views::GridLayout::USE_PREF,
1211                     0,
1212                     0);
1213
1214  external_tab_view_->SetLayoutManager(layout);
1215
1216  layout->StartRow(0, 0);
1217  layout->AddView(info_bar_container);
1218  layout->StartRow(1, 0);
1219  layout->AddView(tab_contents_container_);
1220  GetWidget()->SetContentsView(external_tab_view_);
1221  // Note that SetTabContents must be called after AddChildView is called
1222  tab_contents_container_->SetWebContents(web_contents());
1223}
1224
1225// static
1226ExternalTabContainer* ExternalTabContainer::Create(
1227    AutomationProvider* automation_provider,
1228    AutomationResourceMessageFilter* filter) {
1229  return new ExternalTabContainerWin(automation_provider, filter);
1230}
1231
1232// static
1233ExternalTabContainer* ExternalTabContainer::GetContainerForTab(
1234    HWND tab_window) {
1235  HWND parent_window = ::GetParent(tab_window);
1236  if (!::IsWindow(parent_window)) {
1237    return NULL;
1238  }
1239  if (!ExternalTabContainerWin::IsExternalTabContainer(parent_window)) {
1240    return NULL;
1241  }
1242  ExternalTabContainer* container = reinterpret_cast<ExternalTabContainer*>(
1243      ViewProp::GetValue(parent_window, kWindowObjectKey));
1244  return container;
1245}
1246
1247// static
1248scoped_refptr<ExternalTabContainer> ExternalTabContainer::RemovePendingTab(
1249    uintptr_t cookie) {
1250  return ExternalTabContainerWin::RemovePendingExternalTab(cookie);
1251}
1252
1253///////////////////////////////////////////////////////////////////////////////
1254// TemporaryPopupExternalTabContainerWin
1255
1256TemporaryPopupExternalTabContainerWin::TemporaryPopupExternalTabContainerWin(
1257    AutomationProvider* automation,
1258    AutomationResourceMessageFilter* filter)
1259    : ExternalTabContainerWin(automation, filter) {
1260}
1261
1262TemporaryPopupExternalTabContainerWin::~TemporaryPopupExternalTabContainerWin(
1263    ) {
1264  DVLOG(1) << __FUNCTION__;
1265}
1266
1267WebContents* TemporaryPopupExternalTabContainerWin::OpenURLFromTab(
1268    WebContents* source,
1269    const OpenURLParams& params) {
1270  if (!automation_)
1271    return NULL;
1272
1273  OpenURLParams forward_params = params;
1274  if (params.disposition == CURRENT_TAB) {
1275    DCHECK(route_all_top_level_navigations_);
1276    forward_params.disposition = NEW_FOREGROUND_TAB;
1277  }
1278  WebContents* new_contents =
1279      ExternalTabContainerWin::OpenURLFromTab(source, forward_params);
1280  // support only one navigation for a dummy tab before it is killed.
1281  ::DestroyWindow(GetNativeView());
1282  return new_contents;
1283}
1284