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