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