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