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/location_bar/icon_label_bubble_view.h"
6
7 #include <algorithm>
8 #include <memory>
9 #include <utility>
10
11 #include "chrome/browser/ui/layout_constants.h"
12 #include "chrome/browser/ui/omnibox/omnibox_theme.h"
13 #include "ui/accessibility/ax_node_data.h"
14 #include "ui/base/l10n/l10n_util.h"
15 #include "ui/compositor/layer_animator.h"
16 #include "ui/compositor/scoped_layer_animation_settings.h"
17 #include "ui/gfx/canvas.h"
18 #include "ui/gfx/geometry/insets.h"
19 #include "ui/gfx/geometry/rect.h"
20 #include "ui/gfx/scoped_canvas.h"
21 #include "ui/gfx/skia_util.h"
22 #include "ui/views/accessibility/ax_virtual_view.h"
23 #include "ui/views/accessibility/view_accessibility.h"
24 #include "ui/views/animation/flood_fill_ink_drop_ripple.h"
25 #include "ui/views/animation/ink_drop_highlight.h"
26 #include "ui/views/animation/ink_drop_impl.h"
27 #include "ui/views/animation/ink_drop_mask.h"
28 #include "ui/views/animation/ink_drop_ripple.h"
29 #include "ui/views/background.h"
30 #include "ui/views/border.h"
31 #include "ui/views/controls/highlight_path_generator.h"
32 #include "ui/views/controls/image_view.h"
33 #include "ui/views/widget/widget.h"
34
35 namespace {
36
37 // Amount of space reserved for the separator that appears after the icon or
38 // label.
39 constexpr int kIconLabelBubbleSeparatorWidth = 1;
40
41 // Amount of space on either side of the separator that appears after the icon
42 // or label.
43 constexpr int kIconLabelBubbleSpaceBesideSeparator = 8;
44
45 // The length of the separator's fade animation. These values are empirical.
46 constexpr int kIconLabelBubbleFadeInDurationMs = 250;
47 constexpr int kIconLabelBubbleFadeOutDurationMs = 175;
48
49 } // namespace
50
GetIconLabelBubbleInkDropColor() const51 SkColor IconLabelBubbleView::Delegate::GetIconLabelBubbleInkDropColor() const {
52 return GetIconLabelBubbleSurroundingForegroundColor();
53 }
54
SeparatorView(IconLabelBubbleView * owner)55 IconLabelBubbleView::SeparatorView::SeparatorView(IconLabelBubbleView* owner) {
56 DCHECK(owner);
57 owner_ = owner;
58
59 SetPaintToLayer();
60 layer()->SetFillsBoundsOpaquely(false);
61 }
62
OnPaint(gfx::Canvas * canvas)63 void IconLabelBubbleView::SeparatorView::OnPaint(gfx::Canvas* canvas) {
64 // This uses the surrounding context as the base color instead of
65 // IconLabelBubbleView::GetForegroundColor() so that if the
66 // IconLabelBubbleView has been emphasized (e.g. red text for a security
67 // error) the separator will still blend into the background.
68 const SkColor separator_color = SkColorSetA(
69 owner_->delegate_->GetIconLabelBubbleSurroundingForegroundColor(), 0x69);
70 const float x = GetLocalBounds().right() -
71 owner_->GetEndPaddingWithSeparator() -
72 1.0f / canvas->image_scale();
73 canvas->DrawLine(gfx::PointF(x, GetLocalBounds().y()),
74 gfx::PointF(x, GetLocalBounds().bottom()), separator_color);
75 }
76
OnThemeChanged()77 void IconLabelBubbleView::SeparatorView::OnThemeChanged() {
78 views::View::OnThemeChanged();
79 SchedulePaint();
80 }
81
UpdateOpacity()82 void IconLabelBubbleView::SeparatorView::UpdateOpacity() {
83 if (!GetVisible())
84 return;
85
86 // When using focus rings are visible we should hide the separator instantly
87 // when the IconLabelBubbleView is focused. Otherwise we should follow the
88 // inkdrop.
89 if (owner_->focus_ring() && owner_->HasFocus()) {
90 layer()->SetOpacity(0.0f);
91 return;
92 }
93
94 views::InkDrop* ink_drop = owner_->GetInkDrop();
95 DCHECK(ink_drop);
96
97 // If an inkdrop highlight or ripple is animating in or visible, the
98 // separator should fade out.
99 views::InkDropState state = ink_drop->GetTargetInkDropState();
100 float opacity = 0.0f;
101 float duration = kIconLabelBubbleFadeOutDurationMs;
102 if (!ink_drop->IsHighlightFadingInOrVisible() &&
103 (state == views::InkDropState::HIDDEN ||
104 state == views::InkDropState::ACTION_TRIGGERED ||
105 state == views::InkDropState::DEACTIVATED)) {
106 opacity = 1.0f;
107 duration = kIconLabelBubbleFadeInDurationMs;
108 }
109
110 ui::ScopedLayerAnimationSettings animation(layer()->GetAnimator());
111 animation.SetTransitionDuration(base::TimeDelta::FromMilliseconds(duration));
112 animation.SetTweenType(gfx::Tween::Type::EASE_IN);
113 layer()->SetOpacity(opacity);
114 }
115
116 class IconLabelBubbleView::HighlightPathGenerator
117 : public views::HighlightPathGenerator {
118 public:
119 HighlightPathGenerator() = default;
120
121 // views::HighlightPathGenerator:
GetHighlightPath(const views::View * view)122 SkPath GetHighlightPath(const views::View* view) override {
123 return static_cast<const IconLabelBubbleView*>(view)->GetHighlightPath();
124 }
125
126 private:
127 DISALLOW_COPY_AND_ASSIGN(HighlightPathGenerator);
128 };
129
IconLabelBubbleView(const gfx::FontList & font_list,Delegate * delegate)130 IconLabelBubbleView::IconLabelBubbleView(const gfx::FontList& font_list,
131 Delegate* delegate)
132 : delegate_(delegate),
133 separator_view_(AddChildView(std::make_unique<SeparatorView>(this))) {
134 DCHECK(delegate_);
135
136 SetFontList(font_list);
137 SetHorizontalAlignment(gfx::ALIGN_LEFT);
138
139 separator_view_->SetVisible(ShouldShowSeparator());
140
141 SetInkDropVisibleOpacity(GetOmniboxStateOpacity(OmniboxPartState::SELECTED));
142 SetInkDropHighlightOpacity(GetOmniboxStateOpacity(OmniboxPartState::HOVERED));
143
144 views::HighlightPathGenerator::Install(
145 this, std::make_unique<HighlightPathGenerator>());
146 SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
147
148 UpdateBorder();
149
150 SetNotifyEnterExitOnChild(true);
151
152 // Flip the canvas in RTL so the separator is drawn on the correct side.
153 separator_view_->SetFlipCanvasOnPaintForRTLUI(true);
154
155 auto alert_view = std::make_unique<views::AXVirtualView>();
156 alert_view->GetCustomData().role = ax::mojom::Role::kAlert;
157 alert_virtual_view_ = alert_view.get();
158 GetViewAccessibility().AddVirtualChildView(std::move(alert_view));
159 }
160
~IconLabelBubbleView()161 IconLabelBubbleView::~IconLabelBubbleView() {}
162
InkDropAnimationStarted()163 void IconLabelBubbleView::InkDropAnimationStarted() {
164 separator_view_->UpdateOpacity();
165 }
166
InkDropRippleAnimationEnded(views::InkDropState state)167 void IconLabelBubbleView::InkDropRippleAnimationEnded(
168 views::InkDropState state) {}
169
ShouldShowLabel() const170 bool IconLabelBubbleView::ShouldShowLabel() const {
171 if (slide_animation_.is_animating() || is_animation_paused_)
172 return !IsShrinking() || (width() > image()->GetPreferredSize().width());
173 return label()->GetVisible() && !label()->GetText().empty();
174 }
175
SetLabel(const base::string16 & label_text)176 void IconLabelBubbleView::SetLabel(const base::string16& label_text) {
177 SetAccessibleName(label_text);
178 label()->SetText(label_text);
179 separator_view_->SetVisible(ShouldShowSeparator());
180 separator_view_->UpdateOpacity();
181 }
182
SetFontList(const gfx::FontList & font_list)183 void IconLabelBubbleView::SetFontList(const gfx::FontList& font_list) {
184 label()->SetFontList(font_list);
185 }
186
GetForegroundColor() const187 SkColor IconLabelBubbleView::GetForegroundColor() const {
188 return delegate_->GetIconLabelBubbleSurroundingForegroundColor();
189 }
190
UpdateLabelColors()191 void IconLabelBubbleView::UpdateLabelColors() {
192 SetEnabledTextColors(GetForegroundColor());
193 label()->SetBackgroundColor(delegate_->GetIconLabelBubbleBackgroundColor());
194 }
195
ShouldShowSeparator() const196 bool IconLabelBubbleView::ShouldShowSeparator() const {
197 return ShouldShowLabel();
198 }
199
GetWidthBetween(int min,int max) const200 int IconLabelBubbleView::GetWidthBetween(int min, int max) const {
201 // TODO(https://crbug.com/8944): Disable animations globally instead of having
202 // piecemeal opt ins for respecting prefers reduced motion.
203 if (gfx::Animation::PrefersReducedMotion())
204 return max;
205
206 if (!slide_animation_.is_animating() && !is_animation_paused_)
207 return max;
208
209 double progress = is_animation_paused_ ? pause_animation_state_
210 : slide_animation_.GetCurrentValue();
211 // This tween matches the default for SlideAnimation.
212 const gfx::Tween::Type kTween = gfx::Tween::EASE_OUT;
213 if (progress < open_state_fraction_) {
214 double state =
215 gfx::Tween::CalculateValue(kTween, progress / open_state_fraction_);
216 return gfx::Tween::IntValueBetween(state, min, max);
217 }
218
219 if (progress <= (1 - open_state_fraction_))
220 return max;
221
222 double state = gfx::Tween::CalculateValue(
223 kTween, (progress - (1 - open_state_fraction_)) / open_state_fraction_);
224 // Note |min| and |max| are reversed.
225 return gfx::Tween::IntValueBetween(state, max, min);
226 }
227
IsShrinking() const228 bool IconLabelBubbleView::IsShrinking() const {
229 if (!slide_animation_.is_animating() || is_animation_paused_)
230 return false;
231 return slide_animation_.IsClosing() ||
232 (open_state_fraction_ < 1.0 &&
233 slide_animation_.GetCurrentValue() > (1.0 - open_state_fraction_));
234 }
235
ShowBubble(const ui::Event & event)236 bool IconLabelBubbleView::ShowBubble(const ui::Event& event) {
237 return false;
238 }
239
IsBubbleShowing() const240 bool IconLabelBubbleView::IsBubbleShowing() const {
241 return false;
242 }
243
OnTouchUiChanged()244 void IconLabelBubbleView::OnTouchUiChanged() {
245 UpdateBorder();
246
247 // PreferredSizeChanged() incurs an expensive layout of the location bar, so
248 // only call it when this view is showing.
249 if (GetVisible())
250 PreferredSizeChanged();
251 }
252
CalculatePreferredSize() const253 gfx::Size IconLabelBubbleView::CalculatePreferredSize() const {
254 return GetSizeForLabelWidth(label()->GetPreferredSize().width());
255 }
256
Layout()257 void IconLabelBubbleView::Layout() {
258 ink_drop_container()->SetBoundsRect(GetLocalBounds());
259
260 // We may not have horizontal room for both the image and the trailing
261 // padding. When the view is expanding (or showing-label steady state), the
262 // image. When the view is contracting (or hidden-label steady state), whittle
263 // away at the trailing padding instead.
264 int bubble_trailing_padding = GetEndPaddingWithSeparator();
265 int image_width = image()->GetPreferredSize().width();
266 const int space_shortage = image_width + bubble_trailing_padding - width();
267 if (space_shortage > 0) {
268 if (ShouldShowLabel())
269 image_width -= space_shortage;
270 else
271 bubble_trailing_padding -= space_shortage;
272 }
273 image()->SetBounds(GetInsets().left(), 0, image_width, height());
274
275 // Compute the label bounds. The label gets whatever size is left over after
276 // accounting for the preferred image width and padding amounts. Note that if
277 // the label has zero size it doesn't actually matter what we compute its X
278 // value to be, since it won't be visible.
279 const int label_x = image()->bounds().right() + GetInternalSpacing();
280 int label_width = std::max(0, width() - label_x - bubble_trailing_padding -
281 GetWidthBetweenIconAndSeparator());
282 label()->SetBounds(label_x, 0, label_width, height());
283
284 // The separator should be the same height as the icons.
285 const int separator_height = GetLayoutConstant(LOCATION_BAR_ICON_SIZE);
286 gfx::Rect separator_bounds(label()->bounds());
287 separator_bounds.Inset(0, (separator_bounds.height() - separator_height) / 2);
288
289 float separator_width =
290 GetWidthBetweenIconAndSeparator() + GetEndPaddingWithSeparator();
291 int separator_x = label()->GetText().empty() ? image()->bounds().right()
292 : label()->bounds().right();
293 separator_view_->SetBounds(separator_x, separator_bounds.y(), separator_width,
294 separator_height);
295
296 if (focus_ring()) {
297 focus_ring()->Layout();
298 focus_ring()->SchedulePaint();
299 }
300 }
301
OnMousePressed(const ui::MouseEvent & event)302 bool IconLabelBubbleView::OnMousePressed(const ui::MouseEvent& event) {
303 suppress_button_release_ = IsBubbleShowing();
304 return LabelButton::OnMousePressed(event);
305 }
306
OnThemeChanged()307 void IconLabelBubbleView::OnThemeChanged() {
308 LabelButton::OnThemeChanged();
309
310 // LabelButton::OnThemeChanged() sets a views::Background on the label
311 // under certain conditions. We don't want that, so unset the background.
312 label()->SetBackground(nullptr);
313
314 UpdateLabelColors();
315 }
316
CreateInkDrop()317 std::unique_ptr<views::InkDrop> IconLabelBubbleView::CreateInkDrop() {
318 std::unique_ptr<views::InkDropImpl> ink_drop =
319 CreateDefaultFloodFillInkDropImpl();
320 ink_drop->SetShowHighlightOnFocus(!focus_ring());
321 ink_drop->AddObserver(this);
322 return std::move(ink_drop);
323 }
324
GetInkDropBaseColor() const325 SkColor IconLabelBubbleView::GetInkDropBaseColor() const {
326 return delegate_->GetIconLabelBubbleInkDropColor();
327 }
328
IsTriggerableEvent(const ui::Event & event)329 bool IconLabelBubbleView::IsTriggerableEvent(const ui::Event& event) {
330 if (event.IsMouseEvent())
331 return !IsBubbleShowing() && !suppress_button_release_;
332
333 return true;
334 }
335
ShouldUpdateInkDropOnClickCanceled() const336 bool IconLabelBubbleView::ShouldUpdateInkDropOnClickCanceled() const {
337 // The click might be cancelled because the bubble is still opened. In this
338 // case, the ink drop state should not be hidden by Button.
339 return false;
340 }
341
NotifyClick(const ui::Event & event)342 void IconLabelBubbleView::NotifyClick(const ui::Event& event) {
343 LabelButton::NotifyClick(event);
344 ShowBubble(event);
345 }
346
OnFocus()347 void IconLabelBubbleView::OnFocus() {
348 separator_view_->UpdateOpacity();
349 LabelButton::OnFocus();
350 }
351
OnBlur()352 void IconLabelBubbleView::OnBlur() {
353 separator_view_->UpdateOpacity();
354 LabelButton::OnBlur();
355 }
356
AnimationEnded(const gfx::Animation * animation)357 void IconLabelBubbleView::AnimationEnded(const gfx::Animation* animation) {
358 if (animation != &slide_animation_)
359 return views::LabelButton::AnimationEnded(animation);
360
361 if (!is_animation_paused_) {
362 // If there is no separator to show, then that means we want the text to
363 // disappear after animating.
364 ResetSlideAnimation(/*show_label=*/ShouldShowSeparator());
365 PreferredSizeChanged();
366 }
367
368 GetInkDrop()->SetShowHighlightOnHover(true);
369 GetInkDrop()->SetShowHighlightOnFocus(!focus_ring());
370 }
371
AnimationProgressed(const gfx::Animation * animation)372 void IconLabelBubbleView::AnimationProgressed(const gfx::Animation* animation) {
373 if (animation != &slide_animation_)
374 return views::LabelButton::AnimationProgressed(animation);
375
376 if (!is_animation_paused_)
377 PreferredSizeChanged();
378 }
379
AnimationCanceled(const gfx::Animation * animation)380 void IconLabelBubbleView::AnimationCanceled(const gfx::Animation* animation) {
381 if (animation != &slide_animation_)
382 return views::LabelButton::AnimationCanceled(animation);
383
384 AnimationEnded(animation);
385 }
386
GetAccessibleNodeData(ui::AXNodeData * node_data)387 void IconLabelBubbleView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
388 LabelButton::GetAccessibleNodeData(node_data);
389 if (GetAccessibleName().empty())
390 node_data->SetNameExplicitlyEmpty();
391 }
392
SetImageModel(const ui::ImageModel & image_model)393 void IconLabelBubbleView::SetImageModel(const ui::ImageModel& image_model) {
394 DCHECK(!image_model.IsEmpty());
395 LabelButton::SetImageModel(STATE_NORMAL, image_model);
396 }
397
GetSizeForLabelWidth(int label_width) const398 gfx::Size IconLabelBubbleView::GetSizeForLabelWidth(int label_width) const {
399 gfx::Size image_size = image()->GetPreferredSize();
400 image_size.Enlarge(GetInsets().left() + GetWidthBetweenIconAndSeparator() +
401 GetEndPaddingWithSeparator(),
402 GetInsets().height());
403
404 const bool shrinking = IsShrinking();
405 // The out portion of the in-out animation continues for the last few pixels
406 // even after the label is not visible in order to slide the icon into its
407 // final position. Therefore it is necessary to calculate additional width
408 // even when the label is hidden as long as the animation is still shrinking.
409 if (!ShouldShowLabel() && !shrinking)
410 return image_size;
411
412 const int min_width =
413 shrinking ? image_size.width() : grow_animation_starting_width_;
414 const int max_width = image_size.width() + GetInternalSpacing() + label_width;
415
416 return gfx::Size(GetWidthBetween(min_width, max_width), image_size.height());
417 }
418
GetInternalSpacing() const419 int IconLabelBubbleView::GetInternalSpacing() const {
420 if (image()->GetPreferredSize().IsEmpty())
421 return 0;
422 return (ui::TouchUiController::Get()->touch_ui() ? 10 : 8) +
423 GetExtraInternalSpacing();
424 }
425
GetExtraInternalSpacing() const426 int IconLabelBubbleView::GetExtraInternalSpacing() const {
427 return 0;
428 }
429
GetWidthBetweenIconAndSeparator() const430 int IconLabelBubbleView::GetWidthBetweenIconAndSeparator() const {
431 return ShouldShowSeparator() ? kIconLabelBubbleSpaceBesideSeparator : 0;
432 }
433
GetEndPaddingWithSeparator() const434 int IconLabelBubbleView::GetEndPaddingWithSeparator() const {
435 int end_padding = ShouldShowSeparator() ? kIconLabelBubbleSpaceBesideSeparator
436 : GetInsets().right();
437 if (ShouldShowSeparator())
438 end_padding += kIconLabelBubbleSeparatorWidth;
439 return end_padding;
440 }
441
GetClassName() const442 const char* IconLabelBubbleView::GetClassName() const {
443 return "IconLabelBubbleView";
444 }
445
SetUpForAnimation()446 void IconLabelBubbleView::SetUpForAnimation() {
447 SetInkDropMode(InkDropMode::ON);
448 SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
449 label()->SetElideBehavior(gfx::NO_ELIDE);
450 label()->SetVisible(false);
451 slide_animation_.SetSlideDuration(base::TimeDelta::FromMilliseconds(150));
452 open_state_fraction_ = 1.0;
453 }
454
SetUpForInOutAnimation()455 void IconLabelBubbleView::SetUpForInOutAnimation() {
456 SetUpForAnimation();
457 // The duration of the slide includes the appearance of the label (600ms),
458 // statically showing the label (1800ms), and hiding the label (600ms). The
459 // proportion of time spent in each portion of the animation is controlled by
460 // kIconLabelBubbleOpenTimeFraction.
461 slide_animation_.SetSlideDuration(base::TimeDelta::FromMilliseconds(3000));
462 // The tween is calculated in GetWidthBetween().
463 slide_animation_.SetTweenType(gfx::Tween::LINEAR);
464 open_state_fraction_ = 0.2;
465 }
466
AnimateIn(base::Optional<int> string_id)467 void IconLabelBubbleView::AnimateIn(base::Optional<int> string_id) {
468 if (!label()->GetVisible()) {
469 // Start animation from the current width, otherwise the icon will also be
470 // included if visible.
471 grow_animation_starting_width_ = GetVisible() ? width() : 0;
472 if (string_id) {
473 base::string16 label = l10n_util::GetStringUTF16(string_id.value());
474 SetLabel(label);
475
476 // Send an accessibility alert whose text is the label's text. Doing this
477 // causes a screenreader to immediately announce the text of the button,
478 // which serves to announce it. This is done unconditionally here if there
479 // is text because the animation is intended to draw attention to the
480 // instance anyway.
481 alert_virtual_view_->GetCustomData().SetName(label);
482 alert_virtual_view_->NotifyAccessibilityEvent(ax::mojom::Event::kAlert);
483 }
484 label()->SetVisible(true);
485 ShowAnimation();
486 }
487 }
488
AnimateOut()489 void IconLabelBubbleView::AnimateOut() {
490 if (label()->GetVisible()) {
491 label()->SetVisible(false);
492 HideAnimation();
493 }
494 }
495
ResetSlideAnimation(bool show_label)496 void IconLabelBubbleView::ResetSlideAnimation(bool show_label) {
497 label()->SetVisible(show_label);
498 slide_animation_.Reset(show_label);
499 }
500
ReduceAnimationTimeForTesting()501 void IconLabelBubbleView::ReduceAnimationTimeForTesting() {
502 slide_animation_.SetSlideDuration(base::TimeDelta::FromMilliseconds(1));
503 }
504
PauseAnimation()505 void IconLabelBubbleView::PauseAnimation() {
506 if (slide_animation_.is_animating()) {
507 // If the user clicks while we're animating, the bubble arrow will be
508 // pointing to the image, and if we allow the animation to keep running, the
509 // image will move away from the arrow (or we'll have to move the bubble,
510 // which is even worse). So we want to stop the animation. We have two
511 // choices: jump to the final post-animation state (no label visible), or
512 // pause the animation where we are and continue running after the bubble
513 // closes. The former looks more jerky, so we avoid it unless the animation
514 // hasn't even fully exposed the image yet, in which case pausing with half
515 // an image visible will look broken.
516 if (!is_animation_paused_ && ShouldShowLabel()) {
517 is_animation_paused_ = true;
518 pause_animation_state_ = slide_animation_.GetCurrentValue();
519 }
520 slide_animation_.Reset();
521 }
522 }
523
UnpauseAnimation()524 void IconLabelBubbleView::UnpauseAnimation() {
525 if (is_animation_paused_) {
526 slide_animation_.Reset(pause_animation_state_);
527 is_animation_paused_ = false;
528 ShowAnimation();
529 }
530 }
531
GetAnimationValue() const532 double IconLabelBubbleView::GetAnimationValue() const {
533 return slide_animation_.GetCurrentValue();
534 }
535
ShowAnimation()536 void IconLabelBubbleView::ShowAnimation() {
537 slide_animation_.Show();
538 GetInkDrop()->SetShowHighlightOnHover(false);
539 GetInkDrop()->SetShowHighlightOnFocus(false);
540 }
541
HideAnimation()542 void IconLabelBubbleView::HideAnimation() {
543 slide_animation_.Hide();
544 GetInkDrop()->SetShowHighlightOnHover(false);
545 GetInkDrop()->SetShowHighlightOnFocus(false);
546 }
547
GetHighlightPath() const548 SkPath IconLabelBubbleView::GetHighlightPath() const {
549 gfx::Rect highlight_bounds = GetLocalBounds();
550 if (ShouldShowSeparator())
551 highlight_bounds.Inset(0, 0, GetEndPaddingWithSeparator(), 0);
552 highlight_bounds = GetMirroredRect(highlight_bounds);
553
554 const float corner_radius = highlight_bounds.height() / 2.f;
555 const SkRect rect = RectToSkRect(highlight_bounds);
556
557 return SkPath().addRoundRect(rect, corner_radius, corner_radius);
558 }
559
UpdateBorder()560 void IconLabelBubbleView::UpdateBorder() {
561 // Bubbles are given the full internal height of the location bar so that all
562 // child views in the location bar have the same height. The visible height of
563 // the bubble should be smaller, so use an empty border to shrink down the
564 // content bounds so the background gets painted correctly.
565 SetBorder(views::CreateEmptyBorder(
566 gfx::Insets(GetLayoutConstant(LOCATION_BAR_CHILD_INTERIOR_PADDING),
567 GetLayoutInsets(LOCATION_BAR_ICON_INTERIOR_PADDING).left())));
568 }
569