Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / views / omnibox / omnibox_result_view.cc
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 // For WinDDK ATL compatibility, these ATL headers must come first.
6 #include "build/build_config.h"
7 #if defined(OS_WIN)
8 #include <atlbase.h>  // NOLINT
9 #include <atlwin.h>  // NOLINT
10 #endif
11
12 #include "chrome/browser/ui/views/omnibox/omnibox_result_view.h"
13
14 #include <algorithm>  // NOLINT
15
16 #include "base/i18n/bidi_line_iterator.h"
17 #include "base/memory/scoped_vector.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/strings/string_util.h"
20 #include "chrome/browser/ui/omnibox/omnibox_popup_model.h"
21 #include "chrome/browser/ui/views/location_bar/location_bar_view.h"
22 #include "chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.h"
23 #include "grit/component_scaled_resources.h"
24 #include "grit/generated_resources.h"
25 #include "grit/theme_resources.h"
26 #include "ui/base/l10n/l10n_util.h"
27 #include "ui/base/theme_provider.h"
28 #include "ui/gfx/canvas.h"
29 #include "ui/gfx/color_utils.h"
30 #include "ui/gfx/image/image.h"
31 #include "ui/gfx/range/range.h"
32 #include "ui/gfx/render_text.h"
33 #include "ui/gfx/text_utils.h"
34 #include "ui/native_theme/native_theme.h"
35
36 using ui::NativeTheme;
37
38 namespace {
39
40 // The minimum distance between the top and bottom of the {icon|text} and the
41 // top or bottom of the row.
42 const int kMinimumIconVerticalPadding = 2;
43 const int kMinimumTextVerticalPadding = 3;
44
45 // A mapping from OmniboxResultView's ResultViewState/ColorKind types to
46 // NativeTheme colors.
47 struct TranslationTable {
48   ui::NativeTheme::ColorId id;
49   OmniboxResultView::ResultViewState state;
50   OmniboxResultView::ColorKind kind;
51 } static const kTranslationTable[] = {
52   { NativeTheme::kColorId_ResultsTableNormalBackground,
53     OmniboxResultView::NORMAL, OmniboxResultView::BACKGROUND },
54   { NativeTheme::kColorId_ResultsTableHoveredBackground,
55     OmniboxResultView::HOVERED, OmniboxResultView::BACKGROUND },
56   { NativeTheme::kColorId_ResultsTableSelectedBackground,
57     OmniboxResultView::SELECTED, OmniboxResultView::BACKGROUND },
58   { NativeTheme::kColorId_ResultsTableNormalText,
59     OmniboxResultView::NORMAL, OmniboxResultView::TEXT },
60   { NativeTheme::kColorId_ResultsTableHoveredText,
61     OmniboxResultView::HOVERED, OmniboxResultView::TEXT },
62   { NativeTheme::kColorId_ResultsTableSelectedText,
63     OmniboxResultView::SELECTED, OmniboxResultView::TEXT },
64   { NativeTheme::kColorId_ResultsTableNormalDimmedText,
65     OmniboxResultView::NORMAL, OmniboxResultView::DIMMED_TEXT },
66   { NativeTheme::kColorId_ResultsTableHoveredDimmedText,
67     OmniboxResultView::HOVERED, OmniboxResultView::DIMMED_TEXT },
68   { NativeTheme::kColorId_ResultsTableSelectedDimmedText,
69     OmniboxResultView::SELECTED, OmniboxResultView::DIMMED_TEXT },
70   { NativeTheme::kColorId_ResultsTableNormalUrl,
71     OmniboxResultView::NORMAL, OmniboxResultView::URL },
72   { NativeTheme::kColorId_ResultsTableHoveredUrl,
73     OmniboxResultView::HOVERED, OmniboxResultView::URL },
74   { NativeTheme::kColorId_ResultsTableSelectedUrl,
75     OmniboxResultView::SELECTED, OmniboxResultView::URL },
76   { NativeTheme::kColorId_ResultsTableNormalDivider,
77     OmniboxResultView::NORMAL, OmniboxResultView::DIVIDER },
78   { NativeTheme::kColorId_ResultsTableHoveredDivider,
79     OmniboxResultView::HOVERED, OmniboxResultView::DIVIDER },
80   { NativeTheme::kColorId_ResultsTableSelectedDivider,
81     OmniboxResultView::SELECTED, OmniboxResultView::DIVIDER },
82 };
83
84 }  // namespace
85
86 ////////////////////////////////////////////////////////////////////////////////
87 // OmniboxResultView, public:
88
89 // This class is a utility class for calculations affected by whether the result
90 // view is horizontally mirrored.  The drawing functions can be written as if
91 // all drawing occurs left-to-right, and then use this class to get the actual
92 // coordinates to begin drawing onscreen.
93 class OmniboxResultView::MirroringContext {
94  public:
95   MirroringContext() : center_(0), right_(0) {}
96
97   // Tells the mirroring context to use the provided range as the physical
98   // bounds of the drawing region.  When coordinate mirroring is needed, the
99   // mirror point will be the center of this range.
100   void Initialize(int x, int width) {
101     center_ = x + width / 2;
102     right_ = x + width;
103   }
104
105   // Given a logical range within the drawing region, returns the coordinate of
106   // the possibly-mirrored "left" side.  (This functions exactly like
107   // View::MirroredLeftPointForRect().)
108   int mirrored_left_coord(int left, int right) const {
109     return base::i18n::IsRTL() ? (center_ + (center_ - right)) : left;
110   }
111
112   // Given a logical coordinate within the drawing region, returns the remaining
113   // width available.
114   int remaining_width(int x) const {
115     return right_ - x;
116   }
117
118  private:
119   int center_;
120   int right_;
121
122   DISALLOW_COPY_AND_ASSIGN(MirroringContext);
123 };
124
125 OmniboxResultView::OmniboxResultView(OmniboxPopupContentsView* model,
126                                      int model_index,
127                                      LocationBarView* location_bar_view,
128                                      const gfx::FontList& font_list)
129     : edge_item_padding_(LocationBarView::kItemPadding),
130       item_padding_(LocationBarView::kItemPadding),
131       minimum_text_vertical_padding_(kMinimumTextVerticalPadding),
132       model_(model),
133       model_index_(model_index),
134       location_bar_view_(location_bar_view),
135       font_list_(font_list),
136       font_height_(
137           std::max(font_list.GetHeight(),
138                    font_list.DeriveWithStyle(gfx::Font::BOLD).GetHeight())),
139       mirroring_context_(new MirroringContext()),
140       keyword_icon_(new views::ImageView()),
141       animation_(new gfx::SlideAnimation(this)) {
142   CHECK_GE(model_index, 0);
143   if (default_icon_size_ == 0) {
144     default_icon_size_ =
145         location_bar_view_->GetThemeProvider()->GetImageSkiaNamed(
146             AutocompleteMatch::TypeToIcon(
147                 AutocompleteMatchType::URL_WHAT_YOU_TYPED))->width();
148   }
149   keyword_icon_->set_owned_by_client();
150   keyword_icon_->EnableCanvasFlippingForRTLUI(true);
151   keyword_icon_->SetImage(GetKeywordIcon());
152   keyword_icon_->SizeToPreferredSize();
153 }
154
155 OmniboxResultView::~OmniboxResultView() {
156 }
157
158 SkColor OmniboxResultView::GetColor(
159     ResultViewState state,
160     ColorKind kind) const {
161   for (size_t i = 0; i < arraysize(kTranslationTable); ++i) {
162     if (kTranslationTable[i].state == state &&
163         kTranslationTable[i].kind == kind) {
164       return GetNativeTheme()->GetSystemColor(kTranslationTable[i].id);
165     }
166   }
167
168   NOTREACHED();
169   return SK_ColorRED;
170 }
171
172 void OmniboxResultView::SetMatch(const AutocompleteMatch& match) {
173   match_ = match;
174   ResetRenderTexts();
175   animation_->Reset();
176
177   AutocompleteMatch* associated_keyword_match = match_.associated_keyword.get();
178   if (associated_keyword_match) {
179     keyword_icon_->SetImage(GetKeywordIcon());
180     if (!keyword_icon_->parent())
181       AddChildView(keyword_icon_.get());
182   } else if (keyword_icon_->parent()) {
183     RemoveChildView(keyword_icon_.get());
184   }
185
186   Layout();
187 }
188
189 void OmniboxResultView::ShowKeyword(bool show_keyword) {
190   if (show_keyword)
191     animation_->Show();
192   else
193     animation_->Hide();
194 }
195
196 void OmniboxResultView::Invalidate() {
197   keyword_icon_->SetImage(GetKeywordIcon());
198   // While the text in the RenderTexts may not have changed, the styling
199   // (color/bold) may need to change. So we reset them to cause them to be
200   // recomputed in OnPaint().
201   ResetRenderTexts();
202   SchedulePaint();
203 }
204
205 gfx::Size OmniboxResultView::GetPreferredSize() const {
206   return gfx::Size(0, std::max(
207       default_icon_size_ + (kMinimumIconVerticalPadding * 2),
208       GetTextHeight() + (minimum_text_vertical_padding_ * 2)));
209 }
210
211 ////////////////////////////////////////////////////////////////////////////////
212 // OmniboxResultView, protected:
213
214 OmniboxResultView::ResultViewState OmniboxResultView::GetState() const {
215   if (model_->IsSelectedIndex(model_index_))
216     return SELECTED;
217   return model_->IsHoveredIndex(model_index_) ? HOVERED : NORMAL;
218 }
219
220 int OmniboxResultView::GetTextHeight() const {
221   return font_height_;
222 }
223
224 void OmniboxResultView::PaintMatch(
225     const AutocompleteMatch& match,
226     gfx::RenderText* contents,
227     gfx::RenderText* description,
228     gfx::Canvas* canvas,
229     int x) const {
230   int y = text_bounds_.y();
231
232   if (!separator_rendertext_) {
233     const base::string16& separator =
234         l10n_util::GetStringUTF16(IDS_AUTOCOMPLETE_MATCH_DESCRIPTION_SEPARATOR);
235     separator_rendertext_.reset(CreateRenderText(separator).release());
236     separator_rendertext_->SetColor(GetColor(GetState(), DIMMED_TEXT));
237     separator_width_ = separator_rendertext_->GetContentWidth();
238   }
239
240   int contents_max_width, description_max_width;
241   OmniboxPopupModel::ComputeMatchMaxWidths(
242       contents->GetContentWidth(),
243       separator_width_,
244       description ? description->GetContentWidth() : 0,
245       mirroring_context_->remaining_width(x),
246       !AutocompleteMatch::IsSearchType(match.type),
247       &contents_max_width,
248       &description_max_width);
249
250   x = DrawRenderText(match, contents, true, canvas, x, y, contents_max_width);
251
252   if (description_max_width != 0) {
253     x = DrawRenderText(match, separator_rendertext_.get(), false, canvas, x, y,
254                        separator_width_);
255     DrawRenderText(match, description, false, canvas, x, y,
256                    description_max_width);
257   }
258 }
259
260 int OmniboxResultView::DrawRenderText(
261     const AutocompleteMatch& match,
262     gfx::RenderText* render_text,
263     bool contents,
264     gfx::Canvas* canvas,
265     int x,
266     int y,
267     int max_width) const {
268   DCHECK(!render_text->text().empty());
269
270   const int remaining_width = mirroring_context_->remaining_width(x);
271   int right_x = x + max_width;
272
273   // Infinite suggestions should appear with the leading ellipses vertically
274   // stacked.
275   if (contents &&
276       (match.type == AutocompleteMatchType::SEARCH_SUGGEST_INFINITE)) {
277     // When the directionality of suggestion doesn't match the UI, we try to
278     // vertically stack the ellipsis by restricting the end edge (right_x).
279     const bool is_ui_rtl = base::i18n::IsRTL();
280     const bool is_match_contents_rtl =
281         (render_text->GetTextDirection() == base::i18n::RIGHT_TO_LEFT);
282     const int offset =
283         GetDisplayOffset(match, is_ui_rtl, is_match_contents_rtl);
284
285     scoped_ptr<gfx::RenderText> prefix_render_text(
286         CreateRenderText(base::UTF8ToUTF16(
287             match.GetAdditionalInfo(kACMatchPropertyContentsPrefix))));
288     const int prefix_width = prefix_render_text->GetContentWidth();
289     int prefix_x = x;
290
291     const int max_match_contents_width = model_->max_match_contents_width();
292
293     if (is_ui_rtl != is_match_contents_rtl) {
294       // RTL infinite suggestions appear near the left edge in LTR UI, while LTR
295       // infinite suggestions appear near the right edge in RTL UI. This is
296       // against the natural horizontal alignment of the text. We reduce the
297       // width of the box for suggestion display, so that the suggestions appear
298       // in correct confines.  This reduced width allows us to modify the text
299       // alignment (see below).
300       right_x = x + std::min(remaining_width - prefix_width,
301                              std::max(offset, max_match_contents_width));
302       prefix_x = right_x;
303       // We explicitly set the horizontal alignment so that when LTR suggestions
304       // show in RTL UI (or vice versa), their ellipses appear stacked in a
305       // single column.
306       render_text->SetHorizontalAlignment(
307           is_match_contents_rtl ? gfx::ALIGN_RIGHT : gfx::ALIGN_LEFT);
308     } else {
309       // If the dropdown is wide enough, place the ellipsis at the position
310       // where the omitted text would have ended. Otherwise reduce the offset of
311       // the ellipsis such that the widest suggestion reaches the end of the
312       // dropdown.
313       const int start_offset = std::max(prefix_width,
314           std::min(remaining_width - max_match_contents_width, offset));
315       right_x = x + std::min(remaining_width, start_offset + max_width);
316       x += start_offset;
317       prefix_x = x - prefix_width;
318     }
319     prefix_render_text->SetDirectionalityMode(is_match_contents_rtl ?
320         gfx::DIRECTIONALITY_FORCE_RTL : gfx::DIRECTIONALITY_FORCE_LTR);
321     prefix_render_text->SetHorizontalAlignment(
322           is_match_contents_rtl ? gfx::ALIGN_RIGHT : gfx::ALIGN_LEFT);
323     prefix_render_text->SetDisplayRect(gfx::Rect(
324           mirroring_context_->mirrored_left_coord(
325               prefix_x, prefix_x + prefix_width), y,
326           prefix_width, height()));
327     prefix_render_text->Draw(canvas);
328   }
329
330   // Set the display rect to trigger eliding.
331   render_text->SetDisplayRect(gfx::Rect(
332       mirroring_context_->mirrored_left_coord(x, right_x), y,
333       right_x - x, height()));
334   render_text->Draw(canvas);
335   return right_x;
336 }
337
338 scoped_ptr<gfx::RenderText> OmniboxResultView::CreateRenderText(
339     const base::string16& text) const {
340   scoped_ptr<gfx::RenderText> render_text(gfx::RenderText::CreateInstance());
341   render_text->SetDisplayRect(gfx::Rect(gfx::Size(INT_MAX, 0)));
342   render_text->SetCursorEnabled(false);
343   render_text->SetElideBehavior(gfx::ELIDE_TAIL);
344   render_text->SetFontList(font_list_);
345   render_text->SetText(text);
346   return render_text.Pass();
347 }
348
349 scoped_ptr<gfx::RenderText> OmniboxResultView::CreateClassifiedRenderText(
350     const base::string16& text,
351     const ACMatchClassifications& classifications,
352     bool force_dim) const {
353   scoped_ptr<gfx::RenderText> render_text(CreateRenderText(text));
354   const size_t text_length = render_text->text().length();
355   for (size_t i = 0; i < classifications.size(); ++i) {
356     const size_t text_start = classifications[i].offset;
357     if (text_start >= text_length)
358       break;
359
360     const size_t text_end = (i < (classifications.size() - 1)) ?
361         std::min(classifications[i + 1].offset, text_length) :
362         text_length;
363     const gfx::Range current_range(text_start, text_end);
364
365     // Calculate style-related data.
366     if (classifications[i].style & ACMatchClassification::MATCH)
367       render_text->ApplyStyle(gfx::BOLD, true, current_range);
368
369     ColorKind color_kind = TEXT;
370     if (classifications[i].style & ACMatchClassification::URL) {
371       color_kind = URL;
372       // Consider logical string for domain "ABC.com×™/hello" where ABC are
373       // Hebrew (RTL) characters. This string should ideally show as
374       // "CBA.com/hello". If we do not force LTR on URL, it will appear as
375       // "com/hello.CBA".
376       // With IDN and RTL TLDs, it might be okay to allow RTL rendering of URLs,
377       // but it still has some pitfalls like :
378       // ABC.COM/abc-pqr/xyz/FGH will appear as HGF/abc-pqr/xyz/MOC.CBA which
379       // really confuses the path hierarchy of the URL.
380       // Also, if the URL supports https, the appearance will change into LTR
381       // directionality.
382       // In conclusion, LTR rendering of URL is probably the safest bet.
383       render_text->SetDirectionalityMode(gfx::DIRECTIONALITY_FORCE_LTR);
384     } else if (force_dim ||
385         (classifications[i].style & ACMatchClassification::DIM)) {
386       color_kind = DIMMED_TEXT;
387     }
388     render_text->ApplyColor(GetColor(GetState(), color_kind), current_range);
389   }
390   return render_text.Pass();
391 }
392
393 int OmniboxResultView::GetMatchContentsWidth() const {
394   InitContentsRenderTextIfNecessary();
395   return contents_rendertext_ ? contents_rendertext_->GetContentWidth() : 0;
396 }
397
398 // TODO(skanuj): This is probably identical across all OmniboxResultView rows in
399 // the omnibox dropdown. Consider sharing the result.
400 int OmniboxResultView::GetDisplayOffset(
401     const AutocompleteMatch& match,
402     bool is_ui_rtl,
403     bool is_match_contents_rtl) const {
404   if (match.type != AutocompleteMatchType::SEARCH_SUGGEST_INFINITE)
405     return 0;
406
407   const base::string16& input_text =
408       base::UTF8ToUTF16(match.GetAdditionalInfo(kACMatchPropertyInputText));
409   int contents_start_index = 0;
410   base::StringToInt(match.GetAdditionalInfo(kACMatchPropertyContentsStartIndex),
411                     &contents_start_index);
412
413   scoped_ptr<gfx::RenderText> input_render_text(CreateRenderText(input_text));
414   const gfx::Range& glyph_bounds =
415       input_render_text->GetGlyphBounds(contents_start_index);
416   const int start_padding = is_match_contents_rtl ?
417       std::max(glyph_bounds.start(), glyph_bounds.end()) :
418       std::min(glyph_bounds.start(), glyph_bounds.end());
419
420   return is_ui_rtl ?
421       (input_render_text->GetContentWidth() - start_padding) : start_padding;
422 }
423
424 // static
425 int OmniboxResultView::default_icon_size_ = 0;
426
427 gfx::ImageSkia OmniboxResultView::GetIcon() const {
428   const gfx::Image image = model_->GetIconIfExtensionMatch(model_index_);
429   if (!image.IsEmpty())
430     return image.AsImageSkia();
431
432   int icon = model_->IsStarredMatch(match_) ?
433       IDR_OMNIBOX_STAR : AutocompleteMatch::TypeToIcon(match_.type);
434   if (GetState() == SELECTED) {
435     switch (icon) {
436       case IDR_OMNIBOX_EXTENSION_APP:
437         icon = IDR_OMNIBOX_EXTENSION_APP_SELECTED;
438         break;
439       case IDR_OMNIBOX_HTTP:
440         icon = IDR_OMNIBOX_HTTP_SELECTED;
441         break;
442       case IDR_OMNIBOX_SEARCH:
443         icon = IDR_OMNIBOX_SEARCH_SELECTED;
444         break;
445       case IDR_OMNIBOX_STAR:
446         icon = IDR_OMNIBOX_STAR_SELECTED;
447         break;
448       default:
449         NOTREACHED();
450         break;
451     }
452   }
453   return *(location_bar_view_->GetThemeProvider()->GetImageSkiaNamed(icon));
454 }
455
456 const gfx::ImageSkia* OmniboxResultView::GetKeywordIcon() const {
457   // NOTE: If we ever begin returning icons of varying size, then callers need
458   // to ensure that |keyword_icon_| is resized each time its image is reset.
459   return location_bar_view_->GetThemeProvider()->GetImageSkiaNamed(
460       (GetState() == SELECTED) ? IDR_OMNIBOX_TTS_SELECTED : IDR_OMNIBOX_TTS);
461 }
462
463 bool OmniboxResultView::ShowOnlyKeywordMatch() const {
464   return match_.associated_keyword &&
465       (keyword_icon_->x() <= icon_bounds_.right());
466 }
467
468 void OmniboxResultView::ResetRenderTexts() const {
469   contents_rendertext_.reset();
470   description_rendertext_.reset();
471   separator_rendertext_.reset();
472   keyword_contents_rendertext_.reset();
473   keyword_description_rendertext_.reset();
474 }
475
476 void OmniboxResultView::InitContentsRenderTextIfNecessary() const {
477   if (!contents_rendertext_) {
478     contents_rendertext_.reset(
479         CreateClassifiedRenderText(
480             match_.contents, match_.contents_class, false).release());
481   }
482 }
483
484 void OmniboxResultView::Layout() {
485   const gfx::ImageSkia icon = GetIcon();
486
487   icon_bounds_.SetRect(edge_item_padding_ +
488       ((icon.width() == default_icon_size_) ?
489           0 : LocationBarView::kIconInternalPadding),
490       (height() - icon.height()) / 2, icon.width(), icon.height());
491
492   int text_x = edge_item_padding_ + default_icon_size_ + item_padding_;
493   int text_width = width() - text_x - edge_item_padding_;
494
495   if (match_.associated_keyword.get()) {
496     const int kw_collapsed_size =
497         keyword_icon_->width() + edge_item_padding_;
498     const int max_kw_x = width() - kw_collapsed_size;
499     const int kw_x =
500         animation_->CurrentValueBetween(max_kw_x, edge_item_padding_);
501     const int kw_text_x = kw_x + keyword_icon_->width() + item_padding_;
502
503     text_width = kw_x - text_x - item_padding_;
504     keyword_text_bounds_.SetRect(
505         kw_text_x, 0,
506         std::max(width() - kw_text_x - edge_item_padding_, 0), height());
507     keyword_icon_->SetPosition(
508         gfx::Point(kw_x, (height() - keyword_icon_->height()) / 2));
509   }
510
511   text_bounds_.SetRect(text_x, 0, std::max(text_width, 0), height());
512 }
513
514 void OmniboxResultView::OnBoundsChanged(const gfx::Rect& previous_bounds) {
515   animation_->SetSlideDuration(width() / 4);
516 }
517
518 void OmniboxResultView::OnPaint(gfx::Canvas* canvas) {
519   const ResultViewState state = GetState();
520   if (state != NORMAL)
521     canvas->DrawColor(GetColor(state, BACKGROUND));
522
523   // NOTE: While animating the keyword match, both matches may be visible.
524
525   if (!ShowOnlyKeywordMatch()) {
526     canvas->DrawImageInt(GetIcon(), GetMirroredXForRect(icon_bounds_),
527                          icon_bounds_.y());
528     int x = GetMirroredXForRect(text_bounds_);
529     mirroring_context_->Initialize(x, text_bounds_.width());
530     InitContentsRenderTextIfNecessary();
531     if (!description_rendertext_ && !match_.description.empty()) {
532       description_rendertext_.reset(
533           CreateClassifiedRenderText(
534               match_.description, match_.description_class, true).release());
535     }
536     PaintMatch(match_, contents_rendertext_.get(),
537                description_rendertext_.get(), canvas, x);
538   }
539
540   AutocompleteMatch* keyword_match = match_.associated_keyword.get();
541   if (keyword_match) {
542     int x = GetMirroredXForRect(keyword_text_bounds_);
543     mirroring_context_->Initialize(x, keyword_text_bounds_.width());
544     if (!keyword_contents_rendertext_) {
545       keyword_contents_rendertext_.reset(
546           CreateClassifiedRenderText(keyword_match->contents,
547                                      keyword_match->contents_class,
548                                      false).release());
549     }
550     if (!keyword_description_rendertext_ &&
551         !keyword_match->description.empty()) {
552       keyword_description_rendertext_.reset(
553           CreateClassifiedRenderText(keyword_match->description,
554                                      keyword_match->description_class,
555                                      true).release());
556     }
557     PaintMatch(*keyword_match, keyword_contents_rendertext_.get(),
558                keyword_description_rendertext_.get(), canvas, x);
559   }
560 }
561
562 void OmniboxResultView::AnimationProgressed(const gfx::Animation* animation) {
563   Layout();
564   SchedulePaint();
565 }