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 "ui/views/controls/label.h"
6 
7 #include <stddef.h>
8 
9 #include <algorithm>
10 #include <cmath>
11 #include <limits>
12 #include <utility>
13 #include <vector>
14 
15 #include "base/i18n/rtl.h"
16 #include "base/logging.h"
17 #include "base/strings/string_split.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "build/build_config.h"
20 #include "build/chromeos_buildflags.h"
21 #include "ui/accessibility/ax_enums.mojom.h"
22 #include "ui/accessibility/ax_node_data.h"
23 #include "ui/base/clipboard/scoped_clipboard_writer.h"
24 #include "ui/base/cursor/cursor.h"
25 #include "ui/base/default_style.h"
26 #include "ui/gfx/canvas.h"
27 #include "ui/gfx/color_utils.h"
28 #include "ui/gfx/geometry/insets.h"
29 #include "ui/gfx/text_elider.h"
30 #include "ui/gfx/text_utils.h"
31 #include "ui/native_theme/native_theme.h"
32 #include "ui/strings/grit/ui_strings.h"
33 #include "ui/views/background.h"
34 #include "ui/views/controls/menu/menu_runner.h"
35 #include "ui/views/focus/focus_manager.h"
36 #include "ui/views/metadata/metadata_impl_macros.h"
37 #include "ui/views/native_cursor.h"
38 #include "ui/views/selection_controller.h"
39 
40 namespace {
41 
42 // An enum giving different RenderText properties unique keys for the
43 // OnPropertyChanged call.
44 enum LabelPropertyKey {
45   kLabelText = 1,
46   kLabelShadows,
47   kLabelHorizontalAlignment,
48   kLabelVerticalAlignment,
49   kLabelLineHeight,
50   kLabelObscured,
51   kLabelAllowCharacterBreak,
52 };
53 
IsOpaque(SkColor color)54 bool IsOpaque(SkColor color) {
55   return SkColorGetA(color) == SK_AlphaOPAQUE;
56 }
57 
58 }  // namespace
59 
60 namespace views {
61 
Label()62 Label::Label() : Label(base::string16()) {}
63 
Label(const base::string16 & text)64 Label::Label(const base::string16& text)
65     : Label(text, style::CONTEXT_LABEL, style::STYLE_PRIMARY) {}
66 
Label(const base::string16 & text,int text_context,int text_style,gfx::DirectionalityMode directionality_mode)67 Label::Label(const base::string16& text,
68              int text_context,
69              int text_style,
70              gfx::DirectionalityMode directionality_mode)
71     : text_context_(text_context),
72       text_style_(text_style),
73       context_menu_contents_(this) {
74   Init(text, style::GetFont(text_context, text_style), directionality_mode);
75 }
76 
Label(const base::string16 & text,const CustomFont & font)77 Label::Label(const base::string16& text, const CustomFont& font)
78     : text_context_(style::CONTEXT_LABEL),
79       text_style_(style::STYLE_PRIMARY),
80       context_menu_contents_(this) {
81   Init(text, font.font_list, gfx::DirectionalityMode::DIRECTIONALITY_FROM_TEXT);
82 }
83 
84 Label::~Label() = default;
85 
86 // static
GetDefaultFontList()87 const gfx::FontList& Label::GetDefaultFontList() {
88   return style::GetFont(style::CONTEXT_LABEL, style::STYLE_PRIMARY);
89 }
90 
SetFontList(const gfx::FontList & font_list)91 void Label::SetFontList(const gfx::FontList& font_list) {
92   full_text_->SetFontList(font_list);
93   ClearDisplayText();
94   PreferredSizeChanged();
95 }
96 
GetText() const97 const base::string16& Label::GetText() const {
98   return full_text_->text();
99 }
100 
SetText(const base::string16 & new_text)101 void Label::SetText(const base::string16& new_text) {
102   if (new_text == GetText())
103     return;
104   full_text_->SetText(new_text);
105   ClearDisplayText();
106   OnPropertyChanged(&full_text_ + kLabelText,
107                     kPropertyEffectsPreferredSizeChanged);
108   stored_selection_range_ = gfx::Range::InvalidRange();
109 }
110 
GetTextContext() const111 int Label::GetTextContext() const {
112   return text_context_;
113 }
114 
SetTextContext(int text_context)115 void Label::SetTextContext(int text_context) {
116   if (text_context == text_context_)
117     return;
118   text_context_ = text_context;
119   UpdateColorsFromTheme();
120   OnPropertyChanged(&text_context_, views::kPropertyEffectsPaint);
121 }
122 
GetTextStyle() const123 int Label::GetTextStyle() const {
124   return text_style_;
125 }
126 
SetTextStyle(int style)127 void Label::SetTextStyle(int style) {
128   if (style == text_style_)
129     return;
130 
131   text_style_ = style;
132   // TODO(pkasting): Seems like potentially |full_text_|'s font list and line
133   // height should be updated here?
134   UpdateColorsFromTheme();
135   OnPropertyChanged(&text_style_, kPropertyEffectsPreferredSizeChanged);
136 }
137 
GetAutoColorReadabilityEnabled() const138 bool Label::GetAutoColorReadabilityEnabled() const {
139   return auto_color_readability_enabled_;
140 }
141 
SetAutoColorReadabilityEnabled(bool auto_color_readability_enabled)142 void Label::SetAutoColorReadabilityEnabled(
143     bool auto_color_readability_enabled) {
144   if (auto_color_readability_enabled_ == auto_color_readability_enabled)
145     return;
146   auto_color_readability_enabled_ = auto_color_readability_enabled;
147   RecalculateColors();
148   OnPropertyChanged(&auto_color_readability_enabled_, kPropertyEffectsPaint);
149 }
150 
GetEnabledColor() const151 SkColor Label::GetEnabledColor() const {
152   return actual_enabled_color_;
153 }
154 
SetEnabledColor(SkColor color)155 void Label::SetEnabledColor(SkColor color) {
156   if (enabled_color_set_ && requested_enabled_color_ == color)
157     return;
158   requested_enabled_color_ = color;
159   enabled_color_set_ = true;
160   RecalculateColors();
161   OnPropertyChanged(&requested_enabled_color_, kPropertyEffectsPaint);
162 }
163 
GetBackgroundColor() const164 SkColor Label::GetBackgroundColor() const {
165   return background_color_;
166 }
167 
SetBackgroundColor(SkColor color)168 void Label::SetBackgroundColor(SkColor color) {
169   if (background_color_set_ && background_color_ == color)
170     return;
171   background_color_ = color;
172   background_color_set_ = true;
173   RecalculateColors();
174   OnPropertyChanged(&background_color_, kPropertyEffectsPaint);
175 }
176 
GetSelectionTextColor() const177 SkColor Label::GetSelectionTextColor() const {
178   return actual_selection_text_color_;
179 }
180 
SetSelectionTextColor(SkColor color)181 void Label::SetSelectionTextColor(SkColor color) {
182   if (selection_text_color_set_ && requested_selection_text_color_ == color)
183     return;
184   requested_selection_text_color_ = color;
185   selection_text_color_set_ = true;
186   RecalculateColors();
187   OnPropertyChanged(&requested_selection_text_color_, kPropertyEffectsPaint);
188 }
189 
GetSelectionBackgroundColor() const190 SkColor Label::GetSelectionBackgroundColor() const {
191   return selection_background_color_;
192 }
193 
SetSelectionBackgroundColor(SkColor color)194 void Label::SetSelectionBackgroundColor(SkColor color) {
195   if (selection_background_color_set_ && selection_background_color_ == color)
196     return;
197   selection_background_color_ = color;
198   selection_background_color_set_ = true;
199   RecalculateColors();
200   OnPropertyChanged(&selection_background_color_, kPropertyEffectsPaint);
201 }
202 
GetShadows() const203 const gfx::ShadowValues& Label::GetShadows() const {
204   return full_text_->shadows();
205 }
206 
SetShadows(const gfx::ShadowValues & shadows)207 void Label::SetShadows(const gfx::ShadowValues& shadows) {
208   if (full_text_->shadows() == shadows)
209     return;
210   full_text_->set_shadows(shadows);
211   ClearDisplayText();
212   OnPropertyChanged(&full_text_ + kLabelShadows,
213                     kPropertyEffectsPreferredSizeChanged);
214 }
215 
GetSubpixelRenderingEnabled() const216 bool Label::GetSubpixelRenderingEnabled() const {
217   return subpixel_rendering_enabled_;
218 }
219 
SetSubpixelRenderingEnabled(bool subpixel_rendering_enabled)220 void Label::SetSubpixelRenderingEnabled(bool subpixel_rendering_enabled) {
221   if (subpixel_rendering_enabled_ == subpixel_rendering_enabled)
222     return;
223   subpixel_rendering_enabled_ = subpixel_rendering_enabled;
224   ApplyTextColors();
225   OnPropertyChanged(&subpixel_rendering_enabled_, kPropertyEffectsPaint);
226 }
227 
GetSkipSubpixelRenderingOpacityCheck() const228 bool Label::GetSkipSubpixelRenderingOpacityCheck() const {
229   return skip_subpixel_rendering_opacity_check_;
230 }
231 
SetSkipSubpixelRenderingOpacityCheck(bool skip_subpixel_rendering_opacity_check)232 void Label::SetSkipSubpixelRenderingOpacityCheck(
233     bool skip_subpixel_rendering_opacity_check) {
234   if (skip_subpixel_rendering_opacity_check_ ==
235       skip_subpixel_rendering_opacity_check)
236     return;
237   skip_subpixel_rendering_opacity_check_ =
238       skip_subpixel_rendering_opacity_check;
239   OnPropertyChanged(&skip_subpixel_rendering_opacity_check_,
240                     kPropertyEffectsNone);
241 }
242 
GetHorizontalAlignment() const243 gfx::HorizontalAlignment Label::GetHorizontalAlignment() const {
244   return full_text_->horizontal_alignment();
245 }
246 
SetHorizontalAlignment(gfx::HorizontalAlignment alignment)247 void Label::SetHorizontalAlignment(gfx::HorizontalAlignment alignment) {
248   alignment = gfx::MaybeFlipForRTL(alignment);
249   if (GetHorizontalAlignment() == alignment)
250     return;
251   full_text_->SetHorizontalAlignment(alignment);
252   ClearDisplayText();
253   OnPropertyChanged(&full_text_ + kLabelHorizontalAlignment,
254                     kPropertyEffectsPaint);
255 }
256 
GetVerticalAlignment() const257 gfx::VerticalAlignment Label::GetVerticalAlignment() const {
258   return full_text_->vertical_alignment();
259 }
260 
SetVerticalAlignment(gfx::VerticalAlignment alignment)261 void Label::SetVerticalAlignment(gfx::VerticalAlignment alignment) {
262   if (GetVerticalAlignment() == alignment)
263     return;
264   full_text_->SetVerticalAlignment(alignment);
265   ClearDisplayText();
266   OnPropertyChanged(&full_text_ + kLabelVerticalAlignment,
267                     kPropertyEffectsPaint);
268 }
269 
GetLineHeight() const270 int Label::GetLineHeight() const {
271   // TODO(pkasting): If we can replace SetFontList() with context/style setter
272   // calls, we can eliminate the reference to font_list().GetHeight() here.
273   return line_height_.value_or(
274       std::max(style::GetLineHeight(text_context_, text_style_),
275                font_list().GetHeight()));
276 }
277 
SetLineHeight(int line_height)278 void Label::SetLineHeight(int line_height) {
279   if (line_height_ == line_height)
280     return;
281   line_height_ = line_height;
282   full_text_->SetMinLineHeight(line_height);
283   ClearDisplayText();
284   OnPropertyChanged(&full_text_ + kLabelLineHeight,
285                     kPropertyEffectsPreferredSizeChanged);
286 }
287 
GetMultiLine() const288 bool Label::GetMultiLine() const {
289   return multi_line_;
290 }
291 
SetMultiLine(bool multi_line)292 void Label::SetMultiLine(bool multi_line) {
293   DCHECK(!multi_line || (elide_behavior_ == gfx::ELIDE_TAIL ||
294                          elide_behavior_ == gfx::NO_ELIDE));
295   if (this->GetMultiLine() == multi_line)
296     return;
297   multi_line_ = multi_line;
298   full_text_->SetMultiline(multi_line);
299   ClearDisplayText();
300   OnPropertyChanged(&multi_line_, kPropertyEffectsPreferredSizeChanged);
301 }
302 
GetMaxLines() const303 int Label::GetMaxLines() const {
304   return max_lines_;
305 }
306 
SetMaxLines(int max_lines)307 void Label::SetMaxLines(int max_lines) {
308   if (max_lines_ == max_lines)
309     return;
310   max_lines_ = max_lines;
311   OnPropertyChanged(&max_lines_, kPropertyEffectsPreferredSizeChanged);
312 }
313 
GetObscured() const314 bool Label::GetObscured() const {
315   return full_text_->obscured();
316 }
317 
SetObscured(bool obscured)318 void Label::SetObscured(bool obscured) {
319   if (this->GetObscured() == obscured)
320     return;
321   full_text_->SetObscured(obscured);
322   ClearDisplayText();
323   if (obscured)
324     SetSelectable(false);
325   OnPropertyChanged(&full_text_ + kLabelObscured,
326                     kPropertyEffectsPreferredSizeChanged);
327 }
328 
IsDisplayTextTruncated() const329 bool Label::IsDisplayTextTruncated() const {
330   MaybeBuildDisplayText();
331   if (!full_text_ || full_text_->text().empty())
332     return false;
333   auto text_bounds = GetTextBounds();
334   return (display_text_ &&
335           display_text_->text() != display_text_->GetDisplayText()) ||
336          text_bounds.width() > GetContentsBounds().width() ||
337          text_bounds.height() > GetContentsBounds().height();
338 }
339 
GetAllowCharacterBreak() const340 bool Label::GetAllowCharacterBreak() const {
341   return full_text_->word_wrap_behavior() == gfx::WRAP_LONG_WORDS;
342 }
343 
SetAllowCharacterBreak(bool allow_character_break)344 void Label::SetAllowCharacterBreak(bool allow_character_break) {
345   const gfx::WordWrapBehavior behavior =
346       allow_character_break ? gfx::WRAP_LONG_WORDS : gfx::TRUNCATE_LONG_WORDS;
347   if (full_text_->word_wrap_behavior() == behavior)
348     return;
349   full_text_->SetWordWrapBehavior(behavior);
350   ClearDisplayText();
351   OnPropertyChanged(&full_text_ + kLabelAllowCharacterBreak,
352                     kPropertyEffectsPreferredSizeChanged);
353 }
354 
GetTextIndexOfLine(size_t line) const355 size_t Label::GetTextIndexOfLine(size_t line) const {
356   return full_text_->GetTextIndexOfLine(line);
357 }
358 
SetTruncateLength(size_t truncate_length)359 void Label::SetTruncateLength(size_t truncate_length) {
360   return full_text_->set_truncate_length(truncate_length);
361 }
362 
GetElideBehavior() const363 gfx::ElideBehavior Label::GetElideBehavior() const {
364   return elide_behavior_;
365 }
366 
SetElideBehavior(gfx::ElideBehavior elide_behavior)367 void Label::SetElideBehavior(gfx::ElideBehavior elide_behavior) {
368   DCHECK(!GetMultiLine() || (elide_behavior == gfx::ELIDE_TAIL ||
369                              elide_behavior == gfx::NO_ELIDE));
370   if (elide_behavior_ == elide_behavior)
371     return;
372   elide_behavior_ = elide_behavior;
373   ClearDisplayText();
374   OnPropertyChanged(&elide_behavior_, kPropertyEffectsPreferredSizeChanged);
375 }
376 
GetTooltipText() const377 base::string16 Label::GetTooltipText() const {
378   return tooltip_text_;
379 }
380 
SetTooltipText(const base::string16 & tooltip_text)381 void Label::SetTooltipText(const base::string16& tooltip_text) {
382   DCHECK(handles_tooltips_);
383   if (tooltip_text_ == tooltip_text)
384     return;
385   tooltip_text_ = tooltip_text;
386   OnPropertyChanged(&tooltip_text_, kPropertyEffectsNone);
387 }
388 
GetHandlesTooltips() const389 bool Label::GetHandlesTooltips() const {
390   return handles_tooltips_;
391 }
392 
SetHandlesTooltips(bool enabled)393 void Label::SetHandlesTooltips(bool enabled) {
394   if (handles_tooltips_ == enabled)
395     return;
396   handles_tooltips_ = enabled;
397   OnPropertyChanged(&handles_tooltips_, kPropertyEffectsNone);
398 }
399 
SizeToFit(int fixed_width)400 void Label::SizeToFit(int fixed_width) {
401   DCHECK(GetMultiLine());
402   DCHECK_EQ(0, max_width_);
403   fixed_width_ = fixed_width;
404   SizeToPreferredSize();
405 }
406 
GetMaximumWidth() const407 int Label::GetMaximumWidth() const {
408   return max_width_;
409 }
410 
SetMaximumWidth(int max_width)411 void Label::SetMaximumWidth(int max_width) {
412   DCHECK(GetMultiLine());
413   DCHECK_EQ(0, fixed_width_);
414   if (max_width_ == max_width)
415     return;
416   max_width_ = max_width;
417   OnPropertyChanged(&max_width_, kPropertyEffectsPreferredSizeChanged);
418 }
419 
GetCollapseWhenHidden() const420 bool Label::GetCollapseWhenHidden() const {
421   return collapse_when_hidden_;
422 }
423 
SetCollapseWhenHidden(bool value)424 void Label::SetCollapseWhenHidden(bool value) {
425   if (collapse_when_hidden_ == value)
426     return;
427   collapse_when_hidden_ = value;
428   OnPropertyChanged(&collapse_when_hidden_,
429                     kPropertyEffectsPreferredSizeChanged);
430 }
431 
GetRequiredLines() const432 size_t Label::GetRequiredLines() const {
433   return full_text_->GetNumLines();
434 }
435 
GetDisplayTextForTesting()436 base::string16 Label::GetDisplayTextForTesting() {
437   MaybeBuildDisplayText();
438   return display_text_ ? display_text_->GetDisplayText() : base::string16();
439 }
440 
GetTextDirectionForTesting()441 base::i18n::TextDirection Label::GetTextDirectionForTesting() {
442   return full_text_->GetDisplayTextDirection();
443 }
444 
IsSelectionSupported() const445 bool Label::IsSelectionSupported() const {
446   return !GetObscured();
447 }
448 
GetSelectable() const449 bool Label::GetSelectable() const {
450   return !!selection_controller_;
451 }
452 
SetSelectable(bool value)453 bool Label::SetSelectable(bool value) {
454   if (value == GetSelectable())
455     return true;
456 
457   if (!value) {
458     ClearSelection();
459     stored_selection_range_ = gfx::Range::InvalidRange();
460     selection_controller_.reset();
461     return true;
462   }
463 
464   DCHECK(!stored_selection_range_.IsValid());
465   if (!IsSelectionSupported())
466     return false;
467 
468   selection_controller_ = std::make_unique<SelectionController>(this);
469   return true;
470 }
471 
HasSelection() const472 bool Label::HasSelection() const {
473   const gfx::RenderText* render_text = GetRenderTextForSelectionController();
474   return render_text ? !render_text->selection().is_empty() : false;
475 }
476 
SelectAll()477 void Label::SelectAll() {
478   gfx::RenderText* render_text = GetRenderTextForSelectionController();
479   if (!render_text)
480     return;
481   render_text->SelectAll(false);
482   SchedulePaint();
483 }
484 
ClearSelection()485 void Label::ClearSelection() {
486   gfx::RenderText* render_text = GetRenderTextForSelectionController();
487   if (!render_text)
488     return;
489   render_text->ClearSelection();
490   SchedulePaint();
491 }
492 
SelectRange(const gfx::Range & range)493 void Label::SelectRange(const gfx::Range& range) {
494   gfx::RenderText* render_text = GetRenderTextForSelectionController();
495   if (render_text && render_text->SelectRange(range))
496     SchedulePaint();
497 }
498 
GetSubstringBounds(const gfx::Range & range)499 std::vector<gfx::Rect> Label::GetSubstringBounds(const gfx::Range& range) {
500   auto substring_bounds = full_text_->GetSubstringBounds(range);
501   for (auto& bound : substring_bounds) {
502     bound.Offset(GetInsets().left(), GetInsets().top());
503   }
504   return substring_bounds;
505 }
506 
AddTextChangedCallback(views::PropertyChangedCallback callback)507 views::PropertyChangedSubscription Label::AddTextChangedCallback(
508     views::PropertyChangedCallback callback) {
509   return AddPropertyChangedCallback(&full_text_ + kLabelText,
510                                     std::move(callback));
511 }
512 
GetBaseline() const513 int Label::GetBaseline() const {
514   return GetInsets().top() + font_list().GetBaseline();
515 }
516 
CalculatePreferredSize() const517 gfx::Size Label::CalculatePreferredSize() const {
518   // Return a size of (0, 0) if the label is not visible and if the
519   // |collapse_when_hidden_| flag is set.
520   // TODO(munjal): This logic probably belongs to the View class. But for now,
521   // put it here since putting it in View class means all inheriting classes
522   // need to respect the |collapse_when_hidden_| flag.
523   if (!GetVisible() && collapse_when_hidden_)
524     return gfx::Size();
525 
526   if (GetMultiLine() && fixed_width_ != 0 && !GetText().empty())
527     return gfx::Size(fixed_width_, GetHeightForWidth(fixed_width_));
528 
529   gfx::Size size(GetTextSize());
530   const gfx::Insets insets = GetInsets();
531   size.Enlarge(insets.width(), insets.height());
532 
533   if (GetMultiLine() && max_width_ != 0 && max_width_ < size.width())
534     return gfx::Size(max_width_, GetHeightForWidth(max_width_));
535 
536   if (GetMultiLine() && GetMaxLines() > 0)
537     return gfx::Size(size.width(), GetHeightForWidth(size.width()));
538   return size;
539 }
540 
GetMinimumSize() const541 gfx::Size Label::GetMinimumSize() const {
542   if (!GetVisible() && collapse_when_hidden_)
543     return gfx::Size();
544 
545   // Always reserve vertical space for at least one line.
546   gfx::Size size(0, GetLineHeight());
547   if (elide_behavior_ == gfx::ELIDE_HEAD ||
548       elide_behavior_ == gfx::ELIDE_MIDDLE ||
549       elide_behavior_ == gfx::ELIDE_TAIL ||
550       elide_behavior_ == gfx::ELIDE_EMAIL) {
551     size.set_width(gfx::Canvas::GetStringWidth(
552         base::string16(gfx::kEllipsisUTF16), font_list()));
553   }
554 
555   if (!GetMultiLine()) {
556     if (elide_behavior_ == gfx::NO_ELIDE) {
557       // If elision is disabled on single-line Labels, use text size as minimum.
558       // This is OK because clients can use |gfx::ElideBehavior::TRUNCATE|
559       // to get a non-eliding Label that should size itself less aggressively.
560       size.SetToMax(GetTextSize());
561     } else {
562       size.SetToMin(GetTextSize());
563     }
564   }
565 
566   size.Enlarge(GetInsets().width(), GetInsets().height());
567   return size;
568 }
569 
GetHeightForWidth(int w) const570 int Label::GetHeightForWidth(int w) const {
571   if (!GetVisible() && collapse_when_hidden_)
572     return 0;
573 
574   w -= GetInsets().width();
575   int height = 0;
576   int base_line_height = GetLineHeight();
577   if (!GetMultiLine() || GetText().empty() || w < 0) {
578     height = base_line_height;
579   } else if (w == 0) {
580     height = std::max(GetMaxLines(), 1) * base_line_height;
581   } else {
582     // SetDisplayRect() has a side effect for later calls of GetStringSize().
583     // Be careful to invoke |full_text_->SetDisplayRect(gfx::Rect())| to
584     // cancel this effect before the next time GetStringSize() is called.
585     // It would be beneficial not to cancel here, considering that some layout
586     // managers invoke GetHeightForWidth() for the same width multiple times
587     // and |full_text_| can cache the height.
588     full_text_->SetDisplayRect(gfx::Rect(0, 0, w, 0));
589     int string_height = full_text_->GetStringSize().height();
590     // Cap the number of lines to |GetMaxLines()| if multi-line and non-zero
591     // |GetMaxLines()|.
592     height = GetMultiLine() && GetMaxLines() > 0
593                  ? std::min(GetMaxLines() * base_line_height, string_height)
594                  : string_height;
595   }
596   height -= gfx::ShadowValue::GetMargin(full_text_->shadows()).height();
597   return height + GetInsets().height();
598 }
599 
GetTooltipHandlerForPoint(const gfx::Point & point)600 View* Label::GetTooltipHandlerForPoint(const gfx::Point& point) {
601   if (!handles_tooltips_ ||
602       (tooltip_text_.empty() && !ShouldShowDefaultTooltip()))
603     return nullptr;
604 
605   return HitTestPoint(point) ? this : nullptr;
606 }
607 
GetCanProcessEventsWithinSubtree() const608 bool Label::GetCanProcessEventsWithinSubtree() const {
609   return !!GetRenderTextForSelectionController();
610 }
611 
GetWordLookupClient()612 WordLookupClient* Label::GetWordLookupClient() {
613   return this;
614 }
615 
GetAccessibleNodeData(ui::AXNodeData * node_data)616 void Label::GetAccessibleNodeData(ui::AXNodeData* node_data) {
617   if (text_context_ == style::CONTEXT_DIALOG_TITLE)
618     node_data->role = ax::mojom::Role::kTitleBar;
619   else
620     node_data->role = ax::mojom::Role::kStaticText;
621 
622   node_data->SetName(full_text_->GetDisplayText());
623 }
624 
GetTooltipText(const gfx::Point & p) const625 base::string16 Label::GetTooltipText(const gfx::Point& p) const {
626   if (handles_tooltips_) {
627     if (!tooltip_text_.empty())
628       return tooltip_text_;
629 
630     if (ShouldShowDefaultTooltip())
631       return full_text_->GetDisplayText();
632   }
633 
634   return base::string16();
635 }
636 
CreateRenderText() const637 std::unique_ptr<gfx::RenderText> Label::CreateRenderText() const {
638   // Multi-line labels only support NO_ELIDE and ELIDE_TAIL for now.
639   // TODO(warx): Investigate more elide text support.
640   gfx::ElideBehavior elide_behavior =
641       GetMultiLine() && (elide_behavior_ != gfx::NO_ELIDE) ? gfx::ELIDE_TAIL
642                                                            : elide_behavior_;
643 
644   std::unique_ptr<gfx::RenderText> render_text =
645       gfx::RenderText::CreateRenderText();
646   render_text->SetHorizontalAlignment(GetHorizontalAlignment());
647   render_text->SetVerticalAlignment(GetVerticalAlignment());
648   render_text->SetDirectionalityMode(full_text_->directionality_mode());
649   render_text->SetElideBehavior(elide_behavior);
650   render_text->SetObscured(GetObscured());
651   render_text->SetMinLineHeight(GetLineHeight());
652   render_text->SetFontList(font_list());
653   render_text->set_shadows(GetShadows());
654   render_text->SetCursorEnabled(false);
655   render_text->SetText(GetText());
656   const bool multiline = GetMultiLine();
657   render_text->SetMultiline(multiline);
658   render_text->SetMaxLines(multiline ? GetMaxLines() : 0);
659   render_text->SetWordWrapBehavior(full_text_->word_wrap_behavior());
660 
661   // Setup render text for selection controller.
662   if (GetSelectable()) {
663     render_text->set_focused(HasFocus());
664     if (stored_selection_range_.IsValid())
665       render_text->SelectRange(stored_selection_range_);
666   }
667 
668   return render_text;
669 }
670 
GetTextBounds() const671 gfx::Rect Label::GetTextBounds() const {
672   MaybeBuildDisplayText();
673 
674   if (!display_text_)
675     return gfx::Rect(GetTextSize());
676 
677   return gfx::Rect(gfx::Point() + display_text_->GetLineOffset(0),
678                    display_text_->GetStringSize());
679 }
680 
PaintText(gfx::Canvas * canvas)681 void Label::PaintText(gfx::Canvas* canvas) {
682   MaybeBuildDisplayText();
683 
684   if (display_text_)
685     display_text_->Draw(canvas);
686 
687 #if DCHECK_IS_ON() && !defined(OS_CHROMEOS) && !BUILDFLAG(IS_LACROS)
688   // TODO(crbug.com/1139395): Enable this DCHECK on ChromeOS and LaCrOS by
689   // fixing either this check (to correctly idenfify more paints-on-opaque
690   // cases), refactoring parents to use background() or by fixing
691   // subpixel-rendering issues that the DCHECK detects.
692   if (!display_text_ || display_text_->subpixel_rendering_suppressed() ||
693       skip_subpixel_rendering_opacity_check_)
694     return;
695 
696   // Ensure that, if we're using subpixel rendering, we're painted to an opaque
697   // region. Subpixel rendering will sample from the r,g,b color channels of the
698   // canvas. These values are incorrect when sampling from transparent pixels.
699   // Note that these checks may need to be amended for other methods of painting
700   // opaquely underneath the Label. For now, individual cases can skip this
701   // DCHECK by calling Label::SetSkipSubpixelRenderingOpacityCheck().
702   for (View* view = this; view; view = view->parent()) {
703     // This is our approximation of being painted on an opaque region. If any
704     // parent has an opaque background we assume that that background covers the
705     // text bounds. This is not necessarily true as the background could be
706     // inset from the parent bounds, and get_color() does not imply that all of
707     // the background is painted with the same opaque color.
708     if (view->background() && IsOpaque(view->background()->get_color()))
709       break;
710 
711     if (view->layer()) {
712       // If we aren't painted to an opaque background, we must paint to an
713       // opaque layer.
714       DCHECK(view->layer()->fills_bounds_opaquely());
715       break;
716     }
717   }
718 #endif
719 }
720 
OnBoundsChanged(const gfx::Rect & previous_bounds)721 void Label::OnBoundsChanged(const gfx::Rect& previous_bounds) {
722   ClearDisplayText();
723 }
724 
OnPaint(gfx::Canvas * canvas)725 void Label::OnPaint(gfx::Canvas* canvas) {
726   View::OnPaint(canvas);
727   PaintText(canvas);
728 }
729 
OnThemeChanged()730 void Label::OnThemeChanged() {
731   View::OnThemeChanged();
732   UpdateColorsFromTheme();
733 }
734 
GetCursor(const ui::MouseEvent & event)735 gfx::NativeCursor Label::GetCursor(const ui::MouseEvent& event) {
736   return GetRenderTextForSelectionController() ? GetNativeIBeamCursor()
737                                                : gfx::kNullCursor;
738 }
739 
OnFocus()740 void Label::OnFocus() {
741   gfx::RenderText* render_text = GetRenderTextForSelectionController();
742   if (render_text) {
743     render_text->set_focused(true);
744     SchedulePaint();
745   }
746   View::OnFocus();
747 }
748 
OnBlur()749 void Label::OnBlur() {
750   gfx::RenderText* render_text = GetRenderTextForSelectionController();
751   if (render_text) {
752     render_text->set_focused(false);
753     SchedulePaint();
754   }
755   View::OnBlur();
756 }
757 
OnMousePressed(const ui::MouseEvent & event)758 bool Label::OnMousePressed(const ui::MouseEvent& event) {
759   if (!GetRenderTextForSelectionController())
760     return false;
761 
762   const bool had_focus = HasFocus();
763 
764   // RequestFocus() won't work when the label has FocusBehavior::NEVER. Hence
765   // explicitly set the focused view.
766   // TODO(karandeepb): If a widget with a label having FocusBehavior::NEVER as
767   // the currently focused view (due to selection) was to lose focus, focus
768   // won't be restored to the label (and hence a text selection won't be drawn)
769   // when the widget gets focus again. Fix this.
770   // Tracked in https://crbug.com/630365.
771   if ((event.IsOnlyLeftMouseButton() || event.IsOnlyRightMouseButton()) &&
772       GetFocusManager() && !had_focus) {
773     GetFocusManager()->SetFocusedView(this);
774   }
775 
776 #if (defined(OS_LINUX) || defined(OS_BSD)) && !defined(OS_CHROMEOS)
777   if (event.IsOnlyMiddleMouseButton() && GetFocusManager() && !had_focus)
778     GetFocusManager()->SetFocusedView(this);
779 #endif
780 
781   return selection_controller_->OnMousePressed(
782       event, false,
783       had_focus
784           ? SelectionController::InitialFocusStateOnMousePress::kFocused
785           : SelectionController::InitialFocusStateOnMousePress::kUnFocused);
786 }
787 
OnMouseDragged(const ui::MouseEvent & event)788 bool Label::OnMouseDragged(const ui::MouseEvent& event) {
789   if (!GetRenderTextForSelectionController())
790     return false;
791 
792   return selection_controller_->OnMouseDragged(event);
793 }
794 
OnMouseReleased(const ui::MouseEvent & event)795 void Label::OnMouseReleased(const ui::MouseEvent& event) {
796   if (!GetRenderTextForSelectionController())
797     return;
798 
799   selection_controller_->OnMouseReleased(event);
800 }
801 
OnMouseCaptureLost()802 void Label::OnMouseCaptureLost() {
803   if (!GetRenderTextForSelectionController())
804     return;
805 
806   selection_controller_->OnMouseCaptureLost();
807 }
808 
OnKeyPressed(const ui::KeyEvent & event)809 bool Label::OnKeyPressed(const ui::KeyEvent& event) {
810   if (!GetRenderTextForSelectionController())
811     return false;
812 
813   const bool shift = event.IsShiftDown();
814   const bool control = event.IsControlDown();
815   const bool alt = event.IsAltDown() || event.IsAltGrDown();
816 
817   switch (event.key_code()) {
818     case ui::VKEY_C:
819       if (control && !alt && HasSelection()) {
820         CopyToClipboard();
821         return true;
822       }
823       break;
824     case ui::VKEY_INSERT:
825       if (control && !shift && HasSelection()) {
826         CopyToClipboard();
827         return true;
828       }
829       break;
830     case ui::VKEY_A:
831       if (control && !alt && !GetText().empty()) {
832         SelectAll();
833         DCHECK(HasSelection());
834         UpdateSelectionClipboard();
835         return true;
836       }
837       break;
838     default:
839       break;
840   }
841 
842   return false;
843 }
844 
AcceleratorPressed(const ui::Accelerator & accelerator)845 bool Label::AcceleratorPressed(const ui::Accelerator& accelerator) {
846   // Allow the "Copy" action from the Chrome menu to be invoked. E.g., if a user
847   // selects a Label on a web modal dialog. "Select All" doesn't appear in the
848   // Chrome menu so isn't handled here.
849   if (accelerator.key_code() == ui::VKEY_C && accelerator.IsCtrlDown()) {
850     CopyToClipboard();
851     return true;
852   }
853   return false;
854 }
855 
CanHandleAccelerators() const856 bool Label::CanHandleAccelerators() const {
857   // Focus needs to be checked since the accelerator for the Copy command from
858   // the Chrome menu should only be handled when the current view has focus. See
859   // related comment in BrowserView::CutCopyPaste.
860   return HasFocus() && GetRenderTextForSelectionController() &&
861          View::CanHandleAccelerators();
862 }
863 
OnDeviceScaleFactorChanged(float old_device_scale_factor,float new_device_scale_factor)864 void Label::OnDeviceScaleFactorChanged(float old_device_scale_factor,
865                                        float new_device_scale_factor) {
866   View::OnDeviceScaleFactorChanged(old_device_scale_factor,
867                                    new_device_scale_factor);
868   // When the device scale factor is changed, some font rendering parameters is
869   // changed (especially, hinting). The bounding box of the text has to be
870   // re-computed based on the new parameters. See crbug.com/441439
871   PreferredSizeChanged();
872 }
873 
VisibilityChanged(View * starting_from,bool is_visible)874 void Label::VisibilityChanged(View* starting_from, bool is_visible) {
875   if (!is_visible)
876     ClearDisplayText();
877 }
878 
ShowContextMenuForViewImpl(View * source,const gfx::Point & point,ui::MenuSourceType source_type)879 void Label::ShowContextMenuForViewImpl(View* source,
880                                        const gfx::Point& point,
881                                        ui::MenuSourceType source_type) {
882   if (!GetRenderTextForSelectionController())
883     return;
884 
885   context_menu_runner_ = std::make_unique<MenuRunner>(
886       &context_menu_contents_,
887       MenuRunner::HAS_MNEMONICS | MenuRunner::CONTEXT_MENU);
888   context_menu_runner_->RunMenuAt(GetWidget(), nullptr,
889                                   gfx::Rect(point, gfx::Size()),
890                                   MenuAnchorPosition::kTopLeft, source_type);
891 }
892 
GetWordLookupDataAtPoint(const gfx::Point & point,gfx::DecoratedText * decorated_word,gfx::Point * baseline_point)893 bool Label::GetWordLookupDataAtPoint(const gfx::Point& point,
894                                      gfx::DecoratedText* decorated_word,
895                                      gfx::Point* baseline_point) {
896   gfx::RenderText* render_text = GetRenderTextForSelectionController();
897   return render_text ? render_text->GetWordLookupDataAtPoint(
898                            point, decorated_word, baseline_point)
899                      : false;
900 }
901 
GetWordLookupDataFromSelection(gfx::DecoratedText * decorated_text,gfx::Point * baseline_point)902 bool Label::GetWordLookupDataFromSelection(gfx::DecoratedText* decorated_text,
903                                            gfx::Point* baseline_point) {
904   gfx::RenderText* render_text = GetRenderTextForSelectionController();
905   return render_text
906              ? render_text->GetLookupDataForRange(
907                    render_text->selection(), decorated_text, baseline_point)
908              : false;
909 }
910 
GetRenderTextForSelectionController()911 gfx::RenderText* Label::GetRenderTextForSelectionController() {
912   return const_cast<gfx::RenderText*>(
913       static_cast<const Label*>(this)->GetRenderTextForSelectionController());
914 }
915 
IsReadOnly() const916 bool Label::IsReadOnly() const {
917   return true;
918 }
919 
SupportsDrag() const920 bool Label::SupportsDrag() const {
921   // TODO(crbug.com/661379): Labels should support dragging selected text.
922   return false;
923 }
924 
HasTextBeingDragged() const925 bool Label::HasTextBeingDragged() const {
926   return false;
927 }
928 
SetTextBeingDragged(bool value)929 void Label::SetTextBeingDragged(bool value) {
930   NOTREACHED();
931 }
932 
GetViewHeight() const933 int Label::GetViewHeight() const {
934   return height();
935 }
936 
GetViewWidth() const937 int Label::GetViewWidth() const {
938   return width();
939 }
940 
GetDragSelectionDelay() const941 int Label::GetDragSelectionDelay() const {
942   // Labels don't need to use a repeating timer to update the drag selection.
943   // Since the cursor is disabled for labels, a selection outside the display
944   // area won't change the text in the display area. It is expected that all the
945   // text will fit in the display area for labels anyway.
946   return 0;
947 }
948 
OnBeforePointerAction()949 void Label::OnBeforePointerAction() {}
950 
OnAfterPointerAction(bool text_changed,bool selection_changed)951 void Label::OnAfterPointerAction(bool text_changed, bool selection_changed) {
952   DCHECK(!text_changed);
953   if (selection_changed)
954     SchedulePaint();
955 }
956 
PasteSelectionClipboard()957 bool Label::PasteSelectionClipboard() {
958   NOTREACHED();
959   return false;
960 }
961 
UpdateSelectionClipboard()962 void Label::UpdateSelectionClipboard() {
963 #if (defined(OS_LINUX) && !defined(OS_CHROMEOS)) || defined(OS_BSD)
964   if (!GetObscured()) {
965     ui::ScopedClipboardWriter(ui::ClipboardBuffer::kSelection)
966         .WriteText(GetSelectedText());
967   }
968 #endif
969 }
970 
IsCommandIdChecked(int command_id) const971 bool Label::IsCommandIdChecked(int command_id) const {
972   return true;
973 }
974 
IsCommandIdEnabled(int command_id) const975 bool Label::IsCommandIdEnabled(int command_id) const {
976   switch (command_id) {
977     case MenuCommands::kCopy:
978       return HasSelection() && !GetObscured();
979     case MenuCommands::kSelectAll:
980       return GetRenderTextForSelectionController() && !GetText().empty();
981   }
982   return false;
983 }
984 
ExecuteCommand(int command_id,int event_flags)985 void Label::ExecuteCommand(int command_id, int event_flags) {
986   switch (command_id) {
987     case MenuCommands::kCopy:
988       CopyToClipboard();
989       break;
990     case MenuCommands::kSelectAll:
991       SelectAll();
992       DCHECK(HasSelection());
993       UpdateSelectionClipboard();
994       break;
995     default:
996       NOTREACHED();
997   }
998 }
999 
GetAcceleratorForCommandId(int command_id,ui::Accelerator * accelerator) const1000 bool Label::GetAcceleratorForCommandId(int command_id,
1001                                        ui::Accelerator* accelerator) const {
1002   switch (command_id) {
1003     case MenuCommands::kCopy:
1004       *accelerator = ui::Accelerator(ui::VKEY_C, ui::EF_CONTROL_DOWN);
1005       return true;
1006 
1007     case MenuCommands::kSelectAll:
1008       *accelerator = ui::Accelerator(ui::VKEY_A, ui::EF_CONTROL_DOWN);
1009       return true;
1010 
1011     default:
1012       return false;
1013   }
1014 }
1015 
GetRenderTextForSelectionController() const1016 const gfx::RenderText* Label::GetRenderTextForSelectionController() const {
1017   if (!GetSelectable())
1018     return nullptr;
1019   MaybeBuildDisplayText();
1020 
1021   // This may be null when the content bounds of the view are empty.
1022   return display_text_.get();
1023 }
1024 
Init(const base::string16 & text,const gfx::FontList & font_list,gfx::DirectionalityMode directionality_mode)1025 void Label::Init(const base::string16& text,
1026                  const gfx::FontList& font_list,
1027                  gfx::DirectionalityMode directionality_mode) {
1028   full_text_ = gfx::RenderText::CreateRenderText();
1029   full_text_->SetHorizontalAlignment(gfx::ALIGN_CENTER);
1030   full_text_->SetFontList(font_list);
1031   full_text_->SetCursorEnabled(false);
1032   full_text_->SetWordWrapBehavior(gfx::TRUNCATE_LONG_WORDS);
1033   full_text_->SetMinLineHeight(GetLineHeight());
1034   // NOTE: |full_text_| should not be elided at all. This is used to keep
1035   // some properties and to compute the size of the string.
1036   full_text_->SetElideBehavior(gfx::NO_ELIDE);
1037   full_text_->SetDirectionalityMode(directionality_mode);
1038 
1039   SetText(text);
1040 
1041   // Only selectable labels will get requests to show the context menu, due to
1042   // GetCanProcessEventsWithinSubtree().
1043   BuildContextMenuContents();
1044   set_context_menu_controller(this);
1045 
1046   // This allows the BrowserView to pass the copy command from the Chrome menu
1047   // to the Label.
1048   AddAccelerator(ui::Accelerator(ui::VKEY_C, ui::EF_CONTROL_DOWN));
1049 }
1050 
MaybeBuildDisplayText() const1051 void Label::MaybeBuildDisplayText() const {
1052   if (display_text_)
1053     return;
1054 
1055   gfx::Rect rect = GetContentsBounds();
1056   if (rect.IsEmpty())
1057     return;
1058 
1059   rect.Inset(-gfx::ShadowValue::GetMargin(GetShadows()));
1060   display_text_ = CreateRenderText();
1061   display_text_->SetDisplayRect(rect);
1062   stored_selection_range_ = gfx::Range::InvalidRange();
1063   ApplyTextColors();
1064 }
1065 
GetTextSize() const1066 gfx::Size Label::GetTextSize() const {
1067   gfx::Size size;
1068   if (GetText().empty()) {
1069     size = gfx::Size(0, GetLineHeight());
1070   } else {
1071     // Cancel the display rect of |full_text_|. The display rect may be
1072     // specified in GetHeightForWidth(), and specifying empty Rect cancels
1073     // its effect. See also the comment in GetHeightForWidth().
1074     // TODO(mukai): use gfx::Rect() to compute the ideal size rather than
1075     // the current width(). See crbug.com/468494, crbug.com/467526, and
1076     // the comment for MultilinePreferredSizeTest in label_unittest.cc.
1077     full_text_->SetDisplayRect(gfx::Rect(0, 0, width(), 0));
1078     size = full_text_->GetStringSize();
1079   }
1080   const gfx::Insets shadow_margin = -gfx::ShadowValue::GetMargin(GetShadows());
1081   size.Enlarge(shadow_margin.width(), shadow_margin.height());
1082   return size;
1083 }
1084 
GetForegroundColor(SkColor foreground,SkColor background) const1085 SkColor Label::GetForegroundColor(SkColor foreground,
1086                                   SkColor background) const {
1087   return (auto_color_readability_enabled_ && IsOpaque(background))
1088              ? color_utils::BlendForMinContrast(foreground, background).color
1089              : foreground;
1090 }
1091 
RecalculateColors()1092 void Label::RecalculateColors() {
1093   actual_enabled_color_ =
1094       GetForegroundColor(requested_enabled_color_, background_color_);
1095   // Using GetResultingPaintColor() here allows non-opaque selection backgrounds
1096   // to still participate in auto color readability, assuming
1097   // |background_color_| is itself opaque.
1098   actual_selection_text_color_ =
1099       GetForegroundColor(requested_selection_text_color_,
1100                          color_utils::GetResultingPaintColor(
1101                              selection_background_color_, background_color_));
1102 
1103   ApplyTextColors();
1104   SchedulePaint();
1105 }
1106 
ApplyTextColors() const1107 void Label::ApplyTextColors() const {
1108   if (!display_text_)
1109     return;
1110 
1111   display_text_->SetColor(actual_enabled_color_);
1112   display_text_->set_selection_color(actual_selection_text_color_);
1113   display_text_->set_selection_background_focused_color(
1114       selection_background_color_);
1115   const bool subpixel_rendering_enabled =
1116       subpixel_rendering_enabled_ && IsOpaque(background_color_);
1117   display_text_->set_subpixel_rendering_suppressed(!subpixel_rendering_enabled);
1118 }
1119 
UpdateColorsFromTheme()1120 void Label::UpdateColorsFromTheme() {
1121   ui::NativeTheme* theme = GetNativeTheme();
1122   if (!enabled_color_set_) {
1123     requested_enabled_color_ =
1124         style::GetColor(*this, text_context_, text_style_);
1125   }
1126   if (!background_color_set_) {
1127     background_color_ =
1128         theme->GetSystemColor(ui::NativeTheme::kColorId_DialogBackground);
1129   }
1130   if (!selection_text_color_set_) {
1131     requested_selection_text_color_ = theme->GetSystemColor(
1132         ui::NativeTheme::kColorId_LabelTextSelectionColor);
1133   }
1134   if (!selection_background_color_set_) {
1135     selection_background_color_ = theme->GetSystemColor(
1136         ui::NativeTheme::kColorId_LabelTextSelectionBackgroundFocused);
1137   }
1138   RecalculateColors();
1139 }
1140 
ShouldShowDefaultTooltip() const1141 bool Label::ShouldShowDefaultTooltip() const {
1142   const gfx::Size text_size = GetTextSize();
1143   const gfx::Size size = GetContentsBounds().size();
1144   return !GetObscured() &&
1145          (text_size.width() > size.width() ||
1146           (GetMultiLine() && text_size.height() > size.height()));
1147 }
1148 
ClearDisplayText()1149 void Label::ClearDisplayText() {
1150   // The HasSelection() call below will build |display_text_| in case it is
1151   // empty. Return early to avoid this.
1152   if (!display_text_)
1153     return;
1154 
1155   // Persist the selection range if there is an active selection.
1156   if (HasSelection()) {
1157     stored_selection_range_ =
1158         GetRenderTextForSelectionController()->selection();
1159   }
1160   display_text_ = nullptr;
1161 
1162   SchedulePaint();
1163 }
1164 
GetSelectedText() const1165 base::string16 Label::GetSelectedText() const {
1166   const gfx::RenderText* render_text = GetRenderTextForSelectionController();
1167   return render_text ? render_text->GetTextFromRange(render_text->selection())
1168                      : base::string16();
1169 }
1170 
CopyToClipboard()1171 void Label::CopyToClipboard() {
1172   if (!HasSelection() || GetObscured())
1173     return;
1174   ui::ScopedClipboardWriter(ui::ClipboardBuffer::kCopyPaste)
1175       .WriteText(GetSelectedText());
1176 }
1177 
BuildContextMenuContents()1178 void Label::BuildContextMenuContents() {
1179   context_menu_contents_.AddItemWithStringId(MenuCommands::kCopy, IDS_APP_COPY);
1180   context_menu_contents_.AddItemWithStringId(MenuCommands::kSelectAll,
1181                                              IDS_APP_SELECT_ALL);
1182 }
1183 
1184 BEGIN_METADATA(Label, View)
1185 ADD_PROPERTY_METADATA(base::string16, Text)
1186 ADD_PROPERTY_METADATA(int, TextContext)
1187 ADD_PROPERTY_METADATA(int, TextStyle)
1188 ADD_PROPERTY_METADATA(bool, AutoColorReadabilityEnabled)
1189 ADD_PROPERTY_METADATA(SkColor, EnabledColor)
1190 ADD_PROPERTY_METADATA(gfx::ElideBehavior, ElideBehavior)
1191 ADD_PROPERTY_METADATA(SkColor, BackgroundColor)
1192 ADD_PROPERTY_METADATA(SkColor, SelectionTextColor)
1193 ADD_PROPERTY_METADATA(SkColor, SelectionBackgroundColor)
1194 ADD_PROPERTY_METADATA(bool, SubpixelRenderingEnabled)
1195 ADD_PROPERTY_METADATA(bool, SkipSubpixelRenderingOpacityCheck)
1196 ADD_PROPERTY_METADATA(gfx::ShadowValues, Shadows)
1197 ADD_PROPERTY_METADATA(gfx::HorizontalAlignment, HorizontalAlignment)
1198 ADD_PROPERTY_METADATA(gfx::VerticalAlignment, VerticalAlignment)
1199 ADD_PROPERTY_METADATA(int, LineHeight)
1200 ADD_PROPERTY_METADATA(bool, MultiLine)
1201 ADD_PROPERTY_METADATA(int, MaxLines)
1202 ADD_PROPERTY_METADATA(bool, Obscured)
1203 ADD_PROPERTY_METADATA(bool, AllowCharacterBreak)
1204 ADD_PROPERTY_METADATA(base::string16, TooltipText)
1205 ADD_PROPERTY_METADATA(bool, HandlesTooltips)
1206 ADD_PROPERTY_METADATA(bool, CollapseWhenHidden)
1207 ADD_PROPERTY_METADATA(int, MaximumWidth)
1208 END_METADATA
1209 
1210 }  // namespace views
1211