/* * Copyright (C) 2007 Apple Inc. * Copyright (C) 2007 Alp Toker * Copyright (C) 2008 Collabora Ltd. * Copyright (C) 2008 INdT - Instituto Nokia de Tecnologia * Copyright (C) 2009-2010 ProFUSION embedded systems * Copyright (C) 2009-2011 Samsung Electronics * Copyright (c) 2012 Intel Corporation. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * */ #include "config.h" #include "RenderThemeEfl.h" #include "CSSValueKeywords.h" #include "CairoUtilitiesEfl.h" #include "ExceptionCodePlaceholder.h" #include "FloatRoundedRect.h" #include "FontDescription.h" #include "GraphicsContext.h" #include "HTMLInputElement.h" #include "InputTypeNames.h" #include "NotImplemented.h" #include "Page.h" #include "PaintInfo.h" #include "PlatformContextCairo.h" #include "RenderBox.h" #include "RenderObject.h" #include "RenderProgress.h" #include "RenderSlider.h" #include "ScrollbarThemeEfl.h" #include "Settings.h" #include "UserAgentScripts.h" #include "UserAgentStyleSheets.h" #include #include #include #include #include #include namespace WebCore { // TODO: change from object count to ecore_evas size (bytes) // TODO: as objects are webpage/user defined and they can be very large. #define RENDER_THEME_EFL_PART_CACHE_MAX 32 // Initialize default font size. float RenderThemeEfl::defaultFontSize = 16.0f; static const float minCancelButtonSize = 5; static const float maxCancelButtonSize = 21; static const float minSearchDecorationButtonSize = 1; static const float maxSearchDecorationButtonSize = 15; static const float searchFieldDecorationButtonOffset = 3; // Constants for progress tag animation. // These values have been copied from RenderThemeGtk.cpp static const int progressAnimationFrames = 10; static const double progressAnimationInterval = 0.125; static const int sliderThumbWidth = 29; static const int sliderThumbHeight = 11; #define _ASSERT_ON_RELEASE_RETURN(o, fmt, ...) \ do { \ if (!o) { \ EINA_LOG_CRIT(fmt, ## __VA_ARGS__); \ ASSERT(o); \ return; \ } \ } while (0) #define _ASSERT_ON_RELEASE_RETURN_VAL(o, val, fmt, ...) \ do { \ if (!o) { \ EINA_LOG_CRIT(fmt, ## __VA_ARGS__); \ ASSERT(o); \ return val; \ } \ } while (0) static const char* toEdjeGroup(FormType type) { static const char* groups[] = { "webkit/widget/button", "webkit/widget/radio", "webkit/widget/entry", "webkit/widget/checkbox", "webkit/widget/combo", "webkit/widget/progressbar", "webkit/widget/scrollbar/horizontal_thumb", "webkit/widget/scrollbar/horizontal_background", "webkit/widget/scrollbar/vertical_thumb", "webkit/widget/scrollbar/vertical_background", "webkit/widget/search/field", "webkit/widget/search/results_button", "webkit/widget/search/results_decoration", "webkit/widget/search/cancel_button", "webkit/widget/slider/vertical", "webkit/widget/slider/horizontal", "webkit/widget/slider/thumb_vertical", "webkit/widget/slider/thumb_horizontal", "webkit/widget/spinner", 0 }; ASSERT(type >= 0); ASSERT((size_t)type < sizeof(groups) / sizeof(groups[0])); // Out of sync? return groups[type]; } static bool setSourceGroupForEdjeObject(Evas_Object* o, const String& themePath, const char* group) { ASSERT(o); ASSERT(!themePath.isEmpty()); if (!edje_object_file_set(o, themePath.utf8().data(), group)) { const char* message = edje_load_error_str(edje_object_load_error_get(o)); EINA_LOG_ERR("Could not set theme group '%s' of file '%s': %s", group, themePath.utf8().data(), message); return false; } return true; } void RenderThemeEfl::adjustSizeConstraints(RenderStyle& style, FormType type) const { loadThemeIfNeeded(); // These are always valid, even if no theme could be loaded. const ThemePartDesc* desc = m_partDescs + (size_t)type; if (style.minWidth().isIntrinsic()) style.setMinWidth(desc->min.width()); if (style.minHeight().isIntrinsic()) style.setMinHeight(desc->min.height()); if (desc->max.width().value() > 0 && style.maxWidth().isIntrinsicOrAuto()) style.setMaxWidth(desc->max.width()); if (desc->max.height().value() > 0 && style.maxHeight().isIntrinsicOrAuto()) style.setMaxHeight(desc->max.height()); style.setPaddingTop(desc->padding.top()); style.setPaddingBottom(desc->padding.bottom()); style.setPaddingLeft(desc->padding.left()); style.setPaddingRight(desc->padding.right()); } static bool isFormElementTooLargeToDisplay(const IntSize& elementSize) { // This limit of 20000 pixels is hardcoded inside edje -- anything above this size // will be clipped. This value seems to be reasonable enough so that hardcoding it // here won't be a problem. static const int maxEdjeDimension = 20000; return elementSize.width() > maxEdjeDimension || elementSize.height() > maxEdjeDimension; } std::unique_ptr RenderThemeEfl::ThemePartCacheEntry::create(const String& themePath, FormType type, const IntSize& size) { ASSERT(!themePath.isEmpty()); if (isFormElementTooLargeToDisplay(size) || size.isEmpty()) { EINA_LOG_ERR("Cannot render an element of size %dx%d.", size.width(), size.height()); return nullptr; } auto entry = std::make_unique(); entry->m_canvas = EflUniquePtr(ecore_evas_buffer_new(size.width(), size.height())); if (!entry->canvas()) { EINA_LOG_ERR("ecore_evas_buffer_new(%d, %d) failed.", size.width(), size.height()); return nullptr; } // By default EFL creates buffers without alpha. ecore_evas_alpha_set(entry->canvas(), EINA_TRUE); entry->m_edje = EflUniquePtr(edje_object_add(ecore_evas_get(entry->canvas()))); ASSERT(entry->edje()); if (!setSourceGroupForEdjeObject(entry->edje(), themePath, toEdjeGroup(type))) return nullptr; entry->m_surface = createSurfaceForBackingStore(entry->canvas()); if (!entry->surface()) return nullptr; evas_object_resize(entry->edje(), size.width(), size.height()); evas_object_show(entry->edje()); entry->type = type; entry->size = size; return entry; } void RenderThemeEfl::ThemePartCacheEntry::reuse(const String& themePath, FormType newType, const IntSize& newSize) { ASSERT(!themePath.isEmpty()); if (type != newType) { type = newType; if (!setSourceGroupForEdjeObject(edje(), themePath, toEdjeGroup(newType))) { type = FormTypeLast; // Invalidate. return; } } if (size != newSize) { size = newSize; ecore_evas_resize(canvas(), newSize.width(), newSize.height()); evas_object_resize(edje(), newSize.width(), newSize.height()); m_surface = createSurfaceForBackingStore(canvas()); if (!surface()) { type = FormTypeLast; // Invalidate; return; } } } RenderThemeEfl::ThemePartCacheEntry* RenderThemeEfl::getThemePartFromCache(FormType type, const IntSize& size) { size_t reusableNodeIndex = 0; for (size_t i = 0; i < m_partCache.size(); ++i) { ThemePartCacheEntry* candidatedEntry = m_partCache[i].get(); if (candidatedEntry->size == size) { if (candidatedEntry->type == type) { // Found the right item, move it to the head of the list // and return it. auto temp = WTFMove(m_partCache[i]); m_partCache.remove(i); m_partCache.insert(0, WTFMove(temp)); return m_partCache.first().get(); } reusableNodeIndex = i; } } if (m_partCache.size() < RENDER_THEME_EFL_PART_CACHE_MAX) { auto entry = ThemePartCacheEntry::create(themePath(), type, size); if (entry) m_partCache.insert(0, WTFMove(entry)); return m_partCache.first().get(); } // The cache is full, reuse the last item we found that had the // requested size to avoid resizing. If there was none, reuse // the last item of the list. if (!reusableNodeIndex) reusableNodeIndex = m_partCache.size() - 1; ThemePartCacheEntry* reusedEntry = m_partCache[reusableNodeIndex].get(); ASSERT(reusedEntry); reusedEntry->reuse(themePath(), type, size); auto temp = WTFMove(m_partCache[reusableNodeIndex]); m_partCache.remove(reusableNodeIndex); m_partCache.insert(0, WTFMove(temp)); return m_partCache.first().get(); } void RenderThemeEfl::clearThemePartCache() { for (auto& part : m_partCache) part = nullptr; } void RenderThemeEfl::applyEdjeStateFromForm(Evas_Object* object, const ControlStates* states, bool haveBackground) { const char* signals[] = { // keep in sync with WebCore/platform/ThemeTypes.h "hovered", "pressed", "focused", "enabled", "checked", "read-only", "default", "window-inactive", "indeterminate", "spinup" }; edje_object_signal_emit(object, "reset", ""); for (size_t i = 0; i < WTF_ARRAY_LENGTH(signals); ++i) { if (states->states() & (1 << i)) edje_object_signal_emit(object, signals[i], ""); } if (haveBackground) edje_object_signal_emit(object, "styled", ""); } void RenderThemeEfl::applyEdjeRTLState(Evas_Object* edje, const RenderObject& object, FormType type, const IntRect& rect) { if (type == SliderVertical || type == SliderHorizontal) { if (!is(object)) return; // probably have -webkit-appearance: slider.. HTMLInputElement& input = downcast(object).element(); double valueRange = input.maximum() - input.minimum(); auto msg = std::make_unique(); msg->count = 2; // The first parameter of the message decides if the progress bar // grows from the end of the slider or from the beginning. On vertical // sliders, it should always be the same and will not be affected by // text direction settings. if (object.style().direction() == RTL || type == SliderVertical) msg->val[0] = 1; else msg->val[0] = 0; msg->val[1] = (input.valueAsNumber() - input.minimum()) / valueRange; edje_object_message_send(edje, EDJE_MESSAGE_FLOAT_SET, 0, msg.get()); } else if (type == ProgressBar) { const auto& renderProgress = downcast(object); int max = rect.width(); double value = renderProgress.position(); auto msg = std::make_unique(); msg->count = 2; if (object.style().direction() == RTL) msg->val[0] = (1.0 - value) * max; else msg->val[0] = 0; msg->val[1] = value; edje_object_message_send(edje, EDJE_MESSAGE_FLOAT_SET, 0, msg.get()); } } bool RenderThemeEfl::isControlStyled(const RenderStyle& style, const BorderData& border, const FillLayer& background, const Color& backgroundColor) const { return RenderTheme::isControlStyled(style, border, background, backgroundColor) || style.appearance() == MenulistButtonPart; } bool RenderThemeEfl::paintThemePart(const RenderObject& object, FormType type, const PaintInfo& info, const IntRect& rect) { loadThemeIfNeeded(); _ASSERT_ON_RELEASE_RETURN_VAL(edje(), false, "Could not paint native HTML part due to missing theme."); ThemePartCacheEntry* entry = getThemePartFromCache(type, rect.size()); if (!entry) return true; bool haveBackgroundColor = isControlStyled(object.style(), object.style().border(), *object.style().backgroundLayers(), Color::white); ControlStates states(extractControlStatesForRenderer(object)); applyEdjeStateFromForm(entry->edje(), &states, haveBackgroundColor); applyEdjeRTLState(entry->edje(), object, type, rect); edje_object_calc_force(entry->edje()); edje_object_message_signal_process(entry->edje()); evas_render(ecore_evas_get(entry->canvas())); cairo_t* cairo = info.context().platformContext()->cr(); ASSERT(cairo); cairo_save(cairo); cairo_set_source_surface(cairo, entry->surface(), rect.x(), rect.y()); cairo_paint_with_alpha(cairo, 1.0); cairo_restore(cairo); return false; } bool RenderThemeEfl::paintThemePart(const GraphicsContext& context, FormType type, const IntRect& rect) { loadThemeIfNeeded(); _ASSERT_ON_RELEASE_RETURN_VAL(edje(), false, "Could not paint native HTML part due to missing theme."); ThemePartCacheEntry* entry = getThemePartFromCache(type, rect.size()); ASSERT(entry); edje_object_calc_force(entry->edje()); edje_object_message_signal_process(entry->edje()); evas_render(ecore_evas_get(entry->canvas())); cairo_t* cairo = context.platformContext()->cr(); ASSERT(cairo); cairo_save(cairo); cairo_set_source_surface(cairo, entry->surface(), rect.x(), rect.y()); cairo_paint_with_alpha(cairo, 1.0); cairo_restore(cairo); return false; } PassRefPtr RenderThemeEfl::create(Page* page) { return adoptRef(new RenderThemeEfl(page)); } PassRefPtr RenderTheme::themeForPage(Page* page) { if (page) return RenderThemeEfl::create(page); static RenderTheme* fallback = RenderThemeEfl::create(0).leakRef(); return fallback; } static void applyColorCallback(void* data, Evas_Object*, const char* /* signal */, const char* colorClass) { RenderThemeEfl* that = static_cast(data); that->setColorFromThemeClass(colorClass); that->platformColorsDidChange(); // Triggers relayout. } static bool fillColorsFromEdjeClass(Evas_Object* o, const char* colorClass, Color* color1, Color* color2 = 0, Color* color3 = 0) { int r1, g1, b1, a1; int r2, g2, b2, a2; int r3, g3, b3, a3; if (!edje_object_color_class_get(o, colorClass, &r1, &g1, &b1, &a1, &r2, &g2, &b2, &a2, &r3, &g3, &b3, &a3)) return false; if (color1) color1->setRGB(makeRGBA(r1, g1, b1, a1)); if (color2) color2->setRGB(makeRGBA(r2, g2, b2, a2)); if (color3) color3->setRGB(makeRGBA(r3, g3, b3, a3)); return true; } void RenderThemeEfl::setColorFromThemeClass(const char* colorClass) { ASSERT(edje()); if (!strcmp("webkit/selection/foreground", colorClass)) m_supportsSelectionForegroundColor = fillColorsFromEdjeClass(edje(), colorClass, &m_activeSelectionForegroundColor, &m_inactiveSelectionForegroundColor); else if (!strcmp("webkit/selection/background", colorClass)) fillColorsFromEdjeClass(edje(), colorClass, &m_activeSelectionBackgroundColor, &m_inactiveSelectionBackgroundColor); else if (!strcmp("webkit/focus_ring", colorClass)) { if (!fillColorsFromEdjeClass(edje(), colorClass, &m_focusRingColor)) return; // platformFocusRingColor() is only used for the default theme (without page) // The following is ugly, but no other way to do it unless we change it to use page themes as much as possible. RenderTheme::setCustomFocusRingColor(m_focusRingColor); } } void RenderThemeEfl::setThemePath(const String& newThemePath) { if (newThemePath == m_themePath) return; if (newThemePath.isEmpty()) { EINA_LOG_CRIT("No valid theme defined, things will not work properly."); return; } String oldThemePath = m_themePath; m_themePath = newThemePath; // Keep the consistence by restoring the previous theme path // if we cannot load the new one. if (!loadTheme()) m_themePath = oldThemePath; } String RenderThemeEfl::themePath() const { #ifndef NDEBUG if (edje()) { const char* path; edje_object_file_get(edje(), &path, 0); ASSERT(m_themePath == path); } #endif return m_themePath; } bool RenderThemeEfl::loadTheme() { ASSERT(!m_themePath.isEmpty()); if (!canvas()) { m_canvas = EflUniquePtr(ecore_evas_buffer_new(1, 1)); _ASSERT_ON_RELEASE_RETURN_VAL(canvas(), false, "Could not create canvas required by theme, things will not work properly."); } EflUniquePtr o = EflUniquePtr(edje_object_add(ecore_evas_get(canvas()))); _ASSERT_ON_RELEASE_RETURN_VAL(o, false, "Could not create new base Edje object."); if (!setSourceGroupForEdjeObject(o.get(), m_themePath, "webkit/base")) return false; // Keep current theme. // Invalidate existing theme part cache. if (edje()) clearThemePartCache(); // Set new loaded theme, and apply it. m_edje = WTFMove(o); const char* thickness = edje_object_data_get(m_edje.get(), "scrollbar.thickness"); if (thickness && !Settings::mockScrollbarsEnabled()) static_cast(ScrollbarTheme::theme()).setScrollbarThickness(atoi(thickness)); edje_object_signal_callback_add(edje(), "color_class,set", "webkit/selection/foreground", applyColorCallback, this); edje_object_signal_callback_add(edje(), "color_class,set", "webkit/selection/background", applyColorCallback, this); edje_object_signal_callback_add(edje(), "color_class,set", "webkit/focus_ring", applyColorCallback, this); applyPartDescriptionsFrom(m_themePath); setColorFromThemeClass("webkit/selection/foreground"); setColorFromThemeClass("webkit/selection/background"); setColorFromThemeClass("webkit/focus_ring"); platformColorsDidChange(); // Schedules a relayout, do last. return true; } void RenderThemeEfl::applyPartDescriptionFallback(ThemePartDesc* desc) { desc->min.setWidth(Length(0, Fixed)); desc->min.setHeight(Length(0, Fixed)); desc->max.setWidth(Length(0, Fixed)); desc->max.setHeight(Length(0, Fixed)); desc->padding = LengthBox(0, 0, 0, 0); } void RenderThemeEfl::applyPartDescription(Evas_Object* object, ThemePartDesc* desc) { Evas_Coord minw, minh, maxw, maxh; edje_object_size_min_get(object, &minw, &minh); if (!minw && !minh) edje_object_size_min_calc(object, &minw, &minh); desc->min.setWidth(Length(minw, Fixed)); desc->min.setHeight(Length(minh, Fixed)); edje_object_size_max_get(object, &maxw, &maxh); desc->max.setWidth(Length(maxw, Fixed)); desc->max.setHeight(Length(maxh, Fixed)); if (!edje_object_part_exists(object, "text_confinement")) desc->padding = LengthBox(0, 0, 0, 0); else { Evas_Coord px, py, pw, ph; Evas_Coord ox = 0, oy = 0, ow = 0, oh = 0; int top, right, bottom, left; if (minw > 0) ow = minw; else ow = 100; if (minh > 0) oh = minh; else oh = 100; if (maxw > 0 && ow > maxw) ow = maxw; if (maxh > 0 && oh > maxh) oh = maxh; evas_object_move(object, ox, oy); evas_object_resize(object, ow, oh); edje_object_calc_force(object); edje_object_message_signal_process(object); edje_object_part_geometry_get(object, "text_confinement", &px, &py, &pw, &ph); top = py - oy; bottom = (oh + oy) - (ph + py); left = px - ox; right = (ow + ox) - (pw + px); desc->padding = LengthBox(top, right, bottom, left); } } void RenderThemeEfl::applyPartDescriptionsFrom(const String& themePath) { EflUniquePtr temp = EflUniquePtr(edje_object_add(ecore_evas_get(canvas()))); _ASSERT_ON_RELEASE_RETURN(temp, "Could not create Edje object."); for (size_t i = 0; i < FormTypeLast; i++) { FormType type = static_cast(i); m_partDescs[i].type = type; if (!setSourceGroupForEdjeObject(temp.get(), themePath, toEdjeGroup(type))) applyPartDescriptionFallback(m_partDescs + i); else applyPartDescription(temp.get(), m_partDescs + i); } } RenderThemeEfl::RenderThemeEfl(Page* page) : RenderTheme() , m_page(page) , m_activeSelectionBackgroundColor(0, 0, 255) , m_activeSelectionForegroundColor(Color::white) , m_inactiveSelectionBackgroundColor(0, 0, 128) , m_inactiveSelectionForegroundColor(200, 200, 200) , m_focusRingColor(32, 32, 224, 224) , m_sliderThumbColor(Color::darkGray) , m_supportsSelectionForegroundColor(false) { } RenderThemeEfl::~RenderThemeEfl() { clearThemePartCache(); } static bool supportsFocus(ControlPart appearance) { switch (appearance) { case PushButtonPart: case ButtonPart: case TextFieldPart: case TextAreaPart: case SearchFieldPart: case MenulistPart: case RadioPart: case CheckboxPart: case SliderVerticalPart: case SliderHorizontalPart: return true; default: return false; } } bool RenderThemeEfl::supportsFocusRing(const RenderStyle& style) const { return supportsFocus(style.appearance()); } bool RenderThemeEfl::controlSupportsTints(const RenderObject& object) const { return isEnabled(object); } int RenderThemeEfl::baselinePosition(const RenderBox& box) const { if (box.style().appearance() == CheckboxPart || box.style().appearance() == RadioPart) return box.marginTop() + box.height() - 3; return RenderTheme::baselinePosition(box); } Color RenderThemeEfl::platformActiveSelectionBackgroundColor() const { loadThemeIfNeeded(); return m_activeSelectionBackgroundColor; } Color RenderThemeEfl::platformInactiveSelectionBackgroundColor() const { loadThemeIfNeeded(); return m_inactiveSelectionBackgroundColor; } Color RenderThemeEfl::platformActiveSelectionForegroundColor() const { loadThemeIfNeeded(); return m_activeSelectionForegroundColor; } Color RenderThemeEfl::platformInactiveSelectionForegroundColor() const { loadThemeIfNeeded(); return m_inactiveSelectionForegroundColor; } Color RenderThemeEfl::platformFocusRingColor() const { loadThemeIfNeeded(); return m_focusRingColor; } bool RenderThemeEfl::supportsSelectionForegroundColors() const { loadThemeIfNeeded(); return m_supportsSelectionForegroundColor; } bool RenderThemeEfl::paintSliderTrack(const RenderObject& object, const PaintInfo& info, const IntRect& rect) { if (object.style().appearance() == SliderHorizontalPart) paintThemePart(object, SliderHorizontal, info, rect); else paintThemePart(object, SliderVertical, info, rect); #if ENABLE(DATALIST_ELEMENT) paintSliderTicks(object, info, rect); #endif return false; } void RenderThemeEfl::adjustSliderTrackStyle(StyleResolver&, RenderStyle& style, Element*) const { style.setBoxShadow(nullptr); } void RenderThemeEfl::adjustSliderThumbStyle(StyleResolver& styleResolver, RenderStyle& style, Element* element) const { RenderTheme::adjustSliderThumbStyle(styleResolver, style, element); style.setBoxShadow(nullptr); } void RenderThemeEfl::adjustSliderThumbSize(RenderStyle& style, Element*) const { ControlPart part = style.appearance(); if (part == SliderThumbVerticalPart) { style.setWidth(Length(sliderThumbHeight, Fixed)); style.setHeight(Length(sliderThumbWidth, Fixed)); } else if (part == SliderThumbHorizontalPart) { style.setWidth(Length(sliderThumbWidth, Fixed)); style.setHeight(Length(sliderThumbHeight, Fixed)); } } #if ENABLE(DATALIST_ELEMENT) IntSize RenderThemeEfl::sliderTickSize() const { return IntSize(1, 6); } int RenderThemeEfl::sliderTickOffsetFromTrackCenter() const { static const int sliderTickOffset = -12; return sliderTickOffset; } LayoutUnit RenderThemeEfl::sliderTickSnappingThreshold() const { // The same threshold value as the Chromium port. return 5; } #endif bool RenderThemeEfl::supportsDataListUI(const AtomicString& type) const { #if ENABLE(DATALIST_ELEMENT) // FIXME: We need to support other types. return type == InputTypeNames::email() || type == InputTypeNames::range() || type == InputTypeNames::search() || type == InputTypeNames::url(); #else UNUSED_PARAM(type); return false; #endif } bool RenderThemeEfl::paintSliderThumb(const RenderObject& object, const PaintInfo& info, const IntRect& rect) { if (object.style().appearance() == SliderThumbHorizontalPart) paintThemePart(object, SliderThumbHorizontal, info, rect); else paintThemePart(object, SliderThumbVertical, info, rect); return false; } void RenderThemeEfl::adjustCheckboxStyle(StyleResolver& styleResolver, RenderStyle& style, Element* element) const { if (!m_page && element && element->document().page()) { static_cast(element->document().page()->theme()).adjustCheckboxStyle(styleResolver, style, element); return; } adjustSizeConstraints(style, CheckBox); style.resetBorder(); const ThemePartDesc* desc = m_partDescs + (size_t)CheckBox; if (style.width().value() < desc->min.width().value()) style.setWidth(desc->min.width()); if (style.height().value() < desc->min.height().value()) style.setHeight(desc->min.height()); } bool RenderThemeEfl::paintCheckbox(const RenderObject& object, const PaintInfo& info, const IntRect& rect) { return paintThemePart(object, CheckBox, info, rect); } void RenderThemeEfl::adjustRadioStyle(StyleResolver& styleResolver, RenderStyle& style, Element* element) const { if (!m_page && element && element->document().page()) { static_cast(element->document().page()->theme()).adjustRadioStyle(styleResolver, style, element); return; } adjustSizeConstraints(style, RadioButton); style.resetBorder(); const ThemePartDesc* desc = m_partDescs + (size_t)RadioButton; if (style.width().value() < desc->min.width().value()) style.setWidth(desc->min.width()); if (style.height().value() < desc->min.height().value()) style.setHeight(desc->min.height()); } bool RenderThemeEfl::paintRadio(const RenderObject& object, const PaintInfo& info, const IntRect& rect) { return paintThemePart(object, RadioButton, info, rect); } void RenderThemeEfl::adjustButtonStyle(StyleResolver& styleResolver, RenderStyle& style, Element* element) const { if (!m_page && element && element->document().page()) { static_cast(element->document().page()->theme()).adjustButtonStyle(styleResolver, style, element); return; } // adjustSizeConstrains can make SquareButtonPart's size wrong (by adjusting paddings), so call it only for PushButtonPart and ButtonPart if (style.appearance() == PushButtonPart || style.appearance() == ButtonPart) adjustSizeConstraints(style, Button); } bool RenderThemeEfl::paintButton(const RenderObject& object, const PaintInfo& info, const IntRect& rect) { return paintThemePart(object, Button, info, rect); } void RenderThemeEfl::adjustMenuListStyle(StyleResolver& styleResolver, RenderStyle& style, Element* element) const { if (!m_page && element && element->document().page()) { static_cast(element->document().page()->theme()).adjustMenuListStyle(styleResolver, style, element); return; } adjustSizeConstraints(style, ComboBox); style.resetBorder(); style.setWhiteSpace(PRE); style.setLineHeight(RenderStyle::initialLineHeight()); } bool RenderThemeEfl::paintMenuList(const RenderObject& object, const PaintInfo& info, const FloatRect& rect) { return paintThemePart(object, ComboBox, info, IntRect(rect)); } void RenderThemeEfl::adjustMenuListButtonStyle(StyleResolver& styleResolver, RenderStyle& style, Element* element) const { // Height is locked to auto if height is not specified. style.setHeight(Length(Auto)); // The element. int minHeight = style.fontMetrics().height(); minHeight = std::max(minHeight, dropDownBoxMinHeight); style.setMinHeight(Length(minHeight, Fixed)); adjustMenuListStyle(styleResolver, style, element); } bool RenderThemeEfl::paintMenuListButtonDecorations(const RenderBox& object, const PaintInfo& info, const FloatRect& rect) { return paintMenuList(object, info, rect); } void RenderThemeEfl::adjustTextFieldStyle(StyleResolver& styleResolver, RenderStyle& style, Element* element) const { if (!m_page && element && element->document().page()) { static_cast(element->document().page()->theme()).adjustTextFieldStyle(styleResolver, style, element); return; } adjustSizeConstraints(style, TextField); style.resetBorder(); } bool RenderThemeEfl::paintTextField(const RenderObject& object, const PaintInfo& info, const FloatRect& rect) { return paintThemePart(object, TextField, info, IntRect(rect)); } void RenderThemeEfl::adjustTextAreaStyle(StyleResolver&, RenderStyle&, Element*) const { } bool RenderThemeEfl::paintTextArea(const RenderObject& object, const PaintInfo& info, const FloatRect& rect) { return paintTextField(object, info, rect); } void RenderThemeEfl::adjustSearchFieldResultsButtonStyle(StyleResolver& styleResolver, RenderStyle& style, Element* element) const { if (!m_page && element && element->document().page()) { static_cast(element->document().page()->theme()).adjustSearchFieldResultsButtonStyle(styleResolver, style, element); return; } adjustSizeConstraints(style, SearchFieldResultsButton); style.resetBorder(); style.setWhiteSpace(PRE); float fontScale = style.fontSize() / defaultFontSize; int decorationSize = lroundf(std::min(std::max(minSearchDecorationButtonSize, defaultFontSize * fontScale), maxSearchDecorationButtonSize)); style.setWidth(Length(decorationSize + searchFieldDecorationButtonOffset, Fixed)); style.setHeight(Length(decorationSize, Fixed)); } bool RenderThemeEfl::paintSearchFieldResultsButton(const RenderBox& object, const PaintInfo& info, const IntRect& rect) { return paintThemePart(object, SearchFieldResultsButton, info, rect); } void RenderThemeEfl::adjustSearchFieldResultsDecorationPartStyle(StyleResolver& styleResolver, RenderStyle& style, Element* element) const { if (!m_page && element && element->document().page()) { static_cast(element->document().page()->theme()).adjustSearchFieldResultsDecorationPartStyle(styleResolver, style, element); return; } adjustSizeConstraints(style, SearchFieldResultsDecoration); style.resetBorder(); style.setWhiteSpace(PRE); float fontScale = style.fontSize() / defaultFontSize; int decorationSize = lroundf(std::min(std::max(minSearchDecorationButtonSize, defaultFontSize * fontScale), maxSearchDecorationButtonSize)); style.setWidth(Length(decorationSize + searchFieldDecorationButtonOffset, Fixed)); style.setHeight(Length(decorationSize, Fixed)); } bool RenderThemeEfl::paintSearchFieldResultsDecorationPart(const RenderBox& object, const PaintInfo& info, const IntRect& rect) { return paintThemePart(object, SearchFieldResultsDecoration, info, rect); } void RenderThemeEfl::adjustSearchFieldCancelButtonStyle(StyleResolver& styleResolver, RenderStyle& style, Element* element) const { if (!m_page && element && element->document().page()) { static_cast(element->document().page()->theme()).adjustSearchFieldCancelButtonStyle(styleResolver, style, element); return; } adjustSizeConstraints(style, SearchFieldCancelButton); style.resetBorder(); style.setWhiteSpace(PRE); // Logic taken from RenderThemeChromium.cpp. // Scale the button size based on the font size. float fontScale = style.fontSize() / defaultFontSize; int cancelButtonSize = lroundf(std::min(std::max(minCancelButtonSize, defaultFontSize * fontScale), maxCancelButtonSize)); style.setWidth(Length(cancelButtonSize, Fixed)); style.setHeight(Length(cancelButtonSize, Fixed)); } bool RenderThemeEfl::paintSearchFieldCancelButton(const RenderBox& object, const PaintInfo& info, const IntRect& rect) { return paintThemePart(object, SearchFieldCancelButton, info, rect); } void RenderThemeEfl::adjustSearchFieldStyle(StyleResolver& styleResolver, RenderStyle& style, Element* element) const { if (!m_page && element && element->document().page()) { static_cast(element->document().page()->theme()).adjustSearchFieldStyle(styleResolver, style, element); return; } adjustSizeConstraints(style, SearchField); style.resetBorder(); style.setWhiteSpace(PRE); } bool RenderThemeEfl::paintSearchField(const RenderObject& object, const PaintInfo& info, const IntRect& rect) { return paintThemePart(object, SearchField, info, rect); } void RenderThemeEfl::adjustInnerSpinButtonStyle(StyleResolver& styleResolver, RenderStyle& style, Element* element) const { if (!m_page && element && element->document().page()) { static_cast(element->document().page()->theme()).adjustInnerSpinButtonStyle(styleResolver, style, element); return; } adjustSizeConstraints(style, Spinner); } bool RenderThemeEfl::paintInnerSpinButton(const RenderObject& object, const PaintInfo& info, const IntRect& rect) { return paintThemePart(object, Spinner, info, rect); } void RenderThemeEfl::setDefaultFontSize(int size) { defaultFontSize = size; } void RenderThemeEfl::updateCachedSystemFontDescription(CSSValueID, FontCascadeDescription& fontDescription) const { // It was called by RenderEmbeddedObject::paintReplaced to render alternative string. // To avoid cairo_error while rendering, fontDescription should be passed. fontDescription.setOneFamily("Sans"); fontDescription.setSpecifiedSize(defaultFontSize); fontDescription.setIsAbsoluteSize(true); fontDescription.setWeight(FontWeightNormal); fontDescription.setItalic(FontItalicOff); } void RenderThemeEfl::adjustProgressBarStyle(StyleResolver&, RenderStyle& style, Element*) const { style.setBoxShadow(nullptr); } double RenderThemeEfl::animationRepeatIntervalForProgressBar(RenderProgress&) const { return progressAnimationInterval; } double RenderThemeEfl::animationDurationForProgressBar(RenderProgress&) const { return progressAnimationInterval * progressAnimationFrames * 2; // "2" for back and forth; } bool RenderThemeEfl::paintProgressBar(const RenderObject& object, const PaintInfo& info, const IntRect& rect) { if (!object.isProgress()) return true; return paintThemePart(object, ProgressBar, info, rect); } #if ENABLE(VIDEO) String RenderThemeEfl::mediaControlsStyleSheet() { return ASCIILiteral(mediaControlsBaseUserAgentStyleSheet); } String RenderThemeEfl::mediaControlsScript() { StringBuilder scriptBuilder; scriptBuilder.append(mediaControlsLocalizedStringsJavaScript, sizeof(mediaControlsLocalizedStringsJavaScript)); scriptBuilder.append(mediaControlsBaseJavaScript, sizeof(mediaControlsBaseJavaScript)); return scriptBuilder.toString(); } #endif #undef _ASSERT_ON_RELEASE_RETURN #undef _ASSERT_ON_RELEASE_RETURN_VAL }