browser_view.cc revision 8bcbed890bc3ce4d7a057a8f32cab53fa534672e
1// Copyright 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/frame/browser_view.h"
6
7#include <algorithm>
8
9#include "base/auto_reset.h"
10#include "base/command_line.h"
11#include "base/i18n/rtl.h"
12#include "base/memory/scoped_ptr.h"
13#include "base/metrics/histogram.h"
14#include "base/prefs/pref_service.h"
15#include "base/strings/string_number_conversions.h"
16#include "base/strings/utf_string_conversions.h"
17#include "chrome/app/chrome_command_ids.h"
18#include "chrome/app/chrome_dll_resource.h"
19#include "chrome/browser/app_mode/app_mode_utils.h"
20#include "chrome/browser/bookmarks/bookmark_stats.h"
21#include "chrome/browser/browser_process.h"
22#include "chrome/browser/chrome_notification_types.h"
23#include "chrome/browser/extensions/tab_helper.h"
24#include "chrome/browser/infobars/infobar_service.h"
25#include "chrome/browser/native_window_notification_source.h"
26#include "chrome/browser/password_manager/password_manager.h"
27#include "chrome/browser/profiles/avatar_menu.h"
28#include "chrome/browser/profiles/profile.h"
29#include "chrome/browser/profiles/profile_info_cache.h"
30#include "chrome/browser/profiles/profile_manager.h"
31#include "chrome/browser/search/search.h"
32#include "chrome/browser/sessions/tab_restore_service.h"
33#include "chrome/browser/sessions/tab_restore_service_factory.h"
34#include "chrome/browser/speech/tts_controller.h"
35#include "chrome/browser/themes/theme_properties.h"
36#include "chrome/browser/themes/theme_service_factory.h"
37#include "chrome/browser/ui/app_modal_dialogs/app_modal_dialog.h"
38#include "chrome/browser/ui/app_modal_dialogs/app_modal_dialog_queue.h"
39#include "chrome/browser/ui/bookmarks/bookmark_bar_constants.h"
40#include "chrome/browser/ui/bookmarks/bookmark_bubble_delegate.h"
41#include "chrome/browser/ui/bookmarks/bookmark_bubble_sign_in_delegate.h"
42#include "chrome/browser/ui/browser.h"
43#include "chrome/browser/ui/browser_command_controller.h"
44#include "chrome/browser/ui/browser_commands.h"
45#include "chrome/browser/ui/browser_dialogs.h"
46#include "chrome/browser/ui/browser_finder.h"
47#include "chrome/browser/ui/browser_list.h"
48#include "chrome/browser/ui/browser_window_state.h"
49#include "chrome/browser/ui/immersive_fullscreen_configuration.h"
50#include "chrome/browser/ui/ntp_background_util.h"
51#include "chrome/browser/ui/omnibox/omnibox_popup_model.h"
52#include "chrome/browser/ui/omnibox/omnibox_popup_view.h"
53#include "chrome/browser/ui/omnibox/omnibox_view.h"
54#include "chrome/browser/ui/search/search_delegate.h"
55#include "chrome/browser/ui/search/search_model.h"
56#include "chrome/browser/ui/search/search_ui.h"
57#include "chrome/browser/ui/tabs/tab_menu_model.h"
58#include "chrome/browser/ui/tabs/tab_strip_model.h"
59#include "chrome/browser/ui/view_ids.h"
60#include "chrome/browser/ui/views/accelerator_table.h"
61#include "chrome/browser/ui/views/accessibility/invert_bubble_view.h"
62#include "chrome/browser/ui/views/avatar_menu_bubble_view.h"
63#include "chrome/browser/ui/views/avatar_menu_button.h"
64#include "chrome/browser/ui/views/bookmarks/bookmark_bar_view.h"
65#include "chrome/browser/ui/views/bookmarks/bookmark_bubble_view.h"
66#include "chrome/browser/ui/views/browser_dialogs.h"
67#include "chrome/browser/ui/views/download/download_in_progress_dialog_view.h"
68#include "chrome/browser/ui/views/download/download_shelf_view.h"
69#include "chrome/browser/ui/views/frame/browser_view_layout.h"
70#include "chrome/browser/ui/views/frame/browser_view_layout_delegate.h"
71#include "chrome/browser/ui/views/frame/contents_container.h"
72#include "chrome/browser/ui/views/frame/immersive_mode_controller.h"
73#include "chrome/browser/ui/views/frame/top_container_view.h"
74#include "chrome/browser/ui/views/fullscreen_exit_bubble_views.h"
75#include "chrome/browser/ui/views/infobars/infobar_container_view.h"
76#include "chrome/browser/ui/views/location_bar/location_bar_view.h"
77#include "chrome/browser/ui/views/location_bar/location_icon_view.h"
78#include "chrome/browser/ui/views/omnibox/omnibox_view_views.h"
79#include "chrome/browser/ui/views/omnibox/omnibox_views.h"
80#include "chrome/browser/ui/views/password_generation_bubble_view.h"
81#include "chrome/browser/ui/views/status_bubble_views.h"
82#include "chrome/browser/ui/views/tabs/browser_tab_strip_controller.h"
83#include "chrome/browser/ui/views/tabs/tab.h"
84#include "chrome/browser/ui/views/tabs/tab_strip.h"
85#include "chrome/browser/ui/views/toolbar_view.h"
86#include "chrome/browser/ui/views/update_recommended_message_box.h"
87#include "chrome/browser/ui/views/website_settings/website_settings_popup_view.h"
88#include "chrome/browser/ui/window_sizer/window_sizer.h"
89#include "chrome/common/chrome_switches.h"
90#include "chrome/common/pref_names.h"
91#include "chrome/common/url_constants.h"
92#include "content/public/browser/download_manager.h"
93#include "content/public/browser/native_web_keyboard_event.h"
94#include "content/public/browser/notification_service.h"
95#include "content/public/browser/render_view_host.h"
96#include "content/public/browser/render_widget_host.h"
97#include "content/public/browser/user_metrics.h"
98#include "content/public/browser/web_contents.h"
99#include "content/public/browser/web_contents_view.h"
100#include "content/public/common/content_switches.h"
101#include "grit/chromium_strings.h"
102#include "grit/generated_resources.h"
103#include "grit/locale_settings.h"
104#include "grit/theme_resources.h"
105#include "grit/ui_resources.h"
106#include "grit/ui_strings.h"
107#include "grit/webkit_resources.h"
108#include "ui/base/accelerators/accelerator.h"
109#include "ui/base/accessibility/accessible_view_state.h"
110#include "ui/base/hit_test.h"
111#include "ui/base/l10n/l10n_util.h"
112#include "ui/base/resource/resource_bundle.h"
113#include "ui/base/theme_provider.h"
114#include "ui/events/event_utils.h"
115#include "ui/gfx/canvas.h"
116#include "ui/gfx/color_utils.h"
117#include "ui/gfx/sys_color_change_listener.h"
118#include "ui/views/controls/button/menu_button.h"
119#include "ui/views/controls/single_split_view.h"
120#include "ui/views/controls/textfield/textfield.h"
121#include "ui/views/controls/webview/webview.h"
122#include "ui/views/focus/external_focus_tracker.h"
123#include "ui/views/focus/view_storage.h"
124#include "ui/views/layout/grid_layout.h"
125#include "ui/views/widget/native_widget.h"
126#include "ui/views/widget/root_view.h"
127#include "ui/views/widget/widget.h"
128#include "ui/views/window/dialog_delegate.h"
129
130#if defined(USE_ASH)
131#include "ash/launcher/launcher.h"
132#include "ash/launcher/launcher_model.h"
133#include "ash/shell.h"
134#include "chrome/browser/ui/ash/ash_util.h"
135#endif
136
137#if defined(USE_AURA)
138#include "ui/aura/window.h"
139#include "ui/gfx/screen.h"
140#elif defined(OS_WIN)  // !defined(USE_AURA)
141#include "chrome/browser/jumplist_win.h"
142#include "chrome/browser/ui/views/omnibox/omnibox_view_win.h"
143#include "ui/views/widget/native_widget_win.h"
144#include "ui/views/win/scoped_fullscreen_visibility.h"
145#endif
146
147#if defined(OS_WIN)
148#include "base/win/windows_version.h"
149#include "win8/util/win8_util.h"
150#endif
151
152#if defined(ENABLE_ONE_CLICK_SIGNIN)
153#include "chrome/browser/ui/sync/one_click_signin_bubble_delegate.h"
154#include "chrome/browser/ui/sync/one_click_signin_bubble_links_delegate.h"
155#include "chrome/browser/ui/views/sync/one_click_signin_bubble_view.h"
156#endif
157
158using base::TimeDelta;
159using content::NativeWebKeyboardEvent;
160using content::SSLStatus;
161using content::UserMetricsAction;
162using content::WebContents;
163using views::ColumnSet;
164using views::GridLayout;
165using web_modal::WebContentsModalDialogHost;
166
167namespace {
168// The name of a key to store on the window handle so that other code can
169// locate this object using just the handle.
170const char* const kBrowserViewKey = "__BROWSER_VIEW__";
171
172// The number of milliseconds between loading animation frames.
173const int kLoadingAnimationFrameTimeMs = 30;
174
175// TODO(kuan): These functions are temporarily for the bookmark bar while its
176// detached state is at the top of the page;  it'll be moved to float on the
177// content page in the very near future, at which time, these local functions
178// will be removed.
179void PaintDetachedBookmarkBar(gfx::Canvas* canvas,
180                              DetachableToolbarView* view,
181                              ThemeService* theme_service) {
182  // Paint background for detached state; if animating, this is fade in/out.
183  canvas->DrawColor(
184      chrome::GetDetachedBookmarkBarBackgroundColor(theme_service));
185  // Draw the separators above and below bookmark bar;
186  // if animating, these are fading in/out.
187  SkColor separator_color =
188      chrome::GetDetachedBookmarkBarSeparatorColor(theme_service);
189  DetachableToolbarView::PaintHorizontalBorder(canvas, view, true,
190                                               separator_color);
191  // The bottom border needs to be 1-px thick in both regular and retina
192  // displays, so we can't use DetachableToolbarView::PaintHorizontalBorder
193  // which paints a 2-px thick border in retina display.
194  SkPaint paint;
195  paint.setAntiAlias(false);
196  // Sets border to 1-px thick regardless of scale factor.
197  paint.setStrokeWidth(0);
198  // Bottom border is at 50% opacity of top border.
199  paint.setColor(SkColorSetA(separator_color,
200                             SkColorGetA(separator_color) / 2));
201  // Calculate thickness of bottom border as per current scale factor to
202  // determine where to draw the 1-px thick border.
203  float thickness = views::NonClientFrameView::kClientEdgeThickness /
204                    canvas->image_scale();
205  SkScalar y = SkIntToScalar(view->height()) - SkFloatToScalar(thickness);
206  canvas->sk_canvas()->drawLine(SkIntToScalar(0), y,
207                                SkIntToScalar(view->width()), y, paint);
208}
209
210void PaintAttachedBookmarkBar(gfx::Canvas* canvas,
211                              DetachableToolbarView* view,
212                              BrowserView* browser_view,
213                              chrome::HostDesktopType host_desktop_type,
214                              int toolbar_overlap) {
215  // Paint background for attached state, this is fade in/out.
216  gfx::Point background_image_offset =
217      browser_view->OffsetPointForToolbarBackgroundImage(
218          gfx::Point(view->GetMirroredX(), view->y()));
219  DetachableToolbarView::PaintBackgroundAttachedMode(canvas,
220      view->GetThemeProvider(), view->GetLocalBounds(),
221      background_image_offset, host_desktop_type);
222  if (view->height() >= toolbar_overlap) {
223    // Draw the separator below bookmark bar; this is fading in/out.
224    DetachableToolbarView::PaintHorizontalBorder(canvas, view, false,
225        ThemeProperties::GetDefaultColor(
226            ThemeProperties::COLOR_TOOLBAR_SEPARATOR));
227  }
228}
229
230}  // namespace
231
232// static
233const char BrowserView::kViewClassName[] = "BrowserView";
234
235namespace {
236
237bool ShouldSaveOrRestoreWindowPos() {
238#if defined(OS_WIN)
239  // In Windows 8's single window Metro mode the window is always maximized
240  // (without the WS_MAXIMIZE style).
241  if (win8::IsSingleWindowMetroMode())
242    return false;
243#endif
244  return true;
245}
246
247}  // namespace
248
249///////////////////////////////////////////////////////////////////////////////
250
251// Delegate implementation for BrowserViewLayout. Usually just forwards calls
252// into BrowserView.
253class BrowserViewLayoutDelegateImpl : public BrowserViewLayoutDelegate {
254 public:
255  explicit BrowserViewLayoutDelegateImpl(BrowserView* browser_view)
256      : browser_view_(browser_view) {}
257  virtual ~BrowserViewLayoutDelegateImpl() {}
258
259  // BrowserViewLayoutDelegate overrides:
260  virtual views::View* GetWindowSwitcherButton() const OVERRIDE {
261    return browser_view_->window_switcher_button();
262  }
263
264  virtual bool DownloadShelfNeedsLayout() const OVERRIDE {
265    DownloadShelfView* download_shelf = browser_view_->download_shelf_.get();
266    // Re-layout the shelf either if it is visible or if its close animation
267    // is currently running.
268    return download_shelf &&
269           (download_shelf->IsShowing() || download_shelf->IsClosing());
270  }
271
272  virtual bool IsTabStripVisible() const OVERRIDE {
273    return browser_view_->IsTabStripVisible();
274  }
275
276  virtual gfx::Rect GetBoundsForTabStrip(
277      views::View* tab_strip) const OVERRIDE {
278    return browser_view_->frame()->GetBoundsForTabStrip(tab_strip);
279  }
280
281  virtual bool IsToolbarVisible() const OVERRIDE {
282    return browser_view_->IsToolbarVisible();
283  }
284
285  virtual bool IsBookmarkBarVisible() const OVERRIDE {
286    return browser_view_->IsBookmarkBarVisible();
287  }
288
289  virtual FullscreenExitBubbleViews* GetFullscreenExitBubble() const OVERRIDE {
290    return browser_view_->fullscreen_exit_bubble();
291  }
292
293 private:
294  BrowserView* browser_view_;
295
296  DISALLOW_COPY_AND_ASSIGN(BrowserViewLayoutDelegateImpl);
297};
298
299///////////////////////////////////////////////////////////////////////////////
300// BookmarkExtensionBackground, private:
301// This object serves as the views::Background object which is used to layout
302// and paint the bookmark bar.
303class BookmarkExtensionBackground : public views::Background {
304 public:
305  BookmarkExtensionBackground(BrowserView* browser_view,
306                              DetachableToolbarView* host_view,
307                              Browser* browser);
308
309  // View methods overridden from views:Background.
310  virtual void Paint(gfx::Canvas* canvas, views::View* view) const OVERRIDE;
311
312 private:
313  BrowserView* browser_view_;
314
315  // The view hosting this background.
316  DetachableToolbarView* host_view_;
317
318  Browser* browser_;
319
320  DISALLOW_COPY_AND_ASSIGN(BookmarkExtensionBackground);
321};
322
323BookmarkExtensionBackground::BookmarkExtensionBackground(
324    BrowserView* browser_view,
325    DetachableToolbarView* host_view,
326    Browser* browser)
327    : browser_view_(browser_view),
328      host_view_(host_view),
329      browser_(browser) {
330}
331
332void BookmarkExtensionBackground::Paint(gfx::Canvas* canvas,
333                                        views::View* view) const {
334  int toolbar_overlap = host_view_->GetToolbarOverlap();
335  if (!host_view_->IsDetached()) {
336    PaintAttachedBookmarkBar(canvas, host_view_, browser_view_,
337                             browser_->host_desktop_type(), toolbar_overlap);
338    return;
339  }
340
341  // As 'hidden' according to the animation is the full in-tab state, we invert
342  // the value - when current_state is at '0', we expect the bar to be docked.
343  double current_state = 1 - host_view_->GetAnimationValue();
344
345  ThemeService* ts =
346      ThemeServiceFactory::GetForProfile(browser_->profile());
347  if (current_state == 0.0 || current_state == 1.0) {
348    PaintDetachedBookmarkBar(canvas, host_view_, ts);
349    return;
350  }
351  // While animating, set opacity to cross-fade between attached and detached
352  // backgrounds including their respective separators.
353  int detached_alpha = static_cast<uint8>(current_state * 255);
354  int attached_alpha = 255 - detached_alpha;
355  if (browser_->bookmark_bar_state() == BookmarkBar::DETACHED) {
356    // To animate from attached to detached state:
357    // - fade out attached background
358    // - fade in detached background.
359    canvas->SaveLayerAlpha(attached_alpha);
360    PaintAttachedBookmarkBar(canvas, host_view_, browser_view_,
361                             browser_->host_desktop_type(),
362                             toolbar_overlap);
363    canvas->Restore();
364    canvas->SaveLayerAlpha(detached_alpha);
365    PaintDetachedBookmarkBar(canvas, host_view_, ts);
366  } else {
367    // To animate from detached to attached state:
368    // - fade out detached background
369    // - fade in attached background.
370    canvas->SaveLayerAlpha(detached_alpha);
371    PaintDetachedBookmarkBar(canvas, host_view_, ts);
372    canvas->Restore();
373    canvas->SaveLayerAlpha(attached_alpha);
374    PaintAttachedBookmarkBar(canvas, host_view_, browser_view_,
375                             browser_->host_desktop_type(),
376                             toolbar_overlap);
377  }
378  canvas->Restore();
379}
380
381///////////////////////////////////////////////////////////////////////////////
382// BrowserView, public:
383
384BrowserView::BrowserView()
385    : views::ClientView(NULL, NULL),
386      last_focused_view_storage_id_(
387          views::ViewStorage::GetInstance()->CreateStorageID()),
388      frame_(NULL),
389      top_container_(NULL),
390      tabstrip_(NULL),
391      toolbar_(NULL),
392      window_switcher_button_(NULL),
393      find_bar_host_view_(NULL),
394      infobar_container_(NULL),
395      contents_web_view_(NULL),
396      devtools_container_(NULL),
397      contents_container_(NULL),
398      contents_split_(NULL),
399      devtools_dock_side_(DEVTOOLS_DOCK_SIDE_BOTTOM),
400      devtools_window_(NULL),
401      initialized_(false),
402      ignore_layout_(true),
403#if defined(OS_WIN) && !defined(USE_AURA)
404      hung_window_detector_(&hung_plugin_action_),
405      ticker_(0),
406#endif
407      force_location_bar_focus_(false),
408      immersive_mode_controller_(chrome::CreateImmersiveModeController()),
409#if defined(OS_CHROMEOS)
410      scroll_end_effect_controller_(ScrollEndEffectController::Create()),
411#endif
412      color_change_listener_(this),
413      activate_modal_dialog_factory_(this) {
414}
415
416BrowserView::~BrowserView() {
417  // Immersive mode may need to reparent views before they are removed/deleted.
418  immersive_mode_controller_.reset();
419
420  browser_->tab_strip_model()->RemoveObserver(this);
421
422#if defined(OS_WIN) && !defined(USE_AURA)
423  // Stop hung plugin monitoring.
424  ticker_.Stop();
425  ticker_.UnregisterTickHandler(&hung_window_detector_);
426
427  // Terminate the jumplist (must be called before browser_->profile() is
428  // destroyed.
429  if (jumplist_) {
430    jumplist_->Terminate();
431  }
432#endif
433
434  // We destroy the download shelf before |browser_| to remove its child
435  // download views from the set of download observers (since the observed
436  // downloads can be destroyed along with |browser_| and the observer
437  // notifications will call back into deleted objects).
438  BrowserViewLayout* browser_view_layout = GetBrowserViewLayout();
439  if (browser_view_layout)
440    browser_view_layout->set_download_shelf(NULL);
441  download_shelf_.reset();
442
443  // The TabStrip attaches a listener to the model. Make sure we shut down the
444  // TabStrip first so that it can cleanly remove the listener.
445  if (tabstrip_) {
446    tabstrip_->parent()->RemoveChildView(tabstrip_);
447    if (browser_view_layout)
448      browser_view_layout->set_tab_strip(NULL);
449    delete tabstrip_;
450    tabstrip_ = NULL;
451  }
452  // Child views maintain PrefMember attributes that point to
453  // OffTheRecordProfile's PrefService which gets deleted by ~Browser.
454  RemoveAllChildViews(true);
455  toolbar_ = NULL;
456
457  // It is possible that we were forced-closed by the native view system and
458  // that tabs remain in the browser. Close any such remaining tabs. Detach
459  // before destroying in hopes of avoiding less callbacks trying to access
460  // members since destroyed.
461  {
462    ScopedVector<content::WebContents> contents;
463    while (browser_->tab_strip_model()->count())
464      contents.push_back(browser_->tab_strip_model()->DetachWebContentsAt(0));
465  }
466
467  // Explicitly set browser_ to NULL.
468  browser_.reset();
469}
470
471void BrowserView::Init(Browser* browser) {
472  browser_.reset(browser);
473  browser_->tab_strip_model()->AddObserver(this);
474}
475
476// static
477BrowserView* BrowserView::GetBrowserViewForNativeWindow(
478    gfx::NativeWindow window) {
479  views::Widget* widget = views::Widget::GetWidgetForNativeWindow(window);
480  return widget ?
481      reinterpret_cast<BrowserView*>(widget->GetNativeWindowProperty(
482          kBrowserViewKey)) : NULL;
483}
484
485// static
486BrowserView* BrowserView::GetBrowserViewForBrowser(const Browser* browser) {
487  return static_cast<BrowserView*>(browser->window());
488}
489
490void BrowserView::InitStatusBubble() {
491  status_bubble_.reset(new StatusBubbleViews(contents_container_));
492}
493
494gfx::Rect BrowserView::GetToolbarBounds() const {
495  gfx::Rect toolbar_bounds(toolbar_->bounds());
496  if (toolbar_bounds.IsEmpty())
497    return toolbar_bounds;
498  // The apparent toolbar edges are outside the "real" toolbar edges.
499  toolbar_bounds.Inset(-views::NonClientFrameView::kClientEdgeThickness, 0);
500  return toolbar_bounds;
501}
502
503gfx::Rect BrowserView::GetClientAreaBounds() const {
504  gfx::Rect container_bounds = contents_container_->bounds();
505  gfx::Point container_origin = container_bounds.origin();
506  ConvertPointToTarget(this, parent(), &container_origin);
507  container_bounds.set_origin(container_origin);
508  return container_bounds;
509}
510
511gfx::Rect BrowserView::GetFindBarBoundingBox() const {
512  return GetBrowserViewLayout()->GetFindBarBoundingBox();
513}
514
515int BrowserView::GetTabStripHeight() const {
516  // We want to return tabstrip_->height(), but we might be called in the midst
517  // of layout, when that hasn't yet been updated to reflect the current state.
518  // So return what the tabstrip height _ought_ to be right now.
519  return IsTabStripVisible() ? tabstrip_->GetPreferredSize().height() : 0;
520}
521
522gfx::Point BrowserView::OffsetPointForToolbarBackgroundImage(
523    const gfx::Point& point) const {
524  // The background image starts tiling horizontally at the window left edge and
525  // vertically at the top edge of the horizontal tab strip (or where it would
526  // be).  We expect our parent's origin to be the window origin.
527  gfx::Point window_point(point + GetMirroredPosition().OffsetFromOrigin());
528  window_point.Offset(frame_->GetThemeBackgroundXInset(),
529                      -frame_->GetTabStripInsets(false).top);
530  return window_point;
531}
532
533bool BrowserView::IsTabStripVisible() const {
534  if (immersive_mode_controller_->ShouldHideTopViews() &&
535      immersive_mode_controller_->ShouldHideTabIndicators())
536    return false;
537  return browser_->SupportsWindowFeature(Browser::FEATURE_TABSTRIP);
538}
539
540bool BrowserView::IsOffTheRecord() const {
541  return browser_->profile()->IsOffTheRecord();
542}
543
544bool BrowserView::IsGuestSession() const {
545  return browser_->profile()->IsGuestSession();
546}
547
548bool BrowserView::IsRegularOrGuestSession() const {
549  Profile* profile = browser_->profile();
550  return (profile->IsGuestSession() || !profile->IsOffTheRecord());
551}
552
553int BrowserView::GetOTRIconResourceID() const {
554  int otr_resource_id = IDR_OTR_ICON;
555  if (ui::GetDisplayLayout() == ui::LAYOUT_TOUCH) {
556    if (IsFullscreen())
557      otr_resource_id = IDR_OTR_ICON_FULLSCREEN;
558#if defined(OS_WIN)
559    if (win8::IsSingleWindowMetroMode())
560      otr_resource_id = IDR_OTR_ICON_FULLSCREEN;
561#endif
562  }
563
564  return otr_resource_id;
565}
566
567int BrowserView::GetGuestIconResourceID() const {
568  return IDR_GUEST_ICON;
569}
570
571bool BrowserView::ShouldShowAvatar() const {
572  if (!IsBrowserTypeNormal())
573    return false;
574#if defined(OS_CHROMEOS)
575  if (IsOffTheRecord() && !IsGuestSession())
576    return true;
577#else
578  if (IsOffTheRecord())  // Desktop guest is incognito and needs avatar.
579    return true;
580#endif
581  // Tests may not have a profile manager.
582  if (!g_browser_process->profile_manager())
583    return false;
584  ProfileInfoCache& cache =
585      g_browser_process->profile_manager()->GetProfileInfoCache();
586  if (cache.GetIndexOfProfileWithPath(browser_->profile()->GetPath()) ==
587      std::string::npos) {
588    return false;
589  }
590
591  return AvatarMenu::ShouldShowAvatarMenu();
592}
593
594bool BrowserView::GetAccelerator(int cmd_id, ui::Accelerator* accelerator) {
595  // We retrieve the accelerator information for standard accelerators
596  // for cut, copy and paste.
597  if (chrome::GetStandardAcceleratorForCommandId(cmd_id, accelerator))
598    return true;
599  // Else, we retrieve the accelerator information from the accelerator table.
600  for (std::map<ui::Accelerator, int>::const_iterator it =
601           accelerator_table_.begin(); it != accelerator_table_.end(); ++it) {
602    if (it->second == cmd_id) {
603      *accelerator = it->first;
604      return true;
605    }
606  }
607  // Else, we retrieve the accelerator information from Ash (if applicable).
608  return chrome::GetAshAcceleratorForCommandId(
609      cmd_id, browser_->host_desktop_type(), accelerator);
610}
611
612bool BrowserView::IsAcceleratorRegistered(const ui::Accelerator& accelerator) {
613  return accelerator_table_.find(accelerator) != accelerator_table_.end();
614}
615
616WebContents* BrowserView::GetActiveWebContents() const {
617  return browser_->tab_strip_model()->GetActiveWebContents();
618}
619
620gfx::ImageSkia BrowserView::GetOTRAvatarIcon() const {
621  return *GetThemeProvider()->GetImageSkiaNamed(GetOTRIconResourceID());
622}
623
624///////////////////////////////////////////////////////////////////////////////
625// BrowserView, BrowserWindow implementation:
626
627void BrowserView::Show() {
628  // If the window is already visible, just activate it.
629  if (frame_->IsVisible()) {
630    frame_->Activate();
631    return;
632  }
633
634  // Showing the window doesn't make the browser window active right away.
635  // This can cause SetFocusToLocationBar() to skip setting focus to the
636  // location bar. To avoid this we explicilty let SetFocusToLocationBar()
637  // know that it's ok to steal focus.
638  force_location_bar_focus_ = true;
639
640  // Setting the focus doesn't work when the window is invisible, so any focus
641  // initialization that happened before this will be lost.
642  //
643  // We really "should" restore the focus whenever the window becomes unhidden,
644  // but I think initializing is the only time where this can happen where
645  // there is some focus change we need to pick up, and this is easier than
646  // plumbing through an un-hide message all the way from the frame.
647  //
648  // If we do find there are cases where we need to restore the focus on show,
649  // that should be added and this should be removed.
650  RestoreFocus();
651
652  frame_->Show();
653
654  force_location_bar_focus_ = false;
655
656  browser()->OnWindowDidShow();
657
658  chrome::MaybeShowInvertBubbleView(browser_.get(), contents_container_);
659}
660
661void BrowserView::ShowInactive() {
662  if (frame_->IsVisible())
663    return;
664  frame_->ShowInactive();
665}
666
667void BrowserView::Hide() {
668  // Not implemented.
669}
670
671void BrowserView::SetBounds(const gfx::Rect& bounds) {
672  ExitFullscreen();
673  GetWidget()->SetBounds(bounds);
674}
675
676void BrowserView::Close() {
677  frame_->Close();
678}
679
680void BrowserView::Activate() {
681  frame_->Activate();
682}
683
684void BrowserView::Deactivate() {
685  frame_->Deactivate();
686}
687
688bool BrowserView::IsActive() const {
689  return frame_->IsActive();
690}
691
692void BrowserView::FlashFrame(bool flash) {
693  frame_->FlashFrame(flash);
694}
695
696bool BrowserView::IsAlwaysOnTop() const {
697  return false;
698}
699
700void BrowserView::SetAlwaysOnTop(bool always_on_top) {
701  // Not implemented for browser windows.
702  NOTIMPLEMENTED();
703}
704
705gfx::NativeWindow BrowserView::GetNativeWindow() {
706  // While the browser destruction is going on, the widget can already be gone,
707  // but utility functions like FindBrowserWithWindow will come here and crash.
708  // We short circuit therefore.
709  if (!GetWidget())
710    return NULL;
711  return GetWidget()->GetTopLevelWidget()->GetNativeWindow();
712}
713
714BrowserWindowTesting* BrowserView::GetBrowserWindowTesting() {
715  return this;
716}
717
718StatusBubble* BrowserView::GetStatusBubble() {
719  return status_bubble_.get();
720}
721
722namespace {
723  // Only used by ToolbarSizeChanged() below, but placed here because template
724  // arguments (to base::AutoReset<>) must have external linkage.
725  enum CallState { NORMAL, REENTRANT, REENTRANT_FORCE_FAST_RESIZE };
726}
727
728void BrowserView::UpdateTitleBar() {
729  frame_->UpdateWindowTitle();
730  if (ShouldShowWindowIcon() && !loading_animation_timer_.IsRunning())
731    frame_->UpdateWindowIcon();
732}
733
734void BrowserView::BookmarkBarStateChanged(
735    BookmarkBar::AnimateChangeType change_type) {
736  if (bookmark_bar_view_.get()) {
737    BookmarkBar::State new_state = browser_->bookmark_bar_state();
738
739    // We don't properly support animating the bookmark bar to and from the
740    // detached state in immersive fullscreen.
741    bool detached_changed = (new_state == BookmarkBar::DETACHED) ||
742        bookmark_bar_view_->IsDetached();
743    if (detached_changed && immersive_mode_controller_->IsEnabled())
744      change_type = BookmarkBar::DONT_ANIMATE_STATE_CHANGE;
745
746    bookmark_bar_view_->SetBookmarkBarState(new_state, change_type);
747  }
748  if (MaybeShowBookmarkBar(GetActiveWebContents()))
749    Layout();
750}
751
752void BrowserView::UpdateDevTools() {
753  UpdateDevToolsForContents(GetActiveWebContents());
754  Layout();
755}
756
757void BrowserView::UpdateLoadingAnimations(bool should_animate) {
758  if (should_animate) {
759    if (!loading_animation_timer_.IsRunning()) {
760      // Loads are happening, and the timer isn't running, so start it.
761      last_animation_time_ = base::TimeTicks::Now();
762      loading_animation_timer_.Start(FROM_HERE,
763          TimeDelta::FromMilliseconds(kLoadingAnimationFrameTimeMs), this,
764          &BrowserView::LoadingAnimationCallback);
765    }
766  } else {
767    if (loading_animation_timer_.IsRunning()) {
768      last_animation_time_ = base::TimeTicks();
769      loading_animation_timer_.Stop();
770      // Loads are now complete, update the state if a task was scheduled.
771      LoadingAnimationCallback();
772    }
773  }
774}
775
776void BrowserView::SetStarredState(bool is_starred) {
777  GetLocationBarView()->SetStarToggled(is_starred);
778}
779
780void BrowserView::OnActiveTabChanged(content::WebContents* old_contents,
781                                     content::WebContents* new_contents,
782                                     int index,
783                                     int reason) {
784  DCHECK(new_contents);
785
786  // If |contents_container_| already has the correct WebContents, we can save
787  // some work.  This also prevents extra events from being reported by the
788  // Visibility API under Windows, as ChangeWebContents will briefly hide
789  // the WebContents window.
790  bool change_tab_contents =
791      contents_web_view_->web_contents() != new_contents;
792
793  // Update various elements that are interested in knowing the current
794  // WebContents.
795
796  // When we toggle the NTP floating bookmarks bar and/or the info bar,
797  // we don't want any WebContents to be attached, so that we
798  // avoid an unnecessary resize and re-layout of a WebContents.
799  if (change_tab_contents)
800    contents_web_view_->SetWebContents(NULL);
801  infobar_container_->ChangeInfoBarService(
802      InfoBarService::FromWebContents(new_contents));
803  if (bookmark_bar_view_.get()) {
804    bookmark_bar_view_->SetBookmarkBarState(
805        browser_->bookmark_bar_state(),
806        BookmarkBar::DONT_ANIMATE_STATE_CHANGE);
807  }
808  UpdateUIForContents(new_contents);
809
810  // Layout for DevTools _before_ setting the main WebContents to avoid
811  // toggling the size of the main WebContents.
812  UpdateDevToolsForContents(new_contents);
813
814  if (change_tab_contents)
815    contents_web_view_->SetWebContents(new_contents);
816
817  if (!browser_->tab_strip_model()->closing_all() && GetWidget()->IsActive() &&
818      GetWidget()->IsVisible()) {
819    // We only restore focus if our window is visible, to avoid invoking blur
820    // handlers when we are eventually shown.
821    new_contents->GetView()->RestoreFocus();
822  }
823
824  // Update all the UI bits.
825  UpdateTitleBar();
826}
827
828void BrowserView::ZoomChangedForActiveTab(bool can_show_bubble) {
829  GetLocationBarView()->ZoomChangedForActiveTab(
830      can_show_bubble && !toolbar_->IsWrenchMenuShowing());
831}
832
833gfx::Rect BrowserView::GetRestoredBounds() const {
834  return frame_->GetRestoredBounds();
835}
836
837ui::WindowShowState BrowserView::GetRestoredState() const {
838  if (IsMaximized())
839    return ui::SHOW_STATE_MAXIMIZED;
840  if (IsMinimized())
841    return ui::SHOW_STATE_MINIMIZED;
842  return ui::SHOW_STATE_NORMAL;
843}
844
845gfx::Rect BrowserView::GetBounds() const {
846  return frame_->GetWindowBoundsInScreen();
847}
848
849bool BrowserView::IsMaximized() const {
850  return frame_->IsMaximized();
851}
852
853bool BrowserView::IsMinimized() const {
854  return frame_->IsMinimized();
855}
856
857void BrowserView::Maximize() {
858  frame_->Maximize();
859}
860
861void BrowserView::Minimize() {
862  frame_->Minimize();
863}
864
865void BrowserView::Restore() {
866  frame_->Restore();
867}
868
869void BrowserView::EnterFullscreen(
870    const GURL& url, FullscreenExitBubbleType bubble_type) {
871  if (IsFullscreen())
872    return;  // Nothing to do.
873
874  ProcessFullscreen(true, FOR_DESKTOP, url, bubble_type);
875}
876
877void BrowserView::ExitFullscreen() {
878  if (!IsFullscreen())
879    return;  // Nothing to do.
880
881  ProcessFullscreen(false, FOR_DESKTOP, GURL(), FEB_TYPE_NONE);
882}
883
884void BrowserView::UpdateFullscreenExitBubbleContent(
885    const GURL& url,
886    FullscreenExitBubbleType bubble_type) {
887  // Immersive mode has no exit bubble because it has a visible strip at the
888  // top that gives the user a hover target.
889  // TODO(jamescook): Figure out what to do with mouse-lock.
890  if (bubble_type == FEB_TYPE_NONE || ShouldUseImmersiveFullscreenForUrl(url)) {
891    fullscreen_bubble_.reset();
892  } else if (fullscreen_bubble_.get()) {
893    fullscreen_bubble_->UpdateContent(url, bubble_type);
894  } else {
895    fullscreen_bubble_.reset(new FullscreenExitBubbleViews(
896        this, url, bubble_type));
897  }
898}
899
900bool BrowserView::ShouldHideUIForFullscreen() const {
901#if defined(USE_ASH)
902  // Immersive mode needs UI for the slide-down top panel.
903  return IsFullscreen() && !immersive_mode_controller_->IsEnabled();
904#endif
905  return IsFullscreen();
906}
907
908bool BrowserView::IsFullscreen() const {
909  return frame_->IsFullscreen();
910}
911
912bool BrowserView::IsFullscreenBubbleVisible() const {
913  return fullscreen_bubble_ != NULL;
914}
915
916#if defined(OS_WIN)
917void BrowserView::SetMetroSnapMode(bool enable) {
918  HISTOGRAM_COUNTS("Metro.SnapModeToggle", enable);
919  ProcessFullscreen(enable, FOR_METRO, GURL(), FEB_TYPE_NONE);
920}
921
922bool BrowserView::IsInMetroSnapMode() const {
923#if defined(USE_AURA)
924  return false;
925#else
926  return static_cast<views::NativeWidgetWin*>(
927      frame_->native_widget())->IsInMetroSnapMode();
928#endif
929}
930#endif  // defined(OS_WIN)
931
932void BrowserView::RestoreFocus() {
933  WebContents* selected_web_contents = GetActiveWebContents();
934  if (selected_web_contents)
935    selected_web_contents->GetView()->RestoreFocus();
936}
937
938void BrowserView::SetWindowSwitcherButton(views::Button* button) {
939  if (window_switcher_button_)
940    RemoveChildView(window_switcher_button_);
941  window_switcher_button_ = button;
942}
943
944void BrowserView::ToolbarSizeChanged(bool is_animating) {
945  // The call to InfoBarContainer::SetMaxTopArrowHeight() below can result in
946  // reentrancy; |call_state| tracks whether we're reentrant.  We can't just
947  // early-return in this case because we need to layout again so the infobar
948  // container's bounds are set correctly.
949  static CallState call_state = NORMAL;
950
951  // A reentrant call can (and should) use the fast resize path unless both it
952  // and the normal call are both non-animating.
953  bool use_fast_resize =
954      is_animating || (call_state == REENTRANT_FORCE_FAST_RESIZE);
955  if (use_fast_resize)
956    contents_web_view_->SetFastResize(true);
957  UpdateUIForContents(GetActiveWebContents());
958  if (use_fast_resize)
959    contents_web_view_->SetFastResize(false);
960
961  // Inform the InfoBarContainer that the distance to the location icon may have
962  // changed.  We have to do this after the block above so that the toolbars are
963  // laid out correctly for calculating the maximum arrow height below.
964  {
965    base::AutoReset<CallState> resetter(&call_state,
966        is_animating ? REENTRANT_FORCE_FAST_RESIZE : REENTRANT);
967    infobar_container_->SetMaxTopArrowHeight(GetMaxTopInfoBarArrowHeight());
968  }
969
970  // When transitioning from animating to not animating we need to make sure the
971  // contents_container_ gets layed out. If we don't do this and the bounds
972  // haven't changed contents_container_ won't get a Layout out and we'll end up
973  // with a gray rect because the clip wasn't updated.  Note that a reentrant
974  // call never needs to do this, because after it returns, the normal call
975  // wrapping it will do it.
976  if ((call_state == NORMAL) && !is_animating) {
977    contents_web_view_->InvalidateLayout();
978    contents_split_->Layout();
979  }
980}
981
982LocationBar* BrowserView::GetLocationBar() const {
983  return GetLocationBarView();
984}
985
986void BrowserView::SetFocusToLocationBar(bool select_all) {
987  // On Windows, changing focus to the location bar causes the browser
988  // window to become active. This can steal focus if the user has
989  // another window open already. On ChromeOS, changing focus makes a
990  // view believe it has a focus even if the widget doens't have a
991  // focus. Either cases, we need to ignore this when the browser
992  // window isn't active.
993  if (!force_location_bar_focus_ && !IsActive())
994    return;
995
996  // Temporarily reveal the top-of-window views (if not already revealed) so
997  // that the location bar view is visible and is considered focusable. If the
998  // location bar view gains focus, |immersive_mode_controller_| will keep the
999  // top-of-window views revealed.
1000  scoped_ptr<ImmersiveRevealedLock> focus_reveal_lock(
1001      immersive_mode_controller_->GetRevealedLock(
1002          ImmersiveModeController::ANIMATE_REVEAL_YES));
1003
1004  LocationBarView* location_bar = GetLocationBarView();
1005  if (location_bar->IsLocationEntryFocusableInRootView()) {
1006    // Location bar got focus.
1007    location_bar->FocusLocation(select_all);
1008  } else {
1009    // If none of location bar got focus,
1010    // then clear focus.
1011    views::FocusManager* focus_manager = GetFocusManager();
1012    DCHECK(focus_manager);
1013    focus_manager->ClearFocus();
1014  }
1015}
1016
1017void BrowserView::UpdateReloadStopState(bool is_loading, bool force) {
1018  toolbar_->reload_button()->ChangeMode(
1019      is_loading ? ReloadButton::MODE_STOP : ReloadButton::MODE_RELOAD, force);
1020}
1021
1022void BrowserView::UpdateToolbar(content::WebContents* contents) {
1023  // We may end up here during destruction.
1024  if (toolbar_)
1025    toolbar_->Update(contents);
1026}
1027
1028void BrowserView::FocusToolbar() {
1029  // Temporarily reveal the top-of-window views (if not already revealed) so
1030  // that the toolbar is visible and is considered focusable. If the
1031  // toolbar gains focus, |immersive_mode_controller_| will keep the
1032  // top-of-window views revealed.
1033  scoped_ptr<ImmersiveRevealedLock> focus_reveal_lock(
1034      immersive_mode_controller_->GetRevealedLock(
1035          ImmersiveModeController::ANIMATE_REVEAL_YES));
1036
1037  // Start the traversal within the main toolbar. SetPaneFocus stores
1038  // the current focused view before changing focus.
1039  toolbar_->SetPaneFocus(NULL);
1040}
1041
1042void BrowserView::FocusBookmarksToolbar() {
1043  DCHECK(!immersive_mode_controller_->IsEnabled());
1044  if (bookmark_bar_view_.get() &&
1045      bookmark_bar_view_->visible() &&
1046      bookmark_bar_view_->GetPreferredSize().height() != 0) {
1047    bookmark_bar_view_->SetPaneFocusAndFocusDefault();
1048  }
1049}
1050
1051void BrowserView::FocusInfobars() {
1052  if (infobar_container_->child_count() > 0)
1053    infobar_container_->SetPaneFocusAndFocusDefault();
1054}
1055
1056void BrowserView::FocusAppMenu() {
1057  // Chrome doesn't have a traditional menu bar, but it has a menu button in the
1058  // main toolbar that plays the same role.  If the user presses a key that
1059  // would typically focus the menu bar, tell the toolbar to focus the menu
1060  // button.  If the user presses the key again, return focus to the previous
1061  // location.
1062  //
1063  // Not used on the Mac, which has a normal menu bar.
1064  if (toolbar_->IsAppMenuFocused()) {
1065    RestoreFocus();
1066  } else {
1067    DCHECK(!immersive_mode_controller_->IsEnabled());
1068    toolbar_->SetPaneFocusAndFocusAppMenu();
1069  }
1070}
1071
1072void BrowserView::RotatePaneFocus(bool forwards) {
1073  GetWidget()->GetFocusManager()->RotatePaneFocus(
1074      forwards ?
1075          views::FocusManager::kForward : views::FocusManager::kBackward,
1076      views::FocusManager::kWrap);
1077}
1078
1079void BrowserView::DestroyBrowser() {
1080  // After this returns other parts of Chrome are going to be shutdown. Close
1081  // the window now so that we are deleted immediately and aren't left holding
1082  // references to deleted objects.
1083  GetWidget()->RemoveObserver(this);
1084  GetLocationBar()->GetLocationEntry()->model()->popup_model()->RemoveObserver(
1085      this);
1086  frame_->CloseNow();
1087}
1088
1089bool BrowserView::IsBookmarkBarVisible() const {
1090  if (!browser_->SupportsWindowFeature(Browser::FEATURE_BOOKMARKBAR))
1091    return false;
1092  if (!bookmark_bar_view_.get())
1093    return false;
1094  if (bookmark_bar_view_->GetPreferredSize().height() == 0)
1095    return false;
1096  // New tab page needs visible bookmarks even when top-views are hidden.
1097  if (immersive_mode_controller_->ShouldHideTopViews() &&
1098      !bookmark_bar_view_->IsDetached())
1099    return false;
1100  return true;
1101}
1102
1103bool BrowserView::IsBookmarkBarAnimating() const {
1104  return bookmark_bar_view_.get() && bookmark_bar_view_->is_animating();
1105}
1106
1107bool BrowserView::IsTabStripEditable() const {
1108  return tabstrip_->IsTabStripEditable();
1109}
1110
1111bool BrowserView::IsToolbarVisible() const {
1112  if (immersive_mode_controller_->ShouldHideTopViews())
1113    return false;
1114  return browser_->SupportsWindowFeature(Browser::FEATURE_TOOLBAR) ||
1115         browser_->SupportsWindowFeature(Browser::FEATURE_LOCATIONBAR);
1116}
1117
1118gfx::Rect BrowserView::GetRootWindowResizerRect() const {
1119  // Views does not support resizer rects because they caused page cycler
1120  // performance regressions when they were added. See crrev.com/9654
1121  return gfx::Rect();
1122}
1123
1124void BrowserView::DisableInactiveFrame() {
1125#if defined(OS_WIN) && !defined(USE_AURA)
1126  frame_->DisableInactiveRendering();
1127#endif  // No tricks are needed to get the right behavior on Linux.
1128}
1129
1130void BrowserView::ConfirmAddSearchProvider(TemplateURL* template_url,
1131                                           Profile* profile) {
1132  chrome::EditSearchEngine(GetWidget()->GetNativeWindow(), template_url, NULL,
1133                           profile);
1134}
1135
1136void BrowserView::ShowUpdateChromeDialog() {
1137  UpdateRecommendedMessageBox::Show(GetWidget()->GetNativeWindow());
1138}
1139
1140void BrowserView::ShowBookmarkBubble(const GURL& url, bool already_bookmarked) {
1141  scoped_ptr<BookmarkBubbleDelegate> delegate;
1142  delegate.reset(new BookmarkBubbleSignInDelegate(browser_.get()));
1143
1144  BookmarkBubbleView::ShowBubble(GetToolbarView()->GetBookmarkBubbleAnchor(),
1145                                 bookmark_bar_view_.get(),
1146                                 delegate.Pass(),
1147                                 browser_->profile(),
1148                                 url,
1149                                 !already_bookmarked);
1150}
1151
1152void BrowserView::ShowBookmarkPrompt() {
1153  GetLocationBarView()->ShowBookmarkPrompt();
1154}
1155
1156#if defined(ENABLE_ONE_CLICK_SIGNIN)
1157void BrowserView::ShowOneClickSigninBubble(
1158    OneClickSigninBubbleType type,
1159    const string16& email,
1160    const string16& error_message,
1161    const StartSyncCallback& start_sync_callback) {
1162  scoped_ptr<OneClickSigninBubbleDelegate> delegate;
1163  delegate.reset(new OneClickSigninBubbleLinksDelegate(browser()));
1164
1165  views::View* anchor_view;
1166  if (type == BrowserWindow::ONE_CLICK_SIGNIN_BUBBLE_TYPE_BUBBLE)
1167    anchor_view = toolbar_->app_menu();
1168  else
1169    anchor_view = toolbar_->location_bar();
1170
1171  OneClickSigninBubbleView::ShowBubble(type, email, error_message,
1172                                       delegate.Pass(), anchor_view,
1173                                       start_sync_callback);
1174}
1175#endif
1176
1177void BrowserView::SetDownloadShelfVisible(bool visible) {
1178  // This can be called from the superclass destructor, when it destroys our
1179  // child views. At that point, browser_ is already gone.
1180  if (browser_ == NULL)
1181    return;
1182
1183  if (visible && IsDownloadShelfVisible() != visible) {
1184    // Invoke GetDownloadShelf to force the shelf to be created.
1185    GetDownloadShelf();
1186  }
1187
1188  if (browser_ != NULL)
1189    browser_->UpdateDownloadShelfVisibility(visible);
1190
1191  // SetDownloadShelfVisible can force-close the shelf, so make sure we lay out
1192  // everything correctly, as if the animation had finished. This doesn't
1193  // matter for showing the shelf, as the show animation will do it.
1194  ToolbarSizeChanged(false);
1195}
1196
1197bool BrowserView::IsDownloadShelfVisible() const {
1198  return download_shelf_.get() && download_shelf_->IsShowing();
1199}
1200
1201DownloadShelf* BrowserView::GetDownloadShelf() {
1202  if (!download_shelf_.get()) {
1203    download_shelf_.reset(new DownloadShelfView(browser_.get(), this));
1204    download_shelf_->set_owned_by_client();
1205    GetBrowserViewLayout()->set_download_shelf(download_shelf_.get());
1206  }
1207  return download_shelf_.get();
1208}
1209
1210void BrowserView::ConfirmBrowserCloseWithPendingDownloads(
1211    int download_count,
1212    Browser::DownloadClosePreventionType dialog_type,
1213    bool app_modal,
1214    const base::Callback<void(bool)>& callback) {
1215  DownloadInProgressDialogView::Show(
1216      GetNativeWindow(), download_count, dialog_type, app_modal, callback);
1217}
1218
1219void BrowserView::UserChangedTheme() {
1220  frame_->FrameTypeChanged();
1221}
1222
1223int BrowserView::GetExtraRenderViewHeight() const {
1224  // Currently this is only used on linux.
1225  return 0;
1226}
1227
1228void BrowserView::WebContentsFocused(WebContents* contents) {
1229  if (contents_web_view_->GetWebContents() == contents)
1230    contents_web_view_->OnWebContentsFocused(contents);
1231  else
1232    devtools_container_->OnWebContentsFocused(contents);
1233}
1234
1235void BrowserView::ShowWebsiteSettings(Profile* profile,
1236                                      content::WebContents* web_contents,
1237                                      const GURL& url,
1238                                      const content::SSLStatus& ssl) {
1239  WebsiteSettingsPopupView::ShowPopup(
1240      GetLocationBarView()->location_icon_view(), profile,
1241      web_contents, url, ssl, browser_.get());
1242}
1243
1244void BrowserView::ShowAppMenu() {
1245  // Keep the top-of-window views revealed as long as the app menu is visible.
1246  scoped_ptr<ImmersiveRevealedLock> revealed_lock(
1247      immersive_mode_controller_->GetRevealedLock(
1248          ImmersiveModeController::ANIMATE_REVEAL_NO));
1249
1250  toolbar_->app_menu()->Activate();
1251}
1252
1253bool BrowserView::PreHandleKeyboardEvent(const NativeWebKeyboardEvent& event,
1254                                         bool* is_keyboard_shortcut) {
1255  *is_keyboard_shortcut = false;
1256
1257  if ((event.type != WebKit::WebInputEvent::RawKeyDown) &&
1258      (event.type != WebKit::WebInputEvent::KeyUp)) {
1259    return false;
1260  }
1261
1262#if defined(OS_WIN) && !defined(USE_AURA)
1263  // As Alt+F4 is the close-app keyboard shortcut, it needs processing
1264  // immediately.
1265  if (event.windowsKeyCode == ui::VKEY_F4 &&
1266      event.type == WebKit::WebInputEvent::RawKeyDown &&
1267      event.modifiers == NativeWebKeyboardEvent::AltKey) {
1268    DefWindowProc(event.os_event.hwnd, event.os_event.message,
1269                  event.os_event.wParam, event.os_event.lParam);
1270    return true;
1271  }
1272#endif
1273
1274  views::FocusManager* focus_manager = GetFocusManager();
1275  DCHECK(focus_manager);
1276
1277  if (focus_manager->shortcut_handling_suspended())
1278    return false;
1279
1280  ui::Accelerator accelerator(
1281      static_cast<ui::KeyboardCode>(event.windowsKeyCode),
1282      content::GetModifiersFromNativeWebKeyboardEvent(event));
1283  if (event.type == WebKit::WebInputEvent::KeyUp)
1284    accelerator.set_type(ui::ET_KEY_RELEASED);
1285
1286  // What we have to do here is as follows:
1287  // - If the |browser_| is for an app, do nothing.
1288  // - If the |browser_| is not for an app, and the |accelerator| is not
1289  //   associated with the browser (e.g. an Ash shortcut), process it.
1290  // - If the |browser_| is not for an app, and the |accelerator| is associated
1291  //   with the browser, and it is a reserved one (e.g. Ctrl+w), process it.
1292  // - If the |browser_| is not for an app, and the |accelerator| is associated
1293  //   with the browser, and it is not a reserved one, do nothing.
1294
1295  if (browser_->is_app()) {
1296    // Let all keys fall through to a v1 app's web content, even accelerators.
1297    // We don't have to flip |is_keyboard_shortcut| here. If we do that, the app
1298    // might not be able to see a subsequent Char event. See OnHandleInputEvent
1299    // in content/renderer/render_widget.cc for details.
1300    return false;
1301  }
1302
1303  chrome::BrowserCommandController* controller = browser_->command_controller();
1304
1305  // Here we need to retrieve the command id (if any) associated to the
1306  // keyboard event. Instead of looking up the command id in the
1307  // |accelerator_table_| by ourselves, we block the command execution of
1308  // the |browser_| object then send the keyboard event to the
1309  // |focus_manager| as if we are activating an accelerator key.
1310  // Then we can retrieve the command id from the |browser_| object.
1311  bool original_block_command_state = controller->block_command_execution();
1312  controller->SetBlockCommandExecution(true);
1313  // If the |accelerator| is a non-browser shortcut (e.g. Ash shortcut), the
1314  // command execution cannot be blocked and true is returned. However, it is
1315  // okay as long as is_app() is false. See comments in this function.
1316  const bool processed = focus_manager->ProcessAccelerator(accelerator);
1317  const int id = controller->GetLastBlockedCommand(NULL);
1318  controller->SetBlockCommandExecution(original_block_command_state);
1319
1320  // Executing the command may cause |this| object to be destroyed.
1321  if (controller->IsReservedCommandOrKey(id, event)) {
1322    UpdateAcceleratorMetrics(accelerator, id);
1323    return chrome::ExecuteCommand(browser_.get(), id);
1324  }
1325
1326  if (id != -1) {
1327    // |accelerator| is a non-reserved browser shortcut (e.g. Ctrl+f).
1328    if (event.type == WebKit::WebInputEvent::RawKeyDown)
1329      *is_keyboard_shortcut = true;
1330  } else if (processed) {
1331    // |accelerator| is a non-browser shortcut (e.g. F4-F10 on Ash). Report
1332    // that we handled it.
1333    return true;
1334  }
1335
1336  return false;
1337}
1338
1339void BrowserView::HandleKeyboardEvent(const NativeWebKeyboardEvent& event) {
1340  unhandled_keyboard_event_handler_.HandleKeyboardEvent(event,
1341                                                        GetFocusManager());
1342}
1343
1344// TODO(devint): http://b/issue?id=1117225 Cut, Copy, and Paste are always
1345// enabled in the page menu regardless of whether the command will do
1346// anything. When someone selects the menu item, we just act as if they hit
1347// the keyboard shortcut for the command by sending the associated key press
1348// to windows. The real fix to this bug is to disable the commands when they
1349// won't do anything. We'll need something like an overall clipboard command
1350// manager to do that.
1351void BrowserView::Cut() {
1352  // If a WebContent is focused, call RenderWidgetHost::Cut. Otherwise, e.g. if
1353  // Omnibox is focused, send a Ctrl+x key event to Chrome. Using RWH interface
1354  // rather than the fake key event for a WebContent is important since the fake
1355  // event might be consumed by the web content (crbug.com/137908).
1356  DoCutCopyPaste(&content::RenderWidgetHost::Cut,
1357#if defined(OS_WIN)
1358                 WM_CUT,
1359#endif
1360                 IDS_APP_CUT);
1361}
1362
1363void BrowserView::Copy() {
1364  DoCutCopyPaste(&content::RenderWidgetHost::Copy,
1365#if defined(OS_WIN)
1366                 WM_COPY,
1367#endif
1368                 IDS_APP_COPY);
1369}
1370
1371void BrowserView::Paste() {
1372  DoCutCopyPaste(&content::RenderWidgetHost::Paste,
1373#if defined(OS_WIN)
1374                 WM_PASTE,
1375#endif
1376                 IDS_APP_PASTE);
1377}
1378
1379WindowOpenDisposition BrowserView::GetDispositionForPopupBounds(
1380    const gfx::Rect& bounds) {
1381#if defined(OS_WIN)
1382  // If we are in Win8's single window Metro mode, we can't allow popup windows.
1383  return win8::IsSingleWindowMetroMode() ? NEW_BACKGROUND_TAB : NEW_POPUP;
1384#else
1385  return NEW_POPUP;
1386#endif
1387}
1388
1389FindBar* BrowserView::CreateFindBar() {
1390  return chrome::CreateFindBar(this);
1391}
1392
1393WebContentsModalDialogHost* BrowserView::GetWebContentsModalDialogHost() {
1394  return GetBrowserViewLayout()->GetWebContentsModalDialogHost();
1395}
1396
1397///////////////////////////////////////////////////////////////////////////////
1398// BrowserView, BrowserWindowTesting implementation:
1399
1400BookmarkBarView* BrowserView::GetBookmarkBarView() const {
1401  return bookmark_bar_view_.get();
1402}
1403
1404LocationBarView* BrowserView::GetLocationBarView() const {
1405  return toolbar_ ? toolbar_->location_bar() : NULL;
1406}
1407
1408views::View* BrowserView::GetTabContentsContainerView() const {
1409  return contents_web_view_;
1410}
1411
1412ToolbarView* BrowserView::GetToolbarView() const {
1413  return toolbar_;
1414}
1415
1416///////////////////////////////////////////////////////////////////////////////
1417// BrowserView, TabStripModelObserver implementation:
1418
1419void BrowserView::TabDetachedAt(WebContents* contents, int index) {
1420  // We use index here rather than comparing |contents| because by this time
1421  // the model has already removed |contents| from its list, so
1422  // browser_->GetActiveWebContents() will return NULL or something else.
1423  if (index == browser_->tab_strip_model()->active_index()) {
1424    // We need to reset the current tab contents to NULL before it gets
1425    // freed. This is because the focus manager performs some operations
1426    // on the selected WebContents when it is removed.
1427    contents_web_view_->SetWebContents(NULL);
1428    infobar_container_->ChangeInfoBarService(NULL);
1429    UpdateDevToolsForContents(NULL);
1430  }
1431}
1432
1433void BrowserView::TabDeactivated(WebContents* contents) {
1434  // We do not store the focus when closing the tab to work-around bug 4633.
1435  // Some reports seem to show that the focus manager and/or focused view can
1436  // be garbage at that point, it is not clear why.
1437  if (!contents->IsBeingDestroyed())
1438    contents->GetView()->StoreFocus();
1439}
1440
1441void BrowserView::TabStripEmpty() {
1442  // Make sure all optional UI is removed before we are destroyed, otherwise
1443  // there will be consequences (since our view hierarchy will still have
1444  // references to freed views).
1445  UpdateUIForContents(NULL);
1446}
1447
1448///////////////////////////////////////////////////////////////////////////////
1449// BrowserView, ui::AcceleratorProvider implementation:
1450
1451bool BrowserView::GetAcceleratorForCommandId(int command_id,
1452                                             ui::Accelerator* accelerator) {
1453  // Let's let the ToolbarView own the canonical implementation of this method.
1454  return toolbar_->GetAcceleratorForCommandId(command_id, accelerator);
1455}
1456
1457///////////////////////////////////////////////////////////////////////////////
1458// BrowserView, views::WidgetDelegate implementation:
1459
1460bool BrowserView::CanResize() const {
1461  return true;
1462}
1463
1464bool BrowserView::CanMaximize() const {
1465  return true;
1466}
1467
1468bool BrowserView::CanActivate() const {
1469  if (!AppModalDialogQueue::GetInstance()->active_dialog() ||
1470      !AppModalDialogQueue::GetInstance()->active_dialog()->native_dialog())
1471    return true;
1472
1473#if defined(USE_AURA) && defined(OS_CHROMEOS)
1474  // On Aura window manager controls all windows so settings focus via PostTask
1475  // will make only worse because posted task will keep trying to steal focus.
1476  AppModalDialogQueue::GetInstance()->ActivateModalDialog();
1477#else
1478  // If another browser is app modal, flash and activate the modal browser. This
1479  // has to be done in a post task, otherwise if the user clicked on a window
1480  // that doesn't have the modal dialog the windows keep trying to get the focus
1481  // from each other on Windows. http://crbug.com/141650.
1482  base::MessageLoop::current()->PostTask(
1483      FROM_HERE,
1484      base::Bind(&BrowserView::ActivateAppModalDialog,
1485                 activate_modal_dialog_factory_.GetWeakPtr()));
1486#endif
1487  return false;
1488}
1489
1490string16 BrowserView::GetWindowTitle() const {
1491  return browser_->GetWindowTitleForCurrentTab();
1492}
1493
1494string16 BrowserView::GetAccessibleWindowTitle() const {
1495  if (IsOffTheRecord()) {
1496    return l10n_util::GetStringFUTF16(
1497        IDS_ACCESSIBLE_INCOGNITO_WINDOW_TITLE_FORMAT,
1498        GetWindowTitle());
1499  }
1500  return GetWindowTitle();
1501}
1502
1503views::View* BrowserView::GetInitiallyFocusedView() {
1504  return NULL;
1505}
1506
1507bool BrowserView::ShouldShowWindowTitle() const {
1508  // For Ash only, app host windows do not show an icon, crbug.com/119411.
1509  // Child windows (i.e. popups) do show an icon.
1510  if (browser_->host_desktop_type() == chrome::HOST_DESKTOP_TYPE_ASH &&
1511      browser_->is_app() && browser_->app_type() == Browser::APP_TYPE_HOST)
1512    return false;
1513
1514  return browser_->SupportsWindowFeature(Browser::FEATURE_TITLEBAR);
1515}
1516
1517gfx::ImageSkia BrowserView::GetWindowAppIcon() {
1518  if (browser_->is_app()) {
1519    WebContents* contents = browser_->tab_strip_model()->GetActiveWebContents();
1520    extensions::TabHelper* extensions_tab_helper =
1521        contents ? extensions::TabHelper::FromWebContents(contents) : NULL;
1522    if (extensions_tab_helper && extensions_tab_helper->GetExtensionAppIcon())
1523      return gfx::ImageSkia::CreateFrom1xBitmap(
1524          *extensions_tab_helper->GetExtensionAppIcon());
1525  }
1526
1527  return GetWindowIcon();
1528}
1529
1530gfx::ImageSkia BrowserView::GetWindowIcon() {
1531  if (browser_->is_app() || browser_->is_type_popup())
1532    return browser_->GetCurrentPageIcon().AsImageSkia();
1533  return gfx::ImageSkia();
1534}
1535
1536bool BrowserView::ShouldShowWindowIcon() const {
1537  // For Ash only, app host windows do not show an icon, crbug.com/119411.
1538  // Child windows (i.e. popups) do show an icon.
1539  if (browser_->host_desktop_type() == chrome::HOST_DESKTOP_TYPE_ASH &&
1540      browser_->is_app() && browser_->app_type() == Browser::APP_TYPE_HOST)
1541    return false;
1542
1543  return browser_->SupportsWindowFeature(Browser::FEATURE_TITLEBAR);
1544}
1545
1546bool BrowserView::ExecuteWindowsCommand(int command_id) {
1547  // This function handles WM_SYSCOMMAND, WM_APPCOMMAND, and WM_COMMAND.
1548#if defined(OS_WIN)
1549  if (command_id == IDC_DEBUG_FRAME_TOGGLE)
1550    GetWidget()->DebugToggleFrameType();
1551
1552  // In Windows 8 metro mode prevent sizing and moving.
1553  if (win8::IsSingleWindowMetroMode()) {
1554    // Windows uses the 4 lower order bits of |notification_code| for type-
1555    // specific information so we must exclude this when comparing.
1556    static const int sc_mask = 0xFFF0;
1557    if (((command_id & sc_mask) == SC_MOVE) ||
1558        ((command_id & sc_mask) == SC_SIZE) ||
1559        ((command_id & sc_mask) == SC_MAXIMIZE))
1560      return true;
1561  }
1562#endif
1563  // Translate WM_APPCOMMAND command ids into a command id that the browser
1564  // knows how to handle.
1565  int command_id_from_app_command = GetCommandIDForAppCommandID(command_id);
1566  if (command_id_from_app_command != -1)
1567    command_id = command_id_from_app_command;
1568
1569  return chrome::ExecuteCommand(browser_.get(), command_id);
1570}
1571
1572std::string BrowserView::GetWindowName() const {
1573  return chrome::GetWindowPlacementKey(browser_.get());
1574}
1575
1576void BrowserView::SaveWindowPlacement(const gfx::Rect& bounds,
1577                                      ui::WindowShowState show_state) {
1578  // If IsFullscreen() is true, we've just changed into fullscreen mode, and
1579  // we're catching the going-into-fullscreen sizing and positioning calls,
1580  // which we want to ignore.
1581  if (!ShouldSaveOrRestoreWindowPos())
1582    return;
1583
1584  if (!IsFullscreen() && chrome::ShouldSaveWindowPlacement(browser_.get())) {
1585    WidgetDelegate::SaveWindowPlacement(bounds, show_state);
1586    chrome::SaveWindowPlacement(browser_.get(), bounds, show_state);
1587  }
1588}
1589
1590bool BrowserView::GetSavedWindowPlacement(
1591    gfx::Rect* bounds,
1592    ui::WindowShowState* show_state) const {
1593  if (!ShouldSaveOrRestoreWindowPos())
1594    return false;
1595  chrome::GetSavedWindowBoundsAndShowState(browser_.get(), bounds, show_state);
1596
1597  if (browser_->is_type_popup() &&
1598      !browser_->is_app() &&
1599      !browser_->is_devtools()) {
1600    // This is non-app popup window. The value passed in |bounds| represents
1601    // two pieces of information:
1602    // - the position of the window, in screen coordinates (outer position).
1603    // - the size of the content area (inner size).
1604    // We need to use these values to determine the appropriate size and
1605    // position of the resulting window.
1606    if (IsToolbarVisible()) {
1607      // If we're showing the toolbar, we need to adjust |*bounds| to include
1608      // its desired height, since the toolbar is considered part of the
1609      // window's client area as far as GetWindowBoundsForClientBounds is
1610      // concerned...
1611      bounds->set_height(
1612          bounds->height() + toolbar_->GetPreferredSize().height());
1613    }
1614
1615    gfx::Rect window_rect = frame_->non_client_view()->
1616        GetWindowBoundsForClientBounds(*bounds);
1617    window_rect.set_origin(bounds->origin());
1618
1619    // When we are given x/y coordinates of 0 on a created popup window,
1620    // assume none were given by the window.open() command.
1621    if (window_rect.x() == 0 && window_rect.y() == 0) {
1622      gfx::Size size = window_rect.size();
1623      window_rect.set_origin(
1624          WindowSizer::GetDefaultPopupOrigin(size,
1625                                             browser_->host_desktop_type()));
1626    }
1627
1628    *bounds = window_rect;
1629    *show_state = ui::SHOW_STATE_NORMAL;
1630  }
1631
1632  // We return true because we can _always_ locate reasonable bounds using the
1633  // WindowSizer, and we don't want to trigger the Window's built-in "size to
1634  // default" handling because the browser window has no default preferred
1635  // size.
1636  return true;
1637}
1638
1639views::View* BrowserView::GetContentsView() {
1640  return contents_web_view_;
1641}
1642
1643views::ClientView* BrowserView::CreateClientView(views::Widget* widget) {
1644  return this;
1645}
1646
1647void BrowserView::OnWidgetActivationChanged(views::Widget* widget,
1648                                            bool active) {
1649  if (active)
1650    BrowserList::SetLastActive(browser_.get());
1651}
1652
1653void BrowserView::OnWindowBeginUserBoundsChange() {
1654  WebContents* web_contents = GetActiveWebContents();
1655  if (!web_contents)
1656    return;
1657  web_contents->GetRenderViewHost()->NotifyMoveOrResizeStarted();
1658}
1659
1660void BrowserView::OnWidgetMove() {
1661  if (!initialized_) {
1662    // Creating the widget can trigger a move. Ignore it until we've initialized
1663    // things.
1664    return;
1665  }
1666
1667  // Cancel any tabstrip animations, some of them may be invalidated by the
1668  // window being repositioned.
1669  // Comment out for one cycle to see if this fixes dist tests.
1670  // tabstrip_->DestroyDragController();
1671
1672  // status_bubble_ may be NULL if this is invoked during construction.
1673  if (status_bubble_.get())
1674    status_bubble_->Reposition();
1675
1676  BookmarkBubbleView::Hide();
1677
1678  // Close the omnibox popup, if any.
1679  LocationBarView* location_bar_view = GetLocationBarView();
1680  if (location_bar_view)
1681    location_bar_view->GetLocationEntry()->CloseOmniboxPopup();
1682}
1683
1684views::Widget* BrowserView::GetWidget() {
1685  return View::GetWidget();
1686}
1687
1688const views::Widget* BrowserView::GetWidget() const {
1689  return View::GetWidget();
1690}
1691
1692void BrowserView::GetAccessiblePanes(std::vector<views::View*>* panes) {
1693  // This should be in the order of pane traversal of the panes using F6
1694  // (Windows) or Ctrl+Back/Forward (Chrome OS).  If one of these is
1695  // invisible or has no focusable children, it will be automatically
1696  // skipped.
1697  panes->push_back(toolbar_);
1698  if (bookmark_bar_view_.get())
1699    panes->push_back(bookmark_bar_view_.get());
1700  if (infobar_container_)
1701    panes->push_back(infobar_container_);
1702  if (download_shelf_.get())
1703    panes->push_back(download_shelf_.get());
1704  panes->push_back(GetTabContentsContainerView());
1705  if (devtools_container_->visible())
1706    panes->push_back(devtools_container_);
1707}
1708
1709///////////////////////////////////////////////////////////////////////////////
1710// BrowserView, views::ClientView overrides:
1711
1712bool BrowserView::CanClose() {
1713  // You cannot close a frame for which there is an active originating drag
1714  // session.
1715  if (tabstrip_ && !tabstrip_->IsTabStripCloseable())
1716    return false;
1717
1718  // Give beforeunload handlers the chance to cancel the close before we hide
1719  // the window below.
1720  if (!browser_->ShouldCloseWindow())
1721    return false;
1722
1723  bool fast_tab_closing_enabled =
1724    CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableFastUnload);
1725
1726  if (!browser_->tab_strip_model()->empty()) {
1727    // Tab strip isn't empty.  Hide the frame (so it appears to have closed
1728    // immediately) and close all the tabs, allowing the renderers to shut
1729    // down. When the tab strip is empty we'll be called back again.
1730    frame_->Hide();
1731    browser_->OnWindowClosing();
1732    if (fast_tab_closing_enabled)
1733      browser_->tab_strip_model()->CloseAllTabs();
1734    return false;
1735  } else if (fast_tab_closing_enabled &&
1736        !browser_->HasCompletedUnloadProcessing()) {
1737    // The browser needs to finish running unload handlers.
1738    // Hide the frame (so it appears to have closed immediately), and
1739    // the browser will call us back again when it is ready to close.
1740    frame_->Hide();
1741    return false;
1742  }
1743
1744  // Empty TabStripModel, it's now safe to allow the Window to be closed.
1745  content::NotificationService::current()->Notify(
1746      chrome::NOTIFICATION_WINDOW_CLOSED,
1747      content::Source<gfx::NativeWindow>(frame_->GetNativeWindow()),
1748      content::NotificationService::NoDetails());
1749  return true;
1750}
1751
1752int BrowserView::NonClientHitTest(const gfx::Point& point) {
1753  return GetBrowserViewLayout()->NonClientHitTest(point);
1754}
1755
1756gfx::Size BrowserView::GetMinimumSize() {
1757  return GetBrowserViewLayout()->GetMinimumSize();
1758}
1759
1760///////////////////////////////////////////////////////////////////////////////
1761// BrowserView, views::View overrides:
1762
1763const char* BrowserView::GetClassName() const {
1764  return kViewClassName;
1765}
1766
1767void BrowserView::Layout() {
1768  if (ignore_layout_)
1769    return;
1770
1771  views::View::Layout();
1772
1773  // TODO(jamescook): Why was this in the middle of layout code?
1774  toolbar_->location_bar()->SetLocationEntryFocusable(IsToolbarVisible());
1775
1776  // The status bubble position requires that all other layout finish first.
1777  LayoutStatusBubble();
1778}
1779
1780void BrowserView::PaintChildren(gfx::Canvas* canvas) {
1781  // Paint the |infobar_container_| last so that it may paint its
1782  // overlapping tabs.
1783  for (int i = 0; i < child_count(); ++i) {
1784    View* child = child_at(i);
1785    if (child != infobar_container_ && !child->layer())
1786      child->Paint(canvas);
1787  }
1788
1789  infobar_container_->Paint(canvas);
1790}
1791
1792void BrowserView::ViewHierarchyChanged(
1793    const ViewHierarchyChangedDetails& details) {
1794  if (!initialized_ && details.is_add && details.child == this && GetWidget()) {
1795    InitViews();
1796    initialized_ = true;
1797  }
1798}
1799
1800void BrowserView::ChildPreferredSizeChanged(View* child) {
1801  Layout();
1802}
1803
1804void BrowserView::GetAccessibleState(ui::AccessibleViewState* state) {
1805  state->role = ui::AccessibilityTypes::ROLE_CLIENT;
1806}
1807
1808///////////////////////////////////////////////////////////////////////////////
1809// BrowserView, ui::AcceleratorTarget overrides:
1810
1811bool BrowserView::AcceleratorPressed(const ui::Accelerator& accelerator) {
1812#if defined(OS_CHROMEOS)
1813  // If accessibility is enabled, stop speech and return false so that key
1814  // combinations involving Search can be used for extra accessibility
1815  // functionality.
1816  if (accelerator.key_code() == ui::VKEY_LWIN &&
1817      g_browser_process->local_state()->GetBoolean(
1818          prefs::kSpokenFeedbackEnabled)) {
1819    TtsController::GetInstance()->Stop();
1820    return false;
1821  }
1822#endif
1823
1824  std::map<ui::Accelerator, int>::const_iterator iter =
1825      accelerator_table_.find(accelerator);
1826  DCHECK(iter != accelerator_table_.end());
1827  int command_id = iter->second;
1828
1829  chrome::BrowserCommandController* controller = browser_->command_controller();
1830  if (!controller->block_command_execution())
1831    UpdateAcceleratorMetrics(accelerator, command_id);
1832  return chrome::ExecuteCommand(browser_.get(), command_id);
1833}
1834
1835///////////////////////////////////////////////////////////////////////////////
1836// BrowserView, OmniboxPopupModelObserver overrides:
1837void BrowserView::OnOmniboxPopupShownOrHidden() {
1838  infobar_container_->SetMaxTopArrowHeight(GetMaxTopInfoBarArrowHeight());
1839}
1840
1841///////////////////////////////////////////////////////////////////////////////
1842// BrowserView, ImmersiveModeController::Delegate overrides:
1843
1844BookmarkBarView* BrowserView::GetBookmarkBar() {
1845  return bookmark_bar_view_.get();
1846}
1847
1848FullscreenController* BrowserView::GetFullscreenController() {
1849  // Cannot be injected into ImmersiveModeController because it is constructed
1850  // after BrowserView.
1851  return browser()->fullscreen_controller();
1852}
1853
1854void BrowserView::FullscreenStateChanged() {
1855  if (IsFullscreen()) {
1856    ProcessFullscreen(true, FOR_DESKTOP, GURL(),
1857                      FEB_TYPE_BROWSER_FULLSCREEN_EXIT_INSTRUCTION);
1858  } else {
1859    ProcessFullscreen(false, FOR_DESKTOP, GURL(), FEB_TYPE_NONE);
1860  }
1861}
1862
1863void BrowserView::SetImmersiveStyle(bool immersive) {
1864  // Only the tab strip changes its painting style for immersive fullscreen.
1865  if (tabstrip_)
1866    tabstrip_->SetImmersiveStyle(immersive);
1867}
1868
1869WebContents* BrowserView::GetWebContents() {
1870  return GetActiveWebContents();
1871}
1872
1873///////////////////////////////////////////////////////////////////////////////
1874// BrowserView, InfoBarContainer::Delegate overrides:
1875
1876SkColor BrowserView::GetInfoBarSeparatorColor() const {
1877  // NOTE: Keep this in sync with ToolbarView::OnPaint()!
1878  return (IsTabStripVisible() || !frame_->ShouldUseNativeFrame()) ?
1879      ThemeProperties::GetDefaultColor(
1880          ThemeProperties::COLOR_TOOLBAR_SEPARATOR) :
1881      SK_ColorBLACK;
1882}
1883
1884void BrowserView::InfoBarContainerStateChanged(bool is_animating) {
1885  ToolbarSizeChanged(is_animating);
1886}
1887
1888bool BrowserView::DrawInfoBarArrows(int* x) const {
1889  if (x) {
1890    const LocationIconView* location_icon_view =
1891        toolbar_->location_bar()->location_icon_view();
1892    gfx::Point icon_center(location_icon_view->GetImageBounds().CenterPoint());
1893    ConvertPointToTarget(location_icon_view, this, &icon_center);
1894    *x = icon_center.x();
1895  }
1896  return true;
1897}
1898
1899bool BrowserView::SplitHandleMoved(views::SingleSplitView* sender) {
1900  for (int i = 0; i < sender->child_count(); ++i)
1901    sender->child_at(i)->InvalidateLayout();
1902  SchedulePaint();
1903  Layout();
1904  return false;
1905}
1906
1907void BrowserView::OnSysColorChange() {
1908  chrome::MaybeShowInvertBubbleView(browser_.get(), contents_container_);
1909}
1910
1911void BrowserView::InitViews() {
1912  GetWidget()->AddObserver(this);
1913
1914  // Stow a pointer to this object onto the window handle so that we can get at
1915  // it later when all we have is a native view.
1916  GetWidget()->SetNativeWindowProperty(kBrowserViewKey, this);
1917
1918  // Stow a pointer to the browser's profile onto the window handle so that we
1919  // can get it later when all we have is a native view.
1920  GetWidget()->SetNativeWindowProperty(Profile::kProfileKey,
1921                                       browser_->profile());
1922
1923  // Start a hung plugin window detector for this browser object (as long as
1924  // hang detection is not disabled).
1925  if (!CommandLine::ForCurrentProcess()->HasSwitch(
1926          switches::kDisableHangMonitor)) {
1927    InitHangMonitor();
1928  }
1929
1930  LoadAccelerators();
1931
1932  infobar_container_ = new InfoBarContainerView(this);
1933  AddChildView(infobar_container_);
1934
1935  contents_web_view_ = new views::WebView(browser_->profile());
1936  contents_web_view_->set_id(VIEW_ID_TAB_CONTAINER);
1937  contents_web_view_->SetEmbedFullscreenWidgetMode(
1938      implicit_cast<content::WebContentsDelegate*>(browser_.get())->
1939          EmbedsFullscreenWidget());
1940  contents_container_ = new ContentsContainer(contents_web_view_);
1941
1942  SkColor bg_color = GetWidget()->GetThemeProvider()->
1943      GetColor(ThemeProperties::COLOR_TOOLBAR);
1944
1945  devtools_container_ = new views::WebView(browser_->profile());
1946  devtools_container_->set_id(VIEW_ID_DEV_TOOLS_DOCKED);
1947  devtools_container_->SetVisible(false);
1948
1949  views::View* contents_container_view = contents_container_;
1950
1951  contents_split_ = new views::SingleSplitView(
1952      contents_container_view,
1953      devtools_container_,
1954      views::SingleSplitView::VERTICAL_SPLIT,
1955      this);
1956  contents_split_->set_id(VIEW_ID_CONTENTS_SPLIT);
1957  contents_split_->SetAccessibleName(
1958      l10n_util::GetStringUTF16(IDS_ACCNAME_WEB_CONTENTS));
1959  contents_split_->set_background(
1960      views::Background::CreateSolidBackground(bg_color));
1961  AddChildView(contents_split_);
1962  set_contents_view(contents_split_);
1963
1964  InitStatusBubble();
1965
1966  // Top container holds tab strip and toolbar and lives at the front of the
1967  // view hierarchy.
1968  top_container_ = new TopContainerView(this);
1969  AddChildView(top_container_);
1970
1971  // TabStrip takes ownership of the controller.
1972  BrowserTabStripController* tabstrip_controller =
1973      new BrowserTabStripController(browser_.get(),
1974                                    browser_->tab_strip_model());
1975  tabstrip_ = new TabStrip(tabstrip_controller);
1976  top_container_->AddChildView(tabstrip_);
1977  tabstrip_controller->InitFromModel(tabstrip_);
1978
1979  toolbar_ = new ToolbarView(browser_.get());
1980  top_container_->AddChildView(toolbar_);
1981  toolbar_->Init();
1982
1983  // Create do-nothing view for the sake of controlling the z-order of the find
1984  // bar widget.
1985  find_bar_host_view_ = new View();
1986  AddChildView(find_bar_host_view_);
1987
1988  if (window_switcher_button_)
1989    AddChildView(window_switcher_button_);
1990
1991  immersive_mode_controller_->Init(this, GetWidget(), top_container_);
1992
1993  BrowserViewLayout* browser_view_layout = new BrowserViewLayout;
1994  browser_view_layout->Init(new BrowserViewLayoutDelegateImpl(this),
1995                            browser(),
1996                            this,
1997                            top_container_,
1998                            tabstrip_,
1999                            toolbar_,
2000                            infobar_container_,
2001                            contents_split_,
2002                            contents_container_,
2003                            immersive_mode_controller_.get());
2004  SetLayoutManager(browser_view_layout);
2005
2006#if defined(OS_WIN) && !defined(USE_AURA)
2007  // Create a custom JumpList and add it to an observer of TabRestoreService
2008  // so we can update the custom JumpList when a tab is added or removed.
2009  if (JumpList::Enabled()) {
2010    load_complete_listener_.reset(new LoadCompleteListener(this));
2011  }
2012#endif
2013
2014  GetLocationBar()->GetLocationEntry()->model()->popup_model()->AddObserver(
2015      this);
2016
2017  // We're now initialized and ready to process Layout requests.
2018  ignore_layout_ = false;
2019}
2020
2021void BrowserView::LoadingAnimationCallback() {
2022  base::TimeTicks now = base::TimeTicks::Now();
2023  if (!last_animation_time_.is_null()) {
2024    UMA_HISTOGRAM_TIMES(
2025        "Tabs.LoadingAnimationTime",
2026        now - last_animation_time_);
2027  }
2028  last_animation_time_ = now;
2029  if (browser_->is_type_tabbed()) {
2030    // Loading animations are shown in the tab for tabbed windows.  We check the
2031    // browser type instead of calling IsTabStripVisible() because the latter
2032    // will return false for fullscreen windows, but we still need to update
2033    // their animations (so that when they come out of fullscreen mode they'll
2034    // be correct).
2035    tabstrip_->UpdateLoadingAnimations();
2036  } else if (ShouldShowWindowIcon()) {
2037    // ... or in the window icon area for popups and app windows.
2038    WebContents* web_contents =
2039        browser_->tab_strip_model()->GetActiveWebContents();
2040    // GetActiveWebContents can return NULL for example under Purify when
2041    // the animations are running slowly and this function is called on a timer
2042    // through LoadingAnimationCallback.
2043    frame_->UpdateThrobber(web_contents && web_contents->IsLoading());
2044  }
2045}
2046
2047void BrowserView::OnLoadCompleted() {
2048#if defined(OS_WIN) && !defined(USE_AURA)
2049  DCHECK(!jumplist_);
2050  jumplist_ = new JumpList();
2051  jumplist_->AddObserver(browser_->profile());
2052#endif
2053}
2054
2055BrowserViewLayout* BrowserView::GetBrowserViewLayout() const {
2056  return static_cast<BrowserViewLayout*>(GetLayoutManager());
2057}
2058
2059void BrowserView::LayoutStatusBubble() {
2060  // In restored mode, the client area has a client edge between it and the
2061  // frame.
2062  int overlap = StatusBubbleViews::kShadowThickness;
2063  // The extra pixels defined by kClientEdgeThickness is only drawn in frame
2064  // content border on windows for non-aura build.
2065#if !defined(USE_ASH)
2066  overlap +=
2067      IsMaximized() ? 0 : views::NonClientFrameView::kClientEdgeThickness;
2068#endif
2069  int height = status_bubble_->GetPreferredSize().height();
2070  int contents_height = status_bubble_->base_view()->bounds().height();
2071  gfx::Point origin(-overlap, contents_height - height + overlap);
2072  status_bubble_->SetBounds(origin.x(), origin.y(), width() / 3, height);
2073}
2074
2075bool BrowserView::MaybeShowBookmarkBar(WebContents* contents) {
2076  bool show_bookmark_bar = contents &&
2077      browser_->SupportsWindowFeature(Browser::FEATURE_BOOKMARKBAR);
2078  if (!show_bookmark_bar && !bookmark_bar_view_.get())
2079    return false;
2080  if (!bookmark_bar_view_.get()) {
2081    bookmark_bar_view_.reset(new BookmarkBarView(browser_.get(), this));
2082    bookmark_bar_view_->set_owned_by_client();
2083    bookmark_bar_view_->set_background(
2084        new BookmarkExtensionBackground(this,
2085                                        bookmark_bar_view_.get(),
2086                                        browser_.get()));
2087    bookmark_bar_view_->SetBookmarkBarState(
2088        browser_->bookmark_bar_state(),
2089        BookmarkBar::DONT_ANIMATE_STATE_CHANGE);
2090    GetBrowserViewLayout()->set_bookmark_bar(bookmark_bar_view_.get());
2091  }
2092  bookmark_bar_view_->SetVisible(show_bookmark_bar);
2093  bookmark_bar_view_->SetPageNavigator(contents);
2094
2095  // Update parenting for the bookmark bar. This may detach it from all views.
2096  bool needs_layout = false;
2097  views::View* new_parent = NULL;
2098  if (show_bookmark_bar) {
2099    if (bookmark_bar_view_->IsDetached())
2100      new_parent = this;
2101    else
2102      new_parent = top_container_;
2103  }
2104  if (new_parent != bookmark_bar_view_->parent()) {
2105    SetBookmarkBarParent(new_parent);
2106    needs_layout = true;
2107  }
2108
2109  // Check for updates to the desired size.
2110  if (bookmark_bar_view_->GetPreferredSize().height() !=
2111      bookmark_bar_view_->height())
2112    needs_layout = true;
2113
2114  return needs_layout;
2115}
2116
2117void BrowserView::SetBookmarkBarParent(views::View* new_parent) {
2118  if (new_parent == this) {
2119    // Add it underneath |top_container_| or at the end if top container isn't
2120    // found.
2121    int top_container_index = GetIndexOf(top_container_);
2122    if (top_container_index >= 0)
2123      AddChildViewAt(bookmark_bar_view_.get(), top_container_index);
2124    else
2125      AddChildView(bookmark_bar_view_.get());
2126  } else if (new_parent) {
2127    // No special stacking is required for other parents.
2128    new_parent->AddChildView(bookmark_bar_view_.get());
2129  } else {
2130    // Bookmark bar is being detached from all views because it is hidden.
2131    bookmark_bar_view_->parent()->RemoveChildView(bookmark_bar_view_.get());
2132  }
2133}
2134
2135bool BrowserView::MaybeShowInfoBar(WebContents* contents) {
2136  // TODO(beng): Remove this function once the interface between
2137  //             InfoBarContainer, DownloadShelfView and WebContents and this
2138  //             view is sorted out.
2139  return true;
2140}
2141
2142void BrowserView::UpdateDevToolsForContents(WebContents* web_contents) {
2143  DevToolsWindow* new_devtools_window = web_contents ?
2144      DevToolsWindow::GetDockedInstanceForInspectedTab(web_contents) : NULL;
2145  // Fast return in case of the same window having same orientation.
2146  if (devtools_window_ == new_devtools_window) {
2147    if (!new_devtools_window ||
2148        (new_devtools_window->dock_side() == devtools_dock_side_)) {
2149      return;
2150    }
2151  }
2152
2153  // Replace tab contents.
2154  if (devtools_window_ != new_devtools_window) {
2155    devtools_container_->SetWebContents(
2156        new_devtools_window ? new_devtools_window->web_contents() : NULL);
2157  }
2158
2159  // Store last used position.
2160  if (devtools_window_) {
2161    int split_size = contents_split_->GetDividerSize();
2162    if (devtools_dock_side_ == DEVTOOLS_DOCK_SIDE_RIGHT) {
2163      devtools_window_->SetWidth(contents_split_->width() -
2164          split_size - contents_split_->divider_offset());
2165    } else if (devtools_dock_side_ == DEVTOOLS_DOCK_SIDE_BOTTOM) {
2166      devtools_window_->SetHeight(contents_split_->height() -
2167          split_size - contents_split_->divider_offset());
2168    }
2169  }
2170
2171  // Show / hide container if necessary. Changing dock orientation is
2172  // hide + show.
2173  bool should_hide = devtools_window_ && (!new_devtools_window ||
2174      devtools_dock_side_ != new_devtools_window->dock_side());
2175  bool should_show = new_devtools_window && (!devtools_window_ || should_hide);
2176
2177  if (should_hide)
2178    HideDevToolsContainer();
2179
2180  devtools_window_ = new_devtools_window;
2181
2182  if (should_show) {
2183    devtools_dock_side_ = new_devtools_window->dock_side();
2184    ShowDevToolsContainer();
2185  } else if (new_devtools_window) {
2186    UpdateDevToolsSplitPosition();
2187    contents_split_->Layout();
2188  }
2189}
2190
2191void BrowserView::ShowDevToolsContainer() {
2192  if (!devtools_focus_tracker_.get()) {
2193    // Install devtools focus tracker when dev tools window is shown for the
2194    // first time.
2195    devtools_focus_tracker_.reset(
2196        new views::ExternalFocusTracker(devtools_container_,
2197                                        GetFocusManager()));
2198  }
2199
2200  gfx::Size min_devtools_size(devtools_window_->GetMinimumWidth(),
2201      devtools_window_->GetMinimumHeight());
2202  devtools_container_->SetPreferredSize(min_devtools_size);
2203
2204  devtools_container_->SetVisible(true);
2205  devtools_dock_side_ = devtools_window_->dock_side();
2206  bool dock_to_right = devtools_dock_side_ == DEVTOOLS_DOCK_SIDE_RIGHT;
2207  contents_split_->set_orientation(
2208      dock_to_right ? views::SingleSplitView::HORIZONTAL_SPLIT
2209                    : views::SingleSplitView::VERTICAL_SPLIT);
2210  UpdateDevToolsSplitPosition();
2211  contents_split_->InvalidateLayout();
2212  Layout();
2213}
2214
2215void BrowserView::HideDevToolsContainer() {
2216  // Restore focus to the last focused view when hiding devtools window.
2217  devtools_focus_tracker_->FocusLastFocusedExternalView();
2218  devtools_container_->SetVisible(false);
2219  contents_split_->InvalidateLayout();
2220  Layout();
2221}
2222
2223void BrowserView::UpdateDevToolsSplitPosition() {
2224  contents_split_->set_resize_disabled(
2225      devtools_window_->dock_side() == DEVTOOLS_DOCK_SIDE_MINIMIZED);
2226  int split_size = contents_split_->GetDividerSize();
2227  if (devtools_window_->dock_side() == DEVTOOLS_DOCK_SIDE_RIGHT) {
2228    int split_offset = contents_split_->width() - split_size -
2229        devtools_window_->GetWidth(contents_split_->width());
2230    contents_split_->set_divider_offset(split_offset);
2231  } else {
2232    int height = devtools_window_->dock_side() == DEVTOOLS_DOCK_SIDE_MINIMIZED ?
2233        devtools_window_->GetMinimizedHeight() :
2234        devtools_window_->GetHeight(contents_split_->height());
2235    int split_offset = contents_split_->height() - split_size - height;
2236    contents_split_->set_divider_offset(split_offset);
2237  }
2238}
2239
2240void BrowserView::UpdateUIForContents(WebContents* contents) {
2241  bool needs_layout = MaybeShowBookmarkBar(contents);
2242  // TODO(jamescook): This function always returns true. Remove it and figure
2243  // out when layout is actually required.
2244  needs_layout |= MaybeShowInfoBar(contents);
2245  if (needs_layout)
2246    Layout();
2247}
2248
2249void BrowserView::ProcessFullscreen(bool fullscreen,
2250                                    FullscreenType type,
2251                                    const GURL& url,
2252                                    FullscreenExitBubbleType bubble_type) {
2253  // Reduce jankiness during the following position changes by:
2254  //   * Hiding the window until it's in the final position
2255  //   * Ignoring all intervening Layout() calls, which resize the webpage and
2256  //     thus are slow and look ugly
2257  ignore_layout_ = true;
2258  LocationBarView* location_bar = GetLocationBarView();
2259#if defined(OS_WIN) && !defined(USE_AURA)
2260  OmniboxViewWin* omnibox_win =
2261      GetOmniboxViewWin(location_bar->GetLocationEntry());
2262#endif
2263
2264  if (type == FOR_METRO || !fullscreen) {
2265    // Hide the fullscreen bubble as soon as possible, since the mode toggle can
2266    // take enough time for the user to notice.
2267    fullscreen_bubble_.reset();
2268  }
2269
2270  if (fullscreen) {
2271    // Move focus out of the location bar if necessary.
2272    views::FocusManager* focus_manager = GetFocusManager();
2273    DCHECK(focus_manager);
2274    // Look for focus in the location bar itself or any child view.
2275    if (location_bar->Contains(focus_manager->GetFocusedView()))
2276      focus_manager->ClearFocus();
2277
2278#if defined(OS_WIN) && !defined(USE_AURA)
2279    if (omnibox_win) {
2280      // If we don't hide the edit and force it to not show until we come out of
2281      // fullscreen, then if the user was on the New Tab Page, the edit contents
2282      // will appear atop the web contents once we go into fullscreen mode. This
2283      // has something to do with how we move the main window while it's hidden;
2284      // if we don't hide the main window below, we don't get this problem.
2285      omnibox_win->set_force_hidden(true);
2286      ShowWindow(omnibox_win->m_hWnd, SW_HIDE);
2287    }
2288#endif
2289  }
2290#if defined(OS_WIN) && !defined(USE_AURA)
2291  views::ScopedFullscreenVisibility visibility(frame_->GetNativeView());
2292#endif
2293
2294  if (type == FOR_METRO) {
2295#if defined(OS_WIN) && !defined(USE_AURA)
2296    // Enter metro snap mode.
2297    static_cast<views::NativeWidgetWin*>(
2298        frame_->native_widget())->SetMetroSnapFullscreen(fullscreen);
2299#endif
2300  } else {
2301    // Toggle fullscreen mode.
2302    frame_->SetFullscreen(fullscreen);
2303  }
2304
2305  // Enable immersive before the browser refreshes its list of enabled commands.
2306  if (ShouldUseImmersiveFullscreenForUrl(url))
2307    immersive_mode_controller_->SetEnabled(fullscreen);
2308
2309  browser_->WindowFullscreenStateChanged();
2310
2311  if (fullscreen) {
2312    if (!chrome::IsRunningInAppMode() && type != FOR_METRO)
2313      UpdateFullscreenExitBubbleContent(url, bubble_type);
2314  } else {
2315#if defined(OS_WIN) && !defined(USE_AURA)
2316    if (omnibox_win) {
2317      // Show the edit again since we're no longer in fullscreen mode.
2318      omnibox_win->set_force_hidden(false);
2319      ShowWindow(omnibox_win->m_hWnd, SW_SHOW);
2320    }
2321#endif
2322  }
2323
2324  // Undo our anti-jankiness hacks and force a re-layout. We also need to
2325  // recompute the height of the infobar top arrow because toggling in and out
2326  // of fullscreen changes it. Calling ToolbarSizeChanged() will do both these
2327  // things since it computes the arrow height directly and forces a layout
2328  // indirectly via UpdateUIForContents().
2329  ignore_layout_ = false;
2330  ToolbarSizeChanged(false);
2331}
2332
2333bool BrowserView::ShouldUseImmersiveFullscreenForUrl(const GURL& url) const {
2334  bool is_browser_fullscreen = url.is_empty();
2335  return ImmersiveFullscreenConfiguration::UseImmersiveFullscreen() &&
2336      is_browser_fullscreen && IsBrowserTypeNormal();
2337}
2338
2339void BrowserView::LoadAccelerators() {
2340#if defined(OS_WIN) && !defined(USE_AURA)
2341  HACCEL accelerator_table = AtlLoadAccelerators(IDR_MAINFRAME);
2342  DCHECK(accelerator_table);
2343
2344  // We have to copy the table to access its contents.
2345  int count = CopyAcceleratorTable(accelerator_table, 0, 0);
2346  if (count == 0) {
2347    // Nothing to do in that case.
2348    return;
2349  }
2350
2351  ACCEL* accelerators = static_cast<ACCEL*>(malloc(sizeof(ACCEL) * count));
2352  CopyAcceleratorTable(accelerator_table, accelerators, count);
2353
2354  views::FocusManager* focus_manager = GetFocusManager();
2355  DCHECK(focus_manager);
2356
2357  // Let's fill our own accelerator table.
2358  for (int i = 0; i < count; ++i) {
2359    ui::Accelerator accelerator(
2360        static_cast<ui::KeyboardCode>(accelerators[i].key),
2361        ui::GetModifiersFromACCEL(accelerators[i]));
2362    accelerator_table_[accelerator] = accelerators[i].cmd;
2363
2364    // Also register with the focus manager.
2365    focus_manager->RegisterAccelerator(
2366        accelerator, ui::AcceleratorManager::kNormalPriority, this);
2367  }
2368
2369  // We don't need the Windows accelerator table anymore.
2370  free(accelerators);
2371#else
2372  views::FocusManager* focus_manager = GetFocusManager();
2373  DCHECK(focus_manager);
2374
2375  // Let's fill our own accelerator table.
2376  const bool is_app_mode = chrome::IsRunningInForcedAppMode();
2377  const std::vector<chrome::AcceleratorMapping> accelerator_list(
2378      chrome::GetAcceleratorList());
2379  for (std::vector<chrome::AcceleratorMapping>::const_iterator it =
2380           accelerator_list.begin(); it != accelerator_list.end(); ++it) {
2381    // In app mode, only allow accelerators of white listed commands to pass
2382    // through.
2383    if (is_app_mode && !chrome::IsCommandAllowedInAppMode(it->command_id))
2384      continue;
2385
2386    ui::Accelerator accelerator(it->keycode, it->modifiers);
2387    accelerator_table_[accelerator] = it->command_id;
2388
2389    // Also register with the focus manager.
2390    focus_manager->RegisterAccelerator(
2391        accelerator, ui::AcceleratorManager::kNormalPriority, this);
2392  }
2393#endif
2394}
2395
2396int BrowserView::GetCommandIDForAppCommandID(int app_command_id) const {
2397#if defined(OS_WIN)
2398  switch (app_command_id) {
2399    // NOTE: The order here matches the APPCOMMAND declaration order in the
2400    // Windows headers.
2401    case APPCOMMAND_BROWSER_BACKWARD: return IDC_BACK;
2402    case APPCOMMAND_BROWSER_FORWARD:  return IDC_FORWARD;
2403    case APPCOMMAND_BROWSER_REFRESH:  return IDC_RELOAD;
2404    case APPCOMMAND_BROWSER_HOME:     return IDC_HOME;
2405    case APPCOMMAND_BROWSER_STOP:     return IDC_STOP;
2406    case APPCOMMAND_BROWSER_SEARCH:   return IDC_FOCUS_SEARCH;
2407    case APPCOMMAND_HELP:             return IDC_HELP_PAGE_VIA_KEYBOARD;
2408    case APPCOMMAND_NEW:              return IDC_NEW_TAB;
2409    case APPCOMMAND_OPEN:             return IDC_OPEN_FILE;
2410    case APPCOMMAND_CLOSE:            return IDC_CLOSE_TAB;
2411    case APPCOMMAND_SAVE:             return IDC_SAVE_PAGE;
2412    case APPCOMMAND_PRINT:            return IDC_PRINT;
2413    case APPCOMMAND_COPY:             return IDC_COPY;
2414    case APPCOMMAND_CUT:              return IDC_CUT;
2415    case APPCOMMAND_PASTE:            return IDC_PASTE;
2416
2417      // TODO(pkasting): http://b/1113069 Handle these.
2418    case APPCOMMAND_UNDO:
2419    case APPCOMMAND_REDO:
2420    case APPCOMMAND_SPELL_CHECK:
2421    default:                          return -1;
2422  }
2423#else
2424  // App commands are Windows-specific so there's nothing to do here.
2425  return -1;
2426#endif
2427}
2428
2429void BrowserView::InitHangMonitor() {
2430#if defined(OS_WIN) && !defined(USE_AURA)
2431  PrefService* pref_service = g_browser_process->local_state();
2432  if (!pref_service)
2433    return;
2434
2435  int plugin_message_response_timeout =
2436      pref_service->GetInteger(prefs::kPluginMessageResponseTimeout);
2437  int hung_plugin_detect_freq =
2438      pref_service->GetInteger(prefs::kHungPluginDetectFrequency);
2439  if ((hung_plugin_detect_freq > 0) &&
2440      hung_window_detector_.Initialize(GetWidget()->GetNativeView(),
2441                                       plugin_message_response_timeout)) {
2442    ticker_.set_tick_interval(hung_plugin_detect_freq);
2443    ticker_.RegisterTickHandler(&hung_window_detector_);
2444    ticker_.Start();
2445
2446    pref_service->SetInteger(prefs::kPluginMessageResponseTimeout,
2447                             plugin_message_response_timeout);
2448    pref_service->SetInteger(prefs::kHungPluginDetectFrequency,
2449                             hung_plugin_detect_freq);
2450  }
2451#endif
2452}
2453
2454void BrowserView::UpdateAcceleratorMetrics(const ui::Accelerator& accelerator,
2455                                           int command_id) {
2456  const ui::KeyboardCode key_code = accelerator.key_code();
2457  if (command_id == IDC_HELP_PAGE_VIA_KEYBOARD && key_code == ui::VKEY_F1)
2458    content::RecordAction(UserMetricsAction("ShowHelpTabViaF1"));
2459
2460  if (command_id == IDC_BOOKMARK_PAGE)
2461    UMA_HISTOGRAM_ENUMERATION("Bookmarks.EntryPoint",
2462                              BOOKMARK_ENTRY_POINT_ACCELERATOR,
2463                              BOOKMARK_ENTRY_POINT_LIMIT);
2464
2465#if defined(OS_CHROMEOS)
2466  // Collect information about the relative popularity of various accelerators
2467  // on Chrome OS.
2468  switch (command_id) {
2469    case IDC_BACK:
2470      if (key_code == ui::VKEY_BACK)
2471        content::RecordAction(UserMetricsAction("Accel_Back_Backspace"));
2472      else if (key_code == ui::VKEY_BROWSER_BACK)
2473        content::RecordAction(UserMetricsAction("Accel_Back_F1"));
2474      else if (key_code == ui::VKEY_LEFT)
2475        content::RecordAction(UserMetricsAction("Accel_Back_Left"));
2476      break;
2477    case IDC_FORWARD:
2478      if (key_code == ui::VKEY_BACK)
2479        content::RecordAction(UserMetricsAction("Accel_Forward_Backspace"));
2480      else if (key_code == ui::VKEY_BROWSER_FORWARD)
2481        content::RecordAction(UserMetricsAction("Accel_Forward_F2"));
2482      else if (key_code == ui::VKEY_RIGHT)
2483        content::RecordAction(UserMetricsAction("Accel_Forward_Right"));
2484      break;
2485    case IDC_RELOAD:
2486    case IDC_RELOAD_IGNORING_CACHE:
2487      if (key_code == ui::VKEY_R)
2488        content::RecordAction(UserMetricsAction("Accel_Reload_R"));
2489      else if (key_code == ui::VKEY_BROWSER_REFRESH)
2490        content::RecordAction(UserMetricsAction("Accel_Reload_F3"));
2491      break;
2492    case IDC_FOCUS_LOCATION:
2493      if (key_code == ui::VKEY_D)
2494        content::RecordAction(UserMetricsAction("Accel_FocusLocation_D"));
2495      else if (key_code == ui::VKEY_L)
2496        content::RecordAction(UserMetricsAction("Accel_FocusLocation_L"));
2497      break;
2498    case IDC_FOCUS_SEARCH:
2499      if (key_code == ui::VKEY_E)
2500        content::RecordAction(UserMetricsAction("Accel_FocusSearch_E"));
2501      else if (key_code == ui::VKEY_K)
2502        content::RecordAction(UserMetricsAction("Accel_FocusSearch_K"));
2503      break;
2504    default:
2505      // Do nothing.
2506      break;
2507  }
2508#endif
2509}
2510
2511// static
2512BrowserWindow* BrowserWindow::CreateBrowserWindow(Browser* browser) {
2513  // Create the view and the frame. The frame will attach itself via the view
2514  // so we don't need to do anything with the pointer.
2515  BrowserView* view = new BrowserView();
2516  view->Init(browser);
2517  (new BrowserFrame(view))->InitBrowserFrame();
2518  view->GetWidget()->non_client_view()->SetAccessibleName(
2519      l10n_util::GetStringUTF16(IDS_PRODUCT_NAME));
2520  return view;
2521}
2522
2523void BrowserView::ShowAvatarBubble(WebContents* web_contents,
2524                                   const gfx::Rect& rect) {
2525  gfx::Point origin(rect.origin());
2526  views::View::ConvertPointToScreen(GetTabContentsContainerView(), &origin);
2527  gfx::Rect bounds(origin, rect.size());
2528
2529  AvatarMenuBubbleView::ShowBubble(this, views::BubbleBorder::TOP_RIGHT,
2530      views::BubbleBorder::ALIGN_EDGE_TO_ANCHOR_EDGE, bounds, browser_.get());
2531}
2532
2533void BrowserView::ShowAvatarBubbleFromAvatarButton() {
2534  AvatarMenuButton* button = frame_->GetAvatarMenuButton();
2535  if (button)
2536    button->ShowAvatarBubble();
2537}
2538
2539void BrowserView::ShowPasswordGenerationBubble(
2540    const gfx::Rect& rect,
2541    const autofill::PasswordForm& form,
2542    autofill::PasswordGenerator* password_generator) {
2543  // Create a rect in the content bounds that the bubble will point to.
2544  gfx::Point origin(rect.origin());
2545  views::View::ConvertPointToScreen(GetTabContentsContainerView(), &origin);
2546  gfx::Rect bounds(origin, rect.size());
2547
2548  // Create the bubble.
2549  WebContents* web_contents = GetActiveWebContents();
2550  if (!web_contents)
2551    return;
2552
2553  PasswordGenerationBubbleView* bubble =
2554      new PasswordGenerationBubbleView(
2555          form,
2556          bounds,
2557          this,
2558          web_contents->GetRenderViewHost(),
2559          PasswordManager::FromWebContents(web_contents),
2560          password_generator,
2561          browser_.get(),
2562          GetWidget()->GetThemeProvider());
2563
2564  views::BubbleDelegateView::CreateBubble(bubble);
2565  bubble->SetAlignment(views::BubbleBorder::ALIGN_ARROW_TO_MID_ANCHOR);
2566  bubble->GetWidget()->Show();
2567}
2568
2569void BrowserView::OverscrollUpdate(int delta_y) {
2570  if (scroll_end_effect_controller_)
2571    scroll_end_effect_controller_->OverscrollUpdate(delta_y);
2572}
2573
2574int BrowserView::GetRenderViewHeightInsetWithDetachedBookmarkBar() {
2575  if (browser_->bookmark_bar_state() != BookmarkBar::DETACHED ||
2576      !bookmark_bar_view_.get() || !bookmark_bar_view_->IsDetached()) {
2577    return 0;
2578  }
2579  // Don't use bookmark_bar_view_->height() which won't be the final height if
2580  // the bookmark bar is animating.
2581  return chrome::kNTPBookmarkBarHeight -
2582      bookmark_bar_view_->GetFullyDetachedToolbarOverlap();
2583}
2584
2585void BrowserView::DoCutCopyPaste(void (content::RenderWidgetHost::*method)(),
2586#if defined(OS_WIN)
2587                                 int windows_msg_id,
2588#endif
2589                                 int command_id) {
2590  WebContents* contents = browser_->tab_strip_model()->GetActiveWebContents();
2591  if (!contents)
2592    return;
2593  if (DoCutCopyPasteForWebContents(contents, method))
2594    return;
2595
2596  DevToolsWindow* devtools_window =
2597      DevToolsWindow::GetDockedInstanceForInspectedTab(contents);
2598  if (devtools_window &&
2599      DoCutCopyPasteForWebContents(devtools_window->web_contents(), method)) {
2600    return;
2601  }
2602
2603  views::FocusManager* focus_manager = GetFocusManager();
2604  views::View* focused = focus_manager->GetFocusedView();
2605  if (focused &&
2606      (!strcmp(focused->GetClassName(), views::Textfield::kViewClassName) ||
2607       !strcmp(focused->GetClassName(), OmniboxViewViews::kViewClassName))) {
2608    views::Textfield* textfield = static_cast<views::Textfield*>(focused);
2609    textfield->ExecuteCommand(command_id);
2610    return;
2611  }
2612
2613#if defined(OS_WIN) && !defined(USE_AURA)
2614  OmniboxView* omnibox_view = GetLocationBarView()->GetLocationEntry();
2615  if (omnibox_view->model()->has_focus()) {
2616    OmniboxViewWin* omnibox_win = GetOmniboxViewWin(omnibox_view);
2617    ::SendMessage(omnibox_win->GetNativeView(), windows_msg_id, 0, 0);
2618  }
2619#endif
2620}
2621
2622bool BrowserView::DoCutCopyPasteForWebContents(
2623    WebContents* contents,
2624    void (content::RenderWidgetHost::*method)()) {
2625  gfx::NativeView native_view = contents->GetView()->GetContentNativeView();
2626  if (!native_view)
2627    return false;
2628#if defined(USE_AURA)
2629  if (native_view->HasFocus()) {
2630#elif defined(OS_WIN)
2631  if (native_view == ::GetFocus()) {
2632#endif
2633    (contents->GetRenderViewHost()->*method)();
2634    return true;
2635  }
2636
2637  return false;
2638}
2639
2640void BrowserView::ActivateAppModalDialog() const {
2641  // If another browser is app modal, flash and activate the modal browser.
2642  AppModalDialog* active_dialog =
2643      AppModalDialogQueue::GetInstance()->active_dialog();
2644  if (!active_dialog)
2645    return;
2646
2647  Browser* modal_browser =
2648      chrome::FindBrowserWithWebContents(active_dialog->web_contents());
2649  if (modal_browser && (browser_ != modal_browser)) {
2650    modal_browser->window()->FlashFrame(true);
2651    modal_browser->window()->Activate();
2652  }
2653
2654  AppModalDialogQueue::GetInstance()->ActivateModalDialog();
2655}
2656
2657int BrowserView::GetMaxTopInfoBarArrowHeight() {
2658  int top_arrow_height = 0;
2659  // Only show the arrows when not in fullscreen and when there's no omnibox
2660  // popup.
2661  if (!IsFullscreen() &&
2662      !GetLocationBar()->GetLocationEntry()->model()->popup_model()->IsOpen()) {
2663    const LocationIconView* location_icon_view =
2664        toolbar_->location_bar()->location_icon_view();
2665    // The +1 in the next line creates a 1-px gap between icon and arrow tip.
2666    gfx::Point icon_bottom(0, location_icon_view->GetImageBounds().bottom() -
2667        LocationBarView::kIconInternalPadding + 1);
2668    ConvertPointToTarget(location_icon_view, this, &icon_bottom);
2669    gfx::Point infobar_top(0, infobar_container_->GetVerticalOverlap(NULL));
2670    ConvertPointToTarget(infobar_container_, this, &infobar_top);
2671    top_arrow_height = infobar_top.y() - icon_bottom.y();
2672  }
2673  return top_arrow_height;
2674}
2675