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 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 * Library General Public License for more details.
18 *
19 * You should have received a copy of the GNU Library General Public License
20 * along with this library; see the file COPYING.LIB.  If not, write to
21 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
23 *
24 */
25
26#include "config.h"
27#include "RenderThemeEfl.h"
28
29#include "CSSValueKeywords.h"
30#include "FileSystem.h"
31#include "Frame.h"
32#include "FrameView.h"
33#include "GraphicsContext.h"
34#include "NotImplemented.h"
35#include "PaintInfo.h"
36#include "Page.h"
37#include "PlatformContextCairo.h"
38#include "RenderBox.h"
39#include "RenderObject.h"
40#include "RenderProgress.h"
41#include "RenderSlider.h"
42#include "UserAgentStyleSheets.h"
43#include <wtf/text/CString.h>
44
45#include <Ecore_Evas.h>
46#include <Edje.h>
47
48#if ENABLE(VIDEO)
49#include "HTMLMediaElement.h"
50#include "HTMLNames.h"
51#endif
52
53namespace WebCore {
54#if ENABLE(VIDEO)
55using namespace HTMLNames;
56#endif
57
58// TODO: change from object count to ecore_evas size (bytes)
59// TODO: as objects are webpage/user defined and they can be very large.
60#define RENDER_THEME_EFL_PART_CACHE_MAX 32
61
62void RenderThemeEfl::adjustSizeConstraints(RenderStyle* style, FormType type) const
63{
64    const struct ThemePartDesc* desc = m_partDescs + (size_t)type;
65
66    if (style->minWidth().isIntrinsicOrAuto())
67        style->setMinWidth(desc->min.width());
68    if (style->minHeight().isIntrinsicOrAuto())
69        style->setMinHeight(desc->min.height());
70
71    if (desc->max.width().value() > 0 && style->maxWidth().isIntrinsicOrAuto())
72        style->setMaxWidth(desc->max.width());
73    if (desc->max.height().value() > 0 && style->maxHeight().isIntrinsicOrAuto())
74        style->setMaxHeight(desc->max.height());
75
76    style->setPaddingTop(desc->padding.top());
77    style->setPaddingBottom(desc->padding.bottom());
78    style->setPaddingLeft(desc->padding.left());
79    style->setPaddingRight(desc->padding.right());
80}
81
82bool RenderThemeEfl::themePartCacheEntryReset(struct ThemePartCacheEntry* entry, FormType type)
83{
84    const char *file, *group;
85
86    ASSERT(entry);
87
88    edje_object_file_get(m_edje, &file, 0);
89    group = edjeGroupFromFormType(type);
90    ASSERT(file);
91    ASSERT(group);
92
93    if (!edje_object_file_set(entry->o, file, group)) {
94        Edje_Load_Error err = edje_object_load_error_get(entry->o);
95        const char *errmsg = edje_load_error_str(err);
96        EINA_LOG_ERR("Could not load '%s' from theme %s: %s",
97                     group, file, errmsg);
98        return false;
99    }
100    return true;
101}
102
103bool RenderThemeEfl::themePartCacheEntrySurfaceCreate(struct ThemePartCacheEntry* entry)
104{
105    int w, h;
106    cairo_status_t status;
107
108    ASSERT(entry);
109    ASSERT(entry->ee);
110
111    ecore_evas_geometry_get(entry->ee, 0, 0, &w, &h);
112    ASSERT(w > 0);
113    ASSERT(h > 0);
114
115    entry->surface = cairo_image_surface_create_for_data((unsigned char *)ecore_evas_buffer_pixels_get(entry->ee),
116                                                      CAIRO_FORMAT_ARGB32, w, h, w * 4);
117    status = cairo_surface_status(entry->surface);
118    if (status != CAIRO_STATUS_SUCCESS) {
119        EINA_LOG_ERR("Could not create cairo surface: %s",
120                     cairo_status_to_string(status));
121        return false;
122    }
123
124    return true;
125}
126
127// allocate a new entry and fill it with edje group
128struct RenderThemeEfl::ThemePartCacheEntry* RenderThemeEfl::cacheThemePartNew(FormType type, const IntSize& size)
129{
130    struct ThemePartCacheEntry *entry = new struct ThemePartCacheEntry;
131
132    if (!entry) {
133        EINA_LOG_ERR("could not allocate ThemePartCacheEntry.");
134        return 0;
135    }
136
137    entry->ee = ecore_evas_buffer_new(size.width(), size.height());
138    if (!entry->ee) {
139        EINA_LOG_ERR("ecore_evas_buffer_new(%d, %d) failed.",
140                     size.width(), size.height());
141        delete entry;
142        return 0;
143    }
144
145    entry->o = edje_object_add(ecore_evas_get(entry->ee));
146    ASSERT(entry->o);
147    if (!themePartCacheEntryReset(entry, type)) {
148        evas_object_del(entry->o);
149        ecore_evas_free(entry->ee);
150        delete entry;
151        return 0;
152    }
153
154    if (!themePartCacheEntrySurfaceCreate(entry)) {
155        evas_object_del(entry->o);
156        ecore_evas_free(entry->ee);
157        delete entry;
158        return 0;
159    }
160
161    evas_object_resize(entry->o, size.width(), size.height());
162    evas_object_show(entry->o);
163
164    entry->type = type;
165    entry->size = size;
166
167    m_partCache.prepend(entry);
168    return entry;
169}
170
171// just change the edje group and return the same entry
172struct RenderThemeEfl::ThemePartCacheEntry* RenderThemeEfl::cacheThemePartReset(FormType type, struct RenderThemeEfl::ThemePartCacheEntry* entry)
173{
174    if (!themePartCacheEntryReset(entry, type)) {
175        entry->type = FormTypeLast; // invalidate
176        m_partCache.append(entry);
177        return 0;
178    }
179    entry->type = type;
180    m_partCache.prepend(entry);
181    return entry;
182}
183
184// resize entry and reset it
185struct RenderThemeEfl::ThemePartCacheEntry* RenderThemeEfl::cacheThemePartResizeAndReset(FormType type, const IntSize& size, struct RenderThemeEfl::ThemePartCacheEntry* entry)
186{
187    cairo_surface_finish(entry->surface);
188    ecore_evas_resize(entry->ee, size.width(), size.height());
189    evas_object_resize(entry->o, size.width(), size.height());
190
191    if (!themePartCacheEntrySurfaceCreate(entry)) {
192        evas_object_del(entry->o);
193        ecore_evas_free(entry->ee);
194        delete entry;
195        return 0;
196    }
197
198    return cacheThemePartReset(type, entry);
199}
200
201// general purpose get (will create, reuse and all)
202struct RenderThemeEfl::ThemePartCacheEntry* RenderThemeEfl::cacheThemePartGet(FormType type, const IntSize& size)
203{
204    Vector<struct ThemePartCacheEntry *>::iterator itr, end;
205    struct ThemePartCacheEntry *ce_last_size = 0;
206    int i, idxLastSize = -1;
207
208    itr = m_partCache.begin();
209    end = m_partCache.end();
210    for (i = 0; itr != end; i++, itr++) {
211        struct ThemePartCacheEntry *entry = *itr;
212        if (entry->size == size) {
213            if (entry->type == type)
214                return entry;
215            ce_last_size = entry;
216            idxLastSize = i;
217        }
218    }
219
220    if (m_partCache.size() < RENDER_THEME_EFL_PART_CACHE_MAX)
221        return cacheThemePartNew(type, size);
222
223    if (ce_last_size && ce_last_size != m_partCache.first()) {
224        m_partCache.remove(idxLastSize);
225        return cacheThemePartReset(type, ce_last_size);
226    }
227
228    ThemePartCacheEntry* entry = m_partCache.last();
229    m_partCache.removeLast();
230    return cacheThemePartResizeAndReset(type, size, entry);
231}
232
233void RenderThemeEfl::cacheThemePartFlush()
234{
235    Vector<struct ThemePartCacheEntry *>::iterator itr, end;
236
237    itr = m_partCache.begin();
238    end = m_partCache.end();
239    for (; itr != end; itr++) {
240        struct ThemePartCacheEntry *entry = *itr;
241        cairo_surface_finish(entry->surface);
242        evas_object_del(entry->o);
243        ecore_evas_free(entry->ee);
244        delete entry;
245    }
246    m_partCache.clear();
247}
248
249void RenderThemeEfl::applyEdjeStateFromForm(Evas_Object* object, ControlStates states)
250{
251    const char *signals[] = { // keep in sync with WebCore/platform/ThemeTypes.h
252        "hovered",
253        "pressed",
254        "focused",
255        "enabled",
256        "checked",
257        "read-only",
258        "default",
259        "window-inactive",
260        "indeterminate"
261    };
262
263    edje_object_signal_emit(object, "reset", "");
264
265    for (size_t i = 0; i < WTF_ARRAY_LENGTH(signals); ++i) {
266        if (states & (1 << i))
267            edje_object_signal_emit(object, signals[i], "");
268    }
269}
270
271bool RenderThemeEfl::paintThemePart(RenderObject* object, FormType type, const PaintInfo& info, const IntRect& rect)
272{
273    ThemePartCacheEntry* entry;
274    Eina_List* updates;
275    cairo_t* cairo;
276
277    ASSERT(m_canvas);
278    ASSERT(m_edje);
279
280    entry = cacheThemePartGet(type, rect.size());
281    ASSERT(entry);
282    if (!entry)
283        return false;
284
285    applyEdjeStateFromForm(entry->o, controlStatesForRenderer(object));
286
287    cairo = info.context->platformContext()->cr();
288    ASSERT(cairo);
289
290    // Currently, only sliders needs this message; if other widget ever needs special
291    // treatment, move them to special functions.
292    if (type == SliderVertical || type == SliderHorizontal) {
293        RenderSlider* renderSlider = toRenderSlider(object);
294        Edje_Message_Float_Set* msg;
295        int max, value;
296
297        if (type == SliderVertical) {
298            max = rect.height() - renderSlider->thumbRect().height();
299            value = renderSlider->thumbRect().y();
300        } else {
301            max = rect.width() - renderSlider->thumbRect().width();
302            value = renderSlider->thumbRect().x();
303        }
304
305        msg = static_cast<Edje_Message_Float_Set*>(alloca(sizeof(Edje_Message_Float_Set) + sizeof(float)));
306
307        msg->count = 2;
308        msg->val[0] = static_cast<float>(value) / static_cast<float>(max);
309        msg->val[1] = 0.1;
310        edje_object_message_send(entry->o, EDJE_MESSAGE_FLOAT_SET, 0, msg);
311#if ENABLE(PROGRESS_TAG)
312    } else if (type == ProgressBar) {
313        RenderProgress* renderProgress = toRenderProgress(object);
314        Edje_Message_Float_Set* msg;
315        int max;
316        double value;
317
318        msg = static_cast<Edje_Message_Float_Set*>(alloca(sizeof(Edje_Message_Float_Set) + sizeof(float)));
319        max = rect.width();
320        value = renderProgress->position();
321
322        msg->count = 2;
323        if (object->style()->direction() == RTL)
324            msg->val[0] = (1.0 - value) * max;
325        else
326            msg->val[0] = 0;
327        msg->val[1] = value;
328        edje_object_message_send(entry->o, EDJE_MESSAGE_FLOAT_SET, 0, msg);
329#endif
330    }
331
332    edje_object_calc_force(entry->o);
333    edje_object_message_signal_process(entry->o);
334    updates = evas_render_updates(ecore_evas_get(entry->ee));
335    evas_render_updates_free(updates);
336
337    cairo_save(cairo);
338    cairo_set_source_surface(cairo, entry->surface, rect.x(), rect.y());
339    cairo_paint_with_alpha(cairo, 1.0);
340    cairo_restore(cairo);
341
342    return false;
343}
344
345PassRefPtr<RenderTheme> RenderThemeEfl::create(Page* page)
346{
347    return adoptRef(new RenderThemeEfl(page));
348}
349
350PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page)
351{
352    if (page)
353        return RenderThemeEfl::create(page);
354
355    static RenderTheme* fallback = RenderThemeEfl::create(0).releaseRef();
356    return fallback;
357}
358
359static void renderThemeEflColorClassSelectionActive(void* data, Evas_Object* object, const char* signal, const char* source)
360{
361    RenderThemeEfl* that = static_cast<RenderThemeEfl *>(data);
362    int fr, fg, fb, fa, br, bg, bb, ba;
363
364    if (!edje_object_color_class_get(object, source, &fr, &fg, &fb, &fa, &br, &bg, &bb, &ba, 0, 0, 0, 0))
365        return;
366
367    that->setActiveSelectionColor(fr, fg, fb, fa, br, bg, bb, ba);
368}
369
370static void renderThemeEflColorClassSelectionInactive(void* data, Evas_Object* object, const char* signal, const char* source)
371{
372    RenderThemeEfl* that = static_cast<RenderThemeEfl *>(data);
373    int fr, fg, fb, fa, br, bg, bb, ba;
374
375    if (!edje_object_color_class_get(object, source, &fr, &fg, &fb, &fa, &br, &bg, &bb, &ba, 0, 0, 0, 0))
376        return;
377
378    that->setInactiveSelectionColor(fr, fg, fb, fa, br, bg, bb, ba);
379}
380
381static void renderThemeEflColorClassFocusRing(void* data, Evas_Object* object, const char* signal, const char* source)
382{
383    RenderThemeEfl* that = static_cast<RenderThemeEfl *>(data);
384    int fr, fg, fb, fa;
385
386    if (!edje_object_color_class_get(object, source, &fr, &fg, &fb, &fa, 0, 0, 0, 0, 0, 0, 0, 0))
387        return;
388
389    that->setFocusRingColor(fr, fg, fb, fa);
390}
391
392static void renderThemeEflColorClassButtonText(void* data, Evas_Object* object, const char* signal, const char* source)
393{
394    RenderThemeEfl* that = static_cast<RenderThemeEfl *>(data);
395    int fr, fg, fb, fa, br, bg, bb, ba;
396
397    if (!edje_object_color_class_get(object, source, &fr, &fg, &fb, &fa, &br, &bg, &bb, &ba, 0, 0, 0, 0))
398        return;
399
400    that->setButtonTextColor(fr, fg, fb, fa, br, bg, bb, ba);
401}
402
403static void renderThemeEflColorClassComboText(void* data, Evas_Object* object, const char* signal, const char* source)
404{
405    RenderThemeEfl* that = static_cast<RenderThemeEfl *>(data);
406    int fr, fg, fb, fa, br, bg, bb, ba;
407
408    if (!edje_object_color_class_get(object, source, &fr, &fg, &fb, &fa, &br, &bg, &bb, &ba, 0, 0, 0, 0))
409        return;
410
411    that->setComboTextColor(fr, fg, fb, fa, br, bg, bb, ba);
412}
413
414static void renderThemeEflColorClassEntryText(void* data, Evas_Object* object, const char* signal, const char* source)
415{
416    RenderThemeEfl* that = static_cast<RenderThemeEfl *>(data);
417    int fr, fg, fb, fa, br, bg, bb, ba;
418
419    if (!edje_object_color_class_get(object, source, &fr, &fg, &fb, &fa, &br, &bg, &bb, &ba, 0, 0, 0, 0))
420        return;
421
422    that->setEntryTextColor(fr, fg, fb, fa, br, bg, bb, ba);
423}
424
425static void renderThemeEflColorClassSearchText(void* data, Evas_Object* object, const char* signal, const char* source)
426{
427    RenderThemeEfl* that = static_cast<RenderThemeEfl *>(data);
428    int fr, fg, fb, fa, br, bg, bb, ba;
429    if (!edje_object_color_class_get(object, source, &fr, &fg, &fb, &fa, &br, &bg, &bb, &ba, 0, 0, 0, 0))
430        return;
431
432    that->setSearchTextColor(fr, fg, fb, fa, br, bg, bb, ba);
433}
434
435void RenderThemeEfl::createCanvas()
436{
437    ASSERT(!m_canvas);
438    m_canvas = ecore_evas_buffer_new(1, 1);
439    ASSERT(m_canvas);
440}
441
442void RenderThemeEfl::createEdje()
443{
444    ASSERT(!m_edje);
445    Frame* frame = m_page ? m_page->mainFrame() : 0;
446    FrameView* view = frame ? frame->view() : 0;
447    String theme = view ? view->edjeThemeRecursive() : "";
448    if (theme.isEmpty())
449        EINA_LOG_ERR("No theme defined, unable to set RenderThemeEfl.");
450    else {
451        m_edje = edje_object_add(ecore_evas_get(m_canvas));
452        if (!m_edje)
453            EINA_LOG_ERR("Could not create base edje object.");
454        else if (!edje_object_file_set(m_edje, theme.utf8().data(), "webkit/base")) {
455            Edje_Load_Error err = edje_object_load_error_get(m_edje);
456            const char* errmsg = edje_load_error_str(err);
457            EINA_LOG_ERR("Could not load 'webkit/base' from theme %s: %s",
458                         theme.utf8().data(), errmsg);
459            evas_object_del(m_edje);
460            m_edje = 0;
461        } else {
462#define CONNECT(cc, func)                                               \
463            edje_object_signal_callback_add(m_edje, "color_class,set",  \
464                                            "webkit/"cc, func, this)
465
466            CONNECT("selection/active",
467                    renderThemeEflColorClassSelectionActive);
468            CONNECT("selection/inactive",
469                    renderThemeEflColorClassSelectionInactive);
470            CONNECT("focus_ring", renderThemeEflColorClassFocusRing);
471            CONNECT("button/text", renderThemeEflColorClassButtonText);
472            CONNECT("combo/text", renderThemeEflColorClassComboText);
473            CONNECT("entry/text", renderThemeEflColorClassEntryText);
474            CONNECT("search/text", renderThemeEflColorClassSearchText);
475#undef CONNECT
476        }
477    }
478    ASSERT(m_edje);
479}
480
481void RenderThemeEfl::applyEdjeColors()
482{
483    int fr, fg, fb, fa, br, bg, bb, ba;
484    ASSERT(m_edje);
485#define COLOR_GET(cls)                                                  \
486    edje_object_color_class_get(m_edje, "webkit/"cls,                   \
487                                &fr, &fg, &fb, &fa, &br, &bg, &bb, &ba, \
488                                0, 0, 0, 0)
489
490    if (COLOR_GET("selection/active")) {
491        m_activeSelectionForegroundColor = Color(fr, fg, fb, fa);
492        m_activeSelectionBackgroundColor = Color(br, bg, bb, ba);
493    }
494    if (COLOR_GET("selection/inactive")) {
495        m_inactiveSelectionForegroundColor = Color(fr, fg, fb, fa);
496        m_inactiveSelectionBackgroundColor = Color(br, bg, bb, ba);
497    }
498    if (COLOR_GET("focus_ring")) {
499        m_focusRingColor = Color(fr, fg, fb, fa);
500        // webkit just use platformFocusRingColor() for default theme (without page)
501        // this is ugly, but no other way to do it unless we change
502        // it to use page themes as much as possible.
503        RenderTheme::setCustomFocusRingColor(m_focusRingColor);
504    }
505    if (COLOR_GET("button/text")) {
506        m_buttonTextForegroundColor = Color(fr, fg, fb, fa);
507        m_buttonTextBackgroundColor = Color(br, bg, bb, ba);
508    }
509    if (COLOR_GET("combo/text")) {
510        m_comboTextForegroundColor = Color(fr, fg, fb, fa);
511        m_comboTextBackgroundColor = Color(br, bg, bb, ba);
512    }
513    if (COLOR_GET("entry/text")) {
514        m_entryTextForegroundColor = Color(fr, fg, fb, fa);
515        m_entryTextBackgroundColor = Color(br, bg, bb, ba);
516    }
517    if (COLOR_GET("search/text")) {
518        m_searchTextForegroundColor = Color(fr, fg, fb, fa);
519        m_searchTextBackgroundColor = Color(br, bg, bb, ba);
520    }
521#undef COLOR_GET
522    platformColorsDidChange();
523}
524
525void RenderThemeEfl::applyPartDescriptionFallback(struct ThemePartDesc* desc)
526{
527    desc->min.setWidth(Length(0, Fixed));
528    desc->min.setHeight(Length(0, Fixed));
529
530    desc->max.setWidth(Length(0, Fixed));
531    desc->max.setHeight(Length(0, Fixed));
532
533    desc->padding = LengthBox(0, 0, 0, 0);
534}
535
536void RenderThemeEfl::applyPartDescription(Evas_Object* object, struct ThemePartDesc* desc)
537{
538    Evas_Coord minw, minh, maxw, maxh;
539
540    edje_object_size_min_get(object, &minw, &minh);
541    if (!minw && !minh)
542        edje_object_size_min_calc(object, &minw, &minh);
543
544    desc->min.setWidth(Length(minw, Fixed));
545    desc->min.setHeight(Length(minh, Fixed));
546
547    edje_object_size_max_get(object, &maxw, &maxh);
548    desc->max.setWidth(Length(maxw, Fixed));
549    desc->max.setHeight(Length(maxh, Fixed));
550
551    if (!edje_object_part_exists(object, "text_confinement"))
552        desc->padding = LengthBox(0, 0, 0, 0);
553    else {
554        Evas_Coord px, py, pw, ph;
555        Evas_Coord ox = 0, oy = 0, ow = 0, oh = 0;
556        int t, r, b, l;
557
558        if (minw > 0)
559            ow = minw;
560        else
561            ow = 100;
562        if (minh > 0)
563            oh = minh;
564        else
565            oh = 100;
566        if (maxw > 0 && ow > maxw)
567            ow = maxw;
568        if (maxh > 0 && oh > maxh)
569            oh = maxh;
570
571        evas_object_move(object, ox, oy);
572        evas_object_resize(object, ow, oh);
573        edje_object_calc_force(object);
574        edje_object_message_signal_process(object);
575        edje_object_part_geometry_get(object, "text_confinement", &px, &py, &pw, &ph);
576
577        t = py - oy;
578        b = (oh + oy) - (ph + py);
579
580        l = px - ox;
581        r = (ow + ox) - (pw + px);
582
583        desc->padding = LengthBox(t, r, b, l);
584    }
585}
586
587const char* RenderThemeEfl::edjeGroupFromFormType(FormType type) const
588{
589    static const char* groups[] = {
590#define W(n) "webkit/widget/"n
591        W("button"),
592        W("radio"),
593        W("entry"),
594        W("checkbox"),
595        W("combo"),
596#if ENABLE(PROGRESS_TAG)
597        W("progressbar"),
598#endif
599        W("search/field"),
600        W("search/decoration"),
601        W("search/results_button"),
602        W("search/results_decoration"),
603        W("search/cancel_button"),
604        W("slider/vertical"),
605        W("slider/horizontal"),
606#if ENABLE(VIDEO)
607        W("mediacontrol/playpause_button"),
608        W("mediacontrol/mute_button"),
609        W("mediacontrol/seekforward_button"),
610        W("mediacontrol/seekbackward_button"),
611#endif
612#undef W
613        0
614    };
615    ASSERT(type >= 0);
616    ASSERT((size_t)type < sizeof(groups) / sizeof(groups[0])); // out of sync?
617    return groups[type];
618}
619
620void RenderThemeEfl::applyPartDescriptions()
621{
622    Evas_Object* object;
623    unsigned int i;
624    const char* file;
625
626    ASSERT(m_canvas);
627    ASSERT(m_edje);
628
629    edje_object_file_get(m_edje, &file, 0);
630    ASSERT(file);
631
632    object = edje_object_add(ecore_evas_get(m_canvas));
633    if (!object) {
634        EINA_LOG_ERR("Could not create Edje object.");
635        return;
636    }
637
638    for (i = 0; i < FormTypeLast; i++) {
639        FormType type = static_cast<FormType>(i);
640        const char* group = edjeGroupFromFormType(type);
641        m_partDescs[i].type = type;
642        if (!edje_object_file_set(object, file, group)) {
643            Edje_Load_Error err = edje_object_load_error_get(object);
644            const char* errmsg = edje_load_error_str(err);
645            EINA_LOG_ERR("Could not set theme group '%s' of file '%s': %s",
646                         group, file, errmsg);
647
648            applyPartDescriptionFallback(m_partDescs + i);
649        } else
650            applyPartDescription(object, m_partDescs + i);
651    }
652    evas_object_del(object);
653}
654
655void RenderThemeEfl::themeChanged()
656{
657    cacheThemePartFlush();
658
659    if (!m_canvas) {
660        createCanvas();
661        if (!m_canvas)
662            return;
663    }
664
665    if (!m_edje) {
666        createEdje();
667        if (!m_edje)
668            return;
669    }
670
671    applyEdjeColors();
672    applyPartDescriptions();
673}
674
675float RenderThemeEfl::defaultFontSize = 16.0f;
676
677RenderThemeEfl::RenderThemeEfl(Page* page)
678    : RenderTheme()
679    , m_page(page)
680    , m_activeSelectionBackgroundColor(0, 0, 255)
681    , m_activeSelectionForegroundColor(255, 255, 255)
682    , m_inactiveSelectionBackgroundColor(0, 0, 128)
683    , m_inactiveSelectionForegroundColor(200, 200, 200)
684    , m_focusRingColor(32, 32, 224, 224)
685    , m_buttonTextBackgroundColor(0, 0, 0, 0)
686    , m_buttonTextForegroundColor(0, 0, 0)
687    , m_comboTextBackgroundColor(0, 0, 0, 0)
688    , m_comboTextForegroundColor(0, 0, 0)
689    , m_entryTextBackgroundColor(0, 0, 0, 0)
690    , m_entryTextForegroundColor(0, 0, 0)
691    , m_searchTextBackgroundColor(0, 0, 0, 0)
692    , m_searchTextForegroundColor(0, 0, 0)
693    , m_canvas(0)
694    , m_edje(0)
695{
696    if (page && page->mainFrame() && page->mainFrame()->view())
697        themeChanged();
698}
699
700RenderThemeEfl::~RenderThemeEfl()
701{
702    cacheThemePartFlush();
703
704    if (m_canvas) {
705        if (m_edje)
706            evas_object_del(m_edje);
707        ecore_evas_free(m_canvas);
708    }
709}
710
711void RenderThemeEfl::setActiveSelectionColor(int foreR, int foreG, int foreB, int foreA, int backR, int backG, int backB, int backA)
712{
713    m_activeSelectionForegroundColor = Color(foreR, foreG, foreB, foreA);
714    m_activeSelectionBackgroundColor = Color(backR, backG, backB, backA);
715    platformColorsDidChange();
716}
717
718void RenderThemeEfl::setInactiveSelectionColor(int foreR, int foreG, int foreB, int foreA, int backR, int backG, int backB, int backA)
719{
720    m_inactiveSelectionForegroundColor = Color(foreR, foreG, foreB, foreA);
721    m_inactiveSelectionBackgroundColor = Color(backR, backG, backB, backA);
722    platformColorsDidChange();
723}
724
725void RenderThemeEfl::setFocusRingColor(int r, int g, int b, int a)
726{
727    m_focusRingColor = Color(r, g, b, a);
728    // webkit just use platformFocusRingColor() for default theme (without page)
729    // this is ugly, but no other way to do it unless we change
730    // it to use page themes as much as possible.
731    RenderTheme::setCustomFocusRingColor(m_focusRingColor);
732    platformColorsDidChange();
733}
734
735void RenderThemeEfl::setButtonTextColor(int foreR, int foreG, int foreB, int foreA, int backR, int backG, int backB, int backA)
736{
737    m_buttonTextForegroundColor = Color(foreR, foreG, foreB, foreA);
738    m_buttonTextBackgroundColor = Color(backR, backG, backB, backA);
739    platformColorsDidChange();
740}
741
742void RenderThemeEfl::setComboTextColor(int foreR, int foreG, int foreB, int foreA, int backR, int backG, int backB, int backA)
743{
744    m_comboTextForegroundColor = Color(foreR, foreG, foreB, foreA);
745    m_comboTextBackgroundColor = Color(backR, backG, backB, backA);
746    platformColorsDidChange();
747}
748
749void RenderThemeEfl::setEntryTextColor(int foreR, int foreG, int foreB, int foreA, int backR, int backG, int backB, int backA)
750{
751    m_entryTextForegroundColor = Color(foreR, foreG, foreB, foreA);
752    m_entryTextBackgroundColor = Color(backR, backG, backB, backA);
753    platformColorsDidChange();
754}
755
756void RenderThemeEfl::setSearchTextColor(int foreR, int foreG, int foreB, int foreA, int backR, int backG, int backB, int backA)
757{
758    m_searchTextForegroundColor = Color(foreR, foreG, foreB, foreA);
759    m_searchTextBackgroundColor = Color(backR, backG, backB, backA);
760    platformColorsDidChange();
761}
762
763static bool supportsFocus(ControlPart appearance)
764{
765    switch (appearance) {
766    case PushButtonPart:
767    case ButtonPart:
768    case TextFieldPart:
769    case TextAreaPart:
770    case SearchFieldPart:
771    case MenulistPart:
772    case RadioPart:
773    case CheckboxPart:
774    case SliderVerticalPart:
775    case SliderHorizontalPart:
776        return true;
777    default:
778        return false;
779    }
780}
781
782bool RenderThemeEfl::supportsFocusRing(const RenderStyle* style) const
783{
784    return supportsFocus(style->appearance());
785}
786
787bool RenderThemeEfl::controlSupportsTints(const RenderObject* object) const
788{
789    return isEnabled(object);
790}
791
792int RenderThemeEfl::baselinePosition(const RenderObject* object) const
793{
794    if (!object->isBox())
795        return 0;
796
797    if (object->style()->appearance() == CheckboxPart
798    ||  object->style()->appearance() == RadioPart)
799        return toRenderBox(object)->marginTop() + toRenderBox(object)->height() - 3;
800
801    return RenderTheme::baselinePosition(object);
802}
803
804bool RenderThemeEfl::paintSliderTrack(RenderObject* object, const PaintInfo& info, const IntRect& rect)
805{
806    if (object->style()->appearance() == SliderHorizontalPart)
807        return paintThemePart(object, SliderHorizontal, info, rect);
808    return paintThemePart(object, SliderVertical, info, rect);
809}
810
811void RenderThemeEfl::adjustSliderTrackStyle(CSSStyleSelector* selector, RenderStyle* style, Element* element) const
812{
813    if (!m_page && element && element->document()->page()) {
814        static_cast<RenderThemeEfl*>(element->document()->page()->theme())->adjustSliderTrackStyle(selector, style, element);
815        return;
816    }
817
818    adjustSizeConstraints(style, SliderHorizontal);
819    style->resetBorder();
820
821    const struct ThemePartDesc *desc = m_partDescs + (size_t)SliderHorizontal;
822    if (style->width().value() < desc->min.width().value())
823        style->setWidth(desc->min.width());
824    if (style->height().value() < desc->min.height().value())
825        style->setHeight(desc->min.height());
826}
827
828void RenderThemeEfl::adjustSliderThumbStyle(CSSStyleSelector* selector, RenderStyle* style, Element* element) const
829{
830    adjustSliderTrackStyle(selector, style, element);
831}
832
833bool RenderThemeEfl::paintSliderThumb(RenderObject* object, const PaintInfo& info, const IntRect& rect)
834{
835    return paintSliderTrack(object, info, rect);
836}
837
838void RenderThemeEfl::adjustCheckboxStyle(CSSStyleSelector* selector, RenderStyle* style, Element* element) const
839{
840    if (!m_page && element && element->document()->page()) {
841        static_cast<RenderThemeEfl*>(element->document()->page()->theme())->adjustCheckboxStyle(selector, style, element);
842        return;
843    }
844    adjustSizeConstraints(style, CheckBox);
845    style->resetBorder();
846
847    const struct ThemePartDesc *desc = m_partDescs + (size_t)CheckBox;
848    if (style->width().value() < desc->min.width().value())
849        style->setWidth(desc->min.width());
850    if (style->height().value() < desc->min.height().value())
851        style->setHeight(desc->min.height());
852}
853
854bool RenderThemeEfl::paintCheckbox(RenderObject* object, const PaintInfo& info, const IntRect& rect)
855{
856    return paintThemePart(object, CheckBox, info, rect);
857}
858
859void RenderThemeEfl::adjustRadioStyle(CSSStyleSelector* selector, RenderStyle* style, Element* element) const
860{
861    if (!m_page && element && element->document()->page()) {
862        static_cast<RenderThemeEfl*>(element->document()->page()->theme())->adjustRadioStyle(selector, style, element);
863        return;
864    }
865    adjustSizeConstraints(style, RadioButton);
866    style->resetBorder();
867
868    const struct ThemePartDesc *desc = m_partDescs + (size_t)RadioButton;
869    if (style->width().value() < desc->min.width().value())
870        style->setWidth(desc->min.width());
871    if (style->height().value() < desc->min.height().value())
872        style->setHeight(desc->min.height());
873}
874
875bool RenderThemeEfl::paintRadio(RenderObject* object, const PaintInfo& info, const IntRect& rect)
876{
877    return paintThemePart(object, RadioButton, info, rect);
878}
879
880void RenderThemeEfl::adjustButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* element) const
881{
882    if (!m_page && element && element->document()->page()) {
883        static_cast<RenderThemeEfl*>(element->document()->page()->theme())->adjustButtonStyle(selector, style, element);
884        return;
885    }
886
887    adjustSizeConstraints(style, Button);
888
889    if (style->appearance() == PushButtonPart) {
890        style->resetBorder();
891        style->setWhiteSpace(PRE);
892        style->setHeight(Length(Auto));
893        style->setColor(m_buttonTextForegroundColor);
894        style->setBackgroundColor(m_buttonTextBackgroundColor);
895    }
896}
897
898bool RenderThemeEfl::paintButton(RenderObject* object, const PaintInfo& info, const IntRect& rect)
899{
900    return paintThemePart(object, Button, info, rect);
901}
902
903void RenderThemeEfl::adjustMenuListStyle(CSSStyleSelector* selector, RenderStyle* style, Element* element) const
904{
905    if (!m_page && element && element->document()->page()) {
906        static_cast<RenderThemeEfl*>(element->document()->page()->theme())->adjustMenuListStyle(selector, style, element);
907        return;
908    }
909    adjustSizeConstraints(style, ComboBox);
910    style->resetBorder();
911    style->setWhiteSpace(PRE);
912    style->setColor(m_comboTextForegroundColor);
913    style->setBackgroundColor(m_comboTextBackgroundColor);
914}
915
916bool RenderThemeEfl::paintMenuList(RenderObject* object, const PaintInfo& info, const IntRect& rect)
917{
918    return paintThemePart(object, ComboBox, info, rect);
919}
920
921void RenderThemeEfl::adjustTextFieldStyle(CSSStyleSelector* selector, RenderStyle* style, Element* element) const
922{
923    if (!m_page && element && element->document()->page()) {
924        static_cast<RenderThemeEfl*>(element->document()->page()->theme())->adjustTextFieldStyle(selector, style, element);
925        return;
926    }
927    adjustSizeConstraints(style, TextField);
928    style->resetBorder();
929    style->setWhiteSpace(PRE);
930    style->setColor(m_entryTextForegroundColor);
931    style->setBackgroundColor(m_entryTextBackgroundColor);
932}
933
934bool RenderThemeEfl::paintTextField(RenderObject* object, const PaintInfo& info, const IntRect& rect)
935{
936    return paintThemePart(object, TextField, info, rect);
937}
938
939void RenderThemeEfl::adjustTextAreaStyle(CSSStyleSelector* selector, RenderStyle* style, Element* element) const
940{
941    adjustTextFieldStyle(selector, style, element);
942}
943
944bool RenderThemeEfl::paintTextArea(RenderObject* object, const PaintInfo& info, const IntRect& rect)
945{
946    return paintTextField(object, info, rect);
947}
948
949void RenderThemeEfl::adjustSearchFieldDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, Element* element) const
950{
951    if (!m_page && element && element->document()->page()) {
952        static_cast<RenderThemeEfl*>(element->document()->page()->theme())->adjustSearchFieldDecorationStyle(selector, style, element);
953        return;
954    }
955    adjustSizeConstraints(style, SearchFieldDecoration);
956    style->resetBorder();
957    style->setWhiteSpace(PRE);
958}
959
960bool RenderThemeEfl::paintSearchFieldDecoration(RenderObject* object, const PaintInfo& info, const IntRect& rect)
961{
962    return paintThemePart(object, SearchFieldDecoration, info, rect);
963}
964
965void RenderThemeEfl::adjustSearchFieldResultsButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* element) const
966{
967    if (!m_page && element && element->document()->page()) {
968        static_cast<RenderThemeEfl*>(element->document()->page()->theme())->adjustSearchFieldResultsButtonStyle(selector, style, element);
969        return;
970    }
971    adjustSizeConstraints(style, SearchFieldResultsButton);
972    style->resetBorder();
973    style->setWhiteSpace(PRE);
974}
975
976bool RenderThemeEfl::paintSearchFieldResultsButton(RenderObject* object, const PaintInfo& info, const IntRect& rect)
977{
978    return paintThemePart(object, SearchFieldResultsButton, info, rect);
979}
980
981void RenderThemeEfl::adjustSearchFieldResultsDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, Element* element) const
982{
983    if (!m_page && element && element->document()->page()) {
984        static_cast<RenderThemeEfl*>(element->document()->page()->theme())->adjustSearchFieldResultsDecorationStyle(selector, style, element);
985        return;
986    }
987    adjustSizeConstraints(style, SearchFieldResultsDecoration);
988    style->resetBorder();
989    style->setWhiteSpace(PRE);
990}
991
992bool RenderThemeEfl::paintSearchFieldResultsDecoration(RenderObject* object, const PaintInfo& info, const IntRect& rect)
993{
994    return paintThemePart(object, SearchFieldResultsDecoration, info, rect);
995}
996
997void RenderThemeEfl::adjustSearchFieldCancelButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* element) const
998{
999    if (!m_page && element && element->document()->page()) {
1000        static_cast<RenderThemeEfl*>(element->document()->page()->theme())->adjustSearchFieldCancelButtonStyle(selector, style, element);
1001        return;
1002    }
1003    adjustSizeConstraints(style, SearchFieldCancelButton);
1004    style->resetBorder();
1005    style->setWhiteSpace(PRE);
1006}
1007
1008bool RenderThemeEfl::paintSearchFieldCancelButton(RenderObject* object, const PaintInfo& info, const IntRect& rect)
1009{
1010    return paintThemePart(object, SearchFieldCancelButton, info, rect);
1011}
1012
1013void RenderThemeEfl::adjustSearchFieldStyle(CSSStyleSelector* selector, RenderStyle* style, Element* element) const
1014{
1015    if (!m_page && element && element->document()->page()) {
1016        static_cast<RenderThemeEfl*>(element->document()->page()->theme())->adjustSearchFieldStyle(selector, style, element);
1017        return;
1018    }
1019    adjustSizeConstraints(style, SearchField);
1020    style->resetBorder();
1021    style->setWhiteSpace(PRE);
1022    style->setColor(m_searchTextForegroundColor);
1023    style->setBackgroundColor(m_searchTextBackgroundColor);
1024}
1025
1026bool RenderThemeEfl::paintSearchField(RenderObject* object, const PaintInfo& info, const IntRect& rect)
1027{
1028    return paintThemePart(object, SearchField, info, rect);
1029}
1030
1031void RenderThemeEfl::setDefaultFontSize(int size)
1032{
1033    defaultFontSize = size;
1034}
1035
1036void RenderThemeEfl::systemFont(int propId, FontDescription& fontDescription) const
1037{
1038    // It was called by RenderEmbeddedObject::paintReplaced to render alternative string.
1039    // To avoid cairo_error while rendering, fontDescription should be passed.
1040    DEFINE_STATIC_LOCAL(String, fontFace, ("Sans"));
1041    float fontSize = defaultFontSize;
1042
1043    fontDescription.firstFamily().setFamily(fontFace);
1044    fontDescription.setSpecifiedSize(fontSize);
1045    fontDescription.setIsAbsoluteSize(true);
1046    fontDescription.setGenericFamily(FontDescription::NoFamily);
1047    fontDescription.setWeight(FontWeightNormal);
1048    fontDescription.setItalic(false);
1049}
1050
1051#if ENABLE(PROGRESS_TAG)
1052void RenderThemeEfl::adjustProgressBarStyle(CSSStyleSelector* selector, RenderStyle* style, Element* element) const
1053{
1054    style->setBoxShadow(0);
1055}
1056
1057bool RenderThemeEfl::paintProgressBar(RenderObject* object, const PaintInfo& info, const IntRect& rect)
1058{
1059    return paintThemePart(object, ProgressBar, info, rect);
1060}
1061#endif
1062
1063#if ENABLE(VIDEO)
1064bool RenderThemeEfl::emitMediaButtonSignal(FormType formType, MediaControlElementType mediaElementType, const IntRect& rect)
1065{
1066    ThemePartCacheEntry* entry;
1067
1068    entry = cacheThemePartGet(formType, rect.size());
1069    ASSERT(entry);
1070    if (!entry)
1071        return false;
1072
1073    if (mediaElementType == MediaPlayButton)
1074        edje_object_signal_emit(entry->o, "play", "");
1075    else if (mediaElementType == MediaPauseButton)
1076        edje_object_signal_emit(entry->o, "pause", "");
1077    else if (mediaElementType == MediaMuteButton)
1078        edje_object_signal_emit(entry->o, "mute", "");
1079    else if (mediaElementType == MediaUnMuteButton)
1080        edje_object_signal_emit(entry->o, "sound", "");
1081    else if (mediaElementType == MediaSeekForwardButton)
1082        edje_object_signal_emit(entry->o, "seekforward", "");
1083    else if (mediaElementType == MediaSeekBackButton)
1084        edje_object_signal_emit(entry->o, "seekbackward", "");
1085    else
1086        return false;
1087
1088    return true;
1089}
1090
1091String RenderThemeEfl::extraMediaControlsStyleSheet()
1092{
1093    return String(mediaControlsEflUserAgentStyleSheet, sizeof(mediaControlsEflUserAgentStyleSheet));
1094}
1095
1096String RenderThemeEfl::formatMediaControlsCurrentTime(float currentTime, float duration) const
1097{
1098    notImplemented();
1099    return String();
1100}
1101
1102bool RenderThemeEfl::paintMediaFullscreenButton(RenderObject* object, const PaintInfo& info, const IntRect& rect)
1103{
1104    notImplemented();
1105    return false;
1106}
1107
1108bool RenderThemeEfl::paintMediaMuteButton(RenderObject* object, const PaintInfo& info, const IntRect& rect)
1109{
1110    Node* mediaNode = object->node() ? object->node()->shadowAncestorNode() : 0;
1111    if (!mediaNode || (!mediaNode->hasTagName(videoTag) && !mediaNode->hasTagName(audioTag)))
1112        return false;
1113
1114    HTMLMediaElement* mediaElement = static_cast<HTMLMediaElement*>(mediaNode);
1115
1116    if (!emitMediaButtonSignal(MuteUnMuteButton, mediaElement->muted() ? MediaMuteButton : MediaUnMuteButton, rect))
1117        return false;
1118
1119    return paintThemePart(object, MuteUnMuteButton, info, rect);
1120}
1121
1122bool RenderThemeEfl::paintMediaPlayButton(RenderObject* object, const PaintInfo& info, const IntRect& rect)
1123{
1124    Node* node = object->node();
1125    if (!node || !node->isMediaControlElement())
1126        return false;
1127
1128    MediaControlPlayButtonElement* button = static_cast<MediaControlPlayButtonElement*>(node);
1129    if (!emitMediaButtonSignal(PlayPauseButton, button->displayType(), rect))
1130        return false;
1131
1132    return paintThemePart(object, PlayPauseButton, info, rect);
1133}
1134
1135bool RenderThemeEfl::paintMediaSeekBackButton(RenderObject* object, const PaintInfo& info, const IntRect& rect)
1136{
1137    Node* node = object->node();
1138    if (!node || !node->isMediaControlElement())
1139        return 0;
1140
1141    MediaControlSeekButtonElement* button = static_cast<MediaControlSeekButtonElement*>(node);
1142    if (!emitMediaButtonSignal(SeekBackwardButton, button->displayType(), rect))
1143        return false;
1144
1145    return paintThemePart(object, SeekBackwardButton, info, rect);
1146}
1147
1148bool RenderThemeEfl::paintMediaSeekForwardButton(RenderObject* object, const PaintInfo& info, const IntRect& rect)
1149{
1150    Node* node = object->node();
1151    if (!node || !node->isMediaControlElement())
1152        return 0;
1153
1154    MediaControlSeekButtonElement* button = static_cast<MediaControlSeekButtonElement*>(node);
1155    if (!emitMediaButtonSignal(SeekForwardButton, button->displayType(), rect))
1156        return false;
1157
1158    return paintThemePart(object, SeekForwardButton, info, rect);
1159}
1160
1161bool RenderThemeEfl::paintMediaSliderTrack(RenderObject* object, const PaintInfo& info, const IntRect& rect)
1162{
1163    notImplemented();
1164    return false;
1165}
1166
1167bool RenderThemeEfl::paintMediaSliderThumb(RenderObject* object, const PaintInfo& info, const IntRect& rect)
1168{
1169    notImplemented();
1170    return false;
1171}
1172
1173bool RenderThemeEfl::paintMediaVolumeSliderContainer(RenderObject*, const PaintInfo& info, const IntRect& rect)
1174{
1175    notImplemented();
1176    return false;
1177}
1178
1179bool RenderThemeEfl::paintMediaVolumeSliderTrack(RenderObject* object, const PaintInfo& info, const IntRect& rect)
1180{
1181    notImplemented();
1182    return false;
1183}
1184
1185bool RenderThemeEfl::paintMediaVolumeSliderThumb(RenderObject* object, const PaintInfo& info, const IntRect& rect)
1186{
1187    notImplemented();
1188    return false;
1189}
1190
1191bool RenderThemeEfl::paintMediaCurrentTime(RenderObject* object, const PaintInfo& info, const IntRect& rect)
1192{
1193    notImplemented();
1194    return false;
1195}
1196#endif
1197}
1198