CTTE: RenderSlider always has an HTMLInputElement.
[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 "FontDescription.h"
34 #include "GraphicsContext.h"
35 #include "HTMLInputElement.h"
36 #include "InputTypeNames.h"
37 #include "NotImplemented.h"
38 #include "Page.h"
39 #include "PaintInfo.h"
40 #include "PlatformContextCairo.h"
41 #include "RenderBox.h"
42 #include "RenderObject.h"
43 #include "RenderProgress.h"
44 #include "RenderSlider.h"
45 #include "UserAgentStyleSheets.h"
46
47 #include <Ecore_Evas.h>
48 #include <Edje.h>
49 #include <new>
50 #include <wtf/text/CString.h>
51 #include <wtf/text/WTFString.h>
52
53 #if ENABLE(VIDEO)
54 #include "HTMLMediaElement.h"
55 #include "HTMLNames.h"
56 #include "TimeRanges.h"
57 #endif
58
59 namespace WebCore {
60 #if ENABLE(VIDEO)
61 using namespace HTMLNames;
62 #endif
63
64 // TODO: change from object count to ecore_evas size (bytes)
65 // TODO: as objects are webpage/user defined and they can be very large.
66 #define RENDER_THEME_EFL_PART_CACHE_MAX 32
67
68 // Initialize default font size.
69 float RenderThemeEfl::defaultFontSize = 16.0f;
70
71 static const float minCancelButtonSize = 5;
72 static const float maxCancelButtonSize = 21;
73
74 static const float minSearchDecorationButtonSize = 1;
75 static const float maxSearchDecorationButtonSize = 15;
76 static const float searchFieldDecorationButtonOffset = 3;
77
78 // Constants for progress tag animation.
79 // These values have been copied from RenderThemeGtk.cpp
80 static const int progressAnimationFrames = 10;
81 static const double progressAnimationInterval = 0.125;
82
83 static const int sliderThumbWidth = 29;
84 static const int sliderThumbHeight = 11;
85 #if ENABLE(VIDEO)
86 static const int mediaSliderHeight = 14;
87 static const int mediaSliderThumbWidth = 12;
88 static const int mediaSliderThumbHeight = 12;
89 #endif
90
91 #define _ASSERT_ON_RELEASE_RETURN(o, fmt, ...) \
92     do { if (!o) { EINA_LOG_CRIT(fmt, ## __VA_ARGS__); ASSERT(o); return; } } while (0)
93 #define _ASSERT_ON_RELEASE_RETURN_VAL(o, val, fmt, ...) \
94     do { if (!o) { EINA_LOG_CRIT(fmt, ## __VA_ARGS__); ASSERT(o); return val; } } while (0)
95
96
97 static const char* toEdjeGroup(FormType type)
98 {
99     static const char* groups[] = {
100         "webkit/widget/button",
101         "webkit/widget/radio",
102         "webkit/widget/entry",
103         "webkit/widget/checkbox",
104         "webkit/widget/combo",
105 #if ENABLE(PROGRESS_ELEMENT)
106         "webkit/widget/progressbar",
107 #endif
108         "webkit/widget/search/field",
109         "webkit/widget/search/results_button",
110         "webkit/widget/search/results_decoration",
111         "webkit/widget/search/cancel_button",
112         "webkit/widget/slider/vertical",
113         "webkit/widget/slider/horizontal",
114         "webkit/widget/slider/thumb_vertical",
115         "webkit/widget/slider/thumb_horizontal",
116 #if ENABLE(VIDEO)
117         "webkit/widget/mediacontrol/playpause_button",
118         "webkit/widget/mediacontrol/mute_button",
119         "webkit/widget/mediacontrol/seekforward_button",
120         "webkit/widget/mediacontrol/seekbackward_button",
121         "webkit/widget/mediacontrol/fullscreen_button",
122 #endif
123 #if ENABLE(VIDEO_TRACK)
124         "webkit/widget/mediacontrol/toggle_captions_button",
125 #endif
126         "webkit/widget/spinner",
127         0
128     };
129     ASSERT(type >= 0);
130     ASSERT((size_t)type < sizeof(groups) / sizeof(groups[0])); // Out of sync?
131     return groups[type];
132 }
133
134 static bool setSourceGroupForEdjeObject(Evas_Object* o, const String& themePath, const char* group)
135 {
136     ASSERT(o);
137     ASSERT(!themePath.isEmpty());
138
139     if (!edje_object_file_set(o, themePath.utf8().data(), group)) {
140         const char* message = edje_load_error_str(edje_object_load_error_get(o));
141         EINA_LOG_ERR("Could not set theme group '%s' of file '%s': %s", group, themePath.utf8().data(), message);
142         return false;
143     }
144
145     return true;
146 }
147
148 void RenderThemeEfl::adjustSizeConstraints(RenderStyle* style, FormType type) const
149 {
150     loadThemeIfNeeded();
151
152     // These are always valid, even if no theme could be loaded.
153     const ThemePartDesc* desc = m_partDescs + (size_t)type;
154
155     if (style->minWidth().isIntrinsic())
156         style->setMinWidth(desc->min.width());
157     if (style->minHeight().isIntrinsic())
158         style->setMinHeight(desc->min.height());
159
160     if (desc->max.width().value() > 0 && style->maxWidth().isIntrinsicOrAuto())
161         style->setMaxWidth(desc->max.width());
162     if (desc->max.height().value() > 0 && style->maxHeight().isIntrinsicOrAuto())
163         style->setMaxHeight(desc->max.height());
164
165     style->setPaddingTop(desc->padding.top());
166     style->setPaddingBottom(desc->padding.bottom());
167     style->setPaddingLeft(desc->padding.left());
168     style->setPaddingRight(desc->padding.right());
169 }
170
171 static bool isFormElementTooLargeToDisplay(const IntSize& elementSize)
172 {
173     // This limit of 20000 pixels is hardcoded inside edje -- anything above this size
174     // will be clipped. This value seems to be reasonable enough so that hardcoding it
175     // here won't be a problem.
176     static const int maxEdjeDimension = 20000;
177
178     return elementSize.width() > maxEdjeDimension || elementSize.height() > maxEdjeDimension;
179 }
180
181 PassOwnPtr<RenderThemeEfl::ThemePartCacheEntry> RenderThemeEfl::ThemePartCacheEntry::create(const String& themePath, FormType type, const IntSize& size)
182 {
183     ASSERT(!themePath.isEmpty());
184
185     if (isFormElementTooLargeToDisplay(size) || size.isEmpty()) {
186         EINA_LOG_ERR("Cannot render an element of size %dx%d.", size.width(), size.height());
187         return nullptr;
188     }
189
190     OwnPtr<ThemePartCacheEntry> entry = adoptPtr(new ThemePartCacheEntry);
191
192     entry->m_canvas = adoptPtr(ecore_evas_buffer_new(size.width(), size.height()));
193     if (!entry->canvas()) {
194         EINA_LOG_ERR("ecore_evas_buffer_new(%d, %d) failed.", size.width(), size.height());
195         return nullptr;
196     }
197
198     // By default EFL creates buffers without alpha.
199     ecore_evas_alpha_set(entry->canvas(), EINA_TRUE);
200
201     entry->m_edje = adoptRef(edje_object_add(ecore_evas_get(entry->canvas())));
202     ASSERT(entry->edje());
203
204     if (!setSourceGroupForEdjeObject(entry->edje(), themePath, toEdjeGroup(type)))
205         return nullptr;
206
207     entry->m_surface = createSurfaceForBackingStore(entry->canvas());
208     if (!entry->surface())
209         return nullptr;
210
211     evas_object_resize(entry->edje(), size.width(), size.height());
212     evas_object_show(entry->edje());
213
214     entry->type = type;
215     entry->size = size;
216
217     return entry.release();
218 }
219
220 void RenderThemeEfl::ThemePartCacheEntry::reuse(const String& themePath, FormType newType, const IntSize& newSize)
221 {
222     ASSERT(!themePath.isEmpty());
223
224     if (type != newType) {
225         type = newType;
226         if (!setSourceGroupForEdjeObject(edje(), themePath, toEdjeGroup(newType))) {
227             type = FormTypeLast; // Invalidate.
228             return;
229         }
230     }
231
232     if (size != newSize) {
233         size = newSize;
234         ecore_evas_resize(canvas(), newSize.width(), newSize.height());
235         evas_object_resize(edje(), newSize.width(), newSize.height());
236
237         m_surface = createSurfaceForBackingStore(canvas());
238         if (!surface()) {
239             type = FormTypeLast; // Invalidate;
240             return;
241         }
242     }
243 }
244
245 RenderThemeEfl::ThemePartCacheEntry* RenderThemeEfl::getThemePartFromCache(FormType type, const IntSize& size)
246 {
247     void* data;
248     Eina_List* node;
249     Eina_List* reusableNode = 0;
250
251     // Look for the item in the cache.
252     EINA_LIST_FOREACH(m_partCache, node, data) {
253         ThemePartCacheEntry* cachedEntry = static_cast<ThemePartCacheEntry*>(data);
254         if (cachedEntry->size == size) {
255             if (cachedEntry->type == type) {
256                 // Found the right item, move it to the head of the list
257                 // and return it.
258                 m_partCache = eina_list_promote_list(m_partCache, node);
259                 return cachedEntry;
260             }
261             // We reuse in priority the last item in the list that has
262             // the requested size.
263             reusableNode = node;
264         }
265     }
266
267     if (eina_list_count(m_partCache) < RENDER_THEME_EFL_PART_CACHE_MAX) {
268         ThemePartCacheEntry* entry = ThemePartCacheEntry::create(themePath(), type, size).leakPtr();
269         if (entry)
270             m_partCache = eina_list_prepend(m_partCache, entry);
271
272         return entry;
273     }
274
275     // The cache is full, reuse the last item we found that had the
276     // requested size to avoid resizing. If there was none, reuse
277     // the last item of the list.
278     if (!reusableNode)
279         reusableNode = eina_list_last(m_partCache);
280
281     ThemePartCacheEntry* reusedEntry = static_cast<ThemePartCacheEntry*>(eina_list_data_get(reusableNode));
282     ASSERT(reusedEntry);
283     reusedEntry->reuse(themePath(), type, size);
284     m_partCache = eina_list_promote_list(m_partCache, reusableNode);
285
286     return reusedEntry;
287 }
288
289 void RenderThemeEfl::clearThemePartCache()
290 {
291     void* data;
292     EINA_LIST_FREE(m_partCache, data)
293         delete static_cast<ThemePartCacheEntry*>(data);
294
295 }
296
297 void RenderThemeEfl::applyEdjeStateFromForm(Evas_Object* object, ControlStates states, bool haveBackground)
298 {
299     const char *signals[] = { // keep in sync with WebCore/platform/ThemeTypes.h
300         "hovered",
301         "pressed",
302         "focused",
303         "enabled",
304         "checked",
305         "read-only",
306         "default",
307         "window-inactive",
308         "indeterminate",
309         "spinup"
310     };
311
312     edje_object_signal_emit(object, "reset", "");
313
314     for (size_t i = 0; i < WTF_ARRAY_LENGTH(signals); ++i) {
315         if (states & (1 << i))
316             edje_object_signal_emit(object, signals[i], "");
317     }
318
319     if (haveBackground)
320         edje_object_signal_emit(object, "styled", "");
321 }
322
323 void RenderThemeEfl::applyEdjeRTLState(Evas_Object* edje, RenderObject* object, FormType type, const IntRect& rect)
324 {
325     if (type == SliderVertical || type == SliderHorizontal) {
326         if (!object->isSlider())
327             return; // probably have -webkit-appearance: slider..
328
329         RenderSlider* renderSlider = toRenderSlider(object);
330         HTMLInputElement& input = renderSlider->element();
331         double valueRange = input.maximum() - input.minimum();
332
333         OwnPtr<Edje_Message_Float_Set> msg = adoptPtr(static_cast<Edje_Message_Float_Set*>(::operator new (sizeof(Edje_Message_Float_Set) + sizeof(double))));
334         msg->count = 2;
335
336         // The first parameter of the message decides if the progress bar
337         // grows from the end of the slider or from the beginning. On vertical
338         // sliders, it should always be the same and will not be affected by
339         // text direction settings.
340         if (object->style()->direction() == RTL || type == SliderVertical)
341             msg->val[0] = 1;
342         else
343             msg->val[0] = 0;
344
345         msg->val[1] = (input.valueAsNumber() - input.minimum()) / valueRange;
346         edje_object_message_send(edje, EDJE_MESSAGE_FLOAT_SET, 0, msg.get());
347 #if ENABLE(PROGRESS_ELEMENT)
348     } else if (type == ProgressBar) {
349         RenderProgress* renderProgress = toRenderProgress(object);
350
351         int max = rect.width();
352         double value = renderProgress->position();
353
354         OwnPtr<Edje_Message_Float_Set> msg = adoptPtr(static_cast<Edje_Message_Float_Set*>(::operator new (sizeof(Edje_Message_Float_Set) + sizeof(double))));
355         msg->count = 2;
356
357         if (object->style()->direction() == RTL)
358             msg->val[0] = (1.0 - value) * max;
359         else
360             msg->val[0] = 0;
361         msg->val[1] = value;
362         edje_object_message_send(edje, EDJE_MESSAGE_FLOAT_SET, 0, msg.get());
363 #else
364         UNUSED_PARAM(rect);
365 #endif
366     }
367 }
368
369 bool RenderThemeEfl::isControlStyled(const RenderStyle* style, const BorderData& border, const FillLayer& background, const Color& backgroundColor) const
370 {
371     return RenderTheme::isControlStyled(style, border, background, backgroundColor) || style->appearance() == MenulistButtonPart;
372 }
373
374 bool RenderThemeEfl::paintThemePart(RenderObject* object, FormType type, const PaintInfo& info, const IntRect& rect)
375 {
376     loadThemeIfNeeded();
377     _ASSERT_ON_RELEASE_RETURN_VAL(edje(), false, "Could not paint native HTML part due to missing theme.");
378
379     ThemePartCacheEntry* entry = getThemePartFromCache(type, rect.size());
380     if (!entry)
381         return false;
382
383     bool haveBackgroundColor = isControlStyled(object->style(), object->style()->border(), *object->style()->backgroundLayers(), Color::white);
384     applyEdjeStateFromForm(entry->edje(), controlStatesForRenderer(object), haveBackgroundColor);
385
386     applyEdjeRTLState(entry->edje(), object, type, rect);
387
388     edje_object_calc_force(entry->edje());
389     edje_object_message_signal_process(entry->edje());
390     evas_render(ecore_evas_get(entry->canvas()));
391
392     cairo_t* cairo = info.context->platformContext()->cr();
393     ASSERT(cairo);
394
395     cairo_save(cairo);
396     cairo_set_source_surface(cairo, entry->surface(), rect.x(), rect.y());
397     cairo_paint_with_alpha(cairo, 1.0);
398     cairo_restore(cairo);
399
400     return false;
401 }
402
403 PassRefPtr<RenderTheme> RenderThemeEfl::create(Page* page)
404 {
405     return adoptRef(new RenderThemeEfl(page));
406 }
407
408 PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page)
409 {
410     if (page)
411         return RenderThemeEfl::create(page);
412
413     static RenderTheme* fallback = RenderThemeEfl::create(0).leakRef();
414     return fallback;
415 }
416
417 static void applyColorCallback(void* data, Evas_Object*, const char* /* signal */, const char* colorClass)
418 {
419     RenderThemeEfl* that = static_cast<RenderThemeEfl*>(data);
420     that->setColorFromThemeClass(colorClass);
421     that->platformColorsDidChange(); // Triggers relayout.
422 }
423
424 static bool fillColorsFromEdjeClass(Evas_Object* o, const char* colorClass, Color* color1, Color* color2 = 0, Color* color3 = 0)
425 {
426     int r1, g1, b1, a1;
427     int r2, g2, b2, a2;
428     int r3, g3, b3, a3;
429
430     if (!edje_object_color_class_get(o, colorClass, &r1, &g1, &b1, &a1, &r2, &g2, &b2, &a2, &r3, &g3, &b3, &a3))
431         return false;
432
433     if (color1)
434         color1->setRGB(makeRGBA(r1, g1, b1, a1));
435     if (color2)
436         color2->setRGB(makeRGBA(r2, g2, b2, a2));
437     if (color3)
438         color3->setRGB(makeRGBA(r3, g3, b3, a3));
439
440     return true;
441 }
442
443 void RenderThemeEfl::setColorFromThemeClass(const char* colorClass)
444 {
445     ASSERT(edje());
446
447     if (!strcmp("webkit/selection/foreground", colorClass))
448         m_supportsSelectionForegroundColor = fillColorsFromEdjeClass(edje(), colorClass, &m_activeSelectionForegroundColor, &m_inactiveSelectionForegroundColor);
449     else if (!strcmp("webkit/selection/background", colorClass))
450         fillColorsFromEdjeClass(edje(), colorClass, &m_activeSelectionBackgroundColor, &m_inactiveSelectionBackgroundColor);
451     else if (!strcmp("webkit/focus_ring", colorClass)) {
452         if (!fillColorsFromEdjeClass(edje(), colorClass, &m_focusRingColor))
453             return;
454
455         // platformFocusRingColor() is only used for the default theme (without page)
456         // The following is ugly, but no other way to do it unless we change it to use page themes as much as possible.
457         RenderTheme::setCustomFocusRingColor(m_focusRingColor);
458     }
459 }
460
461 void RenderThemeEfl::setThemePath(const String& newThemePath)
462 {
463     if (newThemePath == m_themePath)
464         return;
465
466     if (newThemePath.isEmpty()) {
467         EINA_LOG_CRIT("No valid theme defined, things will not work properly.");
468         return;
469     }
470
471     String oldThemePath = m_themePath;
472     m_themePath = newThemePath;
473
474     // Keep the consistence by restoring the previous theme path
475     // if we cannot load the new one.
476     if (!loadTheme())
477         m_themePath = oldThemePath;
478 }
479
480 String RenderThemeEfl::themePath() const
481 {
482 #ifndef NDEBUG
483     if (edje()) {
484         const char* path;
485         edje_object_file_get(edje(), &path, 0);
486         ASSERT(m_themePath == path);
487     }
488 #endif
489     return m_themePath;
490 }
491
492 bool RenderThemeEfl::loadTheme()
493 {
494     ASSERT(!m_themePath.isEmpty());
495
496     if (!canvas()) {
497         m_canvas = adoptPtr(ecore_evas_buffer_new(1, 1));
498         _ASSERT_ON_RELEASE_RETURN_VAL(canvas(), false,
499                 "Could not create canvas required by theme, things will not work properly.");
500     }
501
502     RefPtr<Evas_Object> o = adoptRef(edje_object_add(ecore_evas_get(canvas())));
503     _ASSERT_ON_RELEASE_RETURN_VAL(o, false, "Could not create new base Edje object.");
504
505     if (!setSourceGroupForEdjeObject(o.get(), m_themePath, "webkit/base"))
506         return false; // Keep current theme.
507
508     // Invalidate existing theme part cache.
509     if (edje())
510         clearThemePartCache();
511
512     // Set new loaded theme, and apply it.
513     m_edje = o;
514
515     edje_object_signal_callback_add(edje(), "color_class,set", "webkit/selection/foreground", applyColorCallback, this);
516     edje_object_signal_callback_add(edje(), "color_class,set", "webkit/selection/background", applyColorCallback, this);
517     edje_object_signal_callback_add(edje(), "color_class,set", "webkit/focus_ring", applyColorCallback, this);
518
519     applyPartDescriptionsFrom(m_themePath);
520
521     setColorFromThemeClass("webkit/selection/foreground");
522     setColorFromThemeClass("webkit/selection/background");
523     setColorFromThemeClass("webkit/focus_ring");
524
525     platformColorsDidChange(); // Schedules a relayout, do last.
526
527     return true;
528 }
529
530 void RenderThemeEfl::applyPartDescriptionFallback(ThemePartDesc* desc)
531 {
532     desc->min.setWidth(Length(0, Fixed));
533     desc->min.setHeight(Length(0, Fixed));
534
535     desc->max.setWidth(Length(0, Fixed));
536     desc->max.setHeight(Length(0, Fixed));
537
538     desc->padding = LengthBox(0, 0, 0, 0);
539 }
540
541 void RenderThemeEfl::applyPartDescription(Evas_Object* object, ThemePartDesc* desc)
542 {
543     Evas_Coord minw, minh, maxw, maxh;
544
545     edje_object_size_min_get(object, &minw, &minh);
546     if (!minw && !minh)
547         edje_object_size_min_calc(object, &minw, &minh);
548
549     desc->min.setWidth(Length(minw, Fixed));
550     desc->min.setHeight(Length(minh, Fixed));
551
552     edje_object_size_max_get(object, &maxw, &maxh);
553     desc->max.setWidth(Length(maxw, Fixed));
554     desc->max.setHeight(Length(maxh, Fixed));
555
556     if (!edje_object_part_exists(object, "text_confinement"))
557         desc->padding = LengthBox(0, 0, 0, 0);
558     else {
559         Evas_Coord px, py, pw, ph;
560         Evas_Coord ox = 0, oy = 0, ow = 0, oh = 0;
561         int t, r, b, l;
562
563         if (minw > 0)
564             ow = minw;
565         else
566             ow = 100;
567         if (minh > 0)
568             oh = minh;
569         else
570             oh = 100;
571         if (maxw > 0 && ow > maxw)
572             ow = maxw;
573         if (maxh > 0 && oh > maxh)
574             oh = maxh;
575
576         evas_object_move(object, ox, oy);
577         evas_object_resize(object, ow, oh);
578         edje_object_calc_force(object);
579         edje_object_message_signal_process(object);
580         edje_object_part_geometry_get(object, "text_confinement", &px, &py, &pw, &ph);
581
582         t = py - oy;
583         b = (oh + oy) - (ph + py);
584
585         l = px - ox;
586         r = (ow + ox) - (pw + px);
587
588         desc->padding = LengthBox(t, r, b, l);
589     }
590 }
591
592 void RenderThemeEfl::applyPartDescriptionsFrom(const String& themePath)
593 {
594     RefPtr<Evas_Object> temp = adoptRef(edje_object_add(ecore_evas_get(canvas())));
595     _ASSERT_ON_RELEASE_RETURN(temp, "Could not create Edje object.");
596
597     for (size_t i = 0; i < FormTypeLast; i++) {
598         FormType type = static_cast<FormType>(i);
599         m_partDescs[i].type = type;
600         if (!setSourceGroupForEdjeObject(temp.get(), themePath, toEdjeGroup(type)))
601             applyPartDescriptionFallback(m_partDescs + i);
602         else
603             applyPartDescription(temp.get(), m_partDescs + i);
604     }
605 }
606
607 RenderThemeEfl::RenderThemeEfl(Page* page)
608     : RenderTheme()
609     , m_page(page)
610     , m_activeSelectionBackgroundColor(0, 0, 255)
611     , m_activeSelectionForegroundColor(Color::white)
612     , m_inactiveSelectionBackgroundColor(0, 0, 128)
613     , m_inactiveSelectionForegroundColor(200, 200, 200)
614     , m_focusRingColor(32, 32, 224, 224)
615     , m_sliderThumbColor(Color::darkGray)
616 #if ENABLE(VIDEO)
617     , m_mediaPanelColor(220, 220, 195) // light tannish color.
618     , m_mediaSliderColor(Color::white)
619 #endif
620     , m_supportsSelectionForegroundColor(false)
621     , m_partCache(0)
622 {
623 }
624
625 RenderThemeEfl::~RenderThemeEfl()
626 {
627     clearThemePartCache();
628 }
629
630 static bool supportsFocus(ControlPart appearance)
631 {
632     switch (appearance) {
633     case PushButtonPart:
634     case ButtonPart:
635     case TextFieldPart:
636     case TextAreaPart:
637     case SearchFieldPart:
638     case MenulistPart:
639     case RadioPart:
640     case CheckboxPart:
641     case SliderVerticalPart:
642     case SliderHorizontalPart:
643         return true;
644     default:
645         return false;
646     }
647 }
648
649 bool RenderThemeEfl::supportsFocusRing(const RenderStyle* style) const
650 {
651     return supportsFocus(style->appearance());
652 }
653
654 bool RenderThemeEfl::controlSupportsTints(const RenderObject* object) const
655 {
656     return isEnabled(object);
657 }
658
659 int RenderThemeEfl::baselinePosition(const RenderObject* object) const
660 {
661     if (!object->isBox())
662         return 0;
663
664     if (object->style()->appearance() == CheckboxPart
665     ||  object->style()->appearance() == RadioPart)
666         return toRenderBox(object)->marginTop() + toRenderBox(object)->height() - 3;
667
668     return RenderTheme::baselinePosition(object);
669 }
670
671 Color RenderThemeEfl::platformActiveSelectionBackgroundColor() const
672 {
673     loadThemeIfNeeded();
674     return m_activeSelectionBackgroundColor;
675 }
676
677 Color RenderThemeEfl::platformInactiveSelectionBackgroundColor() const
678 {
679     loadThemeIfNeeded();
680     return m_inactiveSelectionBackgroundColor;
681 }
682
683 Color RenderThemeEfl::platformActiveSelectionForegroundColor() const
684 {
685     loadThemeIfNeeded();
686     return m_activeSelectionForegroundColor;
687 }
688
689 Color RenderThemeEfl::platformInactiveSelectionForegroundColor() const
690 {
691     loadThemeIfNeeded();
692     return m_inactiveSelectionForegroundColor;
693 }
694
695 Color RenderThemeEfl::platformFocusRingColor() const
696 {
697     loadThemeIfNeeded();
698     return m_focusRingColor;
699 }
700
701 bool RenderThemeEfl::supportsSelectionForegroundColors() const
702 {
703     loadThemeIfNeeded();
704     return m_supportsSelectionForegroundColor;
705 }
706
707 bool RenderThemeEfl::paintSliderTrack(RenderObject* object, const PaintInfo& info, const IntRect& rect)
708 {
709     if (object->style()->appearance() == SliderHorizontalPart)
710         paintThemePart(object, SliderHorizontal, info, rect);
711     else
712         paintThemePart(object, SliderVertical, info, rect);
713
714 #if ENABLE(DATALIST_ELEMENT)
715     paintSliderTicks(object, info, rect);
716 #endif
717
718     return false;
719 }
720
721 void RenderThemeEfl::adjustSliderTrackStyle(StyleResolver*, RenderStyle* style, Element*) const
722 {
723     style->setBoxShadow(nullptr);
724 }
725
726 void RenderThemeEfl::adjustSliderThumbStyle(StyleResolver* styleResolver, RenderStyle* style, Element* element) const
727 {
728     RenderTheme::adjustSliderThumbStyle(styleResolver, style, element);
729     style->setBoxShadow(nullptr);
730 }
731
732 void RenderThemeEfl::adjustSliderThumbSize(RenderStyle* style, Element*) const
733 {
734     ControlPart part = style->appearance();
735     if (part == SliderThumbVerticalPart) {
736         style->setWidth(Length(sliderThumbHeight, Fixed));
737         style->setHeight(Length(sliderThumbWidth, Fixed));
738     } else if (part == SliderThumbHorizontalPart) {
739         style->setWidth(Length(sliderThumbWidth, Fixed));
740         style->setHeight(Length(sliderThumbHeight, Fixed));
741 #if ENABLE(VIDEO)
742     } else if (part == MediaSliderThumbPart) {
743         style->setWidth(Length(mediaSliderThumbWidth, Fixed));
744         style->setHeight(Length(mediaSliderThumbHeight, Fixed));
745 #endif
746     }
747 }
748
749 #if ENABLE(DATALIST_ELEMENT)
750 IntSize RenderThemeEfl::sliderTickSize() const
751 {
752     return IntSize(1, 6);
753 }
754
755 int RenderThemeEfl::sliderTickOffsetFromTrackCenter() const
756 {
757     static const int sliderTickOffset = -12;
758
759     return sliderTickOffset;
760 }
761
762 LayoutUnit RenderThemeEfl::sliderTickSnappingThreshold() const
763 {
764     // The same threshold value as the Chromium port.
765     return 5;
766 }
767 #endif
768
769 bool RenderThemeEfl::supportsDataListUI(const AtomicString& type) const
770 {
771 #if ENABLE(DATALIST_ELEMENT)
772     // FIXME: We need to support other types.
773     return type == InputTypeNames::email()
774         || type == InputTypeNames::range()
775         || type == InputTypeNames::search()
776         || type == InputTypeNames::url();
777 #else
778     UNUSED_PARAM(type);
779     return false;
780 #endif
781 }
782
783 bool RenderThemeEfl::paintSliderThumb(RenderObject* object, const PaintInfo& info, const IntRect& rect)
784 {
785     if (object->style()->appearance() == SliderThumbHorizontalPart)
786         paintThemePart(object, SliderThumbHorizontal, info, rect);
787     else
788         paintThemePart(object, SliderThumbVertical, info, rect);
789
790     return false;
791 }
792
793 void RenderThemeEfl::adjustCheckboxStyle(StyleResolver* styleResolver, RenderStyle* style, Element* element) const
794 {
795     if (!m_page && element && element->document().page()) {
796         static_cast<RenderThemeEfl*>(element->document().page()->theme())->adjustCheckboxStyle(styleResolver, style, element);
797         return;
798     }
799
800     adjustSizeConstraints(style, CheckBox);
801
802     style->resetBorder();
803
804     const ThemePartDesc* desc = m_partDescs + (size_t)CheckBox;
805     if (style->width().value() < desc->min.width().value())
806         style->setWidth(desc->min.width());
807     if (style->height().value() < desc->min.height().value())
808         style->setHeight(desc->min.height());
809 }
810
811 bool RenderThemeEfl::paintCheckbox(RenderObject* object, const PaintInfo& info, const IntRect& rect)
812 {
813     return paintThemePart(object, CheckBox, info, rect);
814 }
815
816 void RenderThemeEfl::adjustRadioStyle(StyleResolver* styleResolver, RenderStyle* style, Element* element) const
817 {
818     if (!m_page && element && element->document().page()) {
819         static_cast<RenderThemeEfl*>(element->document().page()->theme())->adjustRadioStyle(styleResolver, style, element);
820         return;
821     }
822
823     adjustSizeConstraints(style, RadioButton);
824
825     style->resetBorder();
826
827     const ThemePartDesc* desc = m_partDescs + (size_t)RadioButton;
828     if (style->width().value() < desc->min.width().value())
829         style->setWidth(desc->min.width());
830     if (style->height().value() < desc->min.height().value())
831         style->setHeight(desc->min.height());
832 }
833
834 bool RenderThemeEfl::paintRadio(RenderObject* object, const PaintInfo& info, const IntRect& rect)
835 {
836     return paintThemePart(object, RadioButton, info, rect);
837 }
838
839 void RenderThemeEfl::adjustButtonStyle(StyleResolver* styleResolver, RenderStyle* style, Element* element) const
840 {
841     if (!m_page && element && element->document().page()) {
842         static_cast<RenderThemeEfl*>(element->document().page()->theme())->adjustButtonStyle(styleResolver, style, element);
843         return;
844     }
845
846     // adjustSizeConstrains can make SquareButtonPart's size wrong (by adjusting paddings), so call it only for PushButtonPart and ButtonPart
847     if (style->appearance() == PushButtonPart || style->appearance() == ButtonPart)
848         adjustSizeConstraints(style, Button);
849 }
850
851 bool RenderThemeEfl::paintButton(RenderObject* object, const PaintInfo& info, const IntRect& rect)
852 {
853     return paintThemePart(object, Button, info, rect);
854 }
855
856 void RenderThemeEfl::adjustMenuListStyle(StyleResolver* styleResolver, RenderStyle* style, Element* element) const
857 {
858     if (!m_page && element && element->document().page()) {
859         static_cast<RenderThemeEfl*>(element->document().page()->theme())->adjustMenuListStyle(styleResolver, style, element);
860         return;
861     }
862     adjustSizeConstraints(style, ComboBox);
863     style->resetBorder();
864     style->setWhiteSpace(PRE);
865
866     style->setLineHeight(RenderStyle::initialLineHeight());
867 }
868
869 bool RenderThemeEfl::paintMenuList(RenderObject* object, const PaintInfo& info, const IntRect& rect)
870 {
871     return paintThemePart(object, ComboBox, info, rect);
872 }
873
874 void RenderThemeEfl::adjustMenuListButtonStyle(StyleResolver* styleResolver, RenderStyle* style, Element* element) const
875 {
876     // Height is locked to auto if height is not specified.
877     style->setHeight(Length(Auto));
878
879     // The <select> box must be at least 12px high for the button to render the text inside the box without clipping.
880     const int dropDownBoxMinHeight = 12;
881
882     // Calculate min-height of the <select> element.
883     int minHeight = style->fontMetrics().height();
884     minHeight = max(minHeight, dropDownBoxMinHeight);
885     style->setMinHeight(Length(minHeight, Fixed));
886
887     adjustMenuListStyle(styleResolver, style, element);
888 }
889
890 bool RenderThemeEfl::paintMenuListButton(RenderObject* object, const PaintInfo& info, const IntRect& rect)
891 {
892     return paintMenuList(object, info, rect);
893 }
894
895 void RenderThemeEfl::adjustTextFieldStyle(StyleResolver* styleResolver, RenderStyle* style, Element* element) const
896 {
897     if (!m_page && element && element->document().page()) {
898         static_cast<RenderThemeEfl*>(element->document().page()->theme())->adjustTextFieldStyle(styleResolver, style, element);
899         return;
900     }
901     adjustSizeConstraints(style, TextField);
902     style->resetBorder();
903 }
904
905 bool RenderThemeEfl::paintTextField(RenderObject* object, const PaintInfo& info, const IntRect& rect)
906 {
907     return paintThemePart(object, TextField, info, rect);
908 }
909
910 void RenderThemeEfl::adjustTextAreaStyle(StyleResolver*, RenderStyle*, Element*) const
911 {
912 }
913
914 bool RenderThemeEfl::paintTextArea(RenderObject* object, const PaintInfo& info, const IntRect& rect)
915 {
916     return paintTextField(object, info, rect);
917 }
918
919 void RenderThemeEfl::adjustSearchFieldResultsButtonStyle(StyleResolver* styleResolver, RenderStyle* style, Element* element) const
920 {
921     if (!m_page && element && element->document().page()) {
922         static_cast<RenderThemeEfl*>(element->document().page()->theme())->adjustSearchFieldResultsButtonStyle(styleResolver, style, element);
923         return;
924     }
925     adjustSizeConstraints(style, SearchFieldResultsButton);
926     style->resetBorder();
927     style->setWhiteSpace(PRE);
928
929     float fontScale = style->fontSize() / defaultFontSize;
930     int decorationSize = lroundf(std::min(std::max(minSearchDecorationButtonSize, defaultFontSize * fontScale), maxSearchDecorationButtonSize));
931
932     style->setWidth(Length(decorationSize + searchFieldDecorationButtonOffset, Fixed));
933     style->setHeight(Length(decorationSize, Fixed));
934 }
935
936 bool RenderThemeEfl::paintSearchFieldResultsButton(RenderObject* object, const PaintInfo& info, const IntRect& rect)
937 {
938     return paintThemePart(object, SearchFieldResultsButton, info, rect);
939 }
940
941 void RenderThemeEfl::adjustSearchFieldResultsDecorationStyle(StyleResolver* styleResolver, RenderStyle* style, Element* element) const
942 {
943     if (!m_page && element && element->document().page()) {
944         static_cast<RenderThemeEfl*>(element->document().page()->theme())->adjustSearchFieldResultsDecorationStyle(styleResolver, style, element);
945         return;
946     }
947     adjustSizeConstraints(style, SearchFieldResultsDecoration);
948     style->resetBorder();
949     style->setWhiteSpace(PRE);
950
951     float fontScale = style->fontSize() / defaultFontSize;
952     int decorationSize = lroundf(std::min(std::max(minSearchDecorationButtonSize, defaultFontSize * fontScale), maxSearchDecorationButtonSize));
953
954     style->setWidth(Length(decorationSize + searchFieldDecorationButtonOffset, Fixed));
955     style->setHeight(Length(decorationSize, Fixed));
956 }
957
958 bool RenderThemeEfl::paintSearchFieldResultsDecoration(RenderObject* object, const PaintInfo& info, const IntRect& rect)
959 {
960     return paintThemePart(object, SearchFieldResultsDecoration, info, rect);
961 }
962
963 void RenderThemeEfl::adjustSearchFieldCancelButtonStyle(StyleResolver* styleResolver, RenderStyle* style, Element* element) const
964 {
965     if (!m_page && element && element->document().page()) {
966         static_cast<RenderThemeEfl*>(element->document().page()->theme())->adjustSearchFieldCancelButtonStyle(styleResolver, style, element);
967         return;
968     }
969     adjustSizeConstraints(style, SearchFieldCancelButton);
970     style->resetBorder();
971     style->setWhiteSpace(PRE);
972
973     // Logic taken from RenderThemeChromium.cpp.
974     // Scale the button size based on the font size.
975     float fontScale = style->fontSize() / defaultFontSize;
976     int cancelButtonSize = lroundf(std::min(std::max(minCancelButtonSize, defaultFontSize * fontScale), maxCancelButtonSize));
977
978     style->setWidth(Length(cancelButtonSize, Fixed));
979     style->setHeight(Length(cancelButtonSize, Fixed));
980 }
981
982 bool RenderThemeEfl::paintSearchFieldCancelButton(RenderObject* object, const PaintInfo& info, const IntRect& rect)
983 {
984     return paintThemePart(object, SearchFieldCancelButton, info, rect);
985 }
986
987 void RenderThemeEfl::adjustSearchFieldStyle(StyleResolver* styleResolver, RenderStyle* style, Element* element) const
988 {
989     if (!m_page && element && element->document().page()) {
990         static_cast<RenderThemeEfl*>(element->document().page()->theme())->adjustSearchFieldStyle(styleResolver, style, element);
991         return;
992     }
993     adjustSizeConstraints(style, SearchField);
994     style->resetBorder();
995     style->setWhiteSpace(PRE);
996 }
997
998 bool RenderThemeEfl::paintSearchField(RenderObject* object, const PaintInfo& info, const IntRect& rect)
999 {
1000     return paintThemePart(object, SearchField, info, rect);
1001 }
1002
1003 void RenderThemeEfl::adjustInnerSpinButtonStyle(StyleResolver* styleResolver, RenderStyle* style, Element* element) const
1004 {
1005     if (!m_page && element && element->document().page()) {
1006         static_cast<RenderThemeEfl*>(element->document().page()->theme())->adjustInnerSpinButtonStyle(styleResolver, style, element);
1007         return;
1008     }
1009     adjustSizeConstraints(style, Spinner);
1010 }
1011
1012 bool RenderThemeEfl::paintInnerSpinButton(RenderObject* object, const PaintInfo& info, const IntRect& rect)
1013 {
1014     return paintThemePart(object, Spinner, info, rect);
1015 }
1016
1017 void RenderThemeEfl::setDefaultFontSize(int size)
1018 {
1019     defaultFontSize = size;
1020 }
1021
1022 void RenderThemeEfl::systemFont(CSSValueID, FontDescription& fontDescription) const
1023 {
1024     // It was called by RenderEmbeddedObject::paintReplaced to render alternative string.
1025     // To avoid cairo_error while rendering, fontDescription should be passed.
1026     DEFINE_STATIC_LOCAL(String, fontFace, (ASCIILiteral("Sans")));
1027     float fontSize = defaultFontSize;
1028
1029     fontDescription.setOneFamily(fontFace);
1030     fontDescription.setSpecifiedSize(fontSize);
1031     fontDescription.setIsAbsoluteSize(true);
1032     fontDescription.setGenericFamily(FontDescription::NoFamily);
1033     fontDescription.setWeight(FontWeightNormal);
1034     fontDescription.setItalic(false);
1035 }
1036
1037 #if ENABLE(PROGRESS_ELEMENT)
1038 void RenderThemeEfl::adjustProgressBarStyle(StyleResolver*, RenderStyle* style, Element*) const
1039 {
1040     style->setBoxShadow(nullptr);
1041 }
1042
1043 double RenderThemeEfl::animationRepeatIntervalForProgressBar(RenderProgress*) const
1044 {
1045     return progressAnimationInterval;
1046 }
1047
1048 double RenderThemeEfl::animationDurationForProgressBar(RenderProgress*) const
1049 {
1050     return progressAnimationInterval * progressAnimationFrames * 2; // "2" for back and forth;
1051 }
1052
1053 bool RenderThemeEfl::paintProgressBar(RenderObject* object, const PaintInfo& info, const IntRect& rect)
1054 {
1055     if (!object->isProgress())
1056         return true;
1057
1058     return paintThemePart(object, ProgressBar, info, rect);
1059 }
1060 #endif
1061
1062 #if ENABLE(VIDEO)
1063 bool RenderThemeEfl::emitMediaButtonSignal(FormType formType, MediaControlElementType mediaElementType, const IntRect& rect)
1064 {
1065     loadThemeIfNeeded();
1066     _ASSERT_ON_RELEASE_RETURN_VAL(edje(), false, "Could not paint native HTML part due to missing theme.");
1067
1068     ThemePartCacheEntry* entry = getThemePartFromCache(formType, rect.size());
1069     _ASSERT_ON_RELEASE_RETURN_VAL(entry, false, "Could not paint native HTML part due to missing theme part.");
1070
1071     if (mediaElementType == MediaPlayButton)
1072         edje_object_signal_emit(entry->edje(), "play", "");
1073     else if (mediaElementType == MediaPauseButton)
1074         edje_object_signal_emit(entry->edje(), "pause", "");
1075     else if (mediaElementType == MediaMuteButton)
1076         edje_object_signal_emit(entry->edje(), "mute", "");
1077     else if (mediaElementType == MediaUnMuteButton)
1078         edje_object_signal_emit(entry->edje(), "sound", "");
1079     else if (mediaElementType == MediaSeekForwardButton)
1080         edje_object_signal_emit(entry->edje(), "seekforward", "");
1081     else if (mediaElementType == MediaSeekBackButton)
1082         edje_object_signal_emit(entry->edje(), "seekbackward", "");
1083     else if (mediaElementType == MediaEnterFullscreenButton)
1084         edje_object_signal_emit(entry->edje(), "fullscreen_enter", "");
1085     else if (mediaElementType == MediaExitFullscreenButton)
1086         edje_object_signal_emit(entry->edje(), "fullscreen_exit", "");
1087 #if ENABLE(VIDEO_TRACK)
1088     else if (mediaElementType == MediaShowClosedCaptionsButton)
1089         edje_object_signal_emit(entry->edje(), "show_captions", "");
1090     else if (mediaElementType == MediaHideClosedCaptionsButton)
1091         edje_object_signal_emit(entry->edje(), "hide_captions", "");
1092 #endif
1093     else
1094         return false;
1095
1096     return true;
1097 }
1098
1099 String RenderThemeEfl::extraMediaControlsStyleSheet()
1100 {
1101     return String(mediaControlsEflUserAgentStyleSheet, sizeof(mediaControlsEflUserAgentStyleSheet));
1102 }
1103
1104 #if ENABLE(FULLSCREEN_API)
1105 String RenderThemeEfl::extraFullScreenStyleSheet()
1106 {
1107     return String(mediaControlsEflFullscreenUserAgentStyleSheet, sizeof(mediaControlsEflFullscreenUserAgentStyleSheet));
1108 }
1109 #endif
1110
1111 String RenderThemeEfl::formatMediaControlsCurrentTime(float currentTime, float duration) const
1112 {
1113     return formatMediaControlsTime(currentTime) + " / " + formatMediaControlsTime(duration);
1114 }
1115
1116 bool RenderThemeEfl::hasOwnDisabledStateHandlingFor(ControlPart part) const
1117 {
1118     return (part != MediaMuteButtonPart);
1119 }
1120
1121 bool RenderThemeEfl::paintMediaFullscreenButton(RenderObject* object, const PaintInfo& info, const IntRect& rect)
1122 {
1123     Node* mediaNode = object->node() ? object->node()->shadowHost() : 0;
1124     if (!mediaNode)
1125         mediaNode = object->node();
1126     if (!mediaNode || !mediaNode->isElementNode() || !toElement(mediaNode)->isMediaElement())
1127         return false;
1128
1129     HTMLMediaElement* mediaElement = toHTMLMediaElement(mediaNode);
1130     if (!emitMediaButtonSignal(FullScreenButton, mediaElement->isFullscreen() ? MediaExitFullscreenButton : MediaEnterFullscreenButton, rect))
1131         return false;
1132
1133     return paintThemePart(object, FullScreenButton, info, rect);
1134 }
1135
1136 bool RenderThemeEfl::paintMediaMuteButton(RenderObject* object, const PaintInfo& info, const IntRect& rect)
1137 {
1138     Node* mediaNode = object->node() ? object->node()->shadowHost() : 0;
1139     if (!mediaNode)
1140         mediaNode = object->node();
1141     if (!mediaNode || !mediaNode->isElementNode() || !toElement(mediaNode)->isMediaElement())
1142         return false;
1143
1144     HTMLMediaElement* mediaElement = toHTMLMediaElement(mediaNode);
1145
1146     if (!emitMediaButtonSignal(MuteUnMuteButton, mediaElement->muted() ? MediaMuteButton : MediaUnMuteButton, rect))
1147         return false;
1148
1149     return paintThemePart(object, MuteUnMuteButton, info, rect);
1150 }
1151
1152 bool RenderThemeEfl::paintMediaPlayButton(RenderObject* object, const PaintInfo& info, const IntRect& rect)
1153 {
1154     Node* node = object->node();
1155     if (!node || !node->isMediaControlElement())
1156         return false;
1157
1158     if (!emitMediaButtonSignal(PlayPauseButton, mediaControlElementType(node), rect))
1159         return false;
1160
1161     return paintThemePart(object, PlayPauseButton, info, rect);
1162 }
1163
1164 bool RenderThemeEfl::paintMediaSeekBackButton(RenderObject* object, const PaintInfo& info, const IntRect& rect)
1165 {
1166     Node* node = object->node();
1167     if (!node || !node->isMediaControlElement())
1168         return 0;
1169
1170     if (!emitMediaButtonSignal(SeekBackwardButton, mediaControlElementType(node), rect))
1171         return false;
1172
1173     return paintThemePart(object, SeekBackwardButton, info, rect);
1174 }
1175
1176 bool RenderThemeEfl::paintMediaSeekForwardButton(RenderObject* object, const PaintInfo& info, const IntRect& rect)
1177 {
1178     Node* node = object->node();
1179     if (!node || !node->isMediaControlElement())
1180         return 0;
1181
1182     if (!emitMediaButtonSignal(SeekForwardButton, mediaControlElementType(node), rect))
1183         return false;
1184
1185     return paintThemePart(object, SeekForwardButton, info, rect);
1186 }
1187
1188 bool RenderThemeEfl::paintMediaSliderTrack(RenderObject* object, const PaintInfo& info, const IntRect& rect)
1189 {
1190     GraphicsContext* context = info.context;
1191
1192     context->fillRect(FloatRect(rect), m_mediaPanelColor, ColorSpaceDeviceRGB);
1193     context->fillRect(FloatRect(IntRect(rect.x(), rect.y() + (rect.height() - mediaSliderHeight) / 2,
1194                                         rect.width(), mediaSliderHeight)), m_mediaSliderColor, ColorSpaceDeviceRGB);
1195
1196     RenderStyle* style = object->style();
1197     HTMLMediaElement* mediaElement = parentMediaElement(*object);
1198
1199     if (!mediaElement)
1200         return false;
1201
1202     // Draw the buffered ranges. This code is highly inspired from
1203     // Chrome for the gradient code.
1204     float mediaDuration = mediaElement->duration();
1205     RefPtr<TimeRanges> timeRanges = mediaElement->buffered();
1206     IntRect trackRect = rect;
1207     int totalWidth = trackRect.width();
1208
1209     trackRect.inflate(-style->borderLeftWidth());
1210     context->save();
1211     context->setStrokeStyle(NoStroke);
1212
1213     for (unsigned index = 0; index < timeRanges->length(); ++index) {
1214         float start = timeRanges->start(index, IGNORE_EXCEPTION);
1215         float end = timeRanges->end(index, IGNORE_EXCEPTION);
1216         int width = ((end - start) * totalWidth) / mediaDuration;
1217         IntRect rangeRect;
1218         if (!index) {
1219             rangeRect = trackRect;
1220             rangeRect.setWidth(width);
1221         } else {
1222             rangeRect.setLocation(IntPoint(trackRect.x() + start / mediaDuration* totalWidth, trackRect.y()));
1223             rangeRect.setSize(IntSize(width, trackRect.height()));
1224         }
1225
1226         // Don't bother drawing empty range.
1227         if (rangeRect.isEmpty())
1228             continue;
1229
1230         IntPoint sliderTopLeft = rangeRect.location();
1231         IntPoint sliderTopRight = sliderTopLeft;
1232         sliderTopRight.move(0, rangeRect.height());
1233
1234         context->fillRect(FloatRect(rangeRect), m_mediaPanelColor, ColorSpaceDeviceRGB);
1235     }
1236     context->restore();
1237     return true;
1238 }
1239
1240 bool RenderThemeEfl::paintMediaSliderThumb(RenderObject*, const PaintInfo& info, const IntRect& rect)
1241 {
1242     IntSize thumbRect(3, 3);
1243     info.context->fillRoundedRect(rect, thumbRect, thumbRect, thumbRect, thumbRect, m_sliderThumbColor, ColorSpaceDeviceRGB);
1244     return true;
1245 }
1246
1247 bool RenderThemeEfl::paintMediaVolumeSliderContainer(RenderObject*, const PaintInfo&, const IntRect&)
1248 {
1249     notImplemented();
1250     return false;
1251 }
1252
1253 bool RenderThemeEfl::paintMediaVolumeSliderTrack(RenderObject*, const PaintInfo&, const IntRect&)
1254 {
1255     notImplemented();
1256     return false;
1257 }
1258
1259 bool RenderThemeEfl::paintMediaVolumeSliderThumb(RenderObject*, const PaintInfo&, const IntRect&)
1260 {
1261     notImplemented();
1262     return false;
1263 }
1264
1265 bool RenderThemeEfl::paintMediaCurrentTime(RenderObject*, const PaintInfo& info, const IntRect& rect)
1266 {
1267     info.context->fillRect(FloatRect(rect), m_mediaPanelColor, ColorSpaceDeviceRGB);
1268     return true;
1269 }
1270 #endif
1271
1272 #if ENABLE(VIDEO_TRACK)
1273 bool RenderThemeEfl::supportsClosedCaptioning() const
1274 {
1275     return true;
1276 }
1277
1278 bool RenderThemeEfl::paintMediaToggleClosedCaptionsButton(RenderObject* object, const PaintInfo& info, const IntRect& rect)
1279 {
1280     Node* mediaNode = object->node() ? object->node()->shadowHost() : 0;
1281     if (!mediaNode)
1282         mediaNode = object->node();
1283     if (!mediaNode || (!mediaNode->hasTagName(videoTag)))
1284         return false;
1285
1286     HTMLMediaElement* mediaElement = toHTMLMediaElement(mediaNode);
1287     if (!emitMediaButtonSignal(ToggleCaptionsButton, mediaElement->webkitClosedCaptionsVisible() ? MediaShowClosedCaptionsButton : MediaHideClosedCaptionsButton, rect))
1288         return false;
1289
1290     return paintThemePart(object, ToggleCaptionsButton, info, rect);
1291 }
1292 #endif
1293
1294 #undef _ASSERT_ON_RELEASE_RETURN
1295 #undef _ASSERT_ON_RELEASE_RETURN_VAL
1296
1297 }