Use is<>() / downcast<>() for all remaining RenderObject subclasses
[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 PassOwnPtr<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     OwnPtr<ThemePartCacheEntry> entry = adoptPtr(new 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.release();
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     void* data;
226     Eina_List* node;
227     Eina_List* reusableNode = 0;
228
229     // Look for the item in the cache.
230     EINA_LIST_FOREACH(m_partCache, node, data) {
231         ThemePartCacheEntry* cachedEntry = static_cast<ThemePartCacheEntry*>(data);
232         if (cachedEntry->size == size) {
233             if (cachedEntry->type == type) {
234                 // Found the right item, move it to the head of the list
235                 // and return it.
236                 m_partCache = eina_list_promote_list(m_partCache, node);
237                 return cachedEntry;
238             }
239             // We reuse in priority the last item in the list that has
240             // the requested size.
241             reusableNode = node;
242         }
243     }
244
245     if (eina_list_count(m_partCache) < RENDER_THEME_EFL_PART_CACHE_MAX) {
246         ThemePartCacheEntry* entry = ThemePartCacheEntry::create(themePath(), type, size).leakPtr();
247         if (entry)
248             m_partCache = eina_list_prepend(m_partCache, entry);
249
250         return entry;
251     }
252
253     // The cache is full, reuse the last item we found that had the
254     // requested size to avoid resizing. If there was none, reuse
255     // the last item of the list.
256     if (!reusableNode)
257         reusableNode = eina_list_last(m_partCache);
258
259     ThemePartCacheEntry* reusedEntry = static_cast<ThemePartCacheEntry*>(eina_list_data_get(reusableNode));
260     ASSERT(reusedEntry);
261     reusedEntry->reuse(themePath(), type, size);
262     m_partCache = eina_list_promote_list(m_partCache, reusableNode);
263
264     return reusedEntry;
265 }
266
267 void RenderThemeEfl::clearThemePartCache()
268 {
269     void* data;
270     EINA_LIST_FREE(m_partCache, data)
271         delete static_cast<ThemePartCacheEntry*>(data);
272
273 }
274
275 void RenderThemeEfl::applyEdjeStateFromForm(Evas_Object* object, const ControlStates* states, bool haveBackground)
276 {
277     const char *signals[] = { // keep in sync with WebCore/platform/ThemeTypes.h
278         "hovered",
279         "pressed",
280         "focused",
281         "enabled",
282         "checked",
283         "read-only",
284         "default",
285         "window-inactive",
286         "indeterminate",
287         "spinup"
288     };
289
290     edje_object_signal_emit(object, "reset", "");
291
292     for (size_t i = 0; i < WTF_ARRAY_LENGTH(signals); ++i) {
293         if (states->states() & (1 << i))
294             edje_object_signal_emit(object, signals[i], "");
295     }
296
297     if (haveBackground)
298         edje_object_signal_emit(object, "styled", "");
299 }
300
301 void RenderThemeEfl::applyEdjeRTLState(Evas_Object* edje, const RenderObject& object, FormType type, const IntRect& rect)
302 {
303     if (type == SliderVertical || type == SliderHorizontal) {
304         if (!is<RenderSlider>(object))
305             return; // probably have -webkit-appearance: slider..
306
307         HTMLInputElement& input = downcast<RenderSlider>(object).element();
308         double valueRange = input.maximum() - input.minimum();
309
310         OwnPtr<Edje_Message_Float_Set> msg = adoptPtr(static_cast<Edje_Message_Float_Set*>(::operator new (sizeof(Edje_Message_Float_Set) + sizeof(double))));
311         msg->count = 2;
312
313         // The first parameter of the message decides if the progress bar
314         // grows from the end of the slider or from the beginning. On vertical
315         // sliders, it should always be the same and will not be affected by
316         // text direction settings.
317         if (object.style().direction() == RTL || type == SliderVertical)
318             msg->val[0] = 1;
319         else
320             msg->val[0] = 0;
321
322         msg->val[1] = (input.valueAsNumber() - input.minimum()) / valueRange;
323         edje_object_message_send(edje, EDJE_MESSAGE_FLOAT_SET, 0, msg.get());
324     } else if (type == ProgressBar) {
325         const auto& renderProgress = downcast<RenderProgress>(object);
326
327         int max = rect.width();
328         double value = renderProgress.position();
329
330         OwnPtr<Edje_Message_Float_Set> msg = adoptPtr(static_cast<Edje_Message_Float_Set*>(::operator new (sizeof(Edje_Message_Float_Set) + sizeof(double))));
331         msg->count = 2;
332
333         if (object.style().direction() == RTL)
334             msg->val[0] = (1.0 - value) * max;
335         else
336             msg->val[0] = 0;
337         msg->val[1] = value;
338         edje_object_message_send(edje, EDJE_MESSAGE_FLOAT_SET, 0, msg.get());
339     }
340 }
341
342 bool RenderThemeEfl::isControlStyled(const RenderStyle& style, const BorderData& border, const FillLayer& background, const Color& backgroundColor) const
343 {
344     return RenderTheme::isControlStyled(style, border, background, backgroundColor) || style.appearance() == MenulistButtonPart;
345 }
346
347 bool RenderThemeEfl::paintThemePart(const RenderObject& object, FormType type, const PaintInfo& info, const IntRect& rect)
348 {
349     loadThemeIfNeeded();
350     _ASSERT_ON_RELEASE_RETURN_VAL(edje(), false, "Could not paint native HTML part due to missing theme.");
351
352     ThemePartCacheEntry* entry = getThemePartFromCache(type, rect.size());
353     if (!entry)
354         return false;
355
356     bool haveBackgroundColor = isControlStyled(object.style(), object.style().border(), *object.style().backgroundLayers(), Color::white);
357     ControlStates states(extractControlStatesForRenderer(object));
358     applyEdjeStateFromForm(entry->edje(), &states, haveBackgroundColor);
359
360     applyEdjeRTLState(entry->edje(), object, type, rect);
361
362     edje_object_calc_force(entry->edje());
363     edje_object_message_signal_process(entry->edje());
364     evas_render(ecore_evas_get(entry->canvas()));
365
366     cairo_t* cairo = info.context->platformContext()->cr();
367     ASSERT(cairo);
368
369     cairo_save(cairo);
370     cairo_set_source_surface(cairo, entry->surface(), rect.x(), rect.y());
371     cairo_paint_with_alpha(cairo, 1.0);
372     cairo_restore(cairo);
373
374     return false;
375 }
376
377 PassRefPtr<RenderTheme> RenderThemeEfl::create(Page* page)
378 {
379     return adoptRef(new RenderThemeEfl(page));
380 }
381
382 PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page)
383 {
384     if (page)
385         return RenderThemeEfl::create(page);
386
387     static RenderTheme* fallback = RenderThemeEfl::create(0).leakRef();
388     return fallback;
389 }
390
391 static void applyColorCallback(void* data, Evas_Object*, const char* /* signal */, const char* colorClass)
392 {
393     RenderThemeEfl* that = static_cast<RenderThemeEfl*>(data);
394     that->setColorFromThemeClass(colorClass);
395     that->platformColorsDidChange(); // Triggers relayout.
396 }
397
398 static bool fillColorsFromEdjeClass(Evas_Object* o, const char* colorClass, Color* color1, Color* color2 = 0, Color* color3 = 0)
399 {
400     int r1, g1, b1, a1;
401     int r2, g2, b2, a2;
402     int r3, g3, b3, a3;
403
404     if (!edje_object_color_class_get(o, colorClass, &r1, &g1, &b1, &a1, &r2, &g2, &b2, &a2, &r3, &g3, &b3, &a3))
405         return false;
406
407     if (color1)
408         color1->setRGB(makeRGBA(r1, g1, b1, a1));
409     if (color2)
410         color2->setRGB(makeRGBA(r2, g2, b2, a2));
411     if (color3)
412         color3->setRGB(makeRGBA(r3, g3, b3, a3));
413
414     return true;
415 }
416
417 void RenderThemeEfl::setColorFromThemeClass(const char* colorClass)
418 {
419     ASSERT(edje());
420
421     if (!strcmp("webkit/selection/foreground", colorClass))
422         m_supportsSelectionForegroundColor = fillColorsFromEdjeClass(edje(), colorClass, &m_activeSelectionForegroundColor, &m_inactiveSelectionForegroundColor);
423     else if (!strcmp("webkit/selection/background", colorClass))
424         fillColorsFromEdjeClass(edje(), colorClass, &m_activeSelectionBackgroundColor, &m_inactiveSelectionBackgroundColor);
425     else if (!strcmp("webkit/focus_ring", colorClass)) {
426         if (!fillColorsFromEdjeClass(edje(), colorClass, &m_focusRingColor))
427             return;
428
429         // platformFocusRingColor() is only used for the default theme (without page)
430         // The following is ugly, but no other way to do it unless we change it to use page themes as much as possible.
431         RenderTheme::setCustomFocusRingColor(m_focusRingColor);
432     }
433 }
434
435 void RenderThemeEfl::setThemePath(const String& newThemePath)
436 {
437     if (newThemePath == m_themePath)
438         return;
439
440     if (newThemePath.isEmpty()) {
441         EINA_LOG_CRIT("No valid theme defined, things will not work properly.");
442         return;
443     }
444
445     String oldThemePath = m_themePath;
446     m_themePath = newThemePath;
447
448     // Keep the consistence by restoring the previous theme path
449     // if we cannot load the new one.
450     if (!loadTheme())
451         m_themePath = oldThemePath;
452 }
453
454 String RenderThemeEfl::themePath() const
455 {
456 #ifndef NDEBUG
457     if (edje()) {
458         const char* path;
459         edje_object_file_get(edje(), &path, 0);
460         ASSERT(m_themePath == path);
461     }
462 #endif
463     return m_themePath;
464 }
465
466 bool RenderThemeEfl::loadTheme()
467 {
468     ASSERT(!m_themePath.isEmpty());
469
470     if (!canvas()) {
471         m_canvas = EflUniquePtr<Ecore_Evas>(ecore_evas_buffer_new(1, 1));
472         _ASSERT_ON_RELEASE_RETURN_VAL(canvas(), false,
473                 "Could not create canvas required by theme, things will not work properly.");
474     }
475
476     EflUniquePtr<Evas_Object> o = EflUniquePtr<Evas_Object>(edje_object_add(ecore_evas_get(canvas())));
477     _ASSERT_ON_RELEASE_RETURN_VAL(o, false, "Could not create new base Edje object.");
478
479     if (!setSourceGroupForEdjeObject(o.get(), m_themePath, "webkit/base"))
480         return false; // Keep current theme.
481
482     // Invalidate existing theme part cache.
483     if (edje())
484         clearThemePartCache();
485
486     // Set new loaded theme, and apply it.
487     m_edje = WTF::move(o);
488
489     const char* thickness = edje_object_data_get(m_edje.get(), "scrollbar.thickness");
490     if (thickness && !Settings::mockScrollbarsEnabled())
491         static_cast<ScrollbarThemeEfl*>(ScrollbarTheme::theme())->setScrollbarThickness(atoi(thickness));
492
493     edje_object_signal_callback_add(edje(), "color_class,set", "webkit/selection/foreground", applyColorCallback, this);
494     edje_object_signal_callback_add(edje(), "color_class,set", "webkit/selection/background", applyColorCallback, this);
495     edje_object_signal_callback_add(edje(), "color_class,set", "webkit/focus_ring", applyColorCallback, this);
496
497     applyPartDescriptionsFrom(m_themePath);
498
499     setColorFromThemeClass("webkit/selection/foreground");
500     setColorFromThemeClass("webkit/selection/background");
501     setColorFromThemeClass("webkit/focus_ring");
502
503     platformColorsDidChange(); // Schedules a relayout, do last.
504
505     return true;
506 }
507
508 void RenderThemeEfl::applyPartDescriptionFallback(ThemePartDesc* desc)
509 {
510     desc->min.setWidth(Length(0, Fixed));
511     desc->min.setHeight(Length(0, Fixed));
512
513     desc->max.setWidth(Length(0, Fixed));
514     desc->max.setHeight(Length(0, Fixed));
515
516     desc->padding = LengthBox(0, 0, 0, 0);
517 }
518
519 void RenderThemeEfl::applyPartDescription(Evas_Object* object, ThemePartDesc* desc)
520 {
521     Evas_Coord minw, minh, maxw, maxh;
522
523     edje_object_size_min_get(object, &minw, &minh);
524     if (!minw && !minh)
525         edje_object_size_min_calc(object, &minw, &minh);
526
527     desc->min.setWidth(Length(minw, Fixed));
528     desc->min.setHeight(Length(minh, Fixed));
529
530     edje_object_size_max_get(object, &maxw, &maxh);
531     desc->max.setWidth(Length(maxw, Fixed));
532     desc->max.setHeight(Length(maxh, Fixed));
533
534     if (!edje_object_part_exists(object, "text_confinement"))
535         desc->padding = LengthBox(0, 0, 0, 0);
536     else {
537         Evas_Coord px, py, pw, ph;
538         Evas_Coord ox = 0, oy = 0, ow = 0, oh = 0;
539         int t, r, b, l;
540
541         if (minw > 0)
542             ow = minw;
543         else
544             ow = 100;
545         if (minh > 0)
546             oh = minh;
547         else
548             oh = 100;
549         if (maxw > 0 && ow > maxw)
550             ow = maxw;
551         if (maxh > 0 && oh > maxh)
552             oh = maxh;
553
554         evas_object_move(object, ox, oy);
555         evas_object_resize(object, ow, oh);
556         edje_object_calc_force(object);
557         edje_object_message_signal_process(object);
558         edje_object_part_geometry_get(object, "text_confinement", &px, &py, &pw, &ph);
559
560         t = py - oy;
561         b = (oh + oy) - (ph + py);
562
563         l = px - ox;
564         r = (ow + ox) - (pw + px);
565
566         desc->padding = LengthBox(t, r, b, l);
567     }
568 }
569
570 void RenderThemeEfl::applyPartDescriptionsFrom(const String& themePath)
571 {
572     EflUniquePtr<Evas_Object> temp = EflUniquePtr<Evas_Object>(edje_object_add(ecore_evas_get(canvas())));
573     _ASSERT_ON_RELEASE_RETURN(temp, "Could not create Edje object.");
574
575     for (size_t i = 0; i < FormTypeLast; i++) {
576         FormType type = static_cast<FormType>(i);
577         m_partDescs[i].type = type;
578         if (!setSourceGroupForEdjeObject(temp.get(), themePath, toEdjeGroup(type)))
579             applyPartDescriptionFallback(m_partDescs + i);
580         else
581             applyPartDescription(temp.get(), m_partDescs + i);
582     }
583 }
584
585 RenderThemeEfl::RenderThemeEfl(Page* page)
586     : RenderTheme()
587     , m_page(page)
588     , m_activeSelectionBackgroundColor(0, 0, 255)
589     , m_activeSelectionForegroundColor(Color::white)
590     , m_inactiveSelectionBackgroundColor(0, 0, 128)
591     , m_inactiveSelectionForegroundColor(200, 200, 200)
592     , m_focusRingColor(32, 32, 224, 224)
593     , m_sliderThumbColor(Color::darkGray)
594     , m_supportsSelectionForegroundColor(false)
595     , m_partCache(0)
596 {
597 }
598
599 RenderThemeEfl::~RenderThemeEfl()
600 {
601     clearThemePartCache();
602 }
603
604 static bool supportsFocus(ControlPart appearance)
605 {
606     switch (appearance) {
607     case PushButtonPart:
608     case ButtonPart:
609     case TextFieldPart:
610     case TextAreaPart:
611     case SearchFieldPart:
612     case MenulistPart:
613     case RadioPart:
614     case CheckboxPart:
615     case SliderVerticalPart:
616     case SliderHorizontalPart:
617         return true;
618     default:
619         return false;
620     }
621 }
622
623 bool RenderThemeEfl::supportsFocusRing(const RenderStyle& style) const
624 {
625     return supportsFocus(style.appearance());
626 }
627
628 bool RenderThemeEfl::controlSupportsTints(const RenderObject& object) const
629 {
630     return isEnabled(object);
631 }
632
633 int RenderThemeEfl::baselinePosition(const RenderObject& object) const
634 {
635     if (!is<RenderBox>(object))
636         return 0;
637
638     if (object.style().appearance() == CheckboxPart
639     ||  object.style().appearance() == RadioPart)
640         return downcast<RenderBox>(object).marginTop() + downcast<RenderBox>(object).height() - 3;
641
642     return RenderTheme::baselinePosition(object);
643 }
644
645 Color RenderThemeEfl::platformActiveSelectionBackgroundColor() const
646 {
647     loadThemeIfNeeded();
648     return m_activeSelectionBackgroundColor;
649 }
650
651 Color RenderThemeEfl::platformInactiveSelectionBackgroundColor() const
652 {
653     loadThemeIfNeeded();
654     return m_inactiveSelectionBackgroundColor;
655 }
656
657 Color RenderThemeEfl::platformActiveSelectionForegroundColor() const
658 {
659     loadThemeIfNeeded();
660     return m_activeSelectionForegroundColor;
661 }
662
663 Color RenderThemeEfl::platformInactiveSelectionForegroundColor() const
664 {
665     loadThemeIfNeeded();
666     return m_inactiveSelectionForegroundColor;
667 }
668
669 Color RenderThemeEfl::platformFocusRingColor() const
670 {
671     loadThemeIfNeeded();
672     return m_focusRingColor;
673 }
674
675 bool RenderThemeEfl::supportsSelectionForegroundColors() const
676 {
677     loadThemeIfNeeded();
678     return m_supportsSelectionForegroundColor;
679 }
680
681 bool RenderThemeEfl::paintSliderTrack(const RenderObject& object, const PaintInfo& info, const IntRect& rect)
682 {
683     if (object.style().appearance() == SliderHorizontalPart)
684         paintThemePart(object, SliderHorizontal, info, rect);
685     else
686         paintThemePart(object, SliderVertical, info, rect);
687
688 #if ENABLE(DATALIST_ELEMENT)
689     paintSliderTicks(object, info, rect);
690 #endif
691
692     return false;
693 }
694
695 void RenderThemeEfl::adjustSliderTrackStyle(StyleResolver&, RenderStyle& style, Element*) const
696 {
697     style.setBoxShadow(nullptr);
698 }
699
700 void RenderThemeEfl::adjustSliderThumbStyle(StyleResolver& styleResolver, RenderStyle& style, Element* element) const
701 {
702     RenderTheme::adjustSliderThumbStyle(styleResolver, style, element);
703     style.setBoxShadow(nullptr);
704 }
705
706 void RenderThemeEfl::adjustSliderThumbSize(RenderStyle& style, Element*) const
707 {
708     ControlPart part = style.appearance();
709     if (part == SliderThumbVerticalPart) {
710         style.setWidth(Length(sliderThumbHeight, Fixed));
711         style.setHeight(Length(sliderThumbWidth, Fixed));
712     } else if (part == SliderThumbHorizontalPart) {
713         style.setWidth(Length(sliderThumbWidth, Fixed));
714         style.setHeight(Length(sliderThumbHeight, Fixed));
715     }
716 }
717
718 #if ENABLE(DATALIST_ELEMENT)
719 IntSize RenderThemeEfl::sliderTickSize() const
720 {
721     return IntSize(1, 6);
722 }
723
724 int RenderThemeEfl::sliderTickOffsetFromTrackCenter() const
725 {
726     static const int sliderTickOffset = -12;
727
728     return sliderTickOffset;
729 }
730
731 LayoutUnit RenderThemeEfl::sliderTickSnappingThreshold() const
732 {
733     // The same threshold value as the Chromium port.
734     return 5;
735 }
736 #endif
737
738 bool RenderThemeEfl::supportsDataListUI(const AtomicString& type) const
739 {
740 #if ENABLE(DATALIST_ELEMENT)
741     // FIXME: We need to support other types.
742     return type == InputTypeNames::email()
743         || type == InputTypeNames::range()
744         || type == InputTypeNames::search()
745         || type == InputTypeNames::url();
746 #else
747     UNUSED_PARAM(type);
748     return false;
749 #endif
750 }
751
752 bool RenderThemeEfl::paintSliderThumb(const RenderObject& object, const PaintInfo& info, const IntRect& rect)
753 {
754     if (object.style().appearance() == SliderThumbHorizontalPart)
755         paintThemePart(object, SliderThumbHorizontal, info, rect);
756     else
757         paintThemePart(object, SliderThumbVertical, info, rect);
758
759     return false;
760 }
761
762 void RenderThemeEfl::adjustCheckboxStyle(StyleResolver& styleResolver, RenderStyle& style, Element* element) const
763 {
764     if (!m_page && element && element->document().page()) {
765         static_cast<RenderThemeEfl&>(element->document().page()->theme()).adjustCheckboxStyle(styleResolver, style, element);
766         return;
767     }
768
769     adjustSizeConstraints(style, CheckBox);
770
771     style.resetBorder();
772
773     const ThemePartDesc* desc = m_partDescs + (size_t)CheckBox;
774     if (style.width().value() < desc->min.width().value())
775         style.setWidth(desc->min.width());
776     if (style.height().value() < desc->min.height().value())
777         style.setHeight(desc->min.height());
778 }
779
780 bool RenderThemeEfl::paintCheckbox(const RenderObject& object, const PaintInfo& info, const IntRect& rect)
781 {
782     return paintThemePart(object, CheckBox, info, rect);
783 }
784
785 void RenderThemeEfl::adjustRadioStyle(StyleResolver& styleResolver, RenderStyle& style, Element* element) const
786 {
787     if (!m_page && element && element->document().page()) {
788         static_cast<RenderThemeEfl&>(element->document().page()->theme()).adjustRadioStyle(styleResolver, style, element);
789         return;
790     }
791
792     adjustSizeConstraints(style, RadioButton);
793
794     style.resetBorder();
795
796     const ThemePartDesc* desc = m_partDescs + (size_t)RadioButton;
797     if (style.width().value() < desc->min.width().value())
798         style.setWidth(desc->min.width());
799     if (style.height().value() < desc->min.height().value())
800         style.setHeight(desc->min.height());
801 }
802
803 bool RenderThemeEfl::paintRadio(const RenderObject& object, const PaintInfo& info, const IntRect& rect)
804 {
805     return paintThemePart(object, RadioButton, info, rect);
806 }
807
808 void RenderThemeEfl::adjustButtonStyle(StyleResolver& styleResolver, RenderStyle& style, Element* element) const
809 {
810     if (!m_page && element && element->document().page()) {
811         static_cast<RenderThemeEfl&>(element->document().page()->theme()).adjustButtonStyle(styleResolver, style, element);
812         return;
813     }
814
815     // adjustSizeConstrains can make SquareButtonPart's size wrong (by adjusting paddings), so call it only for PushButtonPart and ButtonPart
816     if (style.appearance() == PushButtonPart || style.appearance() == ButtonPart)
817         adjustSizeConstraints(style, Button);
818 }
819
820 bool RenderThemeEfl::paintButton(const RenderObject& object, const PaintInfo& info, const IntRect& rect)
821 {
822     return paintThemePart(object, Button, info, rect);
823 }
824
825 void RenderThemeEfl::adjustMenuListStyle(StyleResolver& styleResolver, RenderStyle& style, Element* element) const
826 {
827     if (!m_page && element && element->document().page()) {
828         static_cast<RenderThemeEfl&>(element->document().page()->theme()).adjustMenuListStyle(styleResolver, style, element);
829         return;
830     }
831     adjustSizeConstraints(style, ComboBox);
832     style.resetBorder();
833     style.setWhiteSpace(PRE);
834
835     style.setLineHeight(RenderStyle::initialLineHeight());
836 }
837
838 bool RenderThemeEfl::paintMenuList(const RenderObject& object, const PaintInfo& info, const FloatRect& rect)
839 {
840     return paintThemePart(object, ComboBox, info, IntRect(rect));
841 }
842
843 void RenderThemeEfl::adjustMenuListButtonStyle(StyleResolver& styleResolver, RenderStyle& style, Element* element) const
844 {
845     // Height is locked to auto if height is not specified.
846     style.setHeight(Length(Auto));
847
848     // The <select> box must be at least 12px high for the button to render the text inside the box without clipping.
849     const int dropDownBoxMinHeight = 12;
850
851     // Calculate min-height of the <select> element.
852     int minHeight = style.fontMetrics().height();
853     minHeight = std::max(minHeight, dropDownBoxMinHeight);
854     style.setMinHeight(Length(minHeight, Fixed));
855
856     adjustMenuListStyle(styleResolver, style, element);
857 }
858
859 bool RenderThemeEfl::paintMenuListButtonDecorations(const RenderObject& object, const PaintInfo& info, const FloatRect& rect)
860 {
861     return paintMenuList(object, info, rect);
862 }
863
864 void RenderThemeEfl::adjustTextFieldStyle(StyleResolver& styleResolver, RenderStyle& style, Element* element) const
865 {
866     if (!m_page && element && element->document().page()) {
867         static_cast<RenderThemeEfl&>(element->document().page()->theme()).adjustTextFieldStyle(styleResolver, style, element);
868         return;
869     }
870     adjustSizeConstraints(style, TextField);
871     style.resetBorder();
872 }
873
874 bool RenderThemeEfl::paintTextField(const RenderObject& object, const PaintInfo& info, const FloatRect& rect)
875 {
876     return paintThemePart(object, TextField, info, IntRect(rect));
877 }
878
879 void RenderThemeEfl::adjustTextAreaStyle(StyleResolver&, RenderStyle&, Element*) const
880 {
881 }
882
883 bool RenderThemeEfl::paintTextArea(const RenderObject& object, const PaintInfo& info, const FloatRect& rect)
884 {
885     return paintTextField(object, info, rect);
886 }
887
888 void RenderThemeEfl::adjustSearchFieldResultsButtonStyle(StyleResolver& styleResolver, RenderStyle& style, Element* element) const
889 {
890     if (!m_page && element && element->document().page()) {
891         static_cast<RenderThemeEfl&>(element->document().page()->theme()).adjustSearchFieldResultsButtonStyle(styleResolver, style, element);
892         return;
893     }
894     adjustSizeConstraints(style, SearchFieldResultsButton);
895     style.resetBorder();
896     style.setWhiteSpace(PRE);
897
898     float fontScale = style.fontSize() / defaultFontSize;
899     int decorationSize = lroundf(std::min(std::max(minSearchDecorationButtonSize, defaultFontSize * fontScale), maxSearchDecorationButtonSize));
900
901     style.setWidth(Length(decorationSize + searchFieldDecorationButtonOffset, Fixed));
902     style.setHeight(Length(decorationSize, Fixed));
903 }
904
905 bool RenderThemeEfl::paintSearchFieldResultsButton(const RenderObject& object, const PaintInfo& info, const IntRect& rect)
906 {
907     return paintThemePart(object, SearchFieldResultsButton, info, rect);
908 }
909
910 void RenderThemeEfl::adjustSearchFieldResultsDecorationPartStyle(StyleResolver& styleResolver, RenderStyle& style, Element* element) const
911 {
912     if (!m_page && element && element->document().page()) {
913         static_cast<RenderThemeEfl&>(element->document().page()->theme()).adjustSearchFieldResultsDecorationPartStyle(styleResolver, style, element);
914         return;
915     }
916     adjustSizeConstraints(style, SearchFieldResultsDecoration);
917     style.resetBorder();
918     style.setWhiteSpace(PRE);
919
920     float fontScale = style.fontSize() / defaultFontSize;
921     int decorationSize = lroundf(std::min(std::max(minSearchDecorationButtonSize, defaultFontSize * fontScale), maxSearchDecorationButtonSize));
922
923     style.setWidth(Length(decorationSize + searchFieldDecorationButtonOffset, Fixed));
924     style.setHeight(Length(decorationSize, Fixed));
925 }
926
927 bool RenderThemeEfl::paintSearchFieldResultsDecorationPart(const RenderObject& object, const PaintInfo& info, const IntRect& rect)
928 {
929     return paintThemePart(object, SearchFieldResultsDecoration, info, rect);
930 }
931
932 void RenderThemeEfl::adjustSearchFieldCancelButtonStyle(StyleResolver& styleResolver, RenderStyle& style, Element* element) const
933 {
934     if (!m_page && element && element->document().page()) {
935         static_cast<RenderThemeEfl&>(element->document().page()->theme()).adjustSearchFieldCancelButtonStyle(styleResolver, style, element);
936         return;
937     }
938     adjustSizeConstraints(style, SearchFieldCancelButton);
939     style.resetBorder();
940     style.setWhiteSpace(PRE);
941
942     // Logic taken from RenderThemeChromium.cpp.
943     // Scale the button size based on the font size.
944     float fontScale = style.fontSize() / defaultFontSize;
945     int cancelButtonSize = lroundf(std::min(std::max(minCancelButtonSize, defaultFontSize * fontScale), maxCancelButtonSize));
946
947     style.setWidth(Length(cancelButtonSize, Fixed));
948     style.setHeight(Length(cancelButtonSize, Fixed));
949 }
950
951 bool RenderThemeEfl::paintSearchFieldCancelButton(const RenderObject& object, const PaintInfo& info, const IntRect& rect)
952 {
953     return paintThemePart(object, SearchFieldCancelButton, info, rect);
954 }
955
956 void RenderThemeEfl::adjustSearchFieldStyle(StyleResolver& styleResolver, RenderStyle& style, Element* element) const
957 {
958     if (!m_page && element && element->document().page()) {
959         static_cast<RenderThemeEfl&>(element->document().page()->theme()).adjustSearchFieldStyle(styleResolver, style, element);
960         return;
961     }
962     adjustSizeConstraints(style, SearchField);
963     style.resetBorder();
964     style.setWhiteSpace(PRE);
965 }
966
967 bool RenderThemeEfl::paintSearchField(const RenderObject& object, const PaintInfo& info, const IntRect& rect)
968 {
969     return paintThemePart(object, SearchField, info, rect);
970 }
971
972 void RenderThemeEfl::adjustInnerSpinButtonStyle(StyleResolver& styleResolver, RenderStyle& style, Element* element) const
973 {
974     if (!m_page && element && element->document().page()) {
975         static_cast<RenderThemeEfl&>(element->document().page()->theme()).adjustInnerSpinButtonStyle(styleResolver, style, element);
976         return;
977     }
978     adjustSizeConstraints(style, Spinner);
979 }
980
981 bool RenderThemeEfl::paintInnerSpinButton(const RenderObject& object, const PaintInfo& info, const IntRect& rect)
982 {
983     return paintThemePart(object, Spinner, info, rect);
984 }
985
986 void RenderThemeEfl::setDefaultFontSize(int size)
987 {
988     defaultFontSize = size;
989 }
990
991 void RenderThemeEfl::systemFont(CSSValueID, FontDescription& fontDescription) const
992 {
993     // It was called by RenderEmbeddedObject::paintReplaced to render alternative string.
994     // To avoid cairo_error while rendering, fontDescription should be passed.
995     fontDescription.setOneFamily("Sans");
996     fontDescription.setSpecifiedSize(defaultFontSize);
997     fontDescription.setIsAbsoluteSize(true);
998     fontDescription.setGenericFamily(FontDescription::NoFamily);
999     fontDescription.setWeight(FontWeightNormal);
1000     fontDescription.setItalic(false);
1001 }
1002
1003 void RenderThemeEfl::adjustProgressBarStyle(StyleResolver&, RenderStyle& style, Element*) const
1004 {
1005     style.setBoxShadow(nullptr);
1006 }
1007
1008 double RenderThemeEfl::animationRepeatIntervalForProgressBar(RenderProgress&) const
1009 {
1010     return progressAnimationInterval;
1011 }
1012
1013 double RenderThemeEfl::animationDurationForProgressBar(RenderProgress&) const
1014 {
1015     return progressAnimationInterval * progressAnimationFrames * 2; // "2" for back and forth;
1016 }
1017
1018 bool RenderThemeEfl::paintProgressBar(const RenderObject& object, const PaintInfo& info, const IntRect& rect)
1019 {
1020     if (!object.isProgress())
1021         return true;
1022
1023     return paintThemePart(object, ProgressBar, info, rect);
1024 }
1025
1026 #if ENABLE(VIDEO)
1027 String RenderThemeEfl::mediaControlsStyleSheet()
1028 {
1029     return ASCIILiteral(mediaControlsBaseUserAgentStyleSheet);
1030 }
1031
1032 String RenderThemeEfl::mediaControlsScript()
1033 {
1034     StringBuilder scriptBuilder;
1035     scriptBuilder.append(mediaControlsLocalizedStringsJavaScript, sizeof(mediaControlsLocalizedStringsJavaScript));
1036     scriptBuilder.append(mediaControlsBaseJavaScript, sizeof(mediaControlsBaseJavaScript));
1037     return scriptBuilder.toString();
1038 }
1039 #endif
1040
1041 #undef _ASSERT_ON_RELEASE_RETURN
1042 #undef _ASSERT_ON_RELEASE_RETURN_VAL
1043
1044 }