Implement system fonts FontDescription caching at RenderTheme level
[WebKit-https.git] / Source / WebCore / platform / efl / RenderThemeEfl.cpp
1 /*
2  * Copyright (C) 2007 Apple Inc.
3  * Copyright (C) 2007 Alp Toker <alp@atoker.com>
4  * Copyright (C) 2008 Collabora Ltd.
5  * Copyright (C) 2008 INdT - Instituto Nokia de Tecnologia
6  * Copyright (C) 2009-2010 ProFUSION embedded systems
7  * Copyright (C) 2009-2011 Samsung Electronics
8  * Copyright (c) 2012 Intel Corporation. All rights reserved.
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Library General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Library General Public License for more details.
19  *
20  * You should have received a copy of the GNU Library General Public License
21  * along with this library; see the file COPYING.LIB.  If not, write to
22  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23  * Boston, MA 02110-1301, USA.
24  *
25  */
26
27 #include "config.h"
28 #include "RenderThemeEfl.h"
29
30 #include "CSSValueKeywords.h"
31 #include "CairoUtilitiesEfl.h"
32 #include "ExceptionCodePlaceholder.h"
33 #include "FloatRoundedRect.h"
34 #include "FontDescription.h"
35 #include "GraphicsContext.h"
36 #include "HTMLInputElement.h"
37 #include "InputTypeNames.h"
38 #include "NotImplemented.h"
39 #include "Page.h"
40 #include "PaintInfo.h"
41 #include "PlatformContextCairo.h"
42 #include "RenderBox.h"
43 #include "RenderObject.h"
44 #include "RenderProgress.h"
45 #include "RenderSlider.h"
46 #include "ScrollbarThemeEfl.h"
47 #include "Settings.h"
48 #include "UserAgentScripts.h"
49 #include "UserAgentStyleSheets.h"
50 #include <Ecore_Evas.h>
51 #include <Edje.h>
52 #include <new>
53 #include <wtf/text/CString.h>
54 #include <wtf/text/StringBuilder.h>
55 #include <wtf/text/WTFString.h>
56
57 namespace WebCore {
58
59 // TODO: change from object count to ecore_evas size (bytes)
60 // TODO: as objects are webpage/user defined and they can be very large.
61 #define RENDER_THEME_EFL_PART_CACHE_MAX 32
62
63 // Initialize default font size.
64 float RenderThemeEfl::defaultFontSize = 16.0f;
65
66 static const float minCancelButtonSize = 5;
67 static const float maxCancelButtonSize = 21;
68
69 static const float minSearchDecorationButtonSize = 1;
70 static const float maxSearchDecorationButtonSize = 15;
71 static const float searchFieldDecorationButtonOffset = 3;
72
73 // Constants for progress tag animation.
74 // These values have been copied from RenderThemeGtk.cpp
75 static const int progressAnimationFrames = 10;
76 static const double progressAnimationInterval = 0.125;
77
78 static const int sliderThumbWidth = 29;
79 static const int sliderThumbHeight = 11;
80
81 #define _ASSERT_ON_RELEASE_RETURN(o, fmt, ...) \
82     do { if (!o) { EINA_LOG_CRIT(fmt, ## __VA_ARGS__); ASSERT(o); return; } } while (0)
83 #define _ASSERT_ON_RELEASE_RETURN_VAL(o, val, fmt, ...) \
84     do { if (!o) { EINA_LOG_CRIT(fmt, ## __VA_ARGS__); ASSERT(o); return val; } } while (0)
85
86
87 static const char* toEdjeGroup(FormType type)
88 {
89     static const char* groups[] = {
90         "webkit/widget/button",
91         "webkit/widget/radio",
92         "webkit/widget/entry",
93         "webkit/widget/checkbox",
94         "webkit/widget/combo",
95         "webkit/widget/progressbar",
96         "webkit/widget/search/field",
97         "webkit/widget/search/results_button",
98         "webkit/widget/search/results_decoration",
99         "webkit/widget/search/cancel_button",
100         "webkit/widget/slider/vertical",
101         "webkit/widget/slider/horizontal",
102         "webkit/widget/slider/thumb_vertical",
103         "webkit/widget/slider/thumb_horizontal",
104         "webkit/widget/spinner",
105         0
106     };
107     ASSERT(type >= 0);
108     ASSERT((size_t)type < sizeof(groups) / sizeof(groups[0])); // Out of sync?
109     return groups[type];
110 }
111
112 static bool setSourceGroupForEdjeObject(Evas_Object* o, const String& themePath, const char* group)
113 {
114     ASSERT(o);
115     ASSERT(!themePath.isEmpty());
116
117     if (!edje_object_file_set(o, themePath.utf8().data(), group)) {
118         const char* message = edje_load_error_str(edje_object_load_error_get(o));
119         EINA_LOG_ERR("Could not set theme group '%s' of file '%s': %s", group, themePath.utf8().data(), message);
120         return false;
121     }
122
123     return true;
124 }
125
126 void RenderThemeEfl::adjustSizeConstraints(RenderStyle& style, FormType type) const
127 {
128     loadThemeIfNeeded();
129
130     // These are always valid, even if no theme could be loaded.
131     const ThemePartDesc* desc = m_partDescs + (size_t)type;
132
133     if (style.minWidth().isIntrinsic())
134         style.setMinWidth(desc->min.width());
135     if (style.minHeight().isIntrinsic())
136         style.setMinHeight(desc->min.height());
137
138     if (desc->max.width().value() > 0 && style.maxWidth().isIntrinsicOrAuto())
139         style.setMaxWidth(desc->max.width());
140     if (desc->max.height().value() > 0 && style.maxHeight().isIntrinsicOrAuto())
141         style.setMaxHeight(desc->max.height());
142
143     style.setPaddingTop(desc->padding.top());
144     style.setPaddingBottom(desc->padding.bottom());
145     style.setPaddingLeft(desc->padding.left());
146     style.setPaddingRight(desc->padding.right());
147 }
148
149 static bool isFormElementTooLargeToDisplay(const IntSize& elementSize)
150 {
151     // This limit of 20000 pixels is hardcoded inside edje -- anything above this size
152     // will be clipped. This value seems to be reasonable enough so that hardcoding it
153     // here won't be a problem.
154     static const int maxEdjeDimension = 20000;
155
156     return elementSize.width() > maxEdjeDimension || elementSize.height() > maxEdjeDimension;
157 }
158
159 std::unique_ptr<RenderThemeEfl::ThemePartCacheEntry> RenderThemeEfl::ThemePartCacheEntry::create(const String& themePath, FormType type, const IntSize& size)
160 {
161     ASSERT(!themePath.isEmpty());
162
163     if (isFormElementTooLargeToDisplay(size) || size.isEmpty()) {
164         EINA_LOG_ERR("Cannot render an element of size %dx%d.", size.width(), size.height());
165         return nullptr;
166     }
167
168     auto entry = std::make_unique<ThemePartCacheEntry>();
169
170     entry->m_canvas = EflUniquePtr<Ecore_Evas>(ecore_evas_buffer_new(size.width(), size.height()));
171     if (!entry->canvas()) {
172         EINA_LOG_ERR("ecore_evas_buffer_new(%d, %d) failed.", size.width(), size.height());
173         return nullptr;
174     }
175
176     // By default EFL creates buffers without alpha.
177     ecore_evas_alpha_set(entry->canvas(), EINA_TRUE);
178
179     entry->m_edje = EflUniquePtr<Evas_Object>(edje_object_add(ecore_evas_get(entry->canvas())));
180     ASSERT(entry->edje());
181
182     if (!setSourceGroupForEdjeObject(entry->edje(), themePath, toEdjeGroup(type)))
183         return nullptr;
184
185     entry->m_surface = createSurfaceForBackingStore(entry->canvas());
186     if (!entry->surface())
187         return nullptr;
188
189     evas_object_resize(entry->edje(), size.width(), size.height());
190     evas_object_show(entry->edje());
191
192     entry->type = type;
193     entry->size = size;
194
195     return entry;
196 }
197
198 void RenderThemeEfl::ThemePartCacheEntry::reuse(const String& themePath, FormType newType, const IntSize& newSize)
199 {
200     ASSERT(!themePath.isEmpty());
201
202     if (type != newType) {
203         type = newType;
204         if (!setSourceGroupForEdjeObject(edje(), themePath, toEdjeGroup(newType))) {
205             type = FormTypeLast; // Invalidate.
206             return;
207         }
208     }
209
210     if (size != newSize) {
211         size = newSize;
212         ecore_evas_resize(canvas(), newSize.width(), newSize.height());
213         evas_object_resize(edje(), newSize.width(), newSize.height());
214
215         m_surface = createSurfaceForBackingStore(canvas());
216         if (!surface()) {
217             type = FormTypeLast; // Invalidate;
218             return;
219         }
220     }
221 }
222
223 RenderThemeEfl::ThemePartCacheEntry* RenderThemeEfl::getThemePartFromCache(FormType type, const IntSize& size)
224 {
225     size_t reusableNodeIndex = 0;
226
227     for (size_t i = 0; i < m_partCache.size(); ++i) {
228         ThemePartCacheEntry* candidatedEntry = m_partCache[i].get();
229         if (candidatedEntry->size == size) {
230             if (candidatedEntry->type == type) {
231                 // Found the right item, move it to the head of the list
232                 // and return it.
233                 auto temp = WTF::move(m_partCache[i]);
234                 m_partCache.remove(i);
235                 m_partCache.insert(0, WTF::move(temp));
236                 return m_partCache.first().get();
237             }
238             reusableNodeIndex = i;
239         }
240     }
241
242     if (m_partCache.size() < RENDER_THEME_EFL_PART_CACHE_MAX) {
243         auto entry = ThemePartCacheEntry::create(themePath(), type, size);
244         if (entry)
245             m_partCache.insert(0, WTF::move(entry));
246
247         return m_partCache.first().get();
248     }
249
250     // The cache is full, reuse the last item we found that had the
251     // requested size to avoid resizing. If there was none, reuse
252     // the last item of the list.
253     if (!reusableNodeIndex)
254         reusableNodeIndex = m_partCache.size() - 1;
255
256     ThemePartCacheEntry* reusedEntry = m_partCache[reusableNodeIndex].get();
257     ASSERT(reusedEntry);
258     reusedEntry->reuse(themePath(), type, size);
259     auto temp = WTF::move(m_partCache[reusableNodeIndex]);
260     m_partCache.remove(reusableNodeIndex);
261     m_partCache.insert(0, WTF::move(temp));
262
263     return m_partCache.first().get();
264 }
265
266 void RenderThemeEfl::clearThemePartCache()
267 {
268     for (auto& part : m_partCache)
269         part = nullptr;
270 }
271
272 void RenderThemeEfl::applyEdjeStateFromForm(Evas_Object* object, const ControlStates* states, bool haveBackground)
273 {
274     const char *signals[] = { // keep in sync with WebCore/platform/ThemeTypes.h
275         "hovered",
276         "pressed",
277         "focused",
278         "enabled",
279         "checked",
280         "read-only",
281         "default",
282         "window-inactive",
283         "indeterminate",
284         "spinup"
285     };
286
287     edje_object_signal_emit(object, "reset", "");
288
289     for (size_t i = 0; i < WTF_ARRAY_LENGTH(signals); ++i) {
290         if (states->states() & (1 << i))
291             edje_object_signal_emit(object, signals[i], "");
292     }
293
294     if (haveBackground)
295         edje_object_signal_emit(object, "styled", "");
296 }
297
298 void RenderThemeEfl::applyEdjeRTLState(Evas_Object* edje, const RenderObject& object, FormType type, const IntRect& rect)
299 {
300     if (type == SliderVertical || type == SliderHorizontal) {
301         if (!is<RenderSlider>(object))
302             return; // probably have -webkit-appearance: slider..
303
304         HTMLInputElement& input = downcast<RenderSlider>(object).element();
305         double valueRange = input.maximum() - input.minimum();
306
307         auto msg = std::make_unique<Edje_Message_Float_Set>();
308         msg->count = 2;
309
310         // The first parameter of the message decides if the progress bar
311         // grows from the end of the slider or from the beginning. On vertical
312         // sliders, it should always be the same and will not be affected by
313         // text direction settings.
314         if (object.style().direction() == RTL || type == SliderVertical)
315             msg->val[0] = 1;
316         else
317             msg->val[0] = 0;
318
319         msg->val[1] = (input.valueAsNumber() - input.minimum()) / valueRange;
320         edje_object_message_send(edje, EDJE_MESSAGE_FLOAT_SET, 0, msg.get());
321     } else if (type == ProgressBar) {
322         const auto& renderProgress = downcast<RenderProgress>(object);
323
324         int max = rect.width();
325         double value = renderProgress.position();
326
327         auto msg = std::make_unique<Edje_Message_Float_Set>();
328         msg->count = 2;
329
330         if (object.style().direction() == RTL)
331             msg->val[0] = (1.0 - value) * max;
332         else
333             msg->val[0] = 0;
334         msg->val[1] = value;
335         edje_object_message_send(edje, EDJE_MESSAGE_FLOAT_SET, 0, msg.get());
336     }
337 }
338
339 bool RenderThemeEfl::isControlStyled(const RenderStyle& style, const BorderData& border, const FillLayer& background, const Color& backgroundColor) const
340 {
341     return RenderTheme::isControlStyled(style, border, background, backgroundColor) || style.appearance() == MenulistButtonPart;
342 }
343
344 bool RenderThemeEfl::paintThemePart(const RenderObject& object, FormType type, const PaintInfo& info, const IntRect& rect)
345 {
346     loadThemeIfNeeded();
347     _ASSERT_ON_RELEASE_RETURN_VAL(edje(), false, "Could not paint native HTML part due to missing theme.");
348
349     ThemePartCacheEntry* entry = getThemePartFromCache(type, rect.size());
350     if (!entry)
351         return false;
352
353     bool haveBackgroundColor = isControlStyled(object.style(), object.style().border(), *object.style().backgroundLayers(), Color::white);
354     ControlStates states(extractControlStatesForRenderer(object));
355     applyEdjeStateFromForm(entry->edje(), &states, haveBackgroundColor);
356
357     applyEdjeRTLState(entry->edje(), object, type, rect);
358
359     edje_object_calc_force(entry->edje());
360     edje_object_message_signal_process(entry->edje());
361     evas_render(ecore_evas_get(entry->canvas()));
362
363     cairo_t* cairo = info.context->platformContext()->cr();
364     ASSERT(cairo);
365
366     cairo_save(cairo);
367     cairo_set_source_surface(cairo, entry->surface(), rect.x(), rect.y());
368     cairo_paint_with_alpha(cairo, 1.0);
369     cairo_restore(cairo);
370
371     return false;
372 }
373
374 PassRefPtr<RenderTheme> RenderThemeEfl::create(Page* page)
375 {
376     return adoptRef(new RenderThemeEfl(page));
377 }
378
379 PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page)
380 {
381     if (page)
382         return RenderThemeEfl::create(page);
383
384     static RenderTheme* fallback = RenderThemeEfl::create(0).leakRef();
385     return fallback;
386 }
387
388 static void applyColorCallback(void* data, Evas_Object*, const char* /* signal */, const char* colorClass)
389 {
390     RenderThemeEfl* that = static_cast<RenderThemeEfl*>(data);
391     that->setColorFromThemeClass(colorClass);
392     that->platformColorsDidChange(); // Triggers relayout.
393 }
394
395 static bool fillColorsFromEdjeClass(Evas_Object* o, const char* colorClass, Color* color1, Color* color2 = 0, Color* color3 = 0)
396 {
397     int r1, g1, b1, a1;
398     int r2, g2, b2, a2;
399     int r3, g3, b3, a3;
400
401     if (!edje_object_color_class_get(o, colorClass, &r1, &g1, &b1, &a1, &r2, &g2, &b2, &a2, &r3, &g3, &b3, &a3))
402         return false;
403
404     if (color1)
405         color1->setRGB(makeRGBA(r1, g1, b1, a1));
406     if (color2)
407         color2->setRGB(makeRGBA(r2, g2, b2, a2));
408     if (color3)
409         color3->setRGB(makeRGBA(r3, g3, b3, a3));
410
411     return true;
412 }
413
414 void RenderThemeEfl::setColorFromThemeClass(const char* colorClass)
415 {
416     ASSERT(edje());
417
418     if (!strcmp("webkit/selection/foreground", colorClass))
419         m_supportsSelectionForegroundColor = fillColorsFromEdjeClass(edje(), colorClass, &m_activeSelectionForegroundColor, &m_inactiveSelectionForegroundColor);
420     else if (!strcmp("webkit/selection/background", colorClass))
421         fillColorsFromEdjeClass(edje(), colorClass, &m_activeSelectionBackgroundColor, &m_inactiveSelectionBackgroundColor);
422     else if (!strcmp("webkit/focus_ring", colorClass)) {
423         if (!fillColorsFromEdjeClass(edje(), colorClass, &m_focusRingColor))
424             return;
425
426         // platformFocusRingColor() is only used for the default theme (without page)
427         // The following is ugly, but no other way to do it unless we change it to use page themes as much as possible.
428         RenderTheme::setCustomFocusRingColor(m_focusRingColor);
429     }
430 }
431
432 void RenderThemeEfl::setThemePath(const String& newThemePath)
433 {
434     if (newThemePath == m_themePath)
435         return;
436
437     if (newThemePath.isEmpty()) {
438         EINA_LOG_CRIT("No valid theme defined, things will not work properly.");
439         return;
440     }
441
442     String oldThemePath = m_themePath;
443     m_themePath = newThemePath;
444
445     // Keep the consistence by restoring the previous theme path
446     // if we cannot load the new one.
447     if (!loadTheme())
448         m_themePath = oldThemePath;
449 }
450
451 String RenderThemeEfl::themePath() const
452 {
453 #ifndef NDEBUG
454     if (edje()) {
455         const char* path;
456         edje_object_file_get(edje(), &path, 0);
457         ASSERT(m_themePath == path);
458     }
459 #endif
460     return m_themePath;
461 }
462
463 bool RenderThemeEfl::loadTheme()
464 {
465     ASSERT(!m_themePath.isEmpty());
466
467     if (!canvas()) {
468         m_canvas = EflUniquePtr<Ecore_Evas>(ecore_evas_buffer_new(1, 1));
469         _ASSERT_ON_RELEASE_RETURN_VAL(canvas(), false,
470                 "Could not create canvas required by theme, things will not work properly.");
471     }
472
473     EflUniquePtr<Evas_Object> o = EflUniquePtr<Evas_Object>(edje_object_add(ecore_evas_get(canvas())));
474     _ASSERT_ON_RELEASE_RETURN_VAL(o, false, "Could not create new base Edje object.");
475
476     if (!setSourceGroupForEdjeObject(o.get(), m_themePath, "webkit/base"))
477         return false; // Keep current theme.
478
479     // Invalidate existing theme part cache.
480     if (edje())
481         clearThemePartCache();
482
483     // Set new loaded theme, and apply it.
484     m_edje = WTF::move(o);
485
486     const char* thickness = edje_object_data_get(m_edje.get(), "scrollbar.thickness");
487     if (thickness && !Settings::mockScrollbarsEnabled())
488         static_cast<ScrollbarThemeEfl*>(ScrollbarTheme::theme())->setScrollbarThickness(atoi(thickness));
489
490     edje_object_signal_callback_add(edje(), "color_class,set", "webkit/selection/foreground", applyColorCallback, this);
491     edje_object_signal_callback_add(edje(), "color_class,set", "webkit/selection/background", applyColorCallback, this);
492     edje_object_signal_callback_add(edje(), "color_class,set", "webkit/focus_ring", applyColorCallback, this);
493
494     applyPartDescriptionsFrom(m_themePath);
495
496     setColorFromThemeClass("webkit/selection/foreground");
497     setColorFromThemeClass("webkit/selection/background");
498     setColorFromThemeClass("webkit/focus_ring");
499
500     platformColorsDidChange(); // Schedules a relayout, do last.
501
502     return true;
503 }
504
505 void RenderThemeEfl::applyPartDescriptionFallback(ThemePartDesc* desc)
506 {
507     desc->min.setWidth(Length(0, Fixed));
508     desc->min.setHeight(Length(0, Fixed));
509
510     desc->max.setWidth(Length(0, Fixed));
511     desc->max.setHeight(Length(0, Fixed));
512
513     desc->padding = LengthBox(0, 0, 0, 0);
514 }
515
516 void RenderThemeEfl::applyPartDescription(Evas_Object* object, ThemePartDesc* desc)
517 {
518     Evas_Coord minw, minh, maxw, maxh;
519
520     edje_object_size_min_get(object, &minw, &minh);
521     if (!minw && !minh)
522         edje_object_size_min_calc(object, &minw, &minh);
523
524     desc->min.setWidth(Length(minw, Fixed));
525     desc->min.setHeight(Length(minh, Fixed));
526
527     edje_object_size_max_get(object, &maxw, &maxh);
528     desc->max.setWidth(Length(maxw, Fixed));
529     desc->max.setHeight(Length(maxh, Fixed));
530
531     if (!edje_object_part_exists(object, "text_confinement"))
532         desc->padding = LengthBox(0, 0, 0, 0);
533     else {
534         Evas_Coord px, py, pw, ph;
535         Evas_Coord ox = 0, oy = 0, ow = 0, oh = 0;
536         int t, r, b, l;
537
538         if (minw > 0)
539             ow = minw;
540         else
541             ow = 100;
542         if (minh > 0)
543             oh = minh;
544         else
545             oh = 100;
546         if (maxw > 0 && ow > maxw)
547             ow = maxw;
548         if (maxh > 0 && oh > maxh)
549             oh = maxh;
550
551         evas_object_move(object, ox, oy);
552         evas_object_resize(object, ow, oh);
553         edje_object_calc_force(object);
554         edje_object_message_signal_process(object);
555         edje_object_part_geometry_get(object, "text_confinement", &px, &py, &pw, &ph);
556
557         t = py - oy;
558         b = (oh + oy) - (ph + py);
559
560         l = px - ox;
561         r = (ow + ox) - (pw + px);
562
563         desc->padding = LengthBox(t, r, b, l);
564     }
565 }
566
567 void RenderThemeEfl::applyPartDescriptionsFrom(const String& themePath)
568 {
569     EflUniquePtr<Evas_Object> temp = EflUniquePtr<Evas_Object>(edje_object_add(ecore_evas_get(canvas())));
570     _ASSERT_ON_RELEASE_RETURN(temp, "Could not create Edje object.");
571
572     for (size_t i = 0; i < FormTypeLast; i++) {
573         FormType type = static_cast<FormType>(i);
574         m_partDescs[i].type = type;
575         if (!setSourceGroupForEdjeObject(temp.get(), themePath, toEdjeGroup(type)))
576             applyPartDescriptionFallback(m_partDescs + i);
577         else
578             applyPartDescription(temp.get(), m_partDescs + i);
579     }
580 }
581
582 RenderThemeEfl::RenderThemeEfl(Page* page)
583     : RenderTheme()
584     , m_page(page)
585     , m_activeSelectionBackgroundColor(0, 0, 255)
586     , m_activeSelectionForegroundColor(Color::white)
587     , m_inactiveSelectionBackgroundColor(0, 0, 128)
588     , m_inactiveSelectionForegroundColor(200, 200, 200)
589     , m_focusRingColor(32, 32, 224, 224)
590     , m_sliderThumbColor(Color::darkGray)
591     , m_supportsSelectionForegroundColor(false)
592 {
593 }
594
595 RenderThemeEfl::~RenderThemeEfl()
596 {
597     clearThemePartCache();
598 }
599
600 static bool supportsFocus(ControlPart appearance)
601 {
602     switch (appearance) {
603     case PushButtonPart:
604     case ButtonPart:
605     case TextFieldPart:
606     case TextAreaPart:
607     case SearchFieldPart:
608     case MenulistPart:
609     case RadioPart:
610     case CheckboxPart:
611     case SliderVerticalPart:
612     case SliderHorizontalPart:
613         return true;
614     default:
615         return false;
616     }
617 }
618
619 bool RenderThemeEfl::supportsFocusRing(const RenderStyle& style) const
620 {
621     return supportsFocus(style.appearance());
622 }
623
624 bool RenderThemeEfl::controlSupportsTints(const RenderObject& object) const
625 {
626     return isEnabled(object);
627 }
628
629 int RenderThemeEfl::baselinePosition(const RenderObject& object) const
630 {
631     if (!is<RenderBox>(object))
632         return 0;
633
634     if (object.style().appearance() == CheckboxPart
635     ||  object.style().appearance() == RadioPart)
636         return downcast<RenderBox>(object).marginTop() + downcast<RenderBox>(object).height() - 3;
637
638     return RenderTheme::baselinePosition(object);
639 }
640
641 Color RenderThemeEfl::platformActiveSelectionBackgroundColor() const
642 {
643     loadThemeIfNeeded();
644     return m_activeSelectionBackgroundColor;
645 }
646
647 Color RenderThemeEfl::platformInactiveSelectionBackgroundColor() const
648 {
649     loadThemeIfNeeded();
650     return m_inactiveSelectionBackgroundColor;
651 }
652
653 Color RenderThemeEfl::platformActiveSelectionForegroundColor() const
654 {
655     loadThemeIfNeeded();
656     return m_activeSelectionForegroundColor;
657 }
658
659 Color RenderThemeEfl::platformInactiveSelectionForegroundColor() const
660 {
661     loadThemeIfNeeded();
662     return m_inactiveSelectionForegroundColor;
663 }
664
665 Color RenderThemeEfl::platformFocusRingColor() const
666 {
667     loadThemeIfNeeded();
668     return m_focusRingColor;
669 }
670
671 bool RenderThemeEfl::supportsSelectionForegroundColors() const
672 {
673     loadThemeIfNeeded();
674     return m_supportsSelectionForegroundColor;
675 }
676
677 bool RenderThemeEfl::paintSliderTrack(const RenderObject& object, const PaintInfo& info, const IntRect& rect)
678 {
679     if (object.style().appearance() == SliderHorizontalPart)
680         paintThemePart(object, SliderHorizontal, info, rect);
681     else
682         paintThemePart(object, SliderVertical, info, rect);
683
684 #if ENABLE(DATALIST_ELEMENT)
685     paintSliderTicks(object, info, rect);
686 #endif
687
688     return false;
689 }
690
691 void RenderThemeEfl::adjustSliderTrackStyle(StyleResolver&, RenderStyle& style, Element*) const
692 {
693     style.setBoxShadow(nullptr);
694 }
695
696 void RenderThemeEfl::adjustSliderThumbStyle(StyleResolver& styleResolver, RenderStyle& style, Element* element) const
697 {
698     RenderTheme::adjustSliderThumbStyle(styleResolver, style, element);
699     style.setBoxShadow(nullptr);
700 }
701
702 void RenderThemeEfl::adjustSliderThumbSize(RenderStyle& style, Element*) const
703 {
704     ControlPart part = style.appearance();
705     if (part == SliderThumbVerticalPart) {
706         style.setWidth(Length(sliderThumbHeight, Fixed));
707         style.setHeight(Length(sliderThumbWidth, Fixed));
708     } else if (part == SliderThumbHorizontalPart) {
709         style.setWidth(Length(sliderThumbWidth, Fixed));
710         style.setHeight(Length(sliderThumbHeight, Fixed));
711     }
712 }
713
714 #if ENABLE(DATALIST_ELEMENT)
715 IntSize RenderThemeEfl::sliderTickSize() const
716 {
717     return IntSize(1, 6);
718 }
719
720 int RenderThemeEfl::sliderTickOffsetFromTrackCenter() const
721 {
722     static const int sliderTickOffset = -12;
723
724     return sliderTickOffset;
725 }
726
727 LayoutUnit RenderThemeEfl::sliderTickSnappingThreshold() const
728 {
729     // The same threshold value as the Chromium port.
730     return 5;
731 }
732 #endif
733
734 bool RenderThemeEfl::supportsDataListUI(const AtomicString& type) const
735 {
736 #if ENABLE(DATALIST_ELEMENT)
737     // FIXME: We need to support other types.
738     return type == InputTypeNames::email()
739         || type == InputTypeNames::range()
740         || type == InputTypeNames::search()
741         || type == InputTypeNames::url();
742 #else
743     UNUSED_PARAM(type);
744     return false;
745 #endif
746 }
747
748 bool RenderThemeEfl::paintSliderThumb(const RenderObject& object, const PaintInfo& info, const IntRect& rect)
749 {
750     if (object.style().appearance() == SliderThumbHorizontalPart)
751         paintThemePart(object, SliderThumbHorizontal, info, rect);
752     else
753         paintThemePart(object, SliderThumbVertical, info, rect);
754
755     return false;
756 }
757
758 void RenderThemeEfl::adjustCheckboxStyle(StyleResolver& styleResolver, RenderStyle& style, Element* element) const
759 {
760     if (!m_page && element && element->document().page()) {
761         static_cast<RenderThemeEfl&>(element->document().page()->theme()).adjustCheckboxStyle(styleResolver, style, element);
762         return;
763     }
764
765     adjustSizeConstraints(style, CheckBox);
766
767     style.resetBorder();
768
769     const ThemePartDesc* desc = m_partDescs + (size_t)CheckBox;
770     if (style.width().value() < desc->min.width().value())
771         style.setWidth(desc->min.width());
772     if (style.height().value() < desc->min.height().value())
773         style.setHeight(desc->min.height());
774 }
775
776 bool RenderThemeEfl::paintCheckbox(const RenderObject& object, const PaintInfo& info, const IntRect& rect)
777 {
778     return paintThemePart(object, CheckBox, info, rect);
779 }
780
781 void RenderThemeEfl::adjustRadioStyle(StyleResolver& styleResolver, RenderStyle& style, Element* element) const
782 {
783     if (!m_page && element && element->document().page()) {
784         static_cast<RenderThemeEfl&>(element->document().page()->theme()).adjustRadioStyle(styleResolver, style, element);
785         return;
786     }
787
788     adjustSizeConstraints(style, RadioButton);
789
790     style.resetBorder();
791
792     const ThemePartDesc* desc = m_partDescs + (size_t)RadioButton;
793     if (style.width().value() < desc->min.width().value())
794         style.setWidth(desc->min.width());
795     if (style.height().value() < desc->min.height().value())
796         style.setHeight(desc->min.height());
797 }
798
799 bool RenderThemeEfl::paintRadio(const RenderObject& object, const PaintInfo& info, const IntRect& rect)
800 {
801     return paintThemePart(object, RadioButton, info, rect);
802 }
803
804 void RenderThemeEfl::adjustButtonStyle(StyleResolver& styleResolver, RenderStyle& style, Element* element) const
805 {
806     if (!m_page && element && element->document().page()) {
807         static_cast<RenderThemeEfl&>(element->document().page()->theme()).adjustButtonStyle(styleResolver, style, element);
808         return;
809     }
810
811     // adjustSizeConstrains can make SquareButtonPart's size wrong (by adjusting paddings), so call it only for PushButtonPart and ButtonPart
812     if (style.appearance() == PushButtonPart || style.appearance() == ButtonPart)
813         adjustSizeConstraints(style, Button);
814 }
815
816 bool RenderThemeEfl::paintButton(const RenderObject& object, const PaintInfo& info, const IntRect& rect)
817 {
818     return paintThemePart(object, Button, info, rect);
819 }
820
821 void RenderThemeEfl::adjustMenuListStyle(StyleResolver& styleResolver, RenderStyle& style, Element* element) const
822 {
823     if (!m_page && element && element->document().page()) {
824         static_cast<RenderThemeEfl&>(element->document().page()->theme()).adjustMenuListStyle(styleResolver, style, element);
825         return;
826     }
827     adjustSizeConstraints(style, ComboBox);
828     style.resetBorder();
829     style.setWhiteSpace(PRE);
830
831     style.setLineHeight(RenderStyle::initialLineHeight());
832 }
833
834 bool RenderThemeEfl::paintMenuList(const RenderObject& object, const PaintInfo& info, const FloatRect& rect)
835 {
836     return paintThemePart(object, ComboBox, info, IntRect(rect));
837 }
838
839 void RenderThemeEfl::adjustMenuListButtonStyle(StyleResolver& styleResolver, RenderStyle& style, Element* element) const
840 {
841     // Height is locked to auto if height is not specified.
842     style.setHeight(Length(Auto));
843
844     // The <select> box must be at least 12px high for the button to render the text inside the box without clipping.
845     const int dropDownBoxMinHeight = 12;
846
847     // Calculate min-height of the <select> element.
848     int minHeight = style.fontMetrics().height();
849     minHeight = std::max(minHeight, dropDownBoxMinHeight);
850     style.setMinHeight(Length(minHeight, Fixed));
851
852     adjustMenuListStyle(styleResolver, style, element);
853 }
854
855 bool RenderThemeEfl::paintMenuListButtonDecorations(const RenderObject& object, const PaintInfo& info, const FloatRect& rect)
856 {
857     return paintMenuList(object, info, rect);
858 }
859
860 void RenderThemeEfl::adjustTextFieldStyle(StyleResolver& styleResolver, RenderStyle& style, Element* element) const
861 {
862     if (!m_page && element && element->document().page()) {
863         static_cast<RenderThemeEfl&>(element->document().page()->theme()).adjustTextFieldStyle(styleResolver, style, element);
864         return;
865     }
866     adjustSizeConstraints(style, TextField);
867     style.resetBorder();
868 }
869
870 bool RenderThemeEfl::paintTextField(const RenderObject& object, const PaintInfo& info, const FloatRect& rect)
871 {
872     return paintThemePart(object, TextField, info, IntRect(rect));
873 }
874
875 void RenderThemeEfl::adjustTextAreaStyle(StyleResolver&, RenderStyle&, Element*) const
876 {
877 }
878
879 bool RenderThemeEfl::paintTextArea(const RenderObject& object, const PaintInfo& info, const FloatRect& rect)
880 {
881     return paintTextField(object, info, rect);
882 }
883
884 void RenderThemeEfl::adjustSearchFieldResultsButtonStyle(StyleResolver& styleResolver, RenderStyle& style, Element* element) const
885 {
886     if (!m_page && element && element->document().page()) {
887         static_cast<RenderThemeEfl&>(element->document().page()->theme()).adjustSearchFieldResultsButtonStyle(styleResolver, style, element);
888         return;
889     }
890     adjustSizeConstraints(style, SearchFieldResultsButton);
891     style.resetBorder();
892     style.setWhiteSpace(PRE);
893
894     float fontScale = style.fontSize() / defaultFontSize;
895     int decorationSize = lroundf(std::min(std::max(minSearchDecorationButtonSize, defaultFontSize * fontScale), maxSearchDecorationButtonSize));
896
897     style.setWidth(Length(decorationSize + searchFieldDecorationButtonOffset, Fixed));
898     style.setHeight(Length(decorationSize, Fixed));
899 }
900
901 bool RenderThemeEfl::paintSearchFieldResultsButton(const RenderObject& object, const PaintInfo& info, const IntRect& rect)
902 {
903     return paintThemePart(object, SearchFieldResultsButton, info, rect);
904 }
905
906 void RenderThemeEfl::adjustSearchFieldResultsDecorationPartStyle(StyleResolver& styleResolver, RenderStyle& style, Element* element) const
907 {
908     if (!m_page && element && element->document().page()) {
909         static_cast<RenderThemeEfl&>(element->document().page()->theme()).adjustSearchFieldResultsDecorationPartStyle(styleResolver, style, element);
910         return;
911     }
912     adjustSizeConstraints(style, SearchFieldResultsDecoration);
913     style.resetBorder();
914     style.setWhiteSpace(PRE);
915
916     float fontScale = style.fontSize() / defaultFontSize;
917     int decorationSize = lroundf(std::min(std::max(minSearchDecorationButtonSize, defaultFontSize * fontScale), maxSearchDecorationButtonSize));
918
919     style.setWidth(Length(decorationSize + searchFieldDecorationButtonOffset, Fixed));
920     style.setHeight(Length(decorationSize, Fixed));
921 }
922
923 bool RenderThemeEfl::paintSearchFieldResultsDecorationPart(const RenderObject& object, const PaintInfo& info, const IntRect& rect)
924 {
925     return paintThemePart(object, SearchFieldResultsDecoration, info, rect);
926 }
927
928 void RenderThemeEfl::adjustSearchFieldCancelButtonStyle(StyleResolver& styleResolver, RenderStyle& style, Element* element) const
929 {
930     if (!m_page && element && element->document().page()) {
931         static_cast<RenderThemeEfl&>(element->document().page()->theme()).adjustSearchFieldCancelButtonStyle(styleResolver, style, element);
932         return;
933     }
934     adjustSizeConstraints(style, SearchFieldCancelButton);
935     style.resetBorder();
936     style.setWhiteSpace(PRE);
937
938     // Logic taken from RenderThemeChromium.cpp.
939     // Scale the button size based on the font size.
940     float fontScale = style.fontSize() / defaultFontSize;
941     int cancelButtonSize = lroundf(std::min(std::max(minCancelButtonSize, defaultFontSize * fontScale), maxCancelButtonSize));
942
943     style.setWidth(Length(cancelButtonSize, Fixed));
944     style.setHeight(Length(cancelButtonSize, Fixed));
945 }
946
947 bool RenderThemeEfl::paintSearchFieldCancelButton(const RenderObject& object, const PaintInfo& info, const IntRect& rect)
948 {
949     return paintThemePart(object, SearchFieldCancelButton, info, rect);
950 }
951
952 void RenderThemeEfl::adjustSearchFieldStyle(StyleResolver& styleResolver, RenderStyle& style, Element* element) const
953 {
954     if (!m_page && element && element->document().page()) {
955         static_cast<RenderThemeEfl&>(element->document().page()->theme()).adjustSearchFieldStyle(styleResolver, style, element);
956         return;
957     }
958     adjustSizeConstraints(style, SearchField);
959     style.resetBorder();
960     style.setWhiteSpace(PRE);
961 }
962
963 bool RenderThemeEfl::paintSearchField(const RenderObject& object, const PaintInfo& info, const IntRect& rect)
964 {
965     return paintThemePart(object, SearchField, info, rect);
966 }
967
968 void RenderThemeEfl::adjustInnerSpinButtonStyle(StyleResolver& styleResolver, RenderStyle& style, Element* element) const
969 {
970     if (!m_page && element && element->document().page()) {
971         static_cast<RenderThemeEfl&>(element->document().page()->theme()).adjustInnerSpinButtonStyle(styleResolver, style, element);
972         return;
973     }
974     adjustSizeConstraints(style, Spinner);
975 }
976
977 bool RenderThemeEfl::paintInnerSpinButton(const RenderObject& object, const PaintInfo& info, const IntRect& rect)
978 {
979     return paintThemePart(object, Spinner, info, rect);
980 }
981
982 void RenderThemeEfl::setDefaultFontSize(int size)
983 {
984     defaultFontSize = size;
985 }
986
987 void RenderThemeEfl::updateCachedSystemFontDescription(CSSValueID, FontDescription& fontDescription) const
988 {
989     // It was called by RenderEmbeddedObject::paintReplaced to render alternative string.
990     // To avoid cairo_error while rendering, fontDescription should be passed.
991     fontDescription.setOneFamily("Sans");
992     fontDescription.setSpecifiedSize(defaultFontSize);
993     fontDescription.setIsAbsoluteSize(true);
994     fontDescription.setWeight(FontWeightNormal);
995     fontDescription.setItalic(FontItalicOff);
996 }
997
998 void RenderThemeEfl::adjustProgressBarStyle(StyleResolver&, RenderStyle& style, Element*) const
999 {
1000     style.setBoxShadow(nullptr);
1001 }
1002
1003 double RenderThemeEfl::animationRepeatIntervalForProgressBar(RenderProgress&) const
1004 {
1005     return progressAnimationInterval;
1006 }
1007
1008 double RenderThemeEfl::animationDurationForProgressBar(RenderProgress&) const
1009 {
1010     return progressAnimationInterval * progressAnimationFrames * 2; // "2" for back and forth;
1011 }
1012
1013 bool RenderThemeEfl::paintProgressBar(const RenderObject& object, const PaintInfo& info, const IntRect& rect)
1014 {
1015     if (!object.isProgress())
1016         return true;
1017
1018     return paintThemePart(object, ProgressBar, info, rect);
1019 }
1020
1021 #if ENABLE(VIDEO)
1022 String RenderThemeEfl::mediaControlsStyleSheet()
1023 {
1024     return ASCIILiteral(mediaControlsBaseUserAgentStyleSheet);
1025 }
1026
1027 String RenderThemeEfl::mediaControlsScript()
1028 {
1029     StringBuilder scriptBuilder;
1030     scriptBuilder.append(mediaControlsLocalizedStringsJavaScript, sizeof(mediaControlsLocalizedStringsJavaScript));
1031     scriptBuilder.append(mediaControlsBaseJavaScript, sizeof(mediaControlsBaseJavaScript));
1032     return scriptBuilder.toString();
1033 }
1034 #endif
1035
1036 #undef _ASSERT_ON_RELEASE_RETURN
1037 #undef _ASSERT_ON_RELEASE_RETURN_VAL
1038
1039 }