1 /**
2  * This file is part of the theme implementation for form controls in WebCore.
3  *
4  * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2012 Apple Computer, Inc.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public License
17  * along with this library; see the file COPYING.LIB.  If not, write to
18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21 
22 #include "third_party/blink/renderer/core/layout/layout_theme.h"
23 
24 #include "build/build_config.h"
25 #include "third_party/blink/public/platform/platform.h"
26 #include "third_party/blink/public/platform/web_rect.h"
27 #include "third_party/blink/public/strings/grit/blink_strings.h"
28 #include "third_party/blink/public/web/blink.h"
29 #include "third_party/blink/renderer/core/css_value_keywords.h"
30 #include "third_party/blink/renderer/core/dom/document.h"
31 #include "third_party/blink/renderer/core/dom/shadow_root.h"
32 #include "third_party/blink/renderer/core/editing/frame_selection.h"
33 #include "third_party/blink/renderer/core/fileapi/file.h"
34 #include "third_party/blink/renderer/core/frame/local_frame.h"
35 #include "third_party/blink/renderer/core/frame/settings.h"
36 #include "third_party/blink/renderer/core/html/forms/html_data_list_element.h"
37 #include "third_party/blink/renderer/core/html/forms/html_data_list_options_collection.h"
38 #include "third_party/blink/renderer/core/html/forms/html_form_control_element.h"
39 #include "third_party/blink/renderer/core/html/forms/html_input_element.h"
40 #include "third_party/blink/renderer/core/html/forms/html_option_element.h"
41 #include "third_party/blink/renderer/core/html/forms/html_select_element.h"
42 #include "third_party/blink/renderer/core/html/forms/spin_button_element.h"
43 #include "third_party/blink/renderer/core/html/forms/text_control_inner_elements.h"
44 #include "third_party/blink/renderer/core/html/html_collection.h"
45 #include "third_party/blink/renderer/core/html/parser/html_parser_idioms.h"
46 #include "third_party/blink/renderer/core/html/shadow/shadow_element_names.h"
47 #include "third_party/blink/renderer/core/html_names.h"
48 #include "third_party/blink/renderer/core/input_type_names.h"
49 #include "third_party/blink/renderer/core/layout/layout_box.h"
50 #include "third_party/blink/renderer/core/layout/layout_theme_mobile.h"
51 #include "third_party/blink/renderer/core/page/focus_controller.h"
52 #include "third_party/blink/renderer/core/page/page.h"
53 #include "third_party/blink/renderer/core/paint/fallback_theme.h"
54 #include "third_party/blink/renderer/core/style/computed_style.h"
55 #include "third_party/blink/renderer/core/style/computed_style_initial_values.h"
56 #include "third_party/blink/renderer/platform/file_metadata.h"
57 #include "third_party/blink/renderer/platform/fonts/font_selector.h"
58 #include "third_party/blink/renderer/platform/graphics/touch_action.h"
59 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
60 #include "third_party/blink/renderer/platform/web_test_support.h"
61 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
62 #include "ui/base/ui_base_features.h"
63 #include "ui/native_theme/native_theme.h"
64 
65 // The methods in this file are shared by all themes on every platform.
66 
67 namespace blink {
68 
69 namespace {
70 
71 // This function should match to the user-agent stylesheet.
AutoAppearanceFor(const Element & element)72 ControlPart AutoAppearanceFor(const Element& element) {
73   if (IsA<HTMLButtonElement>(element))
74     return kButtonPart;
75   if (IsA<HTMLMeterElement>(element))
76     return kMeterPart;
77   if (IsA<HTMLProgressElement>(element))
78     return kProgressBarPart;
79   if (IsA<HTMLTextAreaElement>(element))
80     return kTextAreaPart;
81   if (IsA<SpinButtonElement>(element))
82     return kInnerSpinButtonPart;
83   if (const auto* select = DynamicTo<HTMLSelectElement>(element))
84     return select->UsesMenuList() ? kMenulistPart : kListboxPart;
85 
86   if (const auto* input = DynamicTo<HTMLInputElement>(element)) {
87     const AtomicString& type = input->type();
88     if (type == input_type_names::kCheckbox)
89       return kCheckboxPart;
90     if (type == input_type_names::kRadio)
91       return kRadioPart;
92     if (input->IsTextButton())
93       return kPushButtonPart;
94     if (type == input_type_names::kColor) {
95       return input->FastHasAttribute(html_names::kListAttr) ? kMenulistPart
96                                                             : kSquareButtonPart;
97     }
98     if (type == input_type_names::kRange)
99       return kSliderHorizontalPart;
100     if (type == input_type_names::kSearch)
101       return kSearchFieldPart;
102     if (type == input_type_names::kDate ||
103         type == input_type_names::kDatetimeLocal ||
104         type == input_type_names::kMonth || type == input_type_names::kTime ||
105         type == input_type_names::kWeek) {
106 #if defined(OS_ANDROID)
107       return kMenulistPart;
108 #else
109       return kTextFieldPart;
110 #endif
111     }
112     if (type == input_type_names::kEmail || type == input_type_names::kNumber ||
113         type == input_type_names::kPassword || type == input_type_names::kTel ||
114         type == input_type_names::kText || type == input_type_names::kUrl)
115       return kTextFieldPart;
116 
117     // Type=hidden/image/file.
118     return kNoControlPart;
119   }
120 
121   if (element.IsInUserAgentShadowRoot()) {
122     const AtomicString& id_value =
123         element.FastGetAttribute(html_names::kIdAttr);
124     if (id_value == shadow_element_names::SliderThumb())
125       return kSliderThumbHorizontalPart;
126     if (id_value == shadow_element_names::SearchClearButton() ||
127         id_value == shadow_element_names::ClearButton())
128       return kSearchFieldCancelButtonPart;
129 
130     // Slider container elements and -webkit-meter-inner-element don't have IDs.
131     const AtomicString& shadow_pseudo = element.ShadowPseudoId();
132     if (shadow_pseudo == "-webkit-media-slider-container" ||
133         shadow_pseudo == "-webkit-slider-container")
134       return kSliderHorizontalPart;
135     if (shadow_pseudo == "-webkit-meter-inner-element")
136       return kMeterPart;
137   }
138   return kNoControlPart;
139 }
140 
141 }  // namespace
142 
GetTheme()143 LayoutTheme& LayoutTheme::GetTheme() {
144   if (RuntimeEnabledFeatures::MobileLayoutThemeEnabled()) {
145     DEFINE_STATIC_REF(LayoutTheme, layout_theme_mobile,
146                       (LayoutThemeMobile::Create()));
147     return *layout_theme_mobile;
148   }
149   return NativeTheme();
150 }
151 
LayoutTheme()152 LayoutTheme::LayoutTheme() : has_custom_focus_ring_color_(false) {}
153 
AdjustAppearanceWithAuthorStyle(ControlPart part,const ComputedStyle & style)154 ControlPart LayoutTheme::AdjustAppearanceWithAuthorStyle(
155     ControlPart part,
156     const ComputedStyle& style) {
157   if (IsControlStyled(part, style))
158     return part == kMenulistPart ? kMenulistButtonPart : kNoControlPart;
159   return part;
160 }
161 
AdjustAppearanceWithElementType(const ComputedStyle & style,const Element * element)162 ControlPart LayoutTheme::AdjustAppearanceWithElementType(
163     const ComputedStyle& style,
164     const Element* element) {
165   ControlPart part = style.EffectiveAppearance();
166   if (!element)
167     return kNoControlPart;
168 
169   ControlPart auto_appearance = AutoAppearanceFor(*element);
170   if (part == auto_appearance)
171     return part;
172 
173   switch (part) {
174     // No restrictions.
175     case kNoControlPart:
176     case kMediaSliderPart:
177     case kMediaSliderThumbPart:
178     case kMediaVolumeSliderPart:
179     case kMediaVolumeSliderThumbPart:
180     case kMediaControlPart:
181       return part;
182 
183     // Aliases of 'auto'.
184     // https://drafts.csswg.org/css-ui-4/#typedef-appearance-compat-auto
185     case kAutoPart:
186     case kCheckboxPart:
187     case kRadioPart:
188     case kPushButtonPart:
189     case kSquareButtonPart:
190     case kInnerSpinButtonPart:
191     case kListboxPart:
192     case kMenulistPart:
193     case kMeterPart:
194     case kProgressBarPart:
195     case kSliderHorizontalPart:
196     case kSliderThumbHorizontalPart:
197     case kSearchFieldPart:
198     case kSearchFieldCancelButtonPart:
199     case kTextAreaPart:
200       return auto_appearance;
201 
202       // The following keywords should work well for some element types
203       // even if their default appearances are different from the keywords.
204 
205     case kButtonPart:
206       return (auto_appearance == kPushButtonPart ||
207               auto_appearance == kSquareButtonPart)
208                  ? part
209                  : auto_appearance;
210 
211     case kMenulistButtonPart:
212       return auto_appearance == kMenulistPart ? part : auto_appearance;
213 
214     case kSliderVerticalPart:
215       return auto_appearance == kSliderHorizontalPart ? part : auto_appearance;
216 
217     case kSliderThumbVerticalPart:
218       return auto_appearance == kSliderThumbHorizontalPart ? part
219                                                            : auto_appearance;
220 
221     case kTextFieldPart:
222       if (IsA<HTMLInputElement>(*element) &&
223           To<HTMLInputElement>(*element).type() == input_type_names::kSearch)
224         return part;
225       return auto_appearance;
226   }
227 
228   return part;
229 }
230 
AdjustStyle(ComputedStyle & style,Element * e)231 void LayoutTheme::AdjustStyle(ComputedStyle& style, Element* e) {
232   ControlPart original_part = style.Appearance();
233   style.SetEffectiveAppearance(original_part);
234   if (original_part == ControlPart::kNoControlPart)
235     return;
236 
237   // Force inline and table display styles to be inline-block (except for table-
238   // which is block)
239   if (style.Display() == EDisplay::kInline ||
240       style.Display() == EDisplay::kInlineTable ||
241       style.Display() == EDisplay::kTableRowGroup ||
242       style.Display() == EDisplay::kTableHeaderGroup ||
243       style.Display() == EDisplay::kTableFooterGroup ||
244       style.Display() == EDisplay::kTableRow ||
245       style.Display() == EDisplay::kTableColumnGroup ||
246       style.Display() == EDisplay::kTableColumn ||
247       style.Display() == EDisplay::kTableCell ||
248       style.Display() == EDisplay::kTableCaption)
249     style.SetDisplay(EDisplay::kInlineBlock);
250   else if (style.Display() == EDisplay::kListItem ||
251            style.Display() == EDisplay::kTable)
252     style.SetDisplay(EDisplay::kBlock);
253 
254   // TODO(tkent): We should not update Appearance, which is a source of
255   // getComputedStyle(). https://drafts.csswg.org/css-ui-4/#propdef-appearance
256   // says "Computed value: specified keyword".
257   style.SetAppearance(AdjustAppearanceWithAuthorStyle(original_part, style));
258 
259   ControlPart part = AdjustAppearanceWithAuthorStyle(
260       AdjustAppearanceWithElementType(style, e), style);
261   style.SetEffectiveAppearance(part);
262   DCHECK_NE(part, kAutoPart);
263   if (part == kNoControlPart)
264     return;
265 
266   if (ShouldUseFallbackTheme(style)) {
267     AdjustStyleUsingFallbackTheme(style);
268     return;
269   }
270 
271   AdjustControlPartStyle(style);
272 
273   // Call the appropriate style adjustment method based off the appearance
274   // value.
275   switch (part) {
276     case kMenulistPart:
277       return AdjustMenuListStyle(style, e);
278     case kMenulistButtonPart:
279       return AdjustMenuListButtonStyle(style, e);
280     case kSliderHorizontalPart:
281     case kSliderVerticalPart:
282     case kMediaSliderPart:
283     case kMediaVolumeSliderPart:
284       return AdjustSliderContainerStyle(style, e);
285     case kSliderThumbHorizontalPart:
286     case kSliderThumbVerticalPart:
287       return AdjustSliderThumbStyle(style);
288     case kSearchFieldPart:
289       return AdjustSearchFieldStyle(style);
290     case kSearchFieldCancelButtonPart:
291       return AdjustSearchFieldCancelButtonStyle(style);
292     default:
293       break;
294   }
295 }
296 
ExtraDefaultStyleSheet()297 String LayoutTheme::ExtraDefaultStyleSheet() {
298   if (RuntimeEnabledFeatures::LayoutNGForControlsEnabled()) {
299     return String(R"CSS(
300 input[type="file" i] {
301     overflow: hidden;
302     text-overflow: ellipsis;
303     white-space: pre;
304 }
305 
306 input[type="file" i]::-webkit-file-upload-button {
307     margin-inline-end: 4px;
308 }
309 )CSS");
310   }
311   return g_empty_string;
312 }
313 
ExtraQuirksStyleSheet()314 String LayoutTheme::ExtraQuirksStyleSheet() {
315   return String();
316 }
317 
ExtraFullscreenStyleSheet()318 String LayoutTheme::ExtraFullscreenStyleSheet() {
319   return String();
320 }
321 
ActiveSelectionBackgroundColor(WebColorScheme color_scheme) const322 Color LayoutTheme::ActiveSelectionBackgroundColor(
323     WebColorScheme color_scheme) const {
324   return PlatformActiveSelectionBackgroundColor(color_scheme).BlendWithWhite();
325 }
326 
InactiveSelectionBackgroundColor(WebColorScheme color_scheme) const327 Color LayoutTheme::InactiveSelectionBackgroundColor(
328     WebColorScheme color_scheme) const {
329   return PlatformInactiveSelectionBackgroundColor(color_scheme)
330       .BlendWithWhite();
331 }
332 
ActiveSelectionForegroundColor(WebColorScheme color_scheme) const333 Color LayoutTheme::ActiveSelectionForegroundColor(
334     WebColorScheme color_scheme) const {
335   return PlatformActiveSelectionForegroundColor(color_scheme);
336 }
337 
InactiveSelectionForegroundColor(WebColorScheme color_scheme) const338 Color LayoutTheme::InactiveSelectionForegroundColor(
339     WebColorScheme color_scheme) const {
340   return PlatformInactiveSelectionForegroundColor(color_scheme);
341 }
342 
ActiveListBoxSelectionBackgroundColor(WebColorScheme color_scheme) const343 Color LayoutTheme::ActiveListBoxSelectionBackgroundColor(
344     WebColorScheme color_scheme) const {
345   return PlatformActiveListBoxSelectionBackgroundColor(color_scheme);
346 }
347 
InactiveListBoxSelectionBackgroundColor(WebColorScheme color_scheme) const348 Color LayoutTheme::InactiveListBoxSelectionBackgroundColor(
349     WebColorScheme color_scheme) const {
350   return PlatformInactiveListBoxSelectionBackgroundColor(color_scheme);
351 }
352 
ActiveListBoxSelectionForegroundColor(WebColorScheme color_scheme) const353 Color LayoutTheme::ActiveListBoxSelectionForegroundColor(
354     WebColorScheme color_scheme) const {
355   return PlatformActiveListBoxSelectionForegroundColor(color_scheme);
356 }
357 
InactiveListBoxSelectionForegroundColor(WebColorScheme color_scheme) const358 Color LayoutTheme::InactiveListBoxSelectionForegroundColor(
359     WebColorScheme color_scheme) const {
360   return PlatformInactiveListBoxSelectionForegroundColor(color_scheme);
361 }
362 
PlatformSpellingMarkerUnderlineColor() const363 Color LayoutTheme::PlatformSpellingMarkerUnderlineColor() const {
364   return Color(255, 0, 0);
365 }
366 
PlatformGrammarMarkerUnderlineColor() const367 Color LayoutTheme::PlatformGrammarMarkerUnderlineColor() const {
368   return Color(192, 192, 192);
369 }
370 
PlatformActiveSpellingMarkerHighlightColor() const371 Color LayoutTheme::PlatformActiveSpellingMarkerHighlightColor() const {
372   return Color(255, 0, 0, 102);
373 }
374 
PlatformActiveSelectionBackgroundColor(WebColorScheme color_scheme) const375 Color LayoutTheme::PlatformActiveSelectionBackgroundColor(
376     WebColorScheme color_scheme) const {
377   // Use a blue color by default if the platform theme doesn't define anything.
378   return Color(0, 0, 255);
379 }
380 
PlatformActiveSelectionForegroundColor(WebColorScheme color_scheme) const381 Color LayoutTheme::PlatformActiveSelectionForegroundColor(
382     WebColorScheme color_scheme) const {
383   // Use a white color by default if the platform theme doesn't define anything.
384   return Color::kWhite;
385 }
386 
PlatformInactiveSelectionBackgroundColor(WebColorScheme color_scheme) const387 Color LayoutTheme::PlatformInactiveSelectionBackgroundColor(
388     WebColorScheme color_scheme) const {
389   // Use a grey color by default if the platform theme doesn't define anything.
390   // This color matches Firefox's inactive color.
391   return Color(176, 176, 176);
392 }
393 
PlatformInactiveSelectionForegroundColor(WebColorScheme color_scheme) const394 Color LayoutTheme::PlatformInactiveSelectionForegroundColor(
395     WebColorScheme color_scheme) const {
396   // Use a black color by default.
397   return Color::kBlack;
398 }
399 
PlatformActiveListBoxSelectionBackgroundColor(WebColorScheme color_scheme) const400 Color LayoutTheme::PlatformActiveListBoxSelectionBackgroundColor(
401     WebColorScheme color_scheme) const {
402   return PlatformActiveSelectionBackgroundColor(color_scheme);
403 }
404 
PlatformActiveListBoxSelectionForegroundColor(WebColorScheme color_scheme) const405 Color LayoutTheme::PlatformActiveListBoxSelectionForegroundColor(
406     WebColorScheme color_scheme) const {
407   return PlatformActiveSelectionForegroundColor(color_scheme);
408 }
409 
PlatformInactiveListBoxSelectionBackgroundColor(WebColorScheme color_scheme) const410 Color LayoutTheme::PlatformInactiveListBoxSelectionBackgroundColor(
411     WebColorScheme color_scheme) const {
412   return PlatformInactiveSelectionBackgroundColor(color_scheme);
413 }
414 
PlatformInactiveListBoxSelectionForegroundColor(WebColorScheme color_scheme) const415 Color LayoutTheme::PlatformInactiveListBoxSelectionForegroundColor(
416     WebColorScheme color_scheme) const {
417   return PlatformInactiveSelectionForegroundColor(color_scheme);
418 }
419 
BaselinePositionAdjustment(const ComputedStyle & style) const420 LayoutUnit LayoutTheme::BaselinePositionAdjustment(
421     const ComputedStyle& style) const {
422   return LayoutUnit();
423 }
424 
IsControlContainer(ControlPart appearance) const425 bool LayoutTheme::IsControlContainer(ControlPart appearance) const {
426   // There are more leaves than this, but we'll patch this function as we add
427   // support for more controls.
428   return appearance != kCheckboxPart && appearance != kRadioPart;
429 }
430 
IsControlStyled(ControlPart part,const ComputedStyle & style) const431 bool LayoutTheme::IsControlStyled(ControlPart part,
432                                   const ComputedStyle& style) const {
433   switch (part) {
434     case kPushButtonPart:
435     case kSquareButtonPart:
436     case kButtonPart:
437     case kProgressBarPart:
438       return style.HasAuthorBackground() || style.HasAuthorBorder();
439 
440     case kMenulistPart:
441     case kSearchFieldPart:
442     case kTextAreaPart:
443     case kTextFieldPart:
444       return style.HasAuthorBackground() || style.HasAuthorBorder() ||
445              style.BoxShadow();
446 
447     default:
448       return false;
449   }
450 }
451 
ShouldDrawDefaultFocusRing(const Node * node,const ComputedStyle & style) const452 bool LayoutTheme::ShouldDrawDefaultFocusRing(const Node* node,
453                                              const ComputedStyle& style) const {
454   if (ThemeDrawsFocusRing(style))
455     return false;
456   if (!node)
457     return true;
458   if (!style.HasEffectiveAppearance() && !node->IsLink())
459     return true;
460   // We can't use LayoutTheme::isFocused because outline:auto might be
461   // specified to non-:focus rulesets.
462   if (node->IsFocused() && !node->ShouldHaveFocusAppearance())
463     return false;
464   return true;
465 }
466 
ControlStateChanged(const Node * node,const ComputedStyle & style,ControlState state) const467 bool LayoutTheme::ControlStateChanged(const Node* node,
468                                       const ComputedStyle& style,
469                                       ControlState state) const {
470   if (!style.HasEffectiveAppearance())
471     return false;
472 
473   // Default implementation assumes the controls don't respond to changes in
474   // :hover state
475   if (state == kHoverControlState && !SupportsHover(style))
476     return false;
477 
478   // Assume pressed state is only responded to if the control is enabled.
479   if (state == kPressedControlState && !IsEnabled(node))
480     return false;
481 
482   return true;
483 }
484 
ControlStatesForNode(const Node * node,const ComputedStyle & style)485 ControlStates LayoutTheme::ControlStatesForNode(const Node* node,
486                                                 const ComputedStyle& style) {
487   ControlStates result = 0;
488   if (IsHovered(node)) {
489     result |= kHoverControlState;
490     if (IsSpinUpButtonPartHovered(node))
491       result |= kSpinUpControlState;
492   }
493   if (IsPressed(node)) {
494     result |= kPressedControlState;
495     if (IsSpinUpButtonPartPressed(node))
496       result |= kSpinUpControlState;
497   }
498   if (IsFocused(node) && style.OutlineStyleIsAuto())
499     result |= kFocusControlState;
500   if (IsEnabled(node))
501     result |= kEnabledControlState;
502   if (IsChecked(node))
503     result |= kCheckedControlState;
504   if (IsReadOnlyControl(node))
505     result |= kReadOnlyControlState;
506   if (!IsActive(node))
507     result |= kWindowInactiveControlState;
508   if (IsIndeterminate(node))
509     result |= kIndeterminateControlState;
510   return result;
511 }
512 
IsActive(const Node * node)513 bool LayoutTheme::IsActive(const Node* node) {
514   if (!node)
515     return false;
516 
517   Page* page = node->GetDocument().GetPage();
518   if (!page)
519     return false;
520 
521   return page->GetFocusController().IsActive();
522 }
523 
IsChecked(const Node * node)524 bool LayoutTheme::IsChecked(const Node* node) {
525   if (auto* input = DynamicTo<HTMLInputElement>(node))
526     return input->ShouldAppearChecked();
527   return false;
528 }
529 
IsIndeterminate(const Node * node)530 bool LayoutTheme::IsIndeterminate(const Node* node) {
531   if (auto* input = DynamicTo<HTMLInputElement>(node))
532     return input->ShouldAppearIndeterminate();
533   return false;
534 }
535 
IsEnabled(const Node * node)536 bool LayoutTheme::IsEnabled(const Node* node) {
537   auto* element = DynamicTo<Element>(node);
538   if (!element)
539     return true;
540   return !element->IsDisabledFormControl();
541 }
542 
IsFocused(const Node * node)543 bool LayoutTheme::IsFocused(const Node* node) {
544   if (!node)
545     return false;
546 
547   node = node->FocusDelegate();
548   Document& document = node->GetDocument();
549   LocalFrame* frame = document.GetFrame();
550   return node == document.FocusedElement() && node->IsFocused() &&
551          node->ShouldHaveFocusAppearance() && frame &&
552          frame->Selection().FrameIsFocusedAndActive();
553 }
554 
IsPressed(const Node * node)555 bool LayoutTheme::IsPressed(const Node* node) {
556   if (!node)
557     return false;
558   return node->IsActive();
559 }
560 
IsSpinUpButtonPartPressed(const Node * node)561 bool LayoutTheme::IsSpinUpButtonPartPressed(const Node* node) {
562   const auto* element = DynamicTo<SpinButtonElement>(node);
563   if (!element || !element->IsActive())
564     return false;
565   return element->GetUpDownState() == SpinButtonElement::kUp;
566 }
567 
IsReadOnlyControl(const Node * node)568 bool LayoutTheme::IsReadOnlyControl(const Node* node) {
569   auto* form_control_element = DynamicTo<HTMLFormControlElement>(node);
570   return form_control_element && form_control_element->IsReadOnly();
571 }
572 
IsHovered(const Node * node)573 bool LayoutTheme::IsHovered(const Node* node) {
574   if (!node)
575     return false;
576   const auto* element = DynamicTo<SpinButtonElement>(node);
577   if (!element)
578     return node->IsHovered();
579   return element->IsHovered() &&
580          element->GetUpDownState() != SpinButtonElement::kIndeterminate;
581 }
582 
IsSpinUpButtonPartHovered(const Node * node)583 bool LayoutTheme::IsSpinUpButtonPartHovered(const Node* node) {
584   const auto* element = DynamicTo<SpinButtonElement>(node);
585   if (!element)
586     return false;
587   return element->GetUpDownState() == SpinButtonElement::kUp;
588 }
589 
AdjustCheckboxStyle(ComputedStyle & style) const590 void LayoutTheme::AdjustCheckboxStyle(ComputedStyle& style) const {
591   // A summary of the rules for checkbox designed to match WinIE:
592   // width/height - honored (WinIE actually scales its control for small widths,
593   // but lets it overflow for small heights.)
594   // font-size - not honored (control has no text), but we use it to decide
595   // which control size to use.
596   SetCheckboxSize(style);
597 
598   // padding - not honored by WinIE, needs to be removed.
599   style.ResetPadding();
600 
601   // border - honored by WinIE, but looks terrible (just paints in the control
602   // box and turns off the Windows XP theme) for now, we will not honor it.
603   style.ResetBorder();
604 }
605 
AdjustRadioStyle(ComputedStyle & style) const606 void LayoutTheme::AdjustRadioStyle(ComputedStyle& style) const {
607   // A summary of the rules for checkbox designed to match WinIE:
608   // width/height - honored (WinIE actually scales its control for small widths,
609   // but lets it overflow for small heights.)
610   // font-size - not honored (control has no text), but we use it to decide
611   // which control size to use.
612   SetRadioSize(style);
613 
614   // padding - not honored by WinIE, needs to be removed.
615   style.ResetPadding();
616 
617   // border - honored by WinIE, but looks terrible (just paints in the control
618   // box and turns off the Windows XP theme) for now, we will not honor it.
619   style.ResetBorder();
620 }
621 
AdjustButtonStyle(ComputedStyle & style) const622 void LayoutTheme::AdjustButtonStyle(ComputedStyle& style) const {}
623 
AdjustInnerSpinButtonStyle(ComputedStyle &) const624 void LayoutTheme::AdjustInnerSpinButtonStyle(ComputedStyle&) const {}
625 
AdjustMenuListStyle(ComputedStyle & style,Element *) const626 void LayoutTheme::AdjustMenuListStyle(ComputedStyle& style, Element*) const {
627   // Menulists should have visible overflow
628   // https://bugs.webkit.org/show_bug.cgi?id=21287
629   style.SetOverflowX(EOverflow::kVisible);
630   style.SetOverflowY(EOverflow::kVisible);
631 }
632 
AnimationRepeatIntervalForProgressBar() const633 base::TimeDelta LayoutTheme::AnimationRepeatIntervalForProgressBar() const {
634   return base::TimeDelta();
635 }
636 
AnimationDurationForProgressBar() const637 base::TimeDelta LayoutTheme::AnimationDurationForProgressBar() const {
638   return base::TimeDelta();
639 }
640 
ShouldHaveSpinButton(HTMLInputElement * input_element) const641 bool LayoutTheme::ShouldHaveSpinButton(HTMLInputElement* input_element) const {
642   return input_element->IsSteppable() &&
643          input_element->type() != input_type_names::kRange;
644 }
645 
AdjustMenuListButtonStyle(ComputedStyle &,Element *) const646 void LayoutTheme::AdjustMenuListButtonStyle(ComputedStyle&, Element*) const {}
647 
AdjustSliderContainerStyle(ComputedStyle & style,Element * e) const648 void LayoutTheme::AdjustSliderContainerStyle(ComputedStyle& style,
649                                              Element* e) const {
650   if (e && (e->ShadowPseudoId() == "-webkit-media-slider-container" ||
651             e->ShadowPseudoId() == "-webkit-slider-container")) {
652     if (style.EffectiveAppearance() == kSliderVerticalPart) {
653       style.SetTouchAction(TouchAction::kPanX);
654       style.SetEffectiveAppearance(kNoControlPart);
655       style.SetWritingMode(WritingMode::kVerticalRl);
656       // It's always in RTL because the slider value increases up even in LTR.
657       style.SetDirection(TextDirection::kRtl);
658     } else {
659       style.SetTouchAction(TouchAction::kPanY);
660       style.SetEffectiveAppearance(kNoControlPart);
661       style.SetWritingMode(WritingMode::kHorizontalTb);
662     }
663   }
664 }
665 
AdjustSliderThumbStyle(ComputedStyle & style) const666 void LayoutTheme::AdjustSliderThumbStyle(ComputedStyle& style) const {
667   AdjustSliderThumbSize(style);
668 }
669 
AdjustSliderThumbSize(ComputedStyle &) const670 void LayoutTheme::AdjustSliderThumbSize(ComputedStyle&) const {}
671 
AdjustSearchFieldStyle(ComputedStyle &) const672 void LayoutTheme::AdjustSearchFieldStyle(ComputedStyle&) const {}
673 
AdjustSearchFieldCancelButtonStyle(ComputedStyle &) const674 void LayoutTheme::AdjustSearchFieldCancelButtonStyle(ComputedStyle&) const {}
675 
PlatformColorsDidChange()676 void LayoutTheme::PlatformColorsDidChange() {
677   Page::PlatformColorsChanged();
678 }
679 
ColorSchemeDidChange()680 void LayoutTheme::ColorSchemeDidChange() {
681   Page::ColorSchemeChanged();
682 }
683 
SetCaretBlinkInterval(base::TimeDelta interval)684 void LayoutTheme::SetCaretBlinkInterval(base::TimeDelta interval) {
685   caret_blink_interval_ = interval;
686 }
687 
CaretBlinkInterval() const688 base::TimeDelta LayoutTheme::CaretBlinkInterval() const {
689   // Disable the blinking caret in web test mode, as it introduces
690   // a race condition for the pixel tests. http://b/1198440
691   return WebTestSupport::IsRunningWebTest() ? base::TimeDelta()
692                                             : caret_blink_interval_;
693 }
694 
GetCachedFontDescription(CSSValueID system_font_id)695 static FontDescription& GetCachedFontDescription(CSSValueID system_font_id) {
696   DEFINE_STATIC_LOCAL(FontDescription, caption, ());
697   DEFINE_STATIC_LOCAL(FontDescription, icon, ());
698   DEFINE_STATIC_LOCAL(FontDescription, menu, ());
699   DEFINE_STATIC_LOCAL(FontDescription, message_box, ());
700   DEFINE_STATIC_LOCAL(FontDescription, small_caption, ());
701   DEFINE_STATIC_LOCAL(FontDescription, status_bar, ());
702   DEFINE_STATIC_LOCAL(FontDescription, webkit_mini_control, ());
703   DEFINE_STATIC_LOCAL(FontDescription, webkit_small_control, ());
704   DEFINE_STATIC_LOCAL(FontDescription, webkit_control, ());
705   DEFINE_STATIC_LOCAL(FontDescription, default_description, ());
706   switch (system_font_id) {
707     case CSSValueID::kCaption:
708       return caption;
709     case CSSValueID::kIcon:
710       return icon;
711     case CSSValueID::kMenu:
712       return menu;
713     case CSSValueID::kMessageBox:
714       return message_box;
715     case CSSValueID::kSmallCaption:
716       return small_caption;
717     case CSSValueID::kStatusBar:
718       return status_bar;
719     case CSSValueID::kWebkitMiniControl:
720       return webkit_mini_control;
721     case CSSValueID::kWebkitSmallControl:
722       return webkit_small_control;
723     case CSSValueID::kWebkitControl:
724       return webkit_control;
725     case CSSValueID::kNone:
726       return default_description;
727     default:
728       NOTREACHED();
729       return default_description;
730   }
731 }
732 
SystemFont(CSSValueID system_font_id,FontDescription & font_description)733 void LayoutTheme::SystemFont(CSSValueID system_font_id,
734                              FontDescription& font_description) {
735   font_description = GetCachedFontDescription(system_font_id);
736   if (font_description.IsAbsoluteSize())
737     return;
738 
739   FontSelectionValue font_slope = NormalSlopeValue();
740   FontSelectionValue font_weight = NormalWeightValue();
741   float font_size = 0;
742   AtomicString font_family;
743   SystemFont(system_font_id, font_slope, font_weight, font_size, font_family);
744   font_description.SetStyle(font_slope);
745   font_description.SetWeight(font_weight);
746   font_description.SetSpecifiedSize(font_size);
747   font_description.SetIsAbsoluteSize(true);
748   font_description.FirstFamily().SetFamily(font_family);
749   font_description.SetGenericFamily(FontDescription::kNoFamily);
750 }
751 
SystemColor(CSSValueID css_value_id,WebColorScheme color_scheme) const752 Color LayoutTheme::SystemColor(CSSValueID css_value_id,
753                                WebColorScheme color_scheme) const {
754   switch (css_value_id) {
755     case CSSValueID::kActiveborder:
756       return 0xFFFFFFFF;
757     case CSSValueID::kActivecaption:
758       return 0xFFCCCCCC;
759     case CSSValueID::kActivetext:
760       return 0xFFFF0000;
761     case CSSValueID::kAppworkspace:
762       return color_scheme == WebColorScheme::kDark ? 0xFF000000 : 0xFFFFFFFF;
763     case CSSValueID::kBackground:
764       return 0xFF6363CE;
765     case CSSValueID::kButtonface:
766       return color_scheme == WebColorScheme::kDark ? 0xFF404040 : 0xFFC0C0C0;
767     case CSSValueID::kButtonhighlight:
768       return 0xFFDDDDDD;
769     case CSSValueID::kButtonshadow:
770       return 0xFF888888;
771     case CSSValueID::kButtontext:
772       return color_scheme == WebColorScheme::kDark ? 0xFFAAAAAA : 0xFF000000;
773     case CSSValueID::kCaptiontext:
774       return color_scheme == WebColorScheme::kDark ? 0xFFFFFFFF : 0xFF000000;
775     case CSSValueID::kField:
776       return color_scheme == WebColorScheme::kDark ? 0xFF000000 : 0xFFFFFFFF;
777     case CSSValueID::kFieldtext:
778       return color_scheme == WebColorScheme::kDark ? 0xFFFFFFFF : 0xFF000000;
779     case CSSValueID::kGraytext:
780       return 0xFF808080;
781     case CSSValueID::kHighlight:
782       return 0xFFB5D5FF;
783     case CSSValueID::kHighlighttext:
784       return color_scheme == WebColorScheme::kDark ? 0xFFFFFFFF : 0xFF000000;
785     case CSSValueID::kInactiveborder:
786       return 0xFFFFFFFF;
787     case CSSValueID::kInactivecaption:
788       return 0xFFFFFFFF;
789     case CSSValueID::kInactivecaptiontext:
790       return 0xFF7F7F7F;
791     case CSSValueID::kInfobackground:
792       return color_scheme == WebColorScheme::kDark ? 0xFFB46E32 : 0xFFFBFCC5;
793     case CSSValueID::kInfotext:
794       return color_scheme == WebColorScheme::kDark ? 0xFFFFFFFF : 0xFF000000;
795     case CSSValueID::kLinktext:
796       return 0xFF0000EE;
797     case CSSValueID::kMenu:
798       return color_scheme == WebColorScheme::kDark ? 0xFF404040 : 0xFFC0C0C0;
799     case CSSValueID::kMenutext:
800       return color_scheme == WebColorScheme::kDark ? 0xFFFFFFFF : 0xFF000000;
801     case CSSValueID::kScrollbar:
802       return 0xFFFFFFFF;
803     case CSSValueID::kText:
804       return color_scheme == WebColorScheme::kDark ? 0xFFFFFFFF : 0xFF000000;
805     case CSSValueID::kThreeddarkshadow:
806       return 0xFF666666;
807     case CSSValueID::kThreedface:
808       return 0xFFC0C0C0;
809     case CSSValueID::kThreedhighlight:
810       return 0xFFDDDDDD;
811     case CSSValueID::kThreedlightshadow:
812       return 0xFFC0C0C0;
813     case CSSValueID::kThreedshadow:
814       return 0xFF888888;
815     case CSSValueID::kVisitedtext:
816       return 0xFF551A8B;
817     case CSSValueID::kWindow:
818     case CSSValueID::kCanvas:
819       return color_scheme == WebColorScheme::kDark ? 0xFF000000 : 0xFFFFFFFF;
820     case CSSValueID::kWindowframe:
821       return 0xFFCCCCCC;
822     case CSSValueID::kWindowtext:
823     case CSSValueID::kCanvastext:
824       return color_scheme == WebColorScheme::kDark ? 0xFFFFFFFF : 0xFF000000;
825     case CSSValueID::kInternalActiveListBoxSelection:
826       return ActiveListBoxSelectionBackgroundColor(color_scheme);
827     case CSSValueID::kInternalActiveListBoxSelectionText:
828       return ActiveListBoxSelectionForegroundColor(color_scheme);
829     case CSSValueID::kInternalInactiveListBoxSelection:
830       return InactiveListBoxSelectionBackgroundColor(color_scheme);
831     case CSSValueID::kInternalInactiveListBoxSelectionText:
832       return InactiveListBoxSelectionForegroundColor(color_scheme);
833     default:
834       break;
835   }
836   NOTREACHED();
837   return Color();
838 }
839 
PlatformTextSearchHighlightColor(bool active_match,bool in_forced_colors_mode,WebColorScheme color_scheme) const840 Color LayoutTheme::PlatformTextSearchHighlightColor(
841     bool active_match,
842     bool in_forced_colors_mode,
843     WebColorScheme color_scheme) const {
844   if (active_match) {
845     if (in_forced_colors_mode)
846       return GetTheme().SystemColor(CSSValueID::kHighlight, color_scheme);
847     return Color(255, 150, 50);  // Orange.
848   }
849   return Color(255, 255, 0);     // Yellow.
850 }
851 
PlatformTextSearchColor(bool active_match,bool in_forced_colors_mode,WebColorScheme color_scheme) const852 Color LayoutTheme::PlatformTextSearchColor(bool active_match,
853                                            bool in_forced_colors_mode,
854                                            WebColorScheme color_scheme) const {
855   if (in_forced_colors_mode && active_match)
856     return GetTheme().SystemColor(CSSValueID::kHighlighttext, color_scheme);
857   return Color::kBlack;
858 }
859 
TapHighlightColor()860 Color LayoutTheme::TapHighlightColor() {
861   return GetTheme().PlatformTapHighlightColor();
862 }
863 
SetCustomFocusRingColor(const Color & c)864 void LayoutTheme::SetCustomFocusRingColor(const Color& c) {
865   custom_focus_ring_color_ = c;
866   has_custom_focus_ring_color_ = true;
867 }
868 
IsFocusRingOutset() const869 bool LayoutTheme::IsFocusRingOutset() const {
870   return false;
871 }
872 
FocusRingColor() const873 Color LayoutTheme::FocusRingColor() const {
874   return has_custom_focus_ring_color_ ? custom_focus_ring_color_
875                                       : GetTheme().PlatformFocusRingColor();
876 }
877 
DelegatesMenuListRendering() const878 bool LayoutTheme::DelegatesMenuListRendering() const {
879   return delegates_menu_list_rendering_;
880 }
881 
SetDelegatesMenuListRenderingForTesting(bool flag)882 void LayoutTheme::SetDelegatesMenuListRenderingForTesting(bool flag) {
883   delegates_menu_list_rendering_ = flag;
884 }
885 
DisplayNameForFile(const File & file) const886 String LayoutTheme::DisplayNameForFile(const File& file) const {
887   return file.name();
888 }
889 
ShouldOpenPickerWithF4Key() const890 bool LayoutTheme::ShouldOpenPickerWithF4Key() const {
891   return false;
892 }
893 
SupportsCalendarPicker(const AtomicString & type) const894 bool LayoutTheme::SupportsCalendarPicker(const AtomicString& type) const {
895   DCHECK(RuntimeEnabledFeatures::InputMultipleFieldsUIEnabled());
896   if (features::IsFormControlsRefreshEnabled() &&
897       type == input_type_names::kTime)
898     return true;
899 
900   return type == input_type_names::kDate ||
901          type == input_type_names::kDatetime ||
902          type == input_type_names::kDatetimeLocal ||
903          type == input_type_names::kMonth || type == input_type_names::kWeek;
904 }
905 
ShouldUseFallbackTheme(const ComputedStyle &) const906 bool LayoutTheme::ShouldUseFallbackTheme(const ComputedStyle&) const {
907   return false;
908 }
909 
AdjustStyleUsingFallbackTheme(ComputedStyle & style)910 void LayoutTheme::AdjustStyleUsingFallbackTheme(ComputedStyle& style) {
911   ControlPart part = style.EffectiveAppearance();
912   switch (part) {
913     case kCheckboxPart:
914       return AdjustCheckboxStyleUsingFallbackTheme(style);
915     case kRadioPart:
916       return AdjustRadioStyleUsingFallbackTheme(style);
917     default:
918       break;
919   }
920 }
921 
922 // static
SetSizeIfAuto(ComputedStyle & style,const IntSize & size)923 void LayoutTheme::SetSizeIfAuto(ComputedStyle& style, const IntSize& size) {
924   if (style.Width().IsIntrinsicOrAuto())
925     style.SetWidth(Length::Fixed(size.Width()));
926   if (style.Height().IsIntrinsicOrAuto())
927     style.SetHeight(Length::Fixed(size.Height()));
928 }
929 
930 // static
SetMinimumSize(ComputedStyle & style,const LengthSize * part_size,const LengthSize * min_part_size)931 void LayoutTheme::SetMinimumSize(ComputedStyle& style,
932                                  const LengthSize* part_size,
933                                  const LengthSize* min_part_size) {
934   DCHECK(part_size || min_part_size);
935   // We only want to set a minimum size if no explicit size is specified, to
936   // avoid overriding author intentions.
937   if (part_size && style.MinWidth().IsIntrinsicOrAuto() &&
938       style.Width().IsIntrinsicOrAuto())
939     style.SetMinWidth(part_size->Width());
940   else if (min_part_size && min_part_size->Width() != style.MinWidth())
941     style.SetMinWidth(min_part_size->Width());
942   if (part_size && style.MinHeight().IsIntrinsicOrAuto() &&
943       style.Height().IsIntrinsicOrAuto())
944     style.SetMinHeight(part_size->Height());
945   else if (min_part_size && min_part_size->Height() != style.MinHeight())
946     style.SetMinHeight(min_part_size->Height());
947 }
948 
949 // static
SetMinimumSizeIfAuto(ComputedStyle & style,const IntSize & size)950 void LayoutTheme::SetMinimumSizeIfAuto(ComputedStyle& style,
951                                        const IntSize& size) {
952   LengthSize length_size(Length::Fixed(size.Width()),
953                          Length::Fixed(size.Height()));
954   SetMinimumSize(style, &length_size);
955 }
956 
AdjustCheckboxStyleUsingFallbackTheme(ComputedStyle & style) const957 void LayoutTheme::AdjustCheckboxStyleUsingFallbackTheme(
958     ComputedStyle& style) const {
959   // If the width and height are both specified, then we have nothing to do.
960   if (!style.Width().IsIntrinsicOrAuto() && !style.Height().IsAuto())
961     return;
962 
963   IntSize size(GetFallbackTheme().GetPartSize(ui::NativeTheme::kCheckbox,
964                                               ui::NativeTheme::kNormal,
965                                               ui::NativeTheme::ExtraParams()));
966   float zoom_level = style.EffectiveZoom();
967   size.SetWidth(size.Width() * zoom_level);
968   size.SetHeight(size.Height() * zoom_level);
969   SetMinimumSizeIfAuto(style, size);
970   SetSizeIfAuto(style, size);
971 
972   // padding - not honored by WinIE, needs to be removed.
973   style.ResetPadding();
974 
975   // border - honored by WinIE, but looks terrible (just paints in the control
976   // box and turns off the Windows XP theme)
977   // for now, we will not honor it.
978   style.ResetBorder();
979 }
980 
AdjustRadioStyleUsingFallbackTheme(ComputedStyle & style) const981 void LayoutTheme::AdjustRadioStyleUsingFallbackTheme(
982     ComputedStyle& style) const {
983   // If the width and height are both specified, then we have nothing to do.
984   if (!style.Width().IsIntrinsicOrAuto() && !style.Height().IsAuto())
985     return;
986 
987   IntSize size(GetFallbackTheme().GetPartSize(ui::NativeTheme::kRadio,
988                                               ui::NativeTheme::kNormal,
989                                               ui::NativeTheme::ExtraParams()));
990   float zoom_level = style.EffectiveZoom();
991   size.SetWidth(size.Width() * zoom_level);
992   size.SetHeight(size.Height() * zoom_level);
993   SetMinimumSizeIfAuto(style, size);
994   SetSizeIfAuto(style, size);
995 
996   // padding - not honored by WinIE, needs to be removed.
997   style.ResetPadding();
998 
999   // border - honored by WinIE, but looks terrible (just paints in the control
1000   // box and turns off the Windows XP theme)
1001   // for now, we will not honor it.
1002   style.ResetBorder();
1003 }
1004 
ControlPadding(ControlPart part,const FontDescription &,const Length & zoomed_box_top,const Length & zoomed_box_right,const Length & zoomed_box_bottom,const Length & zoomed_box_left,float) const1005 LengthBox LayoutTheme::ControlPadding(ControlPart part,
1006                                       const FontDescription&,
1007                                       const Length& zoomed_box_top,
1008                                       const Length& zoomed_box_right,
1009                                       const Length& zoomed_box_bottom,
1010                                       const Length& zoomed_box_left,
1011                                       float) const {
1012   switch (part) {
1013     case kMenulistPart:
1014     case kMenulistButtonPart:
1015     case kCheckboxPart:
1016     case kRadioPart:
1017       return LengthBox(0);
1018     default:
1019       return LengthBox(zoomed_box_top, zoomed_box_right, zoomed_box_bottom,
1020                        zoomed_box_left);
1021   }
1022 }
1023 
ControlBorder(ControlPart part,const FontDescription &,const LengthBox & zoomed_box,float) const1024 LengthBox LayoutTheme::ControlBorder(ControlPart part,
1025                                      const FontDescription&,
1026                                      const LengthBox& zoomed_box,
1027                                      float) const {
1028   switch (part) {
1029     case kPushButtonPart:
1030     case kMenulistPart:
1031     case kSearchFieldPart:
1032     case kCheckboxPart:
1033     case kRadioPart:
1034       return LengthBox(0);
1035     default:
1036       return zoomed_box;
1037   }
1038 }
1039 
AdjustControlPartStyle(ComputedStyle & style)1040 void LayoutTheme::AdjustControlPartStyle(ComputedStyle& style) {
1041   // Call the appropriate style adjustment method based off the appearance
1042   // value.
1043   switch (style.EffectiveAppearance()) {
1044     case kCheckboxPart:
1045       return AdjustCheckboxStyle(style);
1046     case kRadioPart:
1047       return AdjustRadioStyle(style);
1048     case kPushButtonPart:
1049     case kSquareButtonPart:
1050     case kButtonPart:
1051       return AdjustButtonStyle(style);
1052     case kInnerSpinButtonPart:
1053       return AdjustInnerSpinButtonStyle(style);
1054     default:
1055       break;
1056   }
1057 }
1058 
HasCustomFocusRingColor() const1059 bool LayoutTheme::HasCustomFocusRingColor() const {
1060   return has_custom_focus_ring_color_;
1061 }
1062 
GetCustomFocusRingColor() const1063 Color LayoutTheme::GetCustomFocusRingColor() const {
1064   return custom_focus_ring_color_;
1065 }
1066 
1067 }  // namespace blink
1068