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