1// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/ui/views/frame/browser_view.h"
6
7#if defined(OS_LINUX)
8#include <gtk/gtk.h>
9#endif
10
11#include "base/auto_reset.h"
12#include "base/command_line.h"
13#include "base/i18n/rtl.h"
14#include "base/metrics/histogram.h"
15#include "base/string_number_conversions.h"
16#include "base/utf_string_conversions.h"
17#include "chrome/app/chrome_command_ids.h"
18#include "chrome/app/chrome_dll_resource.h"
19#include "chrome/browser/autocomplete/autocomplete_popup_model.h"
20#include "chrome/browser/autocomplete/autocomplete_popup_view.h"
21#include "chrome/browser/automation/ui_controls.h"
22#include "chrome/browser/bookmarks/bookmark_utils.h"
23#include "chrome/browser/browser_process.h"
24#include "chrome/browser/debugger/devtools_window.h"
25#include "chrome/browser/download/download_manager.h"
26#include "chrome/browser/extensions/extension_tab_helper.h"
27#include "chrome/browser/extensions/extension_tts_api.h"
28#include "chrome/browser/instant/instant_controller.h"
29#include "chrome/browser/metrics/user_metrics.h"
30#include "chrome/browser/ntp_background_util.h"
31#include "chrome/browser/page_info_window.h"
32#include "chrome/browser/prefs/pref_service.h"
33#include "chrome/browser/profiles/profile.h"
34#include "chrome/browser/sessions/tab_restore_service.h"
35#include "chrome/browser/sidebar/sidebar_container.h"
36#include "chrome/browser/sidebar/sidebar_manager.h"
37#include "chrome/browser/tabs/tab_strip_model.h"
38#include "chrome/browser/themes/theme_service.h"
39#include "chrome/browser/ui/app_modal_dialogs/app_modal_dialog_queue.h"
40#include "chrome/browser/ui/browser.h"
41#include "chrome/browser/ui/browser_dialogs.h"
42#include "chrome/browser/ui/browser_list.h"
43#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
44#include "chrome/browser/ui/toolbar/wrench_menu_model.h"
45#include "chrome/browser/ui/view_ids.h"
46#include "chrome/browser/ui/views/bookmarks/bookmark_bar_view.h"
47#include "chrome/browser/ui/views/browser_dialogs.h"
48#include "chrome/browser/ui/views/default_search_view.h"
49#include "chrome/browser/ui/views/download/download_in_progress_dialog_view.h"
50#include "chrome/browser/ui/views/download/download_shelf_view.h"
51#include "chrome/browser/ui/views/frame/browser_view_layout.h"
52#include "chrome/browser/ui/views/frame/contents_container.h"
53#include "chrome/browser/ui/views/fullscreen_exit_bubble.h"
54#include "chrome/browser/ui/views/infobars/infobar_container_view.h"
55#include "chrome/browser/ui/views/location_bar/location_icon_view.h"
56#include "chrome/browser/ui/views/status_bubble_views.h"
57#include "chrome/browser/ui/views/tab_contents/tab_contents_container.h"
58#include "chrome/browser/ui/views/tabs/browser_tab_strip_controller.h"
59#include "chrome/browser/ui/views/tabs/tab_strip_factory.h"
60#include "chrome/browser/ui/views/theme_install_bubble_view.h"
61#include "chrome/browser/ui/views/toolbar_view.h"
62#include "chrome/browser/ui/views/update_recommended_message_box.h"
63#include "chrome/browser/ui/views/window.h"
64#include "chrome/browser/ui/webui/bug_report_ui.h"
65#include "chrome/browser/ui/window_sizer.h"
66#include "chrome/common/chrome_switches.h"
67#include "chrome/common/extensions/extension_resource.h"
68#include "chrome/common/native_window_notification_source.h"
69#include "chrome/common/pref_names.h"
70#include "chrome/common/url_constants.h"
71#include "content/browser/renderer_host/render_widget_host_view.h"
72#include "content/browser/tab_contents/tab_contents.h"
73#include "content/browser/tab_contents/tab_contents_view.h"
74#include "content/common/notification_service.h"
75#include "grit/app_resources.h"
76#include "grit/chromium_strings.h"
77#include "grit/generated_resources.h"
78#include "grit/locale_settings.h"
79#include "grit/theme_resources.h"
80#include "grit/webkit_resources.h"
81#include "ui/base/accessibility/accessible_view_state.h"
82#include "ui/base/l10n/l10n_util.h"
83#include "ui/base/resource/resource_bundle.h"
84#include "ui/gfx/canvas_skia.h"
85#include "views/controls/single_split_view.h"
86#include "views/events/event.h"
87#include "views/focus/external_focus_tracker.h"
88#include "views/focus/view_storage.h"
89#include "views/layout/grid_layout.h"
90#include "views/widget/root_view.h"
91#include "views/window/dialog_delegate.h"
92#include "views/window/window.h"
93
94#if defined(OS_WIN)
95#include "chrome/browser/aeropeek_manager.h"
96#include "chrome/browser/jumplist_win.h"
97#include "ui/base/message_box_win.h"
98#include "ui/base/view_prop.h"
99#include "views/window/window_win.h"
100#elif defined(OS_LINUX)
101#include "chrome/browser/ui/views/accelerator_table_gtk.h"
102#include "views/window/hit_test.h"
103#include "views/window/window_gtk.h"
104#endif
105
106#if defined(OS_CHROMEOS)
107#include "chrome/browser/ui/views/keyboard_overlay_dialog_view.h"
108#endif
109
110using base::TimeDelta;
111using views::ColumnSet;
112using views::GridLayout;
113
114// The height of the status bubble.
115static const int kStatusBubbleHeight = 20;
116// The name of a key to store on the window handle so that other code can
117// locate this object using just the handle.
118static const char* const kBrowserViewKey = "__BROWSER_VIEW__";
119// How frequently we check for hung plugin windows.
120static const int kDefaultHungPluginDetectFrequency = 2000;
121// How long do we wait before we consider a window hung (in ms).
122static const int kDefaultPluginMessageResponseTimeout = 30000;
123// The number of milliseconds between loading animation frames.
124static const int kLoadingAnimationFrameTimeMs = 30;
125// The amount of space we expect the window border to take up.
126static const int kWindowBorderWidth = 5;
127
128// How round the 'new tab' style bookmarks bar is.
129static const int kNewtabBarRoundness = 5;
130// ------------
131
132// Returned from BrowserView::GetClassName.
133const char BrowserView::kViewClassName[] = "browser/ui/views/BrowserView";
134
135#if defined(OS_CHROMEOS)
136// Get a normal browser window of given |profile| to use as dialog parent
137// if given |browser| is not one. Otherwise, returns browser window of
138// |browser|. If |profile| is NULL, |browser|'s profile is used to find the
139// normal browser.
140static gfx::NativeWindow GetNormalBrowserWindowForBrowser(Browser* browser,
141                                                          Profile* profile) {
142  if (browser->type() != Browser::TYPE_NORMAL) {
143    Browser* normal_browser = BrowserList::FindBrowserWithType(
144        profile ? profile : browser->profile(),
145        Browser::TYPE_NORMAL, true);
146    if (normal_browser && normal_browser->window())
147      return normal_browser->window()->GetNativeHandle();
148  }
149
150  return browser->window()->GetNativeHandle();
151}
152#endif  // defined(OS_CHROMEOS)
153
154///////////////////////////////////////////////////////////////////////////////
155// BookmarkExtensionBackground, private:
156// This object serves as the views::Background object which is used to layout
157// and paint the bookmark bar.
158class BookmarkExtensionBackground : public views::Background {
159 public:
160  explicit BookmarkExtensionBackground(BrowserView* browser_view,
161                                       DetachableToolbarView* host_view,
162                                       Browser* browser);
163
164  // View methods overridden from views:Background.
165  virtual void Paint(gfx::Canvas* canvas, views::View* view) const;
166
167 private:
168  BrowserView* browser_view_;
169
170  // The view hosting this background.
171  DetachableToolbarView* host_view_;
172
173  Browser* browser_;
174
175  DISALLOW_COPY_AND_ASSIGN(BookmarkExtensionBackground);
176};
177
178BookmarkExtensionBackground::BookmarkExtensionBackground(
179    BrowserView* browser_view,
180    DetachableToolbarView* host_view,
181    Browser* browser)
182    : browser_view_(browser_view),
183      host_view_(host_view),
184      browser_(browser) {
185}
186
187void BookmarkExtensionBackground::Paint(gfx::Canvas* canvas,
188                                        views::View* view) const {
189  ui::ThemeProvider* tp = host_view_->GetThemeProvider();
190  int toolbar_overlap = host_view_->GetToolbarOverlap();
191  // The client edge is drawn below the toolbar bounds.
192  if (toolbar_overlap)
193    toolbar_overlap += views::NonClientFrameView::kClientEdgeThickness;
194  if (host_view_->IsDetached()) {
195    // Draw the background to match the new tab page.
196    int height = 0;
197    TabContents* contents = browser_->GetSelectedTabContents();
198    if (contents && contents->view())
199      height = contents->view()->GetContainerSize().height();
200    NtpBackgroundUtil::PaintBackgroundDetachedMode(
201        host_view_->GetThemeProvider(), canvas,
202        gfx::Rect(0, toolbar_overlap, host_view_->width(),
203                  host_view_->height() - toolbar_overlap), height);
204
205    // As 'hidden' according to the animation is the full in-tab state,
206    // we invert the value - when current_state is at '0', we expect the
207    // bar to be docked.
208    double current_state = 1 - host_view_->GetAnimationValue();
209    double h_padding =
210        static_cast<double>(BookmarkBarView::kNewtabHorizontalPadding) *
211        current_state;
212    double v_padding =
213        static_cast<double>(BookmarkBarView::kNewtabVerticalPadding) *
214        current_state;
215
216    SkRect rect;
217    double roundness = 0;
218    DetachableToolbarView::CalculateContentArea(current_state, h_padding,
219        v_padding, &rect, &roundness, host_view_);
220    DetachableToolbarView::PaintContentAreaBackground(canvas, tp, rect,
221                                                      roundness);
222    DetachableToolbarView::PaintContentAreaBorder(canvas, tp, rect, roundness);
223    if (!toolbar_overlap)
224      DetachableToolbarView::PaintHorizontalBorder(canvas, host_view_);
225  } else {
226    DetachableToolbarView::PaintBackgroundAttachedMode(canvas, host_view_,
227        browser_view_->OffsetPointForToolbarBackgroundImage(
228        gfx::Point(host_view_->GetMirroredX(), host_view_->y())));
229    if (host_view_->height() >= toolbar_overlap)
230      DetachableToolbarView::PaintHorizontalBorder(canvas, host_view_);
231  }
232}
233
234///////////////////////////////////////////////////////////////////////////////
235// ResizeCorner, private:
236
237class ResizeCorner : public views::View {
238 public:
239  ResizeCorner() {
240    EnableCanvasFlippingForRTLUI(true);
241  }
242
243  virtual void OnPaint(gfx::Canvas* canvas) {
244    views::Window* window = GetWindow();
245    if (!window || (window->IsMaximized() || window->IsFullscreen()))
246      return;
247
248    SkBitmap* bitmap = ResourceBundle::GetSharedInstance().GetBitmapNamed(
249        IDR_TEXTAREA_RESIZER);
250    bitmap->buildMipMap(false);
251    canvas->DrawBitmapInt(*bitmap, width() - bitmap->width(),
252                          height() - bitmap->height());
253  }
254
255  static gfx::Size GetSize() {
256    // This is disabled until we find what makes us slower when we let
257    // WebKit know that we have a resizer rect...
258    // int scrollbar_thickness = gfx::scrollbar_size();
259    // return gfx::Size(scrollbar_thickness, scrollbar_thickness);
260    return gfx::Size();
261  }
262
263  virtual gfx::Size GetPreferredSize() {
264    views::Window* window = GetWindow();
265    return (!window || window->IsMaximized() || window->IsFullscreen()) ?
266        gfx::Size() : GetSize();
267  }
268
269  virtual void Layout() {
270    if (parent()) {
271      gfx::Size ps = GetPreferredSize();
272      // No need to handle Right to left text direction here,
273      // our parent must take care of it for us...
274      // TODO(alekseys): fix it.
275      SetBounds(parent()->width() - ps.width(),
276                parent()->height() - ps.height(), ps.width(), ps.height());
277    }
278  }
279
280 private:
281  // Returns the WindowWin we're displayed in. Returns NULL if we're not
282  // currently in a window.
283  views::Window* GetWindow() {
284    views::Widget* widget = GetWidget();
285    return widget ? widget->GetWindow() : NULL;
286  }
287
288  DISALLOW_COPY_AND_ASSIGN(ResizeCorner);
289};
290
291///////////////////////////////////////////////////////////////////////////////
292// BrowserView, public:
293
294BrowserView::BrowserView(Browser* browser)
295    : views::ClientView(NULL, NULL),
296      last_focused_view_storage_id_(
297          views::ViewStorage::GetInstance()->CreateStorageID()),
298      frame_(NULL),
299      browser_(browser),
300      active_bookmark_bar_(NULL),
301      tabstrip_(NULL),
302      toolbar_(NULL),
303      infobar_container_(NULL),
304      sidebar_container_(NULL),
305      sidebar_split_(NULL),
306      contents_container_(NULL),
307      devtools_container_(NULL),
308      preview_container_(NULL),
309      contents_(NULL),
310      contents_split_(NULL),
311      initialized_(false),
312      ignore_layout_(true)
313#if defined(OS_WIN)
314      , hung_window_detector_(&hung_plugin_action_),
315      ticker_(0)
316#endif
317                 {
318  browser_->tabstrip_model()->AddObserver(this);
319
320  registrar_.Add(this,
321                 NotificationType::SIDEBAR_CHANGED,
322                 Source<SidebarManager>(SidebarManager::GetInstance()));
323}
324
325BrowserView::~BrowserView() {
326  browser_->tabstrip_model()->RemoveObserver(this);
327
328#if defined(OS_WIN)
329  // Remove this observer.
330  if (aeropeek_manager_.get())
331    browser_->tabstrip_model()->RemoveObserver(aeropeek_manager_.get());
332
333  // Stop hung plugin monitoring.
334  ticker_.Stop();
335  ticker_.UnregisterTickHandler(&hung_window_detector_);
336#endif
337
338  // We destroy the download shelf before |browser_| to remove its child
339  // download views from the set of download observers (since the observed
340  // downloads can be destroyed along with |browser_| and the observer
341  // notifications will call back into deleted objects).
342  download_shelf_.reset();
343
344  // The TabStrip attaches a listener to the model. Make sure we shut down the
345  // TabStrip first so that it can cleanly remove the listener.
346  if (tabstrip_) {
347    tabstrip_->parent()->RemoveChildView(tabstrip_);
348    delete tabstrip_;
349    tabstrip_ = NULL;
350  }
351  // Child views maintain PrefMember attributes that point to
352  // OffTheRecordProfile's PrefService which gets deleted by ~Browser.
353  RemoveAllChildViews(true);
354  // Explicitly set browser_ to NULL.
355  browser_.reset();
356}
357
358// static
359BrowserView* BrowserView::GetBrowserViewForNativeWindow(
360    gfx::NativeWindow window) {
361#if defined(OS_WIN)
362  if (IsWindow(window)) {
363    return reinterpret_cast<BrowserView*>(
364        ui::ViewProp::GetValue(window, kBrowserViewKey));
365  }
366#else
367  if (window) {
368    return static_cast<BrowserView*>(
369        g_object_get_data(G_OBJECT(window), kBrowserViewKey));
370  }
371#endif
372  return NULL;
373}
374
375gfx::Rect BrowserView::GetToolbarBounds() const {
376  gfx::Rect toolbar_bounds(toolbar_->bounds());
377  if (toolbar_bounds.IsEmpty())
378    return toolbar_bounds;
379  // When using vertical tabs, the toolbar appears to extend behind the tab
380  // column.
381  if (UseVerticalTabs())
382    toolbar_bounds.Inset(tabstrip_->x() - toolbar_bounds.x(), 0, 0, 0);
383  // The apparent toolbar edges are outside the "real" toolbar edges.
384  toolbar_bounds.Inset(-views::NonClientFrameView::kClientEdgeThickness, 0);
385  return toolbar_bounds;
386}
387
388gfx::Rect BrowserView::GetClientAreaBounds() const {
389  gfx::Rect container_bounds = contents_->bounds();
390  gfx::Point container_origin = container_bounds.origin();
391  ConvertPointToView(this, parent(), &container_origin);
392  container_bounds.set_origin(container_origin);
393  return container_bounds;
394}
395
396gfx::Rect BrowserView::GetFindBarBoundingBox() const {
397  return GetBrowserViewLayout()->GetFindBarBoundingBox();
398}
399
400int BrowserView::GetTabStripHeight() const {
401  // We want to return tabstrip_->height(), but we might be called in the midst
402  // of layout, when that hasn't yet been updated to reflect the current state.
403  // So return what the tabstrip height _ought_ to be right now.
404  return IsTabStripVisible() ? tabstrip_->GetPreferredSize().height() : 0;
405}
406
407gfx::Point BrowserView::OffsetPointForToolbarBackgroundImage(
408    const gfx::Point& point) const {
409  // The background image starts tiling horizontally at the window left edge and
410  // vertically at the top edge of the horizontal tab strip (or where it would
411  // be).  We expect our parent's origin to be the window origin.
412  gfx::Point window_point(point.Add(GetMirroredPosition()));
413  window_point.Offset(0, -frame_->GetHorizontalTabStripVerticalOffset(false));
414  return window_point;
415}
416
417int BrowserView::GetSidebarWidth() const {
418  if (!sidebar_container_ || !sidebar_container_->IsVisible())
419    return 0;
420  return sidebar_split_->divider_offset();
421}
422
423bool BrowserView::IsTabStripVisible() const {
424  return browser_->SupportsWindowFeature(Browser::FEATURE_TABSTRIP);
425}
426
427bool BrowserView::UseVerticalTabs() const {
428  return browser_->tabstrip_model()->delegate()->UseVerticalTabs();
429}
430
431bool BrowserView::IsOffTheRecord() const {
432  return browser_->profile()->IsOffTheRecord();
433}
434
435bool BrowserView::ShouldShowOffTheRecordAvatar() const {
436  return IsOffTheRecord() && IsBrowserTypeNormal();
437}
438
439bool BrowserView::AcceleratorPressed(const views::Accelerator& accelerator) {
440#if defined(OS_CHROMEOS)
441  // If accessibility is enabled, stop speech and return false so that key
442  // combinations involving Search can be used for extra accessibility
443  // functionality.
444  if (accelerator.GetKeyCode() == ui::VKEY_LWIN &&
445      g_browser_process->local_state()->GetBoolean(
446          prefs::kAccessibilityEnabled)) {
447    ExtensionTtsController::GetInstance()->Stop();
448    return false;
449  }
450#endif
451
452  std::map<views::Accelerator, int>::const_iterator iter =
453      accelerator_table_.find(accelerator);
454  DCHECK(iter != accelerator_table_.end());
455  int command_id = iter->second;
456
457  if (!browser_->block_command_execution())
458    UpdateAcceleratorMetrics(accelerator, command_id);
459  return browser_->ExecuteCommandIfEnabled(command_id);
460}
461
462bool BrowserView::GetAccelerator(int cmd_id, ui::Accelerator* accelerator) {
463  // The standard Ctrl-X, Ctrl-V and Ctrl-C are not defined as accelerators
464  // anywhere so we need to check for them explicitly here.
465  switch (cmd_id) {
466    case IDC_CUT:
467      *accelerator = views::Accelerator(ui::VKEY_X, false, true, false);
468      return true;
469    case IDC_COPY:
470      *accelerator = views::Accelerator(ui::VKEY_C, false, true, false);
471      return true;
472    case IDC_PASTE:
473      *accelerator = views::Accelerator(ui::VKEY_V, false, true, false);
474      return true;
475  }
476  // Else, we retrieve the accelerator information from the accelerator table.
477  std::map<views::Accelerator, int>::iterator it =
478      accelerator_table_.begin();
479  for (; it != accelerator_table_.end(); ++it) {
480    if (it->second == cmd_id) {
481      *accelerator = it->first;
482      return true;
483    }
484  }
485  return false;
486}
487
488bool BrowserView::ActivateAppModalDialog() const {
489  // If another browser is app modal, flash and activate the modal browser.
490  if (AppModalDialogQueue::GetInstance()->HasActiveDialog()) {
491    Browser* active_browser = BrowserList::GetLastActive();
492    if (active_browser && (browser_ != active_browser)) {
493      active_browser->window()->FlashFrame();
494      active_browser->window()->Activate();
495    }
496    AppModalDialogQueue::GetInstance()->ActivateModalDialog();
497    return true;
498  }
499  return false;
500}
501
502TabContents* BrowserView::GetSelectedTabContents() const {
503  return browser_->GetSelectedTabContents();
504}
505
506TabContentsWrapper* BrowserView::GetSelectedTabContentsWrapper() const {
507  return browser_->GetSelectedTabContentsWrapper();
508}
509
510SkBitmap BrowserView::GetOTRAvatarIcon() {
511  static SkBitmap* otr_avatar_ = new SkBitmap();
512
513  if (otr_avatar_->isNull()) {
514    ResourceBundle& rb = ResourceBundle::GetSharedInstance();
515    *otr_avatar_ = *rb.GetBitmapNamed(IDR_OTR_ICON);
516  }
517  return *otr_avatar_;
518}
519
520#if defined(OS_WIN)
521void BrowserView::PrepareToRunSystemMenu(HMENU menu) {
522  system_menu_->UpdateStates();
523}
524#endif
525
526// static
527void BrowserView::RegisterBrowserViewPrefs(PrefService* prefs) {
528  prefs->RegisterIntegerPref(prefs::kPluginMessageResponseTimeout,
529                             kDefaultPluginMessageResponseTimeout);
530  prefs->RegisterIntegerPref(prefs::kHungPluginDetectFrequency,
531                             kDefaultHungPluginDetectFrequency);
532}
533
534bool BrowserView::IsPositionInWindowCaption(const gfx::Point& point) {
535  return GetBrowserViewLayout()->IsPositionInWindowCaption(point);
536}
537
538///////////////////////////////////////////////////////////////////////////////
539// BrowserView, BrowserWindow implementation:
540
541void BrowserView::Show() {
542  // If the window is already visible, just activate it.
543  if (frame_->GetWindow()->IsVisible()) {
544    frame_->GetWindow()->Activate();
545    return;
546  }
547
548  // Setting the focus doesn't work when the window is invisible, so any focus
549  // initialization that happened before this will be lost.
550  //
551  // We really "should" restore the focus whenever the window becomes unhidden,
552  // but I think initializing is the only time where this can happen where
553  // there is some focus change we need to pick up, and this is easier than
554  // plumbing through an un-hide message all the way from the frame.
555  //
556  // If we do find there are cases where we need to restore the focus on show,
557  // that should be added and this should be removed.
558  RestoreFocus();
559
560  frame_->GetWindow()->Show();
561}
562
563void BrowserView::ShowInactive() {
564  views::Window* window = frame_->GetWindow();
565  if (!window->IsVisible())
566    window->ShowInactive();
567}
568
569void BrowserView::SetBounds(const gfx::Rect& bounds) {
570  SetFullscreen(false);
571  GetWidget()->SetBounds(bounds);
572}
573
574void BrowserView::Close() {
575  BrowserBubbleHost::Close();
576
577  frame_->GetWindow()->CloseWindow();
578}
579
580void BrowserView::Activate() {
581  frame_->GetWindow()->Activate();
582}
583
584void BrowserView::Deactivate() {
585  frame_->GetWindow()->Deactivate();
586}
587
588bool BrowserView::IsActive() const {
589  return frame_->GetWindow()->IsActive();
590}
591
592void BrowserView::FlashFrame() {
593#if defined(OS_WIN)
594  FLASHWINFO fwi;
595  fwi.cbSize = sizeof(fwi);
596  fwi.hwnd = frame_->GetWindow()->GetNativeWindow();
597  fwi.dwFlags = FLASHW_ALL;
598  fwi.uCount = 4;
599  fwi.dwTimeout = 0;
600  FlashWindowEx(&fwi);
601#else
602  // Doesn't matter for chrome os.
603#endif
604}
605
606gfx::NativeWindow BrowserView::GetNativeHandle() {
607  return GetWidget()->GetWindow()->GetNativeWindow();
608}
609
610BrowserWindowTesting* BrowserView::GetBrowserWindowTesting() {
611  return this;
612}
613
614StatusBubble* BrowserView::GetStatusBubble() {
615  return status_bubble_.get();
616}
617
618namespace {
619  // Only used by ToolbarSizeChanged() below, but placed here because template
620  // arguments (to AutoReset<>) must have external linkage.
621  enum CallState { NORMAL, REENTRANT, REENTRANT_FORCE_FAST_RESIZE };
622}
623
624void BrowserView::ToolbarSizeChanged(bool is_animating) {
625  // The call to InfoBarContainer::SetMaxTopArrowHeight() below can result in
626  // reentrancy; |call_state| tracks whether we're reentrant.  We can't just
627  // early-return in this case because we need to layout again so the infobar
628  // container's bounds are set correctly.
629  static CallState call_state = NORMAL;
630
631  // A reentrant call can (and should) use the fast resize path unless both it
632  // and the normal call are both non-animating.
633  bool use_fast_resize =
634      is_animating || (call_state == REENTRANT_FORCE_FAST_RESIZE);
635  if (use_fast_resize)
636    contents_container_->SetFastResize(true);
637  UpdateUIForContents(browser_->GetSelectedTabContentsWrapper());
638  if (use_fast_resize)
639    contents_container_->SetFastResize(false);
640
641  // Inform the InfoBarContainer that the distance to the location icon may have
642  // changed.  We have to do this after the block above so that the toolbars are
643  // laid out correctly for calculating the maximum arrow height below.
644  {
645    const LocationIconView* location_icon_view =
646        toolbar_->location_bar()->location_icon_view();
647    // The +1 in the next line creates a 1-px gap between icon and arrow tip.
648    gfx::Point icon_bottom(0, location_icon_view->GetImageBounds().bottom() -
649        LocationBarView::kIconInternalPadding + 1);
650    ConvertPointToView(location_icon_view, this, &icon_bottom);
651    gfx::Point infobar_top(0, infobar_container_->GetVerticalOverlap(NULL));
652    ConvertPointToView(infobar_container_, this, &infobar_top);
653
654    AutoReset<CallState> resetter(&call_state,
655        is_animating ? REENTRANT_FORCE_FAST_RESIZE : REENTRANT);
656    infobar_container_->SetMaxTopArrowHeight(infobar_top.y() - icon_bottom.y());
657  }
658
659  // When transitioning from animating to not animating we need to make sure the
660  // contents_container_ gets layed out. If we don't do this and the bounds
661  // haven't changed contents_container_ won't get a Layout out and we'll end up
662  // with a gray rect because the clip wasn't updated.  Note that a reentrant
663  // call never needs to do this, because after it returns, the normal call
664  // wrapping it will do it.
665  if ((call_state == NORMAL) && !is_animating) {
666    contents_container_->InvalidateLayout();
667    contents_split_->Layout();
668  }
669}
670
671void BrowserView::UpdateTitleBar() {
672  frame_->GetWindow()->UpdateWindowTitle();
673  if (ShouldShowWindowIcon() && !loading_animation_timer_.IsRunning())
674    frame_->GetWindow()->UpdateWindowIcon();
675}
676
677void BrowserView::ShelfVisibilityChanged() {
678  Layout();
679}
680
681void BrowserView::UpdateDevTools() {
682  UpdateDevToolsForContents(GetSelectedTabContentsWrapper());
683  Layout();
684}
685
686void BrowserView::UpdateLoadingAnimations(bool should_animate) {
687  if (should_animate) {
688    if (!loading_animation_timer_.IsRunning()) {
689      // Loads are happening, and the timer isn't running, so start it.
690      last_animation_time_ = base::TimeTicks::Now();
691      loading_animation_timer_.Start(
692          TimeDelta::FromMilliseconds(kLoadingAnimationFrameTimeMs), this,
693          &BrowserView::LoadingAnimationCallback);
694    }
695  } else {
696    if (loading_animation_timer_.IsRunning()) {
697      last_animation_time_ = base::TimeTicks();
698      loading_animation_timer_.Stop();
699      // Loads are now complete, update the state if a task was scheduled.
700      LoadingAnimationCallback();
701    }
702  }
703}
704
705void BrowserView::SetStarredState(bool is_starred) {
706  toolbar_->location_bar()->SetStarToggled(is_starred);
707}
708
709gfx::Rect BrowserView::GetRestoredBounds() const {
710  return frame_->GetWindow()->GetNormalBounds();
711}
712
713gfx::Rect BrowserView::GetBounds() const {
714  return frame_->GetWindow()->GetBounds();
715}
716
717bool BrowserView::IsMaximized() const {
718  return frame_->GetWindow()->IsMaximized();
719}
720
721void BrowserView::SetFullscreen(bool fullscreen) {
722  if (IsFullscreen() == fullscreen)
723    return;  // Nothing to do.
724
725#if defined(OS_WIN)
726  ProcessFullscreen(fullscreen);
727#else
728  // On Linux changing fullscreen is async. Ask the window to change it's
729  // fullscreen state, and when done invoke ProcessFullscreen.
730  frame_->GetWindow()->SetFullscreen(fullscreen);
731#endif
732}
733
734bool BrowserView::IsFullscreen() const {
735  return frame_->GetWindow()->IsFullscreen();
736}
737
738bool BrowserView::IsFullscreenBubbleVisible() const {
739  return fullscreen_bubble_.get() ? true : false;
740}
741
742void BrowserView::FullScreenStateChanged() {
743  ProcessFullscreen(IsFullscreen());
744}
745
746void BrowserView::RestoreFocus() {
747  TabContents* selected_tab_contents = GetSelectedTabContents();
748  if (selected_tab_contents)
749    selected_tab_contents->view()->RestoreFocus();
750}
751
752LocationBar* BrowserView::GetLocationBar() const {
753  return toolbar_->location_bar();
754}
755
756void BrowserView::SetFocusToLocationBar(bool select_all) {
757  LocationBarView* location_bar = toolbar_->location_bar();
758  if (location_bar->IsFocusableInRootView()) {
759    // Location bar got focus.
760    location_bar->FocusLocation(select_all);
761  } else {
762    // If none of location bar/compact navigation bar got focus,
763    // then clear focus.
764    views::FocusManager* focus_manager = GetFocusManager();
765    DCHECK(focus_manager);
766    focus_manager->ClearFocus();
767  }
768}
769
770void BrowserView::UpdateReloadStopState(bool is_loading, bool force) {
771  toolbar_->reload_button()->ChangeMode(
772      is_loading ? ReloadButton::MODE_STOP : ReloadButton::MODE_RELOAD, force);
773}
774
775void BrowserView::UpdateToolbar(TabContentsWrapper* contents,
776                                bool should_restore_state) {
777  toolbar_->Update(contents->tab_contents(), should_restore_state);
778}
779
780void BrowserView::FocusToolbar() {
781  // Start the traversal within the main toolbar, passing it the storage id
782  // of the view where focus should be returned if the user exits the toolbar.
783  SaveFocusedView();
784  toolbar_->SetPaneFocus(last_focused_view_storage_id_, NULL);
785}
786
787void BrowserView::FocusBookmarksToolbar() {
788  if (active_bookmark_bar_ && bookmark_bar_view_->IsVisible()) {
789    SaveFocusedView();
790    bookmark_bar_view_->SetPaneFocus(last_focused_view_storage_id_, NULL);
791  }
792}
793
794void BrowserView::FocusAppMenu() {
795  // Chrome doesn't have a traditional menu bar, but it has a menu button in the
796  // main toolbar that plays the same role.  If the user presses a key that
797  // would typically focus the menu bar, tell the toolbar to focus the menu
798  // button.  If the user presses the key again, return focus to the previous
799  // location.
800  //
801  // Not used on the Mac, which has a normal menu bar.
802  if (toolbar_->IsAppMenuFocused()) {
803    RestoreFocus();
804  } else {
805    SaveFocusedView();
806    toolbar_->SetPaneFocusAndFocusAppMenu(last_focused_view_storage_id_);
807  }
808}
809
810void BrowserView::RotatePaneFocus(bool forwards) {
811  // This gets called when the user presses F6 (forwards) or Shift+F6
812  // (backwards) to rotate to the next pane. Here, our "panes" are the
813  // tab contents and each of our accessible toolbars, infobars, downloads
814  // shelf, etc.  When a pane has focus, all of its controls are accessible
815  // in the tab traversal, and the tab traversal is "trapped" within that pane.
816  //
817  // Get a vector of all panes in the order we want them to be focused,
818  // with NULL to represent the tab contents getting focus. If one of these
819  // is currently invisible or has no focusable children it will be
820  // automatically skipped.
821  std::vector<AccessiblePaneView*> accessible_panes;
822  GetAccessiblePanes(&accessible_panes);
823  int pane_count = static_cast<int>(accessible_panes.size());
824
825  std::vector<views::View*> accessible_views(
826      accessible_panes.begin(), accessible_panes.end());
827  accessible_views.push_back(GetTabContentsContainerView());
828  if (sidebar_container_ && sidebar_container_->IsVisible())
829    accessible_views.push_back(GetSidebarContainerView());
830  if (devtools_container_->IsVisible())
831    accessible_views.push_back(devtools_container_->GetFocusView());
832  int count = static_cast<int>(accessible_views.size());
833
834  // Figure out which view (if any) currently has the focus.
835  views::View* focused_view = GetFocusManager()->GetFocusedView();
836  int index = -1;
837  if (focused_view) {
838    for (int i = 0; i < count; ++i) {
839      if (accessible_views[i] == focused_view ||
840          accessible_views[i]->Contains(focused_view)) {
841        index = i;
842        break;
843      }
844    }
845  }
846
847  // If the focus isn't currently in a pane, save the focus so we
848  // can restore it if the user presses Escape.
849  if (focused_view && index >= pane_count)
850    SaveFocusedView();
851
852  // Try to focus the next pane; if SetPaneFocusAndFocusDefault returns
853  // false it means the pane didn't have any focusable controls, so skip
854  // it and try the next one.
855  for (;;) {
856    if (forwards)
857      index = (index + 1) % count;
858    else
859      index = ((index - 1) + count) % count;
860
861    if (index < pane_count) {
862      if (accessible_panes[index]->SetPaneFocusAndFocusDefault(
863              last_focused_view_storage_id_)) {
864        break;
865      }
866    } else {
867      accessible_views[index]->RequestFocus();
868      break;
869    }
870  }
871}
872
873void BrowserView::SaveFocusedView() {
874  views::ViewStorage* view_storage = views::ViewStorage::GetInstance();
875  if (view_storage->RetrieveView(last_focused_view_storage_id_))
876    view_storage->RemoveView(last_focused_view_storage_id_);
877  views::View* focused_view = GetFocusManager()->GetFocusedView();
878  if (focused_view)
879    view_storage->StoreView(last_focused_view_storage_id_, focused_view);
880}
881
882void BrowserView::DestroyBrowser() {
883  // Explicitly delete the BookmarkBarView now. That way we don't have to
884  // worry about the BookmarkBarView potentially outliving the Browser &
885  // Profile.
886  bookmark_bar_view_.reset();
887  browser_.reset();
888}
889
890bool BrowserView::IsBookmarkBarVisible() const {
891  return browser_->SupportsWindowFeature(Browser::FEATURE_BOOKMARKBAR) &&
892      active_bookmark_bar_ &&
893      (active_bookmark_bar_->GetPreferredSize().height() != 0);
894}
895
896bool BrowserView::IsBookmarkBarAnimating() const {
897  return bookmark_bar_view_.get() && bookmark_bar_view_->is_animating();
898}
899
900bool BrowserView::IsTabStripEditable() const {
901  return tabstrip_->IsTabStripEditable();
902}
903
904bool BrowserView::IsToolbarVisible() const {
905  return browser_->SupportsWindowFeature(Browser::FEATURE_TOOLBAR) ||
906         browser_->SupportsWindowFeature(Browser::FEATURE_LOCATIONBAR);
907}
908
909void BrowserView::DisableInactiveFrame() {
910#if defined(OS_WIN)
911  frame_->GetWindow()->DisableInactiveRendering();
912#endif  // No tricks are needed to get the right behavior on Linux.
913}
914
915void BrowserView::ConfirmSetDefaultSearchProvider(
916    TabContents* tab_contents,
917    TemplateURL* template_url,
918    TemplateURLModel* template_url_model) {
919#if defined(OS_WIN)
920  DefaultSearchView::Show(tab_contents, template_url, template_url_model);
921#else
922  // TODO(levin): Implement for other platforms. Right now this is behind
923  // a command line flag which is off. http://crbug.com/38475
924#endif
925}
926
927void BrowserView::ConfirmAddSearchProvider(const TemplateURL* template_url,
928                                           Profile* profile) {
929  browser::EditSearchEngine(GetWindow()->GetNativeWindow(), template_url, NULL,
930                            profile);
931}
932
933void BrowserView::ToggleBookmarkBar() {
934  bookmark_utils::ToggleWhenVisible(browser_->profile());
935}
936
937void BrowserView::ShowAboutChromeDialog() {
938  DoShowAboutChromeDialog();
939}
940
941views::Window* BrowserView::DoShowAboutChromeDialog() {
942  return browser::ShowAboutChromeView(GetWindow()->GetNativeWindow(),
943                                      browser_->profile());
944}
945
946void BrowserView::ShowUpdateChromeDialog() {
947  UpdateRecommendedMessageBox::ShowMessageBox(GetWindow()->GetNativeWindow());
948}
949
950void BrowserView::ShowTaskManager() {
951  browser::ShowTaskManager();
952}
953
954void BrowserView::ShowBackgroundPages() {
955  browser::ShowBackgroundPages();
956}
957
958void BrowserView::ShowBookmarkBubble(const GURL& url, bool already_bookmarked) {
959  toolbar_->location_bar()->ShowStarBubble(url, !already_bookmarked);
960}
961
962void BrowserView::SetDownloadShelfVisible(bool visible) {
963  // This can be called from the superclass destructor, when it destroys our
964  // child views. At that point, browser_ is already gone.
965  if (browser_ == NULL)
966    return;
967
968  if (visible && IsDownloadShelfVisible() != visible) {
969    // Invoke GetDownloadShelf to force the shelf to be created.
970    GetDownloadShelf();
971  }
972
973  if (browser_ != NULL)
974    browser_->UpdateDownloadShelfVisibility(visible);
975
976  // SetDownloadShelfVisible can force-close the shelf, so make sure we lay out
977  // everything correctly, as if the animation had finished. This doesn't
978  // matter for showing the shelf, as the show animation will do it.
979  ToolbarSizeChanged(false);
980}
981
982bool BrowserView::IsDownloadShelfVisible() const {
983  return download_shelf_.get() && download_shelf_->IsShowing();
984}
985
986DownloadShelf* BrowserView::GetDownloadShelf() {
987  if (!download_shelf_.get()) {
988    download_shelf_.reset(new DownloadShelfView(browser_.get(), this));
989    download_shelf_->set_parent_owned(false);
990  }
991  return download_shelf_.get();
992}
993
994void BrowserView::ShowRepostFormWarningDialog(TabContents* tab_contents) {
995  browser::ShowRepostFormWarningDialog(GetNativeHandle(), tab_contents);
996}
997
998void BrowserView::ShowCollectedCookiesDialog(TabContents* tab_contents) {
999  browser::ShowCollectedCookiesDialog(GetNativeHandle(), tab_contents);
1000}
1001
1002void BrowserView::ShowThemeInstallBubble() {
1003  TabContents* tab_contents = browser_->GetSelectedTabContents();
1004  if (!tab_contents)
1005    return;
1006  ThemeInstallBubbleView::Show(tab_contents);
1007}
1008
1009void BrowserView::ConfirmBrowserCloseWithPendingDownloads() {
1010  DownloadInProgressDialogView* view =
1011      new DownloadInProgressDialogView(browser_.get());
1012  browser::CreateViewsWindow(GetNativeHandle(), gfx::Rect(), view)->Show();
1013}
1014
1015void BrowserView::ShowHTMLDialog(HtmlDialogUIDelegate* delegate,
1016                                 gfx::NativeWindow parent_window) {
1017  // Default to using our window as the parent if the argument is not specified.
1018  gfx::NativeWindow parent = parent_window ? parent_window
1019                                           : GetNativeHandle();
1020#if defined(OS_CHROMEOS)
1021  parent = GetNormalBrowserWindowForBrowser(browser(), NULL);
1022#endif  // defined(OS_CHROMEOS)
1023
1024  browser::ShowHtmlDialog(parent, browser_.get()->profile(), delegate);
1025}
1026
1027void BrowserView::ShowCreateWebAppShortcutsDialog(
1028    TabContentsWrapper* tab_contents) {
1029  browser::ShowCreateWebAppShortcutsDialog(GetNativeHandle(), tab_contents);
1030}
1031
1032void BrowserView::ShowCreateChromeAppShortcutsDialog(Profile* profile,
1033                                                     const Extension* app) {
1034  browser::ShowCreateChromeAppShortcutsDialog(GetNativeHandle(), profile, app);
1035}
1036
1037void BrowserView::UserChangedTheme() {
1038  frame_->GetWindow()->FrameTypeChanged();
1039}
1040
1041int BrowserView::GetExtraRenderViewHeight() const {
1042  // Currently this is only used on linux.
1043  return 0;
1044}
1045
1046void BrowserView::TabContentsFocused(TabContents* tab_contents) {
1047  contents_container_->TabContentsFocused(tab_contents);
1048}
1049
1050void BrowserView::ShowPageInfo(Profile* profile,
1051                               const GURL& url,
1052                               const NavigationEntry::SSLStatus& ssl,
1053                               bool show_history) {
1054  gfx::NativeWindow parent = GetWindow()->GetNativeWindow();
1055
1056  browser::ShowPageInfoBubble(parent, profile, url, ssl, show_history);
1057}
1058
1059void BrowserView::ShowAppMenu() {
1060  toolbar_->app_menu()->Activate();
1061}
1062
1063bool BrowserView::PreHandleKeyboardEvent(const NativeWebKeyboardEvent& event,
1064                                         bool* is_keyboard_shortcut) {
1065  if (event.type != WebKit::WebInputEvent::RawKeyDown)
1066    return false;
1067
1068#if defined(OS_WIN)
1069  // As Alt+F4 is the close-app keyboard shortcut, it needs processing
1070  // immediately.
1071  if (event.windowsKeyCode == ui::VKEY_F4 &&
1072      event.modifiers == NativeWebKeyboardEvent::AltKey) {
1073    DefWindowProc(event.os_event.hwnd, event.os_event.message,
1074                  event.os_event.wParam, event.os_event.lParam);
1075    return true;
1076  }
1077#endif
1078
1079  views::FocusManager* focus_manager = GetFocusManager();
1080  DCHECK(focus_manager);
1081
1082#if defined(OS_LINUX) && !defined(TOUCH_UI)
1083  // Views and WebKit use different tables for GdkEventKey -> views::KeyEvent
1084  // conversion. We need to use View's conversion table here to keep consistent
1085  // behavior with views::FocusManager::OnKeyEvent() method.
1086  // TODO(suzhe): We need to check if Windows code also has this issue, and
1087  // it'll be best if we can unify these conversion tables.
1088  // See http://crbug.com/54315
1089  views::KeyEvent views_event(reinterpret_cast<GdkEvent*>(event.os_event));
1090  views::Accelerator accelerator(views_event.key_code(),
1091                                 views_event.IsShiftDown(),
1092                                 views_event.IsControlDown(),
1093                                 views_event.IsAltDown());
1094#else
1095  views::Accelerator accelerator(
1096      static_cast<ui::KeyboardCode>(event.windowsKeyCode),
1097      (event.modifiers & NativeWebKeyboardEvent::ShiftKey) ==
1098          NativeWebKeyboardEvent::ShiftKey,
1099      (event.modifiers & NativeWebKeyboardEvent::ControlKey) ==
1100          NativeWebKeyboardEvent::ControlKey,
1101      (event.modifiers & NativeWebKeyboardEvent::AltKey) ==
1102          NativeWebKeyboardEvent::AltKey);
1103#endif
1104
1105  // We first find out the browser command associated to the |event|.
1106  // Then if the command is a reserved one, and should be processed
1107  // immediately according to the |event|, the command will be executed
1108  // immediately. Otherwise we just set |*is_keyboard_shortcut| properly and
1109  // return false.
1110
1111  // This piece of code is based on the fact that accelerators registered
1112  // into the |focus_manager| may only trigger a browser command execution.
1113  //
1114  // Here we need to retrieve the command id (if any) associated to the
1115  // keyboard event. Instead of looking up the command id in the
1116  // |accelerator_table_| by ourselves, we block the command execution of
1117  // the |browser_| object then send the keyboard event to the
1118  // |focus_manager| as if we are activating an accelerator key.
1119  // Then we can retrieve the command id from the |browser_| object.
1120  browser_->SetBlockCommandExecution(true);
1121  focus_manager->ProcessAccelerator(accelerator);
1122  int id = browser_->GetLastBlockedCommand(NULL);
1123  browser_->SetBlockCommandExecution(false);
1124
1125  if (id == -1)
1126    return false;
1127
1128  // Executing the command may cause |this| object to be destroyed.
1129#if defined(OS_LINUX) && !defined(TOUCH_UI)
1130  if (browser_->IsReservedCommandOrKey(id, event) &&
1131      !event.match_edit_command) {
1132#else
1133  if (browser_->IsReservedCommandOrKey(id, event)) {
1134#endif
1135    UpdateAcceleratorMetrics(accelerator, id);
1136    return browser_->ExecuteCommandIfEnabled(id);
1137  }
1138
1139  DCHECK(is_keyboard_shortcut != NULL);
1140  *is_keyboard_shortcut = true;
1141
1142  return false;
1143}
1144
1145void BrowserView::HandleKeyboardEvent(const NativeWebKeyboardEvent& event) {
1146#if defined(OS_LINUX) && !defined(TOUCH_UI)
1147  views::Window* window = GetWidget()->GetWindow();
1148  if (window && event.os_event && !event.skip_in_browser) {
1149    views::KeyEvent views_event(reinterpret_cast<GdkEvent*>(event.os_event));
1150    static_cast<views::WindowGtk*>(window)->HandleKeyboardEvent(views_event);
1151  }
1152#else
1153  unhandled_keyboard_event_handler_.HandleKeyboardEvent(event,
1154                                                        GetFocusManager());
1155#endif
1156}
1157
1158// TODO(devint): http://b/issue?id=1117225 Cut, Copy, and Paste are always
1159// enabled in the page menu regardless of whether the command will do
1160// anything. When someone selects the menu item, we just act as if they hit
1161// the keyboard shortcut for the command by sending the associated key press
1162// to windows. The real fix to this bug is to disable the commands when they
1163// won't do anything. We'll need something like an overall clipboard command
1164// manager to do that.
1165void BrowserView::Cut() {
1166  ui_controls::SendKeyPress(GetNativeHandle(), ui::VKEY_X,
1167                            true, false, false, false);
1168}
1169
1170void BrowserView::Copy() {
1171  ui_controls::SendKeyPress(GetNativeHandle(), ui::VKEY_C,
1172                            true, false, false, false);
1173}
1174
1175void BrowserView::Paste() {
1176  ui_controls::SendKeyPress(GetNativeHandle(), ui::VKEY_V,
1177                            true, false, false, false);
1178}
1179
1180void BrowserView::ToggleTabStripMode() {
1181  InitTabStrip(browser_->tabstrip_model());
1182  frame_->TabStripDisplayModeChanged();
1183}
1184
1185void BrowserView::PrepareForInstant() {
1186  contents_->FadeActiveContents();
1187}
1188
1189void BrowserView::ShowInstant(TabContentsWrapper* preview) {
1190  if (!preview_container_)
1191    preview_container_ = new TabContentsContainer();
1192  contents_->SetPreview(preview_container_, preview->tab_contents());
1193  preview_container_->ChangeTabContents(preview->tab_contents());
1194
1195#if defined(OS_WIN)
1196  // Removing the fade is instant (on windows). We need to force the preview to
1197  // draw, otherwise the current page flickers before the new page appears.
1198  RedrawWindow(preview->tab_contents()->view()->GetContentNativeView(), NULL,
1199               NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_NOCHILDREN);
1200#endif
1201
1202  contents_->RemoveFade();
1203}
1204
1205void BrowserView::HideInstant(bool instant_is_active) {
1206  if (instant_is_active)
1207    contents_->ShowFade();
1208  else
1209    contents_->RemoveFade();
1210
1211  if (!preview_container_)
1212    return;
1213
1214  // The contents must be changed before SetPreview is invoked.
1215  preview_container_->ChangeTabContents(NULL);
1216  contents_->SetPreview(NULL, NULL);
1217  delete preview_container_;
1218  preview_container_ = NULL;
1219}
1220
1221gfx::Rect BrowserView::GetInstantBounds() {
1222  return contents_->GetPreviewBounds();
1223}
1224
1225#if defined(OS_CHROMEOS)
1226void BrowserView::ShowKeyboardOverlay(gfx::NativeWindow owning_window) {
1227  KeyboardOverlayDialogView::ShowDialog(owning_window, this);
1228}
1229#endif
1230
1231///////////////////////////////////////////////////////////////////////////////
1232// BrowserView, BrowserWindowTesting implementation:
1233
1234BookmarkBarView* BrowserView::GetBookmarkBarView() const {
1235  return bookmark_bar_view_.get();
1236}
1237
1238LocationBarView* BrowserView::GetLocationBarView() const {
1239  return toolbar_->location_bar();
1240}
1241
1242views::View* BrowserView::GetTabContentsContainerView() const {
1243  return contents_container_->GetFocusView();
1244}
1245
1246views::View* BrowserView::GetSidebarContainerView() const {
1247  if (!sidebar_container_)
1248    return NULL;
1249  return sidebar_container_->GetFocusView();
1250}
1251
1252ToolbarView* BrowserView::GetToolbarView() const {
1253  return toolbar_;
1254}
1255
1256///////////////////////////////////////////////////////////////////////////////
1257// BrowserView, NotificationObserver implementation:
1258
1259void BrowserView::Observe(NotificationType type,
1260                          const NotificationSource& source,
1261                          const NotificationDetails& details) {
1262  switch (type.value) {
1263    case NotificationType::PREF_CHANGED:
1264      if (*Details<std::string>(details).ptr() == prefs::kShowBookmarkBar &&
1265          MaybeShowBookmarkBar(browser_->GetSelectedTabContentsWrapper())) {
1266        Layout();
1267      }
1268      break;
1269
1270    case NotificationType::SIDEBAR_CHANGED:
1271      if (Details<SidebarContainer>(details)->tab_contents() ==
1272          browser_->GetSelectedTabContents()) {
1273        UpdateSidebar();
1274      }
1275      break;
1276
1277    default:
1278      NOTREACHED() << "Got a notification we didn't register for!";
1279      break;
1280  }
1281}
1282
1283///////////////////////////////////////////////////////////////////////////////
1284// BrowserView, TabStripModelObserver implementation:
1285
1286void BrowserView::TabDetachedAt(TabContentsWrapper* contents, int index) {
1287  // We use index here rather than comparing |contents| because by this time
1288  // the model has already removed |contents| from its list, so
1289  // browser_->GetSelectedTabContents() will return NULL or something else.
1290  if (index == browser_->tabstrip_model()->active_index()) {
1291    // We need to reset the current tab contents to NULL before it gets
1292    // freed. This is because the focus manager performs some operations
1293    // on the selected TabContents when it is removed.
1294    contents_container_->ChangeTabContents(NULL);
1295    infobar_container_->ChangeTabContents(NULL);
1296    UpdateSidebarForContents(NULL);
1297    UpdateDevToolsForContents(NULL);
1298  }
1299}
1300
1301void BrowserView::TabDeselected(TabContentsWrapper* contents) {
1302  // We do not store the focus when closing the tab to work-around bug 4633.
1303  // Some reports seem to show that the focus manager and/or focused view can
1304  // be garbage at that point, it is not clear why.
1305  if (!contents->tab_contents()->is_being_destroyed())
1306    contents->view()->StoreFocus();
1307}
1308
1309void BrowserView::TabSelectedAt(TabContentsWrapper* old_contents,
1310                                TabContentsWrapper* new_contents,
1311                                int index,
1312                                bool user_gesture) {
1313  if (old_contents == new_contents)
1314    return;
1315
1316  ProcessTabSelected(new_contents, true);
1317}
1318
1319void BrowserView::TabReplacedAt(TabStripModel* tab_strip_model,
1320                                TabContentsWrapper* old_contents,
1321                                TabContentsWrapper* new_contents,
1322                                int index) {
1323  if (index != browser_->tabstrip_model()->active_index())
1324    return;
1325
1326  // Swap the 'active' and 'preview' and delete what was the active.
1327  contents_->MakePreviewContentsActiveContents();
1328  TabContentsContainer* old_container = contents_container_;
1329  contents_container_ = preview_container_;
1330  old_container->ChangeTabContents(NULL);
1331  delete old_container;
1332  preview_container_ = NULL;
1333
1334  // Update the UI for what was the preview contents and is now active. Pass in
1335  // false to ProcessTabSelected as new_contents is already parented correctly.
1336  ProcessTabSelected(new_contents, false);
1337}
1338
1339void BrowserView::TabStripEmpty() {
1340  // Make sure all optional UI is removed before we are destroyed, otherwise
1341  // there will be consequences (since our view hierarchy will still have
1342  // references to freed views).
1343  UpdateUIForContents(NULL);
1344}
1345
1346///////////////////////////////////////////////////////////////////////////////
1347// BrowserView, ui::SimpleMenuModel::Delegate implementation:
1348
1349bool BrowserView::IsCommandIdChecked(int command_id) const {
1350  // TODO(beng): encoding menu.
1351  // No items in our system menu are check-able.
1352  return false;
1353}
1354
1355bool BrowserView::IsCommandIdEnabled(int command_id) const {
1356  return browser_->command_updater()->IsCommandEnabled(command_id);
1357}
1358
1359bool BrowserView::GetAcceleratorForCommandId(int command_id,
1360                                             ui::Accelerator* accelerator) {
1361  // Let's let the ToolbarView own the canonical implementation of this method.
1362  return toolbar_->GetAcceleratorForCommandId(command_id, accelerator);
1363}
1364
1365bool BrowserView::IsItemForCommandIdDynamic(int command_id) const {
1366  return command_id == IDC_RESTORE_TAB;
1367}
1368
1369string16 BrowserView::GetLabelForCommandId(int command_id) const {
1370  DCHECK(command_id == IDC_RESTORE_TAB);
1371
1372  int string_id = IDS_RESTORE_TAB;
1373  if (IsCommandIdEnabled(command_id)) {
1374    TabRestoreService* trs = browser_->profile()->GetTabRestoreService();
1375    if (trs && trs->entries().front()->type == TabRestoreService::WINDOW)
1376      string_id = IDS_RESTORE_WINDOW;
1377  }
1378  return l10n_util::GetStringUTF16(string_id);
1379}
1380
1381void BrowserView::ExecuteCommand(int command_id) {
1382  browser_->ExecuteCommandIfEnabled(command_id);
1383}
1384
1385///////////////////////////////////////////////////////////////////////////////
1386// BrowserView, views::WindowDelegate implementation:
1387
1388bool BrowserView::CanResize() const {
1389  return true;
1390}
1391
1392bool BrowserView::CanMaximize() const {
1393  return true;
1394}
1395
1396bool BrowserView::CanActivate() const {
1397  return !ActivateAppModalDialog();
1398}
1399
1400bool BrowserView::IsModal() const {
1401  return false;
1402}
1403
1404std::wstring BrowserView::GetWindowTitle() const {
1405  return UTF16ToWideHack(browser_->GetWindowTitleForCurrentTab());
1406}
1407
1408std::wstring BrowserView::GetAccessibleWindowTitle() const {
1409  if (IsOffTheRecord()) {
1410    return UTF16ToWide(l10n_util::GetStringFUTF16(
1411        IDS_ACCESSIBLE_INCOGNITO_WINDOW_TITLE_FORMAT,
1412        WideToUTF16(GetWindowTitle())));
1413  }
1414  return GetWindowTitle();
1415}
1416
1417views::View* BrowserView::GetInitiallyFocusedView() {
1418  // We set the frame not focus on creation so this should never be called.
1419  NOTREACHED();
1420  return NULL;
1421}
1422
1423bool BrowserView::ShouldShowWindowTitle() const {
1424  return browser_->SupportsWindowFeature(Browser::FEATURE_TITLEBAR);
1425}
1426
1427SkBitmap BrowserView::GetWindowAppIcon() {
1428  if (browser_->type() & Browser::TYPE_APP) {
1429    TabContentsWrapper* contents = browser_->GetSelectedTabContentsWrapper();
1430    if (contents && contents->extension_tab_helper()->GetExtensionAppIcon())
1431      return *contents->extension_tab_helper()->GetExtensionAppIcon();
1432  }
1433
1434  return GetWindowIcon();
1435}
1436
1437SkBitmap BrowserView::GetWindowIcon() {
1438  if (browser_->type() & Browser::TYPE_APP)
1439    return browser_->GetCurrentPageIcon();
1440  return SkBitmap();
1441}
1442
1443bool BrowserView::ShouldShowWindowIcon() const {
1444  return browser_->SupportsWindowFeature(Browser::FEATURE_TITLEBAR);
1445}
1446
1447bool BrowserView::ExecuteWindowsCommand(int command_id) {
1448  // This function handles WM_SYSCOMMAND, WM_APPCOMMAND, and WM_COMMAND.
1449
1450  // Translate WM_APPCOMMAND command ids into a command id that the browser
1451  // knows how to handle.
1452  int command_id_from_app_command = GetCommandIDForAppCommandID(command_id);
1453  if (command_id_from_app_command != -1)
1454    command_id = command_id_from_app_command;
1455
1456  return browser_->ExecuteCommandIfEnabled(command_id);
1457}
1458
1459std::wstring BrowserView::GetWindowName() const {
1460  return UTF8ToWide(browser_->GetWindowPlacementKey());
1461}
1462
1463void BrowserView::SaveWindowPlacement(const gfx::Rect& bounds,
1464                                      bool maximized) {
1465  // If IsFullscreen() is true, we've just changed into fullscreen mode, and
1466  // we're catching the going-into-fullscreen sizing and positioning calls,
1467  // which we want to ignore.
1468  if (!IsFullscreen() && browser_->ShouldSaveWindowPlacement()) {
1469    WindowDelegate::SaveWindowPlacement(bounds, maximized);
1470    browser_->SaveWindowPlacement(bounds, maximized);
1471  }
1472}
1473
1474bool BrowserView::GetSavedWindowBounds(gfx::Rect* bounds) const {
1475  *bounds = browser_->GetSavedWindowBounds();
1476  if (browser_->type() & Browser::TYPE_POPUP) {
1477    // We are a popup window. The value passed in |bounds| represents two
1478    // pieces of information:
1479    // - the position of the window, in screen coordinates (outer position).
1480    // - the size of the content area (inner size).
1481    // We need to use these values to determine the appropriate size and
1482    // position of the resulting window.
1483    if (IsToolbarVisible()) {
1484      // If we're showing the toolbar, we need to adjust |*bounds| to include
1485      // its desired height, since the toolbar is considered part of the
1486      // window's client area as far as GetWindowBoundsForClientBounds is
1487      // concerned...
1488      bounds->set_height(
1489          bounds->height() + toolbar_->GetPreferredSize().height());
1490    }
1491
1492    gfx::Rect window_rect = frame_->GetWindow()->non_client_view()->
1493        GetWindowBoundsForClientBounds(*bounds);
1494    window_rect.set_origin(bounds->origin());
1495
1496    // When we are given x/y coordinates of 0 on a created popup window,
1497    // assume none were given by the window.open() command.
1498    if (window_rect.x() == 0 && window_rect.y() == 0) {
1499      gfx::Size size = window_rect.size();
1500      window_rect.set_origin(WindowSizer::GetDefaultPopupOrigin(size));
1501    }
1502
1503    *bounds = window_rect;
1504  }
1505
1506  // We return true because we can _always_ locate reasonable bounds using the
1507  // WindowSizer, and we don't want to trigger the Window's built-in "size to
1508  // default" handling because the browser window has no default preferred
1509  // size.
1510  return true;
1511}
1512
1513bool BrowserView::GetSavedMaximizedState(bool* maximized) const {
1514  *maximized = browser_->GetSavedMaximizedState();
1515  return true;
1516}
1517
1518views::View* BrowserView::GetContentsView() {
1519  return contents_container_;
1520}
1521
1522views::ClientView* BrowserView::CreateClientView(views::Window* window) {
1523  set_window(window);
1524  return this;
1525}
1526
1527void BrowserView::OnWindowActivationChanged(bool active) {
1528  if (active)
1529    BrowserList::SetLastActive(browser_.get());
1530}
1531
1532void BrowserView::OnWindowBeginUserBoundsChange() {
1533  TabContents* tab_contents = GetSelectedTabContents();
1534  if (tab_contents)
1535    tab_contents->WindowMoveOrResizeStarted();
1536}
1537
1538void BrowserView::OnWidgetMove() {
1539  // Cancel any tabstrip animations, some of them may be invalidated by the
1540  // window being repositioned.
1541  // Comment out for one cycle to see if this fixes dist tests.
1542  // tabstrip_->DestroyDragController();
1543
1544  status_bubble_->Reposition();
1545
1546  BrowserBubbleHost::WindowMoved();
1547
1548  browser::HideBookmarkBubbleView();
1549
1550  // Close the omnibox popup, if any.
1551  if (toolbar_ && toolbar_->location_bar())
1552    toolbar_->location_bar()->location_entry()->ClosePopup();
1553}
1554
1555///////////////////////////////////////////////////////////////////////////////
1556// BrowserView, views::ClientView overrides:
1557
1558bool BrowserView::CanClose() {
1559  // You cannot close a frame for which there is an active originating drag
1560  // session.
1561    if (tabstrip_ && !tabstrip_->IsTabStripCloseable())
1562      return false;
1563
1564  // Give beforeunload handlers the chance to cancel the close before we hide
1565  // the window below.
1566  if (!browser_->ShouldCloseWindow())
1567    return false;
1568
1569  if (!browser_->tabstrip_model()->empty()) {
1570    // Tab strip isn't empty.  Hide the frame (so it appears to have closed
1571    // immediately) and close all the tabs, allowing the renderers to shut
1572    // down. When the tab strip is empty we'll be called back again.
1573    frame_->GetWindow()->HideWindow();
1574    browser_->OnWindowClosing();
1575    return false;
1576  }
1577
1578  // Empty TabStripModel, it's now safe to allow the Window to be closed.
1579  NotificationService::current()->Notify(
1580      NotificationType::WINDOW_CLOSED,
1581      Source<gfx::NativeWindow>(frame_->GetWindow()->GetNativeWindow()),
1582      NotificationService::NoDetails());
1583  return true;
1584}
1585
1586int BrowserView::NonClientHitTest(const gfx::Point& point) {
1587#if defined(OS_WIN)
1588  // The following code is not in the LayoutManager because it's
1589  // independent of layout and also depends on the ResizeCorner which
1590  // is private.
1591  if (!frame_->GetWindow()->IsMaximized() &&
1592      !frame_->GetWindow()->IsFullscreen()) {
1593    CRect client_rect;
1594    ::GetClientRect(frame_->GetWindow()->GetNativeWindow(), &client_rect);
1595    gfx::Size resize_corner_size = ResizeCorner::GetSize();
1596    gfx::Rect resize_corner_rect(client_rect.right - resize_corner_size.width(),
1597        client_rect.bottom - resize_corner_size.height(),
1598        resize_corner_size.width(), resize_corner_size.height());
1599    bool rtl_dir = base::i18n::IsRTL();
1600    if (rtl_dir)
1601      resize_corner_rect.set_x(0);
1602    if (resize_corner_rect.Contains(point)) {
1603      if (rtl_dir)
1604        return HTBOTTOMLEFT;
1605      return HTBOTTOMRIGHT;
1606    }
1607  }
1608#endif
1609
1610  return GetBrowserViewLayout()->NonClientHitTest(point);
1611}
1612
1613gfx::Size BrowserView::GetMinimumSize() {
1614  return GetBrowserViewLayout()->GetMinimumSize();
1615}
1616
1617///////////////////////////////////////////////////////////////////////////////
1618// BrowserView, protected
1619
1620void BrowserView::GetAccessiblePanes(
1621    std::vector<AccessiblePaneView*>* panes) {
1622  // This should be in the order of pane traversal of the panes using F6.
1623  // If one of these is invisible or has no focusable children, it will be
1624  // automatically skipped.
1625  panes->push_back(toolbar_);
1626  if (bookmark_bar_view_.get())
1627    panes->push_back(bookmark_bar_view_.get());
1628  if (infobar_container_)
1629    panes->push_back(infobar_container_);
1630  if (download_shelf_.get())
1631    panes->push_back(download_shelf_.get());
1632}
1633
1634///////////////////////////////////////////////////////////////////////////////
1635// BrowserView, views::View overrides:
1636
1637std::string BrowserView::GetClassName() const {
1638  return kViewClassName;
1639}
1640
1641void BrowserView::Layout() {
1642  if (ignore_layout_)
1643    return;
1644  views::View::Layout();
1645
1646  // The status bubble position requires that all other layout finish first.
1647  LayoutStatusBubble();
1648
1649#if defined(OS_WIN)
1650  // Send the margins of the "user-perceived content area" of this
1651  // browser window so AeroPeekManager can render a background-tab image in
1652  // the area.
1653  // TODO(pkasting) correct content inset??
1654  if (aeropeek_manager_.get()) {
1655    gfx::Insets insets(GetFindBarBoundingBox().y() + 1,
1656                       0,
1657                       0,
1658                       0);
1659    aeropeek_manager_->SetContentInsets(insets);
1660  }
1661#endif
1662}
1663
1664void BrowserView::PaintChildren(gfx::Canvas* canvas) {
1665  // Paint the |infobar_container_| last so that it may paint its
1666  // overlapping tabs.
1667  for (int i = 0; i < child_count(); ++i) {
1668    View* child = GetChildViewAt(i);
1669    if (child != infobar_container_)
1670      child->Paint(canvas);
1671  }
1672
1673  infobar_container_->Paint(canvas);
1674}
1675
1676void BrowserView::ViewHierarchyChanged(bool is_add,
1677                                       views::View* parent,
1678                                       views::View* child) {
1679  if (is_add && child == this && GetWidget() && !initialized_) {
1680    Init();
1681    initialized_ = true;
1682  }
1683}
1684
1685void BrowserView::ChildPreferredSizeChanged(View* child) {
1686  Layout();
1687}
1688
1689void BrowserView::GetAccessibleState(ui::AccessibleViewState* state) {
1690  state->name = l10n_util::GetStringUTF16(IDS_PRODUCT_NAME);
1691  state->role = ui::AccessibilityTypes::ROLE_CLIENT;
1692}
1693
1694SkColor BrowserView::GetInfoBarSeparatorColor() const {
1695  // NOTE: Keep this in sync with ToolbarView::OnPaint()!
1696  return (IsTabStripVisible() ||
1697          !frame_->GetWindow()->non_client_view()->UseNativeFrame()) ?
1698      ResourceBundle::toolbar_separator_color : SK_ColorBLACK;
1699}
1700
1701void BrowserView::InfoBarContainerStateChanged(bool is_animating) {
1702  ToolbarSizeChanged(is_animating);
1703}
1704
1705bool BrowserView::DrawInfoBarArrows(int* x) const {
1706  if (x) {
1707    const LocationIconView* location_icon_view =
1708        toolbar_->location_bar()->location_icon_view();
1709    gfx::Point icon_center(location_icon_view->GetImageBounds().CenterPoint());
1710    ConvertPointToView(location_icon_view, this, &icon_center);
1711    *x = icon_center.x();
1712  }
1713  return true;
1714}
1715
1716bool BrowserView::SplitHandleMoved(views::SingleSplitView* view) {
1717  for (int i = 0; i < view->child_count(); ++i)
1718    view->GetChildViewAt(i)->InvalidateLayout();
1719  SchedulePaint();
1720  Layout();
1721  return false;
1722}
1723
1724views::LayoutManager* BrowserView::CreateLayoutManager() const {
1725  return new BrowserViewLayout;
1726}
1727
1728void BrowserView::InitTabStrip(TabStripModel* model) {
1729  // Throw away the existing tabstrip if we're switching display modes.
1730  scoped_ptr<AbstractTabStripView> old_strip(tabstrip_);
1731  if (tabstrip_)
1732    tabstrip_->parent()->RemoveChildView(tabstrip_);
1733
1734  tabstrip_ = CreateTabStrip(browser_.get(), this, model, UseVerticalTabs());
1735}
1736
1737ToolbarView* BrowserView::CreateToolbar() const {
1738  return new ToolbarView(browser_.get());
1739}
1740
1741void BrowserView::Init() {
1742  SetLayoutManager(CreateLayoutManager());
1743  // Stow a pointer to this object onto the window handle so that we can get at
1744  // it later when all we have is a native view.
1745  GetWidget()->native_widget()->SetNativeWindowProperty(kBrowserViewKey, this);
1746
1747  // Stow a pointer to the browser's profile onto the window handle so that we
1748  // can get it later when all we have is a native view.
1749  GetWidget()->native_widget()->SetNativeWindowProperty(Profile::kProfileKey,
1750                                                        browser_->profile());
1751
1752  // Start a hung plugin window detector for this browser object (as long as
1753  // hang detection is not disabled).
1754  if (!CommandLine::ForCurrentProcess()->HasSwitch(
1755          switches::kDisableHangMonitor)) {
1756    InitHangMonitor();
1757  }
1758
1759  LoadAccelerators();
1760
1761  InitTabStrip(browser_->tabstrip_model());
1762
1763  SetToolbar(CreateToolbar());
1764
1765  infobar_container_ = new InfoBarContainerView(this);
1766  AddChildView(infobar_container_);
1767
1768  contents_container_ = new TabContentsContainer;
1769  contents_ = new ContentsContainer(contents_container_);
1770
1771  SkColor bg_color = GetWidget()->GetThemeProvider()->
1772      GetColor(ThemeService::COLOR_TOOLBAR);
1773
1774  bool sidebar_allowed = SidebarManager::IsSidebarAllowed();
1775  if (sidebar_allowed) {
1776    sidebar_container_ = new TabContentsContainer;
1777    sidebar_container_->SetID(VIEW_ID_SIDE_BAR_CONTAINER);
1778    sidebar_container_->SetVisible(false);
1779
1780    sidebar_split_ = new views::SingleSplitView(
1781        contents_,
1782        sidebar_container_,
1783        views::SingleSplitView::HORIZONTAL_SPLIT,
1784        this);
1785    sidebar_split_->SetID(VIEW_ID_SIDE_BAR_SPLIT);
1786    sidebar_split_->SetAccessibleName(
1787        l10n_util::GetStringUTF16(IDS_ACCNAME_SIDE_BAR));
1788    sidebar_split_->set_background(
1789        views::Background::CreateSolidBackground(bg_color));
1790  }
1791
1792  devtools_container_ = new TabContentsContainer;
1793  devtools_container_->SetID(VIEW_ID_DEV_TOOLS_DOCKED);
1794  devtools_container_->SetVisible(false);
1795
1796  views::View* contents_view = contents_;
1797  if (sidebar_allowed)
1798    contents_view = sidebar_split_;
1799
1800  contents_split_ = new views::SingleSplitView(
1801      contents_view,
1802      devtools_container_,
1803      views::SingleSplitView::VERTICAL_SPLIT,
1804      this);
1805  contents_split_->SetID(VIEW_ID_CONTENTS_SPLIT);
1806  contents_split_->SetAccessibleName(
1807      l10n_util::GetStringUTF16(IDS_ACCNAME_WEB_CONTENTS));
1808  contents_split_->set_background(
1809      views::Background::CreateSolidBackground(bg_color));
1810  AddChildView(contents_split_);
1811  set_contents_view(contents_split_);
1812
1813  status_bubble_.reset(new StatusBubbleViews(contents_));
1814
1815#if defined(OS_WIN)
1816  InitSystemMenu();
1817
1818  // Create a custom JumpList and add it to an observer of TabRestoreService
1819  // so we can update the custom JumpList when a tab is added or removed.
1820  if (JumpList::Enabled()) {
1821    jumplist_.reset(new JumpList);
1822    jumplist_->AddObserver(browser_->profile());
1823  }
1824
1825  if (AeroPeekManager::Enabled()) {
1826    aeropeek_manager_.reset(new AeroPeekManager(
1827        frame_->GetWindow()->GetNativeWindow()));
1828    browser_->tabstrip_model()->AddObserver(aeropeek_manager_.get());
1829  }
1830#endif
1831
1832  // We're now initialized and ready to process Layout requests.
1833  ignore_layout_ = false;
1834}
1835
1836void BrowserView::LoadingAnimationCallback() {
1837  base::TimeTicks now = base::TimeTicks::Now();
1838  if (!last_animation_time_.is_null()) {
1839    UMA_HISTOGRAM_TIMES(
1840        "Tabs.LoadingAnimationTime",
1841        now - last_animation_time_);
1842  }
1843  last_animation_time_ = now;
1844  if (browser_->type() == Browser::TYPE_NORMAL) {
1845    // Loading animations are shown in the tab for tabbed windows.  We check the
1846    // browser type instead of calling IsTabStripVisible() because the latter
1847    // will return false for fullscreen windows, but we still need to update
1848    // their animations (so that when they come out of fullscreen mode they'll
1849    // be correct).
1850    tabstrip_->UpdateLoadingAnimations();
1851  } else if (ShouldShowWindowIcon()) {
1852    // ... or in the window icon area for popups and app windows.
1853    TabContents* tab_contents = browser_->GetSelectedTabContents();
1854    // GetSelectedTabContents can return NULL for example under Purify when
1855    // the animations are running slowly and this function is called on a timer
1856    // through LoadingAnimationCallback.
1857    frame_->UpdateThrobber(tab_contents && tab_contents->is_loading());
1858  }
1859}
1860
1861// BrowserView, private --------------------------------------------------------
1862
1863#if defined(OS_WIN)
1864void BrowserView::InitSystemMenu() {
1865  system_menu_contents_.reset(new views::SystemMenuModel(this));
1866  // We add the menu items in reverse order so that insertion_index never needs
1867  // to change.
1868  if (IsBrowserTypeNormal())
1869    BuildSystemMenuForBrowserWindow();
1870  else
1871    BuildSystemMenuForAppOrPopupWindow(browser_->type() == Browser::TYPE_APP);
1872  system_menu_.reset(
1873      new views::NativeMenuWin(system_menu_contents_.get(),
1874                               frame_->GetWindow()->GetNativeWindow()));
1875  system_menu_->Rebuild();
1876}
1877#endif
1878
1879BrowserViewLayout* BrowserView::GetBrowserViewLayout() const {
1880  return static_cast<BrowserViewLayout*>(GetLayoutManager());
1881}
1882
1883void BrowserView::LayoutStatusBubble() {
1884  // In restored mode, the client area has a client edge between it and the
1885  // frame.
1886  int overlap = StatusBubbleViews::kShadowThickness +
1887      (IsMaximized() ? 0 : views::NonClientFrameView::kClientEdgeThickness);
1888  int x = -overlap;
1889  if (UseVerticalTabs() && IsTabStripVisible())
1890    x += tabstrip_->bounds().right();
1891  int height = status_bubble_->GetPreferredSize().height();
1892  int contents_height = status_bubble_->base_view()->bounds().height();
1893  gfx::Point origin(-overlap, contents_height - height + overlap);
1894  status_bubble_->SetBounds(origin.x(), origin.y(), width() / 3, height);
1895}
1896
1897bool BrowserView::MaybeShowBookmarkBar(TabContentsWrapper* contents) {
1898  views::View* new_bookmark_bar_view = NULL;
1899  if (browser_->SupportsWindowFeature(Browser::FEATURE_BOOKMARKBAR)
1900      && contents) {
1901    if (!bookmark_bar_view_.get()) {
1902      bookmark_bar_view_.reset(new BookmarkBarView(contents->profile(),
1903                                                   browser_.get()));
1904      bookmark_bar_view_->set_parent_owned(false);
1905      bookmark_bar_view_->set_background(
1906          new BookmarkExtensionBackground(this, bookmark_bar_view_.get(),
1907                                          browser_.get()));
1908    } else {
1909      bookmark_bar_view_->SetProfile(contents->profile());
1910    }
1911    bookmark_bar_view_->SetPageNavigator(contents->tab_contents());
1912    new_bookmark_bar_view = bookmark_bar_view_.get();
1913  }
1914  return UpdateChildViewAndLayout(new_bookmark_bar_view, &active_bookmark_bar_);
1915}
1916
1917bool BrowserView::MaybeShowInfoBar(TabContentsWrapper* contents) {
1918  // TODO(beng): Remove this function once the interface between
1919  //             InfoBarContainer, DownloadShelfView and TabContents and this
1920  //             view is sorted out.
1921  return true;
1922}
1923
1924void BrowserView::UpdateSidebar() {
1925  UpdateSidebarForContents(GetSelectedTabContentsWrapper());
1926  Layout();
1927}
1928
1929void BrowserView::UpdateSidebarForContents(TabContentsWrapper* tab_contents) {
1930  if (!sidebar_container_)
1931    return;  // Happens when sidebar is not allowed.
1932  if (!SidebarManager::GetInstance())
1933    return;  // Happens only in tests.
1934
1935  TabContents* sidebar_contents = NULL;
1936  if (tab_contents) {
1937    SidebarContainer* client_host = SidebarManager::GetInstance()->
1938        GetActiveSidebarContainerFor(tab_contents->tab_contents());
1939    if (client_host)
1940      sidebar_contents = client_host->sidebar_contents();
1941  }
1942
1943  bool visible = NULL != sidebar_contents &&
1944                 browser_->SupportsWindowFeature(Browser::FEATURE_SIDEBAR);
1945
1946  bool should_show = visible && !sidebar_container_->IsVisible();
1947  bool should_hide = !visible && sidebar_container_->IsVisible();
1948
1949  // Update sidebar content.
1950  TabContents* old_contents = sidebar_container_->tab_contents();
1951  sidebar_container_->ChangeTabContents(sidebar_contents);
1952  SidebarManager::GetInstance()->
1953      NotifyStateChanges(old_contents, sidebar_contents);
1954
1955  // Update sidebar UI width.
1956  if (should_show) {
1957    // Restore split offset.
1958    int sidebar_width = g_browser_process->local_state()->GetInteger(
1959        prefs::kExtensionSidebarWidth);
1960    if (sidebar_width < 0) {
1961      // Initial load, set to default value.
1962      sidebar_width = sidebar_split_->width() / 7;
1963    }
1964    // Make sure user can see both panes.
1965    int min_sidebar_width = sidebar_split_->GetMinimumSize().width();
1966    sidebar_width = std::min(sidebar_split_->width() - min_sidebar_width,
1967                             std::max(min_sidebar_width, sidebar_width));
1968
1969    sidebar_split_->set_divider_offset(
1970        sidebar_split_->width() - sidebar_width);
1971
1972    sidebar_container_->SetVisible(true);
1973    sidebar_split_->InvalidateLayout();
1974    Layout();
1975  } else if (should_hide) {
1976    // Store split offset when hiding sidebar only.
1977    g_browser_process->local_state()->SetInteger(
1978        prefs::kExtensionSidebarWidth,
1979        sidebar_split_->width() - sidebar_split_->divider_offset());
1980
1981    sidebar_container_->SetVisible(false);
1982    sidebar_split_->InvalidateLayout();
1983    Layout();
1984  }
1985}
1986
1987void BrowserView::UpdateDevToolsForContents(TabContentsWrapper* wrapper) {
1988  TabContents* devtools_contents = NULL;
1989  if (wrapper) {
1990    TabContentsWrapper* devtools_contents_wrapper =
1991        DevToolsWindow::GetDevToolsContents(wrapper->tab_contents());
1992    if (devtools_contents_wrapper)
1993      devtools_contents = devtools_contents_wrapper->tab_contents();
1994  }
1995
1996  bool should_show = devtools_contents && !devtools_container_->IsVisible();
1997  bool should_hide = !devtools_contents && devtools_container_->IsVisible();
1998
1999  devtools_container_->ChangeTabContents(devtools_contents);
2000
2001  if (should_show) {
2002    if (!devtools_focus_tracker_.get()) {
2003      // Install devtools focus tracker when dev tools window is shown for the
2004      // first time.
2005      devtools_focus_tracker_.reset(
2006          new views::ExternalFocusTracker(devtools_container_,
2007                                          GetFocusManager()));
2008    }
2009
2010    // Restore split offset.
2011    int split_offset = browser_->profile()->GetPrefs()->
2012        GetInteger(prefs::kDevToolsSplitLocation);
2013    if (split_offset == -1) {
2014      // Initial load, set to default value.
2015      split_offset = 2 * contents_split_->height() / 3;
2016    }
2017    // Make sure user can see both panes.
2018    int min_split_size = contents_split_->height() / 10;
2019    split_offset = std::min(contents_split_->height() - min_split_size,
2020                            std::max(min_split_size, split_offset));
2021    contents_split_->set_divider_offset(split_offset);
2022
2023    devtools_container_->SetVisible(true);
2024    contents_split_->InvalidateLayout();
2025    Layout();
2026  } else if (should_hide) {
2027    // Store split offset when hiding devtools window only.
2028    browser_->profile()->GetPrefs()->SetInteger(prefs::kDevToolsSplitLocation,
2029        contents_split_->divider_offset());
2030
2031    // Restore focus to the last focused view when hiding devtools window.
2032    devtools_focus_tracker_->FocusLastFocusedExternalView();
2033
2034    devtools_container_->SetVisible(false);
2035    contents_split_->InvalidateLayout();
2036    Layout();
2037  }
2038}
2039
2040void BrowserView::UpdateUIForContents(TabContentsWrapper* contents) {
2041  bool needs_layout = MaybeShowBookmarkBar(contents);
2042  needs_layout |= MaybeShowInfoBar(contents);
2043  if (needs_layout)
2044    Layout();
2045}
2046
2047bool BrowserView::UpdateChildViewAndLayout(views::View* new_view,
2048                                           views::View** old_view) {
2049  DCHECK(old_view);
2050  if (*old_view == new_view) {
2051    // The views haven't changed, if the views pref changed schedule a layout.
2052    if (new_view) {
2053      if (new_view->GetPreferredSize().height() != new_view->height())
2054        return true;
2055    }
2056    return false;
2057  }
2058
2059  // The views differ, and one may be null (but not both). Remove the old
2060  // view (if it non-null), and add the new one (if it is non-null). If the
2061  // height has changed, schedule a layout, otherwise reuse the existing
2062  // bounds to avoid scheduling a layout.
2063
2064  int current_height = 0;
2065  if (*old_view) {
2066    current_height = (*old_view)->height();
2067    RemoveChildView(*old_view);
2068  }
2069
2070  int new_height = 0;
2071  if (new_view) {
2072    new_height = new_view->GetPreferredSize().height();
2073    AddChildView(new_view);
2074  }
2075  bool changed = false;
2076  if (new_height != current_height) {
2077    changed = true;
2078  } else if (new_view && *old_view) {
2079    // The view changed, but the new view wants the same size, give it the
2080    // bounds of the last view and have it repaint.
2081    new_view->SetBoundsRect((*old_view)->bounds());
2082    new_view->SchedulePaint();
2083  } else if (new_view) {
2084    DCHECK_EQ(0, new_height);
2085    // The heights are the same, but the old view is null. This only happens
2086    // when the height is zero. Zero out the bounds.
2087    new_view->SetBounds(0, 0, 0, 0);
2088  }
2089  *old_view = new_view;
2090  return changed;
2091}
2092
2093void BrowserView::ProcessFullscreen(bool fullscreen) {
2094  // Reduce jankiness during the following position changes by:
2095  //   * Hiding the window until it's in the final position
2096  //   * Ignoring all intervening Layout() calls, which resize the webpage and
2097  //     thus are slow and look ugly
2098  ignore_layout_ = true;
2099  LocationBarView* location_bar = toolbar_->location_bar();
2100#if defined(OS_WIN)
2101  AutocompleteEditViewWin* edit_view =
2102      static_cast<AutocompleteEditViewWin*>(location_bar->location_entry());
2103#endif
2104  if (!fullscreen) {
2105    // Hide the fullscreen bubble as soon as possible, since the mode toggle can
2106    // take enough time for the user to notice.
2107    fullscreen_bubble_.reset();
2108  } else {
2109    // Move focus out of the location bar if necessary.
2110    views::FocusManager* focus_manager = GetFocusManager();
2111    DCHECK(focus_manager);
2112    if (focus_manager->GetFocusedView() == location_bar)
2113      focus_manager->ClearFocus();
2114
2115#if defined(OS_WIN)
2116    // If we don't hide the edit and force it to not show until we come out of
2117    // fullscreen, then if the user was on the New Tab Page, the edit contents
2118    // will appear atop the web contents once we go into fullscreen mode.  This
2119    // has something to do with how we move the main window while it's hidden;
2120    // if we don't hide the main window below, we don't get this problem.
2121    edit_view->set_force_hidden(true);
2122    ShowWindow(edit_view->m_hWnd, SW_HIDE);
2123#endif
2124  }
2125#if defined(OS_WIN)
2126  static_cast<views::WindowWin*>(
2127      frame_->GetWindow()->native_window())->PushForceHidden();
2128#endif
2129
2130  // Notify bookmark bar, so it can set itself to the appropriate drawing state.
2131  if (bookmark_bar_view_.get())
2132    bookmark_bar_view_->OnFullscreenToggled(fullscreen);
2133
2134  // Toggle fullscreen mode.
2135#if defined(OS_WIN)
2136  frame_->GetWindow()->SetFullscreen(fullscreen);
2137#endif  // No need to invoke SetFullscreen for linux as this code is executed
2138        // once we're already fullscreen on linux.
2139
2140#if defined(OS_LINUX)
2141  // Updating of commands for fullscreen mode is called from SetFullScreen on
2142  // Wndows (see just above), but for ChromeOS, this method (ProcessFullScreen)
2143  // is called after full screen has happened successfully (via GTK's
2144  // window-state-change event), so we have to update commands here.
2145  browser_->UpdateCommandsForFullscreenMode(fullscreen);
2146#endif
2147
2148  if (fullscreen) {
2149    bool is_kiosk =
2150        CommandLine::ForCurrentProcess()->HasSwitch(switches::kKioskMode);
2151    if (!is_kiosk) {
2152      fullscreen_bubble_.reset(new FullscreenExitBubble(GetWidget(),
2153                                                        browser_.get()));
2154    }
2155  } else {
2156#if defined(OS_WIN)
2157    // Show the edit again since we're no longer in fullscreen mode.
2158    edit_view->set_force_hidden(false);
2159    ShowWindow(edit_view->m_hWnd, SW_SHOW);
2160#endif
2161  }
2162
2163  // Undo our anti-jankiness hacks and force the window to relayout now that
2164  // it's in its final position.
2165  ignore_layout_ = false;
2166  Layout();
2167#if defined(OS_WIN)
2168  static_cast<views::WindowWin*>(
2169      frame_->GetWindow()->native_window())->PopForceHidden();
2170#endif
2171}
2172
2173
2174void BrowserView::LoadAccelerators() {
2175#if defined(OS_WIN)
2176  HACCEL accelerator_table = AtlLoadAccelerators(IDR_MAINFRAME);
2177  DCHECK(accelerator_table);
2178
2179  // We have to copy the table to access its contents.
2180  int count = CopyAcceleratorTable(accelerator_table, 0, 0);
2181  if (count == 0) {
2182    // Nothing to do in that case.
2183    return;
2184  }
2185
2186  ACCEL* accelerators = static_cast<ACCEL*>(malloc(sizeof(ACCEL) * count));
2187  CopyAcceleratorTable(accelerator_table, accelerators, count);
2188
2189  views::FocusManager* focus_manager = GetFocusManager();
2190  DCHECK(focus_manager);
2191
2192  // Let's fill our own accelerator table.
2193  for (int i = 0; i < count; ++i) {
2194    bool alt_down = (accelerators[i].fVirt & FALT) == FALT;
2195    bool ctrl_down = (accelerators[i].fVirt & FCONTROL) == FCONTROL;
2196    bool shift_down = (accelerators[i].fVirt & FSHIFT) == FSHIFT;
2197    views::Accelerator accelerator(
2198        static_cast<ui::KeyboardCode>(accelerators[i].key),
2199        shift_down, ctrl_down, alt_down);
2200    accelerator_table_[accelerator] = accelerators[i].cmd;
2201
2202    // Also register with the focus manager.
2203    focus_manager->RegisterAccelerator(accelerator, this);
2204  }
2205
2206  // We don't need the Windows accelerator table anymore.
2207  free(accelerators);
2208#else
2209  views::FocusManager* focus_manager = GetFocusManager();
2210  DCHECK(focus_manager);
2211  // Let's fill our own accelerator table.
2212  for (size_t i = 0; i < browser::kAcceleratorMapLength; ++i) {
2213    views::Accelerator accelerator(browser::kAcceleratorMap[i].keycode,
2214                                   browser::kAcceleratorMap[i].shift_pressed,
2215                                   browser::kAcceleratorMap[i].ctrl_pressed,
2216                                   browser::kAcceleratorMap[i].alt_pressed);
2217    accelerator_table_[accelerator] = browser::kAcceleratorMap[i].command_id;
2218
2219    // Also register with the focus manager.
2220    focus_manager->RegisterAccelerator(accelerator, this);
2221  }
2222#endif
2223}
2224
2225#if defined(OS_WIN)
2226void BrowserView::BuildSystemMenuForBrowserWindow() {
2227  system_menu_contents_->AddSeparator();
2228  system_menu_contents_->AddItemWithStringId(IDC_TASK_MANAGER,
2229                                             IDS_TASK_MANAGER);
2230  system_menu_contents_->AddSeparator();
2231  system_menu_contents_->AddItemWithStringId(IDC_RESTORE_TAB, IDS_RESTORE_TAB);
2232  system_menu_contents_->AddItemWithStringId(IDC_NEW_TAB, IDS_NEW_TAB);
2233  // If it's a regular browser window with tabs, we don't add any more items,
2234  // since it already has menus (Page, Chrome).
2235}
2236
2237void BrowserView::BuildSystemMenuForAppOrPopupWindow(bool is_app) {
2238  if (is_app) {
2239    system_menu_contents_->AddSeparator();
2240    system_menu_contents_->AddItemWithStringId(IDC_TASK_MANAGER,
2241                                               IDS_TASK_MANAGER);
2242  }
2243  system_menu_contents_->AddSeparator();
2244  encoding_menu_contents_.reset(new EncodingMenuModel(browser_.get()));
2245  system_menu_contents_->AddSubMenuWithStringId(IDC_ENCODING_MENU,
2246                                                IDS_ENCODING_MENU,
2247                                                encoding_menu_contents_.get());
2248  zoom_menu_contents_.reset(new ZoomMenuModel(this));
2249  system_menu_contents_->AddSubMenuWithStringId(IDC_ZOOM_MENU, IDS_ZOOM_MENU,
2250                                                zoom_menu_contents_.get());
2251  system_menu_contents_->AddItemWithStringId(IDC_PRINT, IDS_PRINT);
2252  system_menu_contents_->AddItemWithStringId(IDC_FIND, IDS_FIND);
2253  system_menu_contents_->AddSeparator();
2254  system_menu_contents_->AddItemWithStringId(IDC_PASTE, IDS_PASTE);
2255  system_menu_contents_->AddItemWithStringId(IDC_COPY, IDS_COPY);
2256  system_menu_contents_->AddItemWithStringId(IDC_CUT, IDS_CUT);
2257  system_menu_contents_->AddSeparator();
2258  if (is_app) {
2259    system_menu_contents_->AddItemWithStringId(IDC_NEW_TAB,
2260                                               IDS_APP_MENU_NEW_WEB_PAGE);
2261  } else {
2262    system_menu_contents_->AddItemWithStringId(IDC_SHOW_AS_TAB,
2263                                               IDS_SHOW_AS_TAB);
2264  }
2265  system_menu_contents_->AddItemWithStringId(IDC_COPY_URL,
2266                                             IDS_APP_MENU_COPY_URL);
2267  system_menu_contents_->AddSeparator();
2268  system_menu_contents_->AddItemWithStringId(IDC_RELOAD, IDS_APP_MENU_RELOAD);
2269  system_menu_contents_->AddItemWithStringId(IDC_FORWARD,
2270                                             IDS_CONTENT_CONTEXT_FORWARD);
2271  system_menu_contents_->AddItemWithStringId(IDC_BACK,
2272                                             IDS_CONTENT_CONTEXT_BACK);
2273}
2274#endif
2275
2276int BrowserView::GetCommandIDForAppCommandID(int app_command_id) const {
2277#if defined(OS_WIN)
2278  switch (app_command_id) {
2279    // NOTE: The order here matches the APPCOMMAND declaration order in the
2280    // Windows headers.
2281    case APPCOMMAND_BROWSER_BACKWARD: return IDC_BACK;
2282    case APPCOMMAND_BROWSER_FORWARD:  return IDC_FORWARD;
2283    case APPCOMMAND_BROWSER_REFRESH:  return IDC_RELOAD;
2284    case APPCOMMAND_BROWSER_HOME:     return IDC_HOME;
2285    case APPCOMMAND_BROWSER_STOP:     return IDC_STOP;
2286    case APPCOMMAND_BROWSER_SEARCH:   return IDC_FOCUS_SEARCH;
2287    case APPCOMMAND_HELP:             return IDC_HELP_PAGE;
2288    case APPCOMMAND_NEW:              return IDC_NEW_TAB;
2289    case APPCOMMAND_OPEN:             return IDC_OPEN_FILE;
2290    case APPCOMMAND_CLOSE:            return IDC_CLOSE_TAB;
2291    case APPCOMMAND_SAVE:             return IDC_SAVE_PAGE;
2292    case APPCOMMAND_PRINT:            return IDC_PRINT;
2293    case APPCOMMAND_COPY:             return IDC_COPY;
2294    case APPCOMMAND_CUT:              return IDC_CUT;
2295    case APPCOMMAND_PASTE:            return IDC_PASTE;
2296
2297      // TODO(pkasting): http://b/1113069 Handle these.
2298    case APPCOMMAND_UNDO:
2299    case APPCOMMAND_REDO:
2300    case APPCOMMAND_SPELL_CHECK:
2301    default:                          return -1;
2302  }
2303#else
2304  // App commands are Windows-specific so there's nothing to do here.
2305  return -1;
2306#endif
2307}
2308
2309void BrowserView::InitHangMonitor() {
2310#if defined(OS_WIN)
2311  PrefService* pref_service = g_browser_process->local_state();
2312  if (!pref_service)
2313    return;
2314
2315  int plugin_message_response_timeout =
2316      pref_service->GetInteger(prefs::kPluginMessageResponseTimeout);
2317  int hung_plugin_detect_freq =
2318      pref_service->GetInteger(prefs::kHungPluginDetectFrequency);
2319  if ((hung_plugin_detect_freq > 0) &&
2320      hung_window_detector_.Initialize(GetWidget()->GetNativeView(),
2321                                       plugin_message_response_timeout)) {
2322    ticker_.set_tick_interval(hung_plugin_detect_freq);
2323    ticker_.RegisterTickHandler(&hung_window_detector_);
2324    ticker_.Start();
2325
2326    pref_service->SetInteger(prefs::kPluginMessageResponseTimeout,
2327                             plugin_message_response_timeout);
2328    pref_service->SetInteger(prefs::kHungPluginDetectFrequency,
2329                             hung_plugin_detect_freq);
2330  }
2331#endif
2332}
2333
2334void BrowserView::UpdateAcceleratorMetrics(
2335    const views::Accelerator& accelerator, int command_id) {
2336#if defined(OS_CHROMEOS)
2337  // Collect information about the relative popularity of various accelerators
2338  // on Chrome OS.
2339  const ui::KeyboardCode key_code = accelerator.GetKeyCode();
2340  switch (command_id) {
2341    case IDC_BACK:
2342      if (key_code == ui::VKEY_BACK)
2343        UserMetrics::RecordAction(UserMetricsAction("Accel_Back_Backspace"));
2344      else if (key_code == ui::VKEY_F1)
2345        UserMetrics::RecordAction(UserMetricsAction("Accel_Back_F1"));
2346      else if (key_code == ui::VKEY_LEFT)
2347        UserMetrics::RecordAction(UserMetricsAction("Accel_Back_Left"));
2348      break;
2349    case IDC_FORWARD:
2350      if (key_code == ui::VKEY_BACK)
2351        UserMetrics::RecordAction(UserMetricsAction("Accel_Forward_Backspace"));
2352      else if (key_code == ui::VKEY_F2)
2353        UserMetrics::RecordAction(UserMetricsAction("Accel_Forward_F2"));
2354      else if (key_code == ui::VKEY_RIGHT)
2355        UserMetrics::RecordAction(UserMetricsAction("Accel_Forward_Right"));
2356      break;
2357    case IDC_RELOAD:
2358    case IDC_RELOAD_IGNORING_CACHE:
2359      if (key_code == ui::VKEY_R)
2360        UserMetrics::RecordAction(UserMetricsAction("Accel_Reload_R"));
2361      else if (key_code == ui::VKEY_F3)
2362        UserMetrics::RecordAction(UserMetricsAction("Accel_Reload_F3"));
2363      break;
2364    case IDC_FULLSCREEN:
2365      if (key_code == ui::VKEY_F4)
2366        UserMetrics::RecordAction(UserMetricsAction("Accel_Fullscreen_F4"));
2367      break;
2368    case IDC_NEW_TAB:
2369      if (key_code == ui::VKEY_T)
2370        UserMetrics::RecordAction(UserMetricsAction("Accel_NewTab_T"));
2371      break;
2372    case IDC_SEARCH:
2373      if (key_code == ui::VKEY_LWIN)
2374        UserMetrics::RecordAction(UserMetricsAction("Accel_Search_LWin"));
2375      break;
2376    case IDC_FOCUS_LOCATION:
2377      if (key_code == ui::VKEY_D)
2378        UserMetrics::RecordAction(UserMetricsAction("Accel_FocusLocation_D"));
2379      else if (key_code == ui::VKEY_L)
2380        UserMetrics::RecordAction(UserMetricsAction("Accel_FocusLocation_L"));
2381      break;
2382    case IDC_FOCUS_SEARCH:
2383      if (key_code == ui::VKEY_E)
2384        UserMetrics::RecordAction(UserMetricsAction("Accel_FocusSearch_E"));
2385      else if (key_code == ui::VKEY_K)
2386        UserMetrics::RecordAction(UserMetricsAction("Accel_FocusSearch_K"));
2387      break;
2388    default:
2389      // Do nothing.
2390      break;
2391  }
2392#endif
2393}
2394
2395void BrowserView::ProcessTabSelected(TabContentsWrapper* new_contents,
2396                                     bool change_tab_contents) {
2397  // Update various elements that are interested in knowing the current
2398  // TabContents.
2399
2400  // When we toggle the NTP floating bookmarks bar and/or the info bar,
2401  // we don't want any TabContents to be attached, so that we
2402  // avoid an unnecessary resize and re-layout of a TabContents.
2403  if (change_tab_contents)
2404    contents_container_->ChangeTabContents(NULL);
2405  infobar_container_->ChangeTabContents(new_contents->tab_contents());
2406  UpdateUIForContents(new_contents);
2407  if (change_tab_contents)
2408    contents_container_->ChangeTabContents(new_contents->tab_contents());
2409  UpdateSidebarForContents(new_contents);
2410
2411  UpdateDevToolsForContents(new_contents);
2412  // TODO(beng): This should be called automatically by ChangeTabContents, but I
2413  //             am striving for parity now rather than cleanliness. This is
2414  //             required to make features like Duplicate Tab, Undo Close Tab,
2415  //             etc not result in sad tab.
2416  new_contents->tab_contents()->DidBecomeSelected();
2417  if (BrowserList::GetLastActive() == browser_ &&
2418      !browser_->tabstrip_model()->closing_all() && GetWindow()->IsVisible()) {
2419    // We only restore focus if our window is visible, to avoid invoking blur
2420    // handlers when we are eventually shown.
2421    new_contents->view()->RestoreFocus();
2422  }
2423
2424  // Update all the UI bits.
2425  UpdateTitleBar();
2426  // No need to update Toolbar because it's already updated in
2427  // browser.cc.
2428}
2429
2430gfx::Size BrowserView::GetResizeCornerSize() const {
2431  return ResizeCorner::GetSize();
2432}
2433
2434void BrowserView::SetToolbar(ToolbarView* toolbar) {
2435  if (toolbar_) {
2436    RemoveChildView(toolbar_);
2437    delete toolbar_;
2438  }
2439  toolbar_ = toolbar;
2440  if (toolbar) {
2441    AddChildView(toolbar_);
2442    toolbar_->Init(browser_->profile());
2443  }
2444}
2445
2446#if !defined(OS_CHROMEOS)
2447// static
2448BrowserWindow* BrowserWindow::CreateBrowserWindow(Browser* browser) {
2449  // Create the view and the frame. The frame will attach itself via the view
2450  // so we don't need to do anything with the pointer.
2451  BrowserView* view = new BrowserView(browser);
2452  BrowserFrame::Create(view, browser->profile());
2453
2454  view->GetWindow()->non_client_view()->SetAccessibleName(
2455      l10n_util::GetStringUTF16(IDS_PRODUCT_NAME));
2456
2457  return view;
2458}
2459#endif
2460
2461// static
2462FindBar* BrowserWindow::CreateFindBar(Browser* browser) {
2463  return browser::CreateFindBar(static_cast<BrowserView*>(browser->window()));
2464}
2465