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