browser_non_client_frame_view_ash.cc revision 7dbb3d5cf0c15f500944d211057644d6a2f37371
1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h"
6
7#include "ash/shell_delegate.h"
8#include "ash/wm/frame_painter.h"
9#include "ash/wm/workspace/frame_maximize_button.h"
10#include "chrome/browser/themes/theme_properties.h"
11#include "chrome/browser/ui/ash/chrome_shell_delegate.h"
12#include "chrome/browser/ui/browser.h"
13#include "chrome/browser/ui/immersive_fullscreen_configuration.h"
14#include "chrome/browser/ui/views/avatar_menu_button.h"
15#include "chrome/browser/ui/views/frame/browser_frame.h"
16#include "chrome/browser/ui/views/frame/browser_view.h"
17#include "chrome/browser/ui/views/frame/immersive_mode_controller.h"
18#include "chrome/browser/ui/views/tab_icon_view.h"
19#include "chrome/browser/ui/views/tabs/tab_strip.h"
20#include "content/public/browser/web_contents.h"
21#include "grit/ash_resources.h"
22#include "grit/generated_resources.h"  // Accessibility names
23#include "grit/theme_resources.h"
24#include "ui/aura/client/aura_constants.h"
25#include "ui/aura/window.h"
26#include "ui/base/accessibility/accessible_view_state.h"
27#include "ui/base/hit_test.h"
28#include "ui/base/l10n/l10n_util.h"
29#include "ui/base/layout.h"
30#include "ui/base/resource/resource_bundle.h"
31#include "ui/base/theme_provider.h"
32#include "ui/compositor/layer_animator.h"
33#include "ui/compositor/scoped_animation_duration_scale_mode.h"
34#include "ui/gfx/canvas.h"
35#include "ui/gfx/image/image_skia.h"
36#include "ui/views/controls/button/image_button.h"
37#include "ui/views/controls/label.h"
38#include "ui/views/layout/layout_constants.h"
39#include "ui/views/widget/widget.h"
40#include "ui/views/widget/widget_delegate.h"
41
42namespace {
43
44// The avatar ends 2 px above the bottom of the tabstrip (which, given the
45// way the tabstrip draws its bottom edge, will appear like a 1 px gap to the
46// user).
47const int kAvatarBottomSpacing = 2;
48// There are 2 px on each side of the avatar (between the frame border and
49// it on the left, and between it and the tabstrip on the right).
50const int kAvatarSideSpacing = 2;
51// Space between left edge of window and tabstrip.
52const int kTabstripLeftSpacing = 0;
53// Space between right edge of tabstrip and maximize button.
54const int kTabstripRightSpacing = 10;
55// Height of the shadow of the content area, at the top of the toolbar.
56const int kContentShadowHeight = 1;
57// Space between top of window and top of tabstrip for tall headers, such as
58// for restored windows, apps, etc.
59const int kTabstripTopSpacingTall = 7;
60// Space between top of window and top of tabstrip for short headers, such as
61// for maximized windows, pop-ups, etc.
62const int kTabstripTopSpacingShort = 0;
63// Height of the shadow in the tab image, used to ensure clicks in the shadow
64// area still drag restored windows.  This keeps the clickable area large enough
65// to hit easily.
66const int kTabShadowHeight = 4;
67
68}  // namespace
69
70///////////////////////////////////////////////////////////////////////////////
71// BrowserNonClientFrameViewAsh, public:
72
73// static
74const char BrowserNonClientFrameViewAsh::kViewClassName[] =
75    "BrowserNonClientFrameViewAsh";
76
77BrowserNonClientFrameViewAsh::BrowserNonClientFrameViewAsh(
78    BrowserFrame* frame, BrowserView* browser_view)
79    : BrowserNonClientFrameView(frame, browser_view),
80      size_button_(NULL),
81      close_button_(NULL),
82      window_icon_(NULL),
83      frame_painter_(new ash::FramePainter),
84      size_button_minimizes_(false) {
85}
86
87BrowserNonClientFrameViewAsh::~BrowserNonClientFrameViewAsh() {
88}
89
90void BrowserNonClientFrameViewAsh::Init() {
91  // Panels only minimize.
92  ash::FramePainter::SizeButtonBehavior size_button_behavior;
93  size_button_ = new ash::FrameMaximizeButton(this, this);
94  size_button_behavior = ash::FramePainter::SIZE_BUTTON_MAXIMIZES;
95  size_button_->SetAccessibleName(
96      l10n_util::GetStringUTF16(IDS_ACCNAME_MAXIMIZE));
97  AddChildView(size_button_);
98  close_button_ = new views::ImageButton(this);
99  close_button_->SetAccessibleName(
100      l10n_util::GetStringUTF16(IDS_ACCNAME_CLOSE));
101  AddChildView(close_button_);
102
103  // Initializing the TabIconView is expensive, so only do it if we need to.
104  if (browser_view()->ShouldShowWindowIcon()) {
105    window_icon_ = new TabIconView(this);
106    window_icon_->set_is_light(true);
107    AddChildView(window_icon_);
108    window_icon_->Update();
109  }
110
111  // Create incognito icon if necessary.
112  UpdateAvatarInfo();
113
114  // Frame painter handles layout of these buttons.
115  frame_painter_->Init(frame(), window_icon_, size_button_, close_button_,
116                       size_button_behavior);
117}
118
119///////////////////////////////////////////////////////////////////////////////
120// BrowserNonClientFrameView overrides:
121
122gfx::Rect BrowserNonClientFrameViewAsh::GetBoundsForTabStrip(
123    views::View* tabstrip) const {
124  if (!tabstrip)
125    return gfx::Rect();
126  TabStripInsets insets(GetTabStripInsets(false));
127  return gfx::Rect(insets.left, insets.top,
128                   std::max(0, width() - insets.left - insets.right),
129                   tabstrip->GetPreferredSize().height());
130}
131
132BrowserNonClientFrameView::TabStripInsets
133BrowserNonClientFrameViewAsh::GetTabStripInsets(bool force_restored) const {
134  int left = avatar_button() ? kAvatarSideSpacing +
135      browser_view()->GetOTRAvatarIcon().width() + kAvatarSideSpacing :
136      kTabstripLeftSpacing;
137  int right = frame_painter_->GetRightInset() + kTabstripRightSpacing;
138  return TabStripInsets(NonClientTopBorderHeight(force_restored), left, right);
139}
140
141int BrowserNonClientFrameViewAsh::GetThemeBackgroundXInset() const {
142  return frame_painter_->GetThemeBackgroundXInset();
143}
144
145void BrowserNonClientFrameViewAsh::UpdateThrobber(bool running) {
146  if (window_icon_)
147    window_icon_->Update();
148}
149
150///////////////////////////////////////////////////////////////////////////////
151// views::NonClientFrameView overrides:
152
153gfx::Rect BrowserNonClientFrameViewAsh::GetBoundsForClientView() const {
154  int top_height = NonClientTopBorderHeight(false);
155  return frame_painter_->GetBoundsForClientView(top_height, bounds());
156}
157
158gfx::Rect BrowserNonClientFrameViewAsh::GetWindowBoundsForClientBounds(
159    const gfx::Rect& client_bounds) const {
160  int top_height = NonClientTopBorderHeight(false);
161  return frame_painter_->GetWindowBoundsForClientBounds(top_height,
162                                                        client_bounds);
163}
164
165int BrowserNonClientFrameViewAsh::NonClientHitTest(const gfx::Point& point) {
166  int hit_test = frame_painter_->NonClientHitTest(this, point);
167  // When the window is restored we want a large click target above the tabs
168  // to drag the window, so redirect clicks in the tab's shadow to caption.
169  if (hit_test == HTCLIENT &&
170      !(frame()->IsMaximized() || frame()->IsFullscreen())) {
171    // Convert point to client coordinates.
172    gfx::Point client_point(point);
173    View::ConvertPointToTarget(this, frame()->client_view(), &client_point);
174    // Report hits in shadow at top of tabstrip as caption.
175    gfx::Rect tabstrip_bounds(browser_view()->tabstrip()->bounds());
176    if (client_point.y() < tabstrip_bounds.y() + kTabShadowHeight)
177      hit_test = HTCAPTION;
178  }
179  return hit_test;
180}
181
182void BrowserNonClientFrameViewAsh::GetWindowMask(const gfx::Size& size,
183                                                  gfx::Path* window_mask) {
184  // Aura does not use window masks.
185}
186
187void BrowserNonClientFrameViewAsh::ResetWindowControls() {
188  if (ImmersiveFullscreenConfiguration::UseImmersiveFullscreen()) {
189    // Hide the caption buttons in immersive mode because it's confusing when
190    // the user hovers or clicks in the top-right of the screen and hits one.
191    // Only show them during a reveal.
192    ImmersiveModeController* controller =
193        browser_view()->immersive_mode_controller();
194    if (controller->IsEnabled()) {
195      bool revealed = controller->IsRevealed();
196      size_button_->SetVisible(revealed);
197      close_button_->SetVisible(revealed);
198    } else {
199      size_button_->SetVisible(true);
200      close_button_->SetVisible(true);
201    }
202  }
203
204  size_button_->SetState(views::CustomButton::STATE_NORMAL);
205  // The close button isn't affected by this constraint.
206}
207
208void BrowserNonClientFrameViewAsh::UpdateWindowIcon() {
209  if (window_icon_)
210    window_icon_->SchedulePaint();
211}
212
213void BrowserNonClientFrameViewAsh::UpdateWindowTitle() {
214  if (!frame()->IsFullscreen())
215    frame_painter_->SchedulePaintForTitle(BrowserFrame::GetTitleFont());
216}
217
218///////////////////////////////////////////////////////////////////////////////
219// views::View overrides:
220
221void BrowserNonClientFrameViewAsh::OnPaint(gfx::Canvas* canvas) {
222  if (!ShouldPaint())
223    return;
224  // The primary header image changes based on window activation state and
225  // theme, so we look it up for each paint.
226  int theme_frame_image_id = GetThemeFrameImageId();
227  int theme_frame_overlay_image_id = GetThemeFrameOverlayImageId();
228
229  ui::ThemeProvider* theme_provider = GetThemeProvider();
230  ash::FramePainter::Themed header_themed = ash::FramePainter::THEMED_NO;
231  if (theme_provider->HasCustomImage(theme_frame_image_id) ||
232      (theme_frame_overlay_image_id != 0 &&
233       theme_provider->HasCustomImage(theme_frame_overlay_image_id))) {
234    header_themed = ash::FramePainter::THEMED_YES;
235  }
236
237  if (frame_painter_->ShouldUseMinimalHeaderStyle(header_themed))
238    theme_frame_image_id = IDR_AURA_WINDOW_HEADER_BASE_MINIMAL;
239
240  frame_painter_->PaintHeader(
241      this,
242      canvas,
243      ShouldPaintAsActive() ?
244          ash::FramePainter::ACTIVE : ash::FramePainter::INACTIVE,
245      theme_frame_image_id,
246      theme_frame_overlay_image_id);
247  if (browser_view()->ShouldShowWindowTitle())
248    frame_painter_->PaintTitleBar(this, canvas, BrowserFrame::GetTitleFont());
249  if (browser_view()->IsToolbarVisible())
250    PaintToolbarBackground(canvas);
251  else
252    PaintContentEdge(canvas);
253}
254
255void BrowserNonClientFrameViewAsh::Layout() {
256  frame_painter_->LayoutHeader(this, UseShortHeader());
257  if (avatar_button())
258    LayoutAvatar();
259  BrowserNonClientFrameView::Layout();
260}
261
262const char* BrowserNonClientFrameViewAsh::GetClassName() const {
263  return kViewClassName;
264}
265
266bool BrowserNonClientFrameViewAsh::HitTestRect(const gfx::Rect& rect) const {
267  // If the rect is outside the bounds of the client area, claim it.
268  if (NonClientFrameView::HitTestRect(rect))
269    return true;
270
271  // Otherwise claim it only if it's in a non-tab portion of the tabstrip.
272  if (!browser_view()->tabstrip())
273    return false;
274  gfx::Rect tabstrip_bounds(browser_view()->tabstrip()->bounds());
275  gfx::Point tabstrip_origin(tabstrip_bounds.origin());
276  View::ConvertPointToTarget(frame()->client_view(), this, &tabstrip_origin);
277  tabstrip_bounds.set_origin(tabstrip_origin);
278  if (rect.bottom() > tabstrip_bounds.bottom())
279    return false;
280
281  // We convert from our parent's coordinates since we assume we fill its bounds
282  // completely. We need to do this since we're not a parent of the tabstrip,
283  // meaning ConvertPointToTarget would otherwise return something bogus.
284  // TODO(tdanderson): Initialize |browser_view_point| using |rect| instead of
285  // its center point once GetEventHandlerForRect() is implemented.
286  gfx::Point browser_view_point(rect.CenterPoint());
287  View::ConvertPointToTarget(parent(), browser_view(), &browser_view_point);
288  return browser_view()->IsPositionInWindowCaption(browser_view_point);
289}
290
291void BrowserNonClientFrameViewAsh::GetAccessibleState(
292    ui::AccessibleViewState* state) {
293  state->role = ui::AccessibilityTypes::ROLE_TITLEBAR;
294}
295
296gfx::Size BrowserNonClientFrameViewAsh::GetMinimumSize() {
297  return frame_painter_->GetMinimumSize(this);
298}
299
300void BrowserNonClientFrameViewAsh::OnThemeChanged() {
301  BrowserNonClientFrameView::OnThemeChanged();
302  frame_painter_->OnThemeChanged();
303}
304
305///////////////////////////////////////////////////////////////////////////////
306// views::ButtonListener overrides:
307
308void BrowserNonClientFrameViewAsh::ButtonPressed(views::Button* sender,
309                                                 const ui::Event& event) {
310  // When shift-clicking slow down animations for visual debugging.
311  // We used to do this via an event filter that looked for the shift key being
312  // pressed but this interfered with several normal keyboard shortcuts.
313  scoped_ptr<ui::ScopedAnimationDurationScaleMode> slow_duration_mode;
314  if (event.IsShiftDown()) {
315    slow_duration_mode.reset(new ui::ScopedAnimationDurationScaleMode(
316        ui::ScopedAnimationDurationScaleMode::SLOW_DURATION));
317  }
318
319  ash::UserMetricsAction action =
320      ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_MAXIMIZE;
321
322  if (sender == size_button_) {
323    // The maximize button may move out from under the cursor.
324    ResetWindowControls();
325    if (size_button_minimizes_) {
326      frame()->Minimize();
327      action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_MINIMIZE;
328    } else if (frame()->IsFullscreen()) { // Can be clicked in immersive mode.
329      frame()->SetFullscreen(false);
330      action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_EXIT_FULLSCREEN;
331    } else if (frame()->IsMaximized()) {
332      frame()->Restore();
333      action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_RESTORE;
334    } else {
335      frame()->Maximize();
336    }
337    // |this| may be deleted - some windows delete their frames on maximize.
338  } else if (sender == close_button_) {
339    frame()->Close();
340    action = ash::UMA_WINDOW_CLOSE_BUTTON_CLICK;
341  } else {
342    return;
343  }
344  ChromeShellDelegate::instance()->RecordUserMetricsAction(action);
345}
346
347///////////////////////////////////////////////////////////////////////////////
348// chrome::TabIconViewModel overrides:
349
350bool BrowserNonClientFrameViewAsh::ShouldTabIconViewAnimate() const {
351  // This function is queried during the creation of the window as the
352  // TabIconView we host is initialized, so we need to NULL check the selected
353  // WebContents because in this condition there is not yet a selected tab.
354  content::WebContents* current_tab = browser_view()->GetActiveWebContents();
355  return current_tab ? current_tab->IsLoading() : false;
356}
357
358gfx::ImageSkia BrowserNonClientFrameViewAsh::GetFaviconForTabIconView() {
359  views::WidgetDelegate* delegate = frame()->widget_delegate();
360  if (!delegate)
361    return gfx::ImageSkia();
362  return delegate->GetWindowIcon();
363}
364
365///////////////////////////////////////////////////////////////////////////////
366// BrowserNonClientFrameViewAsh, private:
367
368
369int BrowserNonClientFrameViewAsh::NonClientTopBorderHeight(
370    bool force_restored) const {
371  if (force_restored)
372    return kTabstripTopSpacingTall;
373  if (frame()->IsFullscreen())
374    return 0;
375  // Windows with tab strips need a smaller non-client area.
376  if (browser_view()->IsTabStripVisible()) {
377    if (UseShortHeader())
378      return kTabstripTopSpacingShort;
379    return kTabstripTopSpacingTall;
380  }
381  // For windows without a tab strip (popups, etc.) ensure we have enough space
382  // to see the window caption buttons.
383  return close_button_->bounds().bottom() - kContentShadowHeight;
384}
385
386bool BrowserNonClientFrameViewAsh::UseShortHeader() const {
387  // Restored browser -> tall header
388  // Maximized browser -> short header
389  // Fullscreen browser (header shows with immersive reveal) -> short header
390  // Popup&App window -> tall header
391  // Panel -> short header
392  // Dialogs use short header and are handled via CustomFrameViewAsh.
393  Browser* browser = browser_view()->browser();
394  switch (browser->type()) {
395    case Browser::TYPE_TABBED:
396      return frame()->IsMaximized() || frame()->IsFullscreen();
397    case Browser::TYPE_POPUP:
398      return false;
399    default:
400      NOTREACHED();
401      return false;
402  }
403}
404
405void BrowserNonClientFrameViewAsh::LayoutAvatar() {
406  DCHECK(avatar_button());
407  gfx::ImageSkia incognito_icon = browser_view()->GetOTRAvatarIcon();
408
409  if (frame()->IsFullscreen()) {
410    ImmersiveModeController* immersive_controller =
411        browser_view()->immersive_mode_controller();
412    // Hide the incognito icon when the top-of-window views are closed in
413    // immersive mode as the tab indicators are too short for the incognito
414    // icon to still be recongizable.
415    if (immersive_controller->IsEnabled() &&
416        !immersive_controller->IsRevealed()) {
417      avatar_button()->SetBoundsRect(gfx::Rect());
418      return;
419    }
420  }
421
422  int avatar_bottom = GetTabStripInsets(false).top +
423      browser_view()->GetTabStripHeight() - kAvatarBottomSpacing;
424  int avatar_restored_y = avatar_bottom - incognito_icon.height();
425  int avatar_y = (frame()->IsMaximized() || frame()->IsFullscreen()) ?
426      NonClientTopBorderHeight(false) + kContentShadowHeight :
427      avatar_restored_y;
428  gfx::Rect avatar_bounds(kAvatarSideSpacing,
429                          avatar_y,
430                          incognito_icon.width(),
431                          avatar_bottom - avatar_y);
432  avatar_button()->SetBoundsRect(avatar_bounds);
433}
434
435bool BrowserNonClientFrameViewAsh::ShouldPaint() const {
436  // Immersive mode windows are fullscreen, but need to paint during a reveal.
437  return !frame()->IsFullscreen() ||
438      browser_view()->immersive_mode_controller()->IsRevealed();
439}
440
441void BrowserNonClientFrameViewAsh::PaintToolbarBackground(gfx::Canvas* canvas) {
442  gfx::Rect toolbar_bounds(browser_view()->GetToolbarBounds());
443  if (toolbar_bounds.IsEmpty())
444    return;
445  gfx::Point toolbar_origin(toolbar_bounds.origin());
446  View::ConvertPointToTarget(browser_view(), this, &toolbar_origin);
447  toolbar_bounds.set_origin(toolbar_origin);
448
449  int x = toolbar_bounds.x();
450  int w = toolbar_bounds.width();
451  int y = toolbar_bounds.y();
452  int h = toolbar_bounds.height();
453
454  // Gross hack: We split the toolbar images into two pieces, since sometimes
455  // (popup mode) the toolbar isn't tall enough to show the whole image.  The
456  // split happens between the top shadow section and the bottom gradient
457  // section so that we never break the gradient.
458  int split_point = kFrameShadowThickness * 2;
459  int bottom_y = y + split_point;
460  ui::ThemeProvider* tp = GetThemeProvider();
461  int bottom_edge_height = h - split_point;
462
463  canvas->FillRect(gfx::Rect(x, bottom_y, w, bottom_edge_height),
464                   tp->GetColor(ThemeProperties::COLOR_TOOLBAR));
465
466  // Paint the main toolbar image.  Since this image is also used to draw the
467  // tab background, we must use the tab strip offset to compute the image
468  // source y position.  If you have to debug this code use an image editor
469  // to paint a diagonal line through the toolbar image and ensure it lines up
470  // across the tab and toolbar.
471  gfx::ImageSkia* theme_toolbar = tp->GetImageSkiaNamed(IDR_THEME_TOOLBAR);
472  canvas->TileImageInt(
473      *theme_toolbar,
474      x + GetThemeBackgroundXInset(),
475      bottom_y - GetTabStripInsets(false).top,
476      x, bottom_y,
477      w, theme_toolbar->height());
478
479  // The content area line has a shadow that extends a couple of pixels above
480  // the toolbar bounds.
481  const int kContentShadowHeight = 2;
482  gfx::ImageSkia* toolbar_top = tp->GetImageSkiaNamed(IDR_TOOLBAR_SHADE_TOP);
483  canvas->TileImageInt(*toolbar_top,
484                       0, 0,
485                       x, y - kContentShadowHeight,
486                       w, split_point + kContentShadowHeight + 1);
487
488  // Draw the "lightening" shade line around the edges of the toolbar.
489  gfx::ImageSkia* toolbar_left = tp->GetImageSkiaNamed(IDR_TOOLBAR_SHADE_LEFT);
490  canvas->TileImageInt(*toolbar_left,
491                       0, 0,
492                       x + kClientEdgeThickness,
493                       y + kClientEdgeThickness + kContentShadowHeight,
494                       toolbar_left->width(), theme_toolbar->height());
495  gfx::ImageSkia* toolbar_right =
496      tp->GetImageSkiaNamed(IDR_TOOLBAR_SHADE_RIGHT);
497  canvas->TileImageInt(*toolbar_right,
498                       0, 0,
499                       w - toolbar_right->width() - 2 * kClientEdgeThickness,
500                       y + kClientEdgeThickness + kContentShadowHeight,
501                       toolbar_right->width(), theme_toolbar->height());
502
503  // Draw the content/toolbar separator.
504  canvas->FillRect(
505      gfx::Rect(x + kClientEdgeThickness,
506                toolbar_bounds.bottom() - kClientEdgeThickness,
507                w - (2 * kClientEdgeThickness),
508                kClientEdgeThickness),
509      ThemeProperties::GetDefaultColor(
510          ThemeProperties::COLOR_TOOLBAR_SEPARATOR));
511}
512
513void BrowserNonClientFrameViewAsh::PaintContentEdge(gfx::Canvas* canvas) {
514  canvas->FillRect(gfx::Rect(0, close_button_->bounds().bottom(),
515                             width(), kClientEdgeThickness),
516      ThemeProperties::GetDefaultColor(
517          ThemeProperties::COLOR_TOOLBAR_SEPARATOR));
518}
519
520int BrowserNonClientFrameViewAsh::GetThemeFrameImageId() const {
521  bool is_incognito = browser_view()->IsOffTheRecord() &&
522                      !browser_view()->IsGuestSession();
523  if (browser_view()->IsBrowserTypeNormal()) {
524    // Use the standard resource ids to allow users to theme the frames.
525    if (ShouldPaintAsActive()) {
526      return is_incognito ?
527          IDR_THEME_FRAME_INCOGNITO : IDR_THEME_FRAME;
528    }
529    return is_incognito ?
530        IDR_THEME_FRAME_INCOGNITO_INACTIVE : IDR_THEME_FRAME_INACTIVE;
531  }
532  // Never theme app and popup windows.
533  if (ShouldPaintAsActive()) {
534    return is_incognito ?
535        IDR_AURA_WINDOW_HEADER_BASE_INCOGNITO_ACTIVE :
536        IDR_AURA_WINDOW_HEADER_BASE_ACTIVE;
537  }
538  return is_incognito ?
539      IDR_AURA_WINDOW_HEADER_BASE_INCOGNITO_INACTIVE :
540      IDR_AURA_WINDOW_HEADER_BASE_INACTIVE;
541}
542
543int BrowserNonClientFrameViewAsh::GetThemeFrameOverlayImageId() const {
544  ui::ThemeProvider* tp = GetThemeProvider();
545  if (tp->HasCustomImage(IDR_THEME_FRAME_OVERLAY) &&
546      browser_view()->IsBrowserTypeNormal() &&
547      !browser_view()->IsOffTheRecord()) {
548    return ShouldPaintAsActive() ?
549        IDR_THEME_FRAME_OVERLAY : IDR_THEME_FRAME_OVERLAY_INACTIVE;
550  }
551  return 0;
552}
553