Changeset 128274 in webkit
- Timestamp:
- Sep 12, 2012 12:55:48 AM (12 years ago)
- Location:
- trunk/Source/WebCore
- Files:
-
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebCore/ChangeLog
r128269 r128274 1 2012-09-12 Kenneth Rohde Christiansen <kenneth@webkit.org> 2 3 [EFL] Clean up the RenderTheme Edje caching 4 https://bugs.webkit.org/show_bug.cgi?id=96016 5 6 Reviewed by Gyuyoung Kim. 7 8 Refactor the Edje parts caching to be easier to understand and use 9 proper C++ constructs. 10 11 * platform/efl/RenderThemeEfl.cpp: 12 (WebCore): 13 (WebCore::toEdjeGroup): 14 15 Converts the enum to the given edje group name. 16 17 (WebCore::setSourceGroupForEdjeObject): 18 19 Basically a wrapper around evas_object_file_set, but handles 20 errors, which was done slightly differently all over. 21 22 (WebCore::RenderThemeEfl::ThemePartCacheEntry::ThemePartCacheEntry): 23 (WebCore::RenderThemeEfl::ThemePartCacheEntry::~ThemePartCacheEntry): 24 25 (WebCore::createCairoSurfaceFor): 26 (WebCore::isFormElementTooLargeToDisplay): 27 28 Methods used when creating ThemePartCacheEntry'es. 29 30 (WebCore::RenderThemeEfl::ThemePartCacheEntry::create): 31 (WebCore::RenderThemeEfl::ThemePartCacheEntry::reuse): 32 33 New methods for creating and reusing an cache entry. If you do 34 not supply a new size, the original size will be used, and it is 35 thus more effective. 36 37 (WebCore::RenderThemeEfl::getThemePartFromCache): 38 39 New method for requesting a theme part. If it doesn't exist 40 it will additinally be loaded and added to the cache. 41 42 (WebCore::RenderThemeEfl::flushThemePartCache): 43 44 Remove the entire cache and free the assets. 45 46 (WebCore::RenderThemeEfl::paintThemePart): 47 (WebCore::RenderThemeEfl::themePath): 48 (WebCore::RenderThemeEfl::loadTheme): 49 (WebCore::RenderThemeEfl::applyPartDescriptionsFrom): 50 (WebCore::RenderThemeEfl::~RenderThemeEfl): 51 (WebCore::RenderThemeEfl::emitMediaButtonSignal): 52 * platform/efl/RenderThemeEfl.h: 53 (RenderThemeEfl): 54 (ThemePartCacheEntry): 55 1 56 2012-09-12 Alexandre Elias <aelias@chromium.org> 2 57 -
trunk/Source/WebCore/platform/efl/RenderThemeEfl.cpp
r128149 r128274 85 85 do { if (!o) { EINA_LOG_CRIT(fmt, ## __VA_ARGS__); ASSERT(o); return val; } } while (0) 86 86 87 void RenderThemeEfl::adjustSizeConstraints(RenderStyle* style, FormType type) const 88 { 89 loadThemeIfNeeded(); 90 _ASSERT_ON_RELEASE_RETURN(m_edje, "Could not paint native HTML part due to missing theme."); 91 92 const struct ThemePartDesc* desc = m_partDescs + (size_t)type; 93 94 if (style->minWidth().isIntrinsic()) 95 style->setMinWidth(desc->min.width()); 96 if (style->minHeight().isIntrinsic()) 97 style->setMinHeight(desc->min.height()); 98 99 if (desc->max.width().value() > 0 && style->maxWidth().isIntrinsicOrAuto()) 100 style->setMaxWidth(desc->max.width()); 101 if (desc->max.height().value() > 0 && style->maxHeight().isIntrinsicOrAuto()) 102 style->setMaxHeight(desc->max.height()); 103 104 style->setPaddingTop(desc->padding.top()); 105 style->setPaddingBottom(desc->padding.bottom()); 106 style->setPaddingLeft(desc->padding.left()); 107 style->setPaddingRight(desc->padding.right()); 108 } 109 110 bool RenderThemeEfl::themePartCacheEntryReset(struct ThemePartCacheEntry* entry, FormType type) 111 { 112 const char *file, *group; 113 114 ASSERT(entry); 115 ASSERT(m_edje); 116 117 edje_object_file_get(m_edje, &file, 0); 118 group = edjeGroupFromFormType(type); 119 ASSERT(file); 120 ASSERT(group); 121 122 if (!edje_object_file_set(entry->o, file, group)) { 123 Edje_Load_Error err = edje_object_load_error_get(entry->o); 124 const char *errmsg = edje_load_error_str(err); 125 EINA_LOG_ERR("Could not load '%s' from theme %s: %s", group, file, errmsg); 126 return false; 127 } 128 return true; 129 } 130 131 bool RenderThemeEfl::themePartCacheEntrySurfaceCreate(struct ThemePartCacheEntry* entry) 132 { 133 int w, h; 134 cairo_status_t status; 135 136 ASSERT(entry); 137 ASSERT(entry->ee); 138 139 ecore_evas_geometry_get(entry->ee, 0, 0, &w, &h); 140 ASSERT(w > 0); 141 ASSERT(h > 0); 142 143 entry->surface = cairo_image_surface_create_for_data((unsigned char *)ecore_evas_buffer_pixels_get(entry->ee), 144 CAIRO_FORMAT_ARGB32, w, h, w * 4); 145 status = cairo_surface_status(entry->surface); 146 if (status != CAIRO_STATUS_SUCCESS) { 147 EINA_LOG_ERR("Could not create cairo surface: %s", 148 cairo_status_to_string(status)); 149 return false; 150 } 151 152 return true; 153 } 154 155 bool RenderThemeEfl::isFormElementTooLargeToDisplay(const IntSize& elementSize) 156 { 157 // This limit of 20000 pixels is hardcoded inside edje -- anything above this size 158 // will be clipped. This value seems to be reasonable enough so that hardcoding it 159 // here won't be a problem. 160 static const int maxEdjeDimension = 20000; 161 162 return elementSize.width() > maxEdjeDimension || elementSize.height() > maxEdjeDimension; 163 } 164 165 // allocate a new entry and fill it with edje group 166 struct RenderThemeEfl::ThemePartCacheEntry* RenderThemeEfl::cacheThemePartNew(FormType type, const IntSize& size) 167 { 168 if (isFormElementTooLargeToDisplay(size)) { 169 EINA_LOG_ERR("Cannot render an element of size %dx%d.", size.width(), size.height()); 170 return 0; 171 } 172 173 ThemePartCacheEntry* entry = new ThemePartCacheEntry; 174 if (!entry) { 175 EINA_LOG_ERR("could not allocate ThemePartCacheEntry."); 176 return 0; 177 } 178 179 entry->ee = ecore_evas_buffer_new(size.width(), size.height()); 180 if (!entry->ee) { 181 EINA_LOG_ERR("ecore_evas_buffer_new(%d, %d) failed.", 182 size.width(), size.height()); 183 delete entry; 184 return 0; 185 } 186 187 // By default EFL creates buffers without alpha. 188 ecore_evas_alpha_set(entry->ee, EINA_TRUE); 189 190 entry->o = edje_object_add(ecore_evas_get(entry->ee)); 191 ASSERT(entry->o); 192 if (!themePartCacheEntryReset(entry, type)) { 193 evas_object_del(entry->o); 194 ecore_evas_free(entry->ee); 195 delete entry; 196 return 0; 197 } 198 199 if (!themePartCacheEntrySurfaceCreate(entry)) { 200 evas_object_del(entry->o); 201 ecore_evas_free(entry->ee); 202 delete entry; 203 return 0; 204 } 205 206 evas_object_resize(entry->o, size.width(), size.height()); 207 evas_object_show(entry->o); 208 209 entry->type = type; 210 entry->size = size; 211 212 m_partCache.prepend(entry); 213 return entry; 214 } 215 216 // just change the edje group and return the same entry 217 struct RenderThemeEfl::ThemePartCacheEntry* RenderThemeEfl::cacheThemePartReset(FormType type, struct RenderThemeEfl::ThemePartCacheEntry* entry) 218 { 219 if (!themePartCacheEntryReset(entry, type)) { 220 entry->type = FormTypeLast; // invalidate 221 m_partCache.append(entry); 222 return 0; 223 } 224 entry->type = type; 225 m_partCache.prepend(entry); 226 return entry; 227 } 228 229 // resize entry and reset it 230 struct RenderThemeEfl::ThemePartCacheEntry* RenderThemeEfl::cacheThemePartResizeAndReset(FormType type, const IntSize& size, struct RenderThemeEfl::ThemePartCacheEntry* entry) 231 { 232 cairo_surface_finish(entry->surface); 233 234 entry->size = size; 235 ecore_evas_resize(entry->ee, size.width(), size.height()); 236 evas_object_resize(entry->o, size.width(), size.height()); 237 238 if (!themePartCacheEntrySurfaceCreate(entry)) { 239 evas_object_del(entry->o); 240 ecore_evas_free(entry->ee); 241 delete entry; 242 return 0; 243 } 244 245 return cacheThemePartReset(type, entry); 246 } 247 248 // general purpose get (will create, reuse and all) 249 struct RenderThemeEfl::ThemePartCacheEntry* RenderThemeEfl::cacheThemePartGet(FormType type, const IntSize& size) 250 { 251 Vector<struct ThemePartCacheEntry *>::iterator itr, end; 252 struct ThemePartCacheEntry *ce_last_size = 0; 253 int i, idxLastSize = -1; 254 255 itr = m_partCache.begin(); 256 end = m_partCache.end(); 257 for (i = 0; itr != end; i++, itr++) { 258 struct ThemePartCacheEntry *entry = *itr; 259 if (entry->size == size) { 260 if (entry->type == type) 261 return entry; 262 ce_last_size = entry; 263 idxLastSize = i; 264 } 265 } 266 267 if (m_partCache.size() < RENDER_THEME_EFL_PART_CACHE_MAX) 268 return cacheThemePartNew(type, size); 269 270 if (ce_last_size && ce_last_size != m_partCache.first()) { 271 m_partCache.remove(idxLastSize); 272 return cacheThemePartReset(type, ce_last_size); 273 } 274 275 ThemePartCacheEntry* entry = m_partCache.last(); 276 m_partCache.removeLast(); 277 return cacheThemePartResizeAndReset(type, size, entry); 278 } 279 280 void RenderThemeEfl::cacheThemePartFlush() 281 { 282 Vector<struct ThemePartCacheEntry *>::iterator itr, end; 283 284 itr = m_partCache.begin(); 285 end = m_partCache.end(); 286 for (; itr != end; itr++) { 287 struct ThemePartCacheEntry *entry = *itr; 288 cairo_surface_destroy(entry->surface); 289 evas_object_del(entry->o); 290 ecore_evas_free(entry->ee); 291 delete entry; 292 } 293 m_partCache.clear(); 294 } 295 296 void RenderThemeEfl::applyEdjeStateFromForm(Evas_Object* object, ControlStates states) 297 { 298 const char *signals[] = { // keep in sync with WebCore/platform/ThemeTypes.h 299 "hovered", 300 "pressed", 301 "focused", 302 "enabled", 303 "checked", 304 "read-only", 305 "default", 306 "window-inactive", 307 "indeterminate", 308 "spinup" 309 }; 310 311 edje_object_signal_emit(object, "reset", ""); 312 313 for (size_t i = 0; i < WTF_ARRAY_LENGTH(signals); ++i) { 314 if (states & (1 << i)) 315 edje_object_signal_emit(object, signals[i], ""); 316 } 317 } 318 319 bool RenderThemeEfl::paintThemePart(RenderObject* object, FormType type, const PaintInfo& info, const IntRect& rect) 320 { 321 loadThemeIfNeeded(); 322 _ASSERT_ON_RELEASE_RETURN_VAL(m_edje, false, "Could not paint native HTML part due to missing theme."); 323 324 ThemePartCacheEntry* entry; 325 Eina_List* updates; 326 cairo_t* cairo; 327 328 entry = cacheThemePartGet(type, rect.size()); 329 if (!entry) 330 return false; 331 332 applyEdjeStateFromForm(entry->o, controlStatesForRenderer(object)); 333 334 cairo = info.context->platformContext()->cr(); 335 ASSERT(cairo); 336 337 // Currently, only sliders needs this message; if other widget ever needs special 338 // treatment, move them to special functions. 339 if (type == SliderVertical || type == SliderHorizontal) { 340 if (!object->isSlider()) 341 return true; // probably have -webkit-appearance: slider.. 342 343 RenderSlider* renderSlider = toRenderSlider(object); 344 HTMLInputElement* input = renderSlider->node()->toInputElement(); 345 double valueRange = input->maximum() - input->minimum(); 346 347 OwnArrayPtr<char> buffer = adoptArrayPtr(new char[sizeof(Edje_Message_Float_Set) + sizeof(double)]); 348 Edje_Message_Float_Set* msg = new(buffer.get()) Edje_Message_Float_Set; 349 msg->count = 2; 350 351 // The first parameter of the message decides if the progress bar 352 // grows from the end of the slider or from the beginning. On vertical 353 // sliders, it should always be the same and will not be affected by 354 // text direction settings. 355 if (object->style()->direction() == RTL || type == SliderVertical) 356 msg->val[0] = 1; 357 else 358 msg->val[0] = 0; 359 360 msg->val[1] = (input->valueAsNumber() - input->minimum()) / valueRange; 361 edje_object_message_send(entry->o, EDJE_MESSAGE_FLOAT_SET, 0, msg); 362 #if ENABLE(PROGRESS_ELEMENT) 363 } else if (type == ProgressBar) { 364 RenderProgress* renderProgress = toRenderProgress(object); 365 366 int max = rect.width(); 367 double value = renderProgress->position(); 368 369 OwnArrayPtr<char> buffer = adoptArrayPtr(new char[sizeof(Edje_Message_Float_Set) + sizeof(double)]); 370 Edje_Message_Float_Set* msg = new(buffer.get()) Edje_Message_Float_Set; 371 msg->count = 2; 372 373 if (object->style()->direction() == RTL) 374 msg->val[0] = (1.0 - value) * max; 375 else 376 msg->val[0] = 0; 377 msg->val[1] = value; 378 edje_object_message_send(entry->o, EDJE_MESSAGE_FLOAT_SET, 0, msg); 379 #endif 380 } 381 382 edje_object_calc_force(entry->o); 383 edje_object_message_signal_process(entry->o); 384 updates = evas_render_updates(ecore_evas_get(entry->ee)); 385 evas_render_updates_free(updates); 386 387 cairo_save(cairo); 388 cairo_set_source_surface(cairo, entry->surface, rect.x(), rect.y()); 389 cairo_paint_with_alpha(cairo, 1.0); 390 cairo_restore(cairo); 391 392 return false; 393 } 394 395 PassRefPtr<RenderTheme> RenderThemeEfl::create(Page* page) 396 { 397 return adoptRef(new RenderThemeEfl(page)); 398 } 399 400 PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page) 401 { 402 if (page) 403 return RenderThemeEfl::create(page); 404 405 static RenderTheme* fallback = RenderThemeEfl::create(0).leakRef(); 406 return fallback; 407 } 408 409 static void applyColorCallback(void* data, Evas_Object*, const char* /* signal */, const char* colorClass) 410 { 411 RenderThemeEfl* that = static_cast<RenderThemeEfl*>(data); 412 that->setColorFromThemeClass(colorClass); 413 that->platformColorsDidChange(); // Triggers relayout. 414 } 415 416 static void fillColorsFromEdjeClass(Evas_Object* o, const char* colorClass, Color* color1, Color* color2 = 0, Color* color3 = 0) 417 { 418 int r1, g1, b1, a1; 419 int r2, g2, b2, a2; 420 int r3, g3, b3, a3; 421 422 bool ok = edje_object_color_class_get(o, colorClass, &r1, &g1, &b1, &a1, &r2, &g2, &b2, &a2, &r3, &g3, &b3, &a3); 423 _ASSERT_ON_RELEASE_RETURN(ok, "Could not get color class '%s'\n", colorClass); 424 425 if (color1) 426 color1->setRGB(makeRGBA(r1, g1, b1, a1)); 427 if (color2) 428 color2->setRGB(makeRGBA(r2, g2, b2, a2)); 429 if (color3) 430 color3->setRGB(makeRGBA(r3, g3, b3, a3)); 431 } 432 433 void RenderThemeEfl::setColorFromThemeClass(const char* colorClass) 434 { 435 ASSERT(m_edje); 436 437 if (!strcmp("webkit/selection/active", colorClass)) 438 fillColorsFromEdjeClass(m_edje, colorClass, &m_activeSelectionForegroundColor, &m_activeSelectionBackgroundColor); 439 else if (!strcmp("webkit/selection/inactive", colorClass)) 440 fillColorsFromEdjeClass(m_edje, colorClass, &m_inactiveSelectionForegroundColor, &m_inactiveSelectionBackgroundColor); 441 else if (!strcmp("webkit/focus_ring", colorClass)) { 442 fillColorsFromEdjeClass(m_edje, colorClass, &m_focusRingColor); 443 // platformFocusRingColor() is only used for the default theme (without page) 444 // The following is ugly, but no other way to do it unless we change it to use page themes as much as possible. 445 RenderTheme::setCustomFocusRingColor(m_focusRingColor); 446 } 447 } 448 449 void RenderThemeEfl::setThemePath(const String& path) 450 { 451 if (path == m_themePath) 452 return; 453 454 m_themePath = path; 455 456 if (m_themePath.isEmpty()) { 457 EINA_LOG_ERR("No theme defined, unable to set theme for HTML Forms."); 458 return; 459 } 460 461 if (!m_canvas) { 462 m_canvas = ecore_evas_buffer_new(1, 1); 463 if (!m_canvas) 464 EINA_LOG_ERR("Could not create canvas."); 465 } 466 467 loadTheme(); 468 } 469 470 bool RenderThemeEfl::loadTheme() 471 { 472 Evas_Object* o = edje_object_add(ecore_evas_get(m_canvas)); 473 _ASSERT_ON_RELEASE_RETURN_VAL(o, false, "Could not create new base Edje object."); 474 475 if (!edje_object_file_set(o, m_themePath.utf8().data(), "webkit/base")) { 476 Edje_Load_Error err = edje_object_load_error_get(o); 477 const char* errmsg = edje_load_error_str(err); 478 EINA_LOG_ERR("Could not set file '%s': %s", m_themePath.utf8().data(), errmsg); 479 evas_object_del(o); 480 return false; // Keep current theme. 481 } 482 483 // Get rid of existing theme. 484 if (m_edje) { 485 cacheThemePartFlush(); 486 evas_object_del(m_edje); 487 } 488 489 // Set new loaded theme, and apply it. 490 m_edje = o; 491 492 edje_object_signal_callback_add(m_edje, "color_class,set", "webkit/selection/active", applyColorCallback, this); 493 edje_object_signal_callback_add(m_edje, "color_class,set", "webkit/selection/inactive", applyColorCallback, this); 494 edje_object_signal_callback_add(m_edje, "color_class,set", "webkit/focus_ring", applyColorCallback, this); 495 496 applyPartDescriptionsFrom(m_edje); 497 498 setColorFromThemeClass("webkit/selection/active"); 499 setColorFromThemeClass("webkit/selection/inactive"); 500 setColorFromThemeClass("webkit/focus_ring"); 501 502 platformColorsDidChange(); // Schedules a relayout, do last. 503 504 return true; 505 } 506 507 void RenderThemeEfl::applyPartDescriptionFallback(struct ThemePartDesc* desc) 508 { 509 desc->min.setWidth(Length(0, Fixed)); 510 desc->min.setHeight(Length(0, Fixed)); 511 512 desc->max.setWidth(Length(0, Fixed)); 513 desc->max.setHeight(Length(0, Fixed)); 514 515 desc->padding = LengthBox(0, 0, 0, 0); 516 } 517 518 void RenderThemeEfl::applyPartDescription(Evas_Object* object, struct ThemePartDesc* desc) 519 { 520 Evas_Coord minw, minh, maxw, maxh; 521 522 edje_object_size_min_get(object, &minw, &minh); 523 if (!minw && !minh) 524 edje_object_size_min_calc(object, &minw, &minh); 525 526 desc->min.setWidth(Length(minw, Fixed)); 527 desc->min.setHeight(Length(minh, Fixed)); 528 529 edje_object_size_max_get(object, &maxw, &maxh); 530 desc->max.setWidth(Length(maxw, Fixed)); 531 desc->max.setHeight(Length(maxh, Fixed)); 532 533 if (!edje_object_part_exists(object, "text_confinement")) 534 desc->padding = LengthBox(0, 0, 0, 0); 535 else { 536 Evas_Coord px, py, pw, ph; 537 Evas_Coord ox = 0, oy = 0, ow = 0, oh = 0; 538 int t, r, b, l; 539 540 if (minw > 0) 541 ow = minw; 542 else 543 ow = 100; 544 if (minh > 0) 545 oh = minh; 546 else 547 oh = 100; 548 if (maxw > 0 && ow > maxw) 549 ow = maxw; 550 if (maxh > 0 && oh > maxh) 551 oh = maxh; 552 553 evas_object_move(object, ox, oy); 554 evas_object_resize(object, ow, oh); 555 edje_object_calc_force(object); 556 edje_object_message_signal_process(object); 557 edje_object_part_geometry_get(object, "text_confinement", &px, &py, &pw, &ph); 558 559 t = py - oy; 560 b = (oh + oy) - (ph + py); 561 562 l = px - ox; 563 r = (ow + ox) - (pw + px); 564 565 desc->padding = LengthBox(t, r, b, l); 566 } 567 } 568 569 const char* RenderThemeEfl::edjeGroupFromFormType(FormType type) const 87 88 static const char* toEdjeGroup(FormType type) 570 89 { 571 90 static const char* groups[] = { … … 603 122 }; 604 123 ASSERT(type >= 0); 605 ASSERT((size_t)type < sizeof(groups) / sizeof(groups[0])); // out of sync?124 ASSERT((size_t)type < sizeof(groups) / sizeof(groups[0])); // Out of sync? 606 125 return groups[type]; 607 126 } 608 127 609 void RenderThemeEfl::applyPartDescriptionsFrom(Evas_Object* o) 610 { 611 Evas_Object* object; 612 unsigned int i; 613 const char* file; 614 128 static bool setSourceGroupForEdjeObject(Evas_Object* o, const String& themePath, const char* group) 129 { 615 130 ASSERT(o); 616 617 edje_object_file_get(o, &file, 0); 618 ASSERT(file); 619 if (!file) { 620 EINA_LOG_ERR("Could not retrieve Edje theme file."); 621 return; 622 } 623 624 object = edje_object_add(ecore_evas_get(m_canvas)); 625 if (!object) { 626 EINA_LOG_ERR("Could not create Edje object."); 627 return; 628 } 629 630 for (i = 0; i < FormTypeLast; i++) { 131 ASSERT(!themePath.isEmpty()); 132 133 if (!edje_object_file_set(o, themePath.utf8().data(), group)) { 134 const char* message = edje_load_error_str(edje_object_load_error_get(o)); 135 EINA_LOG_ERR("Could not set theme group '%s' of file '%s': %s", group, themePath.utf8().data(), message); 136 return false; 137 } 138 139 return true; 140 } 141 142 void RenderThemeEfl::adjustSizeConstraints(RenderStyle* style, FormType type) const 143 { 144 loadThemeIfNeeded(); 145 146 // These are always valid, even if no theme could be loaded. 147 const ThemePartDesc* desc = m_partDescs + (size_t)type; 148 149 if (style->minWidth().isIntrinsic()) 150 style->setMinWidth(desc->min.width()); 151 if (style->minHeight().isIntrinsic()) 152 style->setMinHeight(desc->min.height()); 153 154 if (desc->max.width().value() > 0 && style->maxWidth().isIntrinsicOrAuto()) 155 style->setMaxWidth(desc->max.width()); 156 if (desc->max.height().value() > 0 && style->maxHeight().isIntrinsicOrAuto()) 157 style->setMaxHeight(desc->max.height()); 158 159 style->setPaddingTop(desc->padding.top()); 160 style->setPaddingBottom(desc->padding.bottom()); 161 style->setPaddingLeft(desc->padding.left()); 162 style->setPaddingRight(desc->padding.right()); 163 } 164 165 RenderThemeEfl::ThemePartCacheEntry::ThemePartCacheEntry() 166 : ee(0), o(0), surface(0) 167 { 168 } 169 170 RenderThemeEfl::ThemePartCacheEntry::~ThemePartCacheEntry() 171 { 172 if (surface) 173 cairo_surface_destroy(surface); 174 if (o) 175 evas_object_del(o); 176 if (ee) 177 ecore_evas_free(ee); 178 } 179 180 static cairo_surface_t* createCairoSurfaceFor(Ecore_Evas* ee) 181 { 182 ASSERT(ee); 183 184 int width, height; 185 ecore_evas_geometry_get(ee, 0, 0, &width, &height); 186 ASSERT(width > 0 && height > 0); 187 188 unsigned char* buffer = static_cast<unsigned char*>(const_cast<void*>(ecore_evas_buffer_pixels_get(ee))); 189 cairo_surface_t* surface = cairo_image_surface_create_for_data(buffer, CAIRO_FORMAT_ARGB32, width, height, width * 4); 190 191 cairo_status_t status = cairo_surface_status(surface); 192 if (status != CAIRO_STATUS_SUCCESS) { 193 EINA_LOG_ERR("Could not create cairo surface: %s", cairo_status_to_string(status)); 194 return 0; 195 } 196 197 return surface; 198 } 199 200 static bool isFormElementTooLargeToDisplay(const IntSize& elementSize) 201 { 202 // This limit of 20000 pixels is hardcoded inside edje -- anything above this size 203 // will be clipped. This value seems to be reasonable enough so that hardcoding it 204 // here won't be a problem. 205 static const int maxEdjeDimension = 20000; 206 207 return elementSize.width() > maxEdjeDimension || elementSize.height() > maxEdjeDimension; 208 } 209 210 RenderThemeEfl::ThemePartCacheEntry* RenderThemeEfl::ThemePartCacheEntry::create(const String& themePath, FormType type, const IntSize& size) 211 { 212 ASSERT(!themePath.isEmpty()); 213 ASSERT(!size.isEmpty()); 214 215 if (isFormElementTooLargeToDisplay(size)) { 216 EINA_LOG_ERR("Cannot render an element of size %dx%d.", size.width(), size.height()); 217 return 0; 218 } 219 220 OwnPtr<ThemePartCacheEntry*> entry = adoptPtr(new ThemePartCacheEntry); 221 222 entry->ee = ecore_evas_buffer_new(size.width(), size.height()); 223 if (!entry->ee) { 224 EINA_LOG_ERR("ecore_evas_buffer_new(%d, %d) failed.", size.width(), size.height()); 225 return 0; 226 } 227 228 // By default EFL creates buffers without alpha. 229 ecore_evas_alpha_set(entry->ee, EINA_TRUE); 230 231 entry->o = edje_object_add(ecore_evas_get(entry->ee)); 232 ASSERT(entry->o); 233 234 if (!setSourceGroupForEdjeObject(entry->o, themePath, toEdjeGroup(type))) 235 return 0; 236 237 entry->surface = createCairoSurfaceFor(entry->ee); 238 if (!entry->surface) 239 return 0; 240 241 evas_object_resize(entry->o, size.width(), size.height()); 242 evas_object_show(entry->o); 243 244 entry->type = type; 245 entry->size = size; 246 247 return entry.leakPtr(); 248 } 249 250 void RenderThemeEfl::ThemePartCacheEntry::reuse(const String& themePath, FormType newType, const IntSize& newSize) 251 { 252 ASSERT(!themePath.isEmpty()); 253 254 if (!newSize.isEmpty()) { 255 cairo_surface_finish(surface); 256 257 size = newSize; 258 ecore_evas_resize(ee, newSize.width(), newSize.height()); 259 evas_object_resize(o, newSize.width(), newSize.height()); 260 261 surface = createCairoSurfaceFor(ee); 262 if (!surface) { 263 type = FormTypeLast; // Invalidate; 264 return; 265 } 266 } 267 268 if (!setSourceGroupForEdjeObject(o, themePath, toEdjeGroup(newType))) { 269 type = FormTypeLast; // Invalidate. 270 return; 271 } 272 273 type = newType; 274 } 275 276 RenderThemeEfl::ThemePartCacheEntry* RenderThemeEfl::getThemePartFromCache(FormType type, const IntSize& size) 277 { 278 Vector<ThemePartCacheEntry*>::iterator it, end; 279 size_t lastWithRequestedSize = notFound; 280 281 it = m_partCache.begin(); 282 end = m_partCache.end(); 283 for (size_t i = 0; it != end; i++, it++) { 284 ThemePartCacheEntry* entry = *it; 285 if (entry->size == size) { 286 if (entry->type == type) 287 return entry; 288 lastWithRequestedSize = i; 289 } 290 } 291 292 if (m_partCache.size() < RENDER_THEME_EFL_PART_CACHE_MAX) { 293 ThemePartCacheEntry* entry = ThemePartCacheEntry::create(themePath(), type, size); 294 m_partCache.prepend(entry); 295 return entry; 296 } 297 298 // We have a full cache now! 299 EINA_LOG_INFO("RenderTheme cache is full, reusing."); 300 301 if (lastWithRequestedSize != notFound && lastWithRequestedSize != 1) { 302 ThemePartCacheEntry* entry = m_partCache.at(lastWithRequestedSize); 303 entry->reuse(themePath(), type); 304 m_partCache.remove(lastWithRequestedSize); 305 m_partCache.prepend(entry); 306 return entry; 307 } 308 309 ThemePartCacheEntry* entry = m_partCache.last(); 310 entry->reuse(themePath(), type, size); 311 m_partCache.removeLast(); 312 m_partCache.prepend(entry); 313 return entry; 314 } 315 316 void RenderThemeEfl::flushThemePartCache() 317 { 318 Vector<ThemePartCacheEntry*>::iterator it, end; 319 320 it = m_partCache.begin(); 321 end = m_partCache.end(); 322 for (; it != end; it++) 323 delete (*it); 324 325 m_partCache.clear(); 326 } 327 328 void RenderThemeEfl::applyEdjeStateFromForm(Evas_Object* object, ControlStates states) 329 { 330 const char *signals[] = { // keep in sync with WebCore/platform/ThemeTypes.h 331 "hovered", 332 "pressed", 333 "focused", 334 "enabled", 335 "checked", 336 "read-only", 337 "default", 338 "window-inactive", 339 "indeterminate", 340 "spinup" 341 }; 342 343 edje_object_signal_emit(object, "reset", ""); 344 345 for (size_t i = 0; i < WTF_ARRAY_LENGTH(signals); ++i) { 346 if (states & (1 << i)) 347 edje_object_signal_emit(object, signals[i], ""); 348 } 349 } 350 351 bool RenderThemeEfl::paintThemePart(RenderObject* object, FormType type, const PaintInfo& info, const IntRect& rect) 352 { 353 loadThemeIfNeeded(); 354 _ASSERT_ON_RELEASE_RETURN_VAL(m_edje, false, "Could not paint native HTML part due to missing theme."); 355 356 ThemePartCacheEntry* entry; 357 Eina_List* updates; 358 cairo_t* cairo; 359 360 entry = getThemePartFromCache(type, rect.size()); 361 if (!entry) 362 return false; 363 364 applyEdjeStateFromForm(entry->o, controlStatesForRenderer(object)); 365 366 cairo = info.context->platformContext()->cr(); 367 ASSERT(cairo); 368 369 // Currently, only sliders needs this message; if other widget ever needs special 370 // treatment, move them to special functions. 371 if (type == SliderVertical || type == SliderHorizontal) { 372 if (!object->isSlider()) 373 return true; // probably have -webkit-appearance: slider.. 374 375 RenderSlider* renderSlider = toRenderSlider(object); 376 HTMLInputElement* input = renderSlider->node()->toInputElement(); 377 double valueRange = input->maximum() - input->minimum(); 378 379 OwnArrayPtr<char> buffer = adoptArrayPtr(new char[sizeof(Edje_Message_Float_Set) + sizeof(double)]); 380 Edje_Message_Float_Set* msg = new(buffer.get()) Edje_Message_Float_Set; 381 msg->count = 2; 382 383 // The first parameter of the message decides if the progress bar 384 // grows from the end of the slider or from the beginning. On vertical 385 // sliders, it should always be the same and will not be affected by 386 // text direction settings. 387 if (object->style()->direction() == RTL || type == SliderVertical) 388 msg->val[0] = 1; 389 else 390 msg->val[0] = 0; 391 392 msg->val[1] = (input->valueAsNumber() - input->minimum()) / valueRange; 393 edje_object_message_send(entry->o, EDJE_MESSAGE_FLOAT_SET, 0, msg); 394 #if ENABLE(PROGRESS_ELEMENT) 395 } else if (type == ProgressBar) { 396 RenderProgress* renderProgress = toRenderProgress(object); 397 398 int max = rect.width(); 399 double value = renderProgress->position(); 400 401 OwnArrayPtr<char> buffer = adoptArrayPtr(new char[sizeof(Edje_Message_Float_Set) + sizeof(double)]); 402 Edje_Message_Float_Set* msg = new(buffer.get()) Edje_Message_Float_Set; 403 msg->count = 2; 404 405 if (object->style()->direction() == RTL) 406 msg->val[0] = (1.0 - value) * max; 407 else 408 msg->val[0] = 0; 409 msg->val[1] = value; 410 edje_object_message_send(entry->o, EDJE_MESSAGE_FLOAT_SET, 0, msg); 411 #endif 412 } 413 414 edje_object_calc_force(entry->o); 415 edje_object_message_signal_process(entry->o); 416 updates = evas_render_updates(ecore_evas_get(entry->ee)); 417 evas_render_updates_free(updates); 418 419 cairo_save(cairo); 420 cairo_set_source_surface(cairo, entry->surface, rect.x(), rect.y()); 421 cairo_paint_with_alpha(cairo, 1.0); 422 cairo_restore(cairo); 423 424 return false; 425 } 426 427 PassRefPtr<RenderTheme> RenderThemeEfl::create(Page* page) 428 { 429 return adoptRef(new RenderThemeEfl(page)); 430 } 431 432 PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page) 433 { 434 if (page) 435 return RenderThemeEfl::create(page); 436 437 static RenderTheme* fallback = RenderThemeEfl::create(0).leakRef(); 438 return fallback; 439 } 440 441 static void applyColorCallback(void* data, Evas_Object*, const char* /* signal */, const char* colorClass) 442 { 443 RenderThemeEfl* that = static_cast<RenderThemeEfl*>(data); 444 that->setColorFromThemeClass(colorClass); 445 that->platformColorsDidChange(); // Triggers relayout. 446 } 447 448 static void fillColorsFromEdjeClass(Evas_Object* o, const char* colorClass, Color* color1, Color* color2 = 0, Color* color3 = 0) 449 { 450 int r1, g1, b1, a1; 451 int r2, g2, b2, a2; 452 int r3, g3, b3, a3; 453 454 bool ok = edje_object_color_class_get(o, colorClass, &r1, &g1, &b1, &a1, &r2, &g2, &b2, &a2, &r3, &g3, &b3, &a3); 455 _ASSERT_ON_RELEASE_RETURN(ok, "Could not get color class '%s'\n", colorClass); 456 457 if (color1) 458 color1->setRGB(makeRGBA(r1, g1, b1, a1)); 459 if (color2) 460 color2->setRGB(makeRGBA(r2, g2, b2, a2)); 461 if (color3) 462 color3->setRGB(makeRGBA(r3, g3, b3, a3)); 463 } 464 465 void RenderThemeEfl::setColorFromThemeClass(const char* colorClass) 466 { 467 ASSERT(m_edje); 468 469 if (!strcmp("webkit/selection/active", colorClass)) 470 fillColorsFromEdjeClass(m_edje, colorClass, &m_activeSelectionForegroundColor, &m_activeSelectionBackgroundColor); 471 else if (!strcmp("webkit/selection/inactive", colorClass)) 472 fillColorsFromEdjeClass(m_edje, colorClass, &m_inactiveSelectionForegroundColor, &m_inactiveSelectionBackgroundColor); 473 else if (!strcmp("webkit/focus_ring", colorClass)) { 474 fillColorsFromEdjeClass(m_edje, colorClass, &m_focusRingColor); 475 // platformFocusRingColor() is only used for the default theme (without page) 476 // The following is ugly, but no other way to do it unless we change it to use page themes as much as possible. 477 RenderTheme::setCustomFocusRingColor(m_focusRingColor); 478 } 479 } 480 481 void RenderThemeEfl::setThemePath(const String& path) 482 { 483 if (path == m_themePath) 484 return; 485 486 if (path.isEmpty()) { 487 EINA_LOG_CRIT("No valid theme defined, things will not work properly."); 488 return; 489 } 490 491 m_themePath = path; 492 493 loadTheme(); 494 } 495 496 String RenderThemeEfl::themePath() const 497 { 498 #if !NDEBUG 499 if (m_edje) { 500 const char* path; 501 edje_object_file_get(o, &path, 0); 502 ASSERT(m_themePath == path); 503 } 504 #endif 505 return m_themePath; 506 } 507 508 bool RenderThemeEfl::loadTheme() 509 { 510 ASSERT(!m_themePath.isEmpty()); 511 512 if (!m_canvas) { 513 m_canvas = ecore_evas_buffer_new(1, 1); 514 _ASSERT_ON_RELEASE_RETURN_VAL(m_canvas, false, 515 "Could not create canvas required by theme, things will not work properly."); 516 } 517 518 Evas_Object* o = edje_object_add(ecore_evas_get(m_canvas)); 519 _ASSERT_ON_RELEASE_RETURN_VAL(o, false, "Could not create new base Edje object."); 520 521 if (!setSourceGroupForEdjeObject(o, m_themePath, "webkit/base")) { 522 evas_object_del(o); 523 return false; // Keep current theme. 524 } 525 526 // Get rid of existing theme. 527 if (m_edje) { 528 flushThemePartCache(); 529 evas_object_del(m_edje); 530 } 531 532 // Set new loaded theme, and apply it. 533 m_edje = o; 534 535 edje_object_signal_callback_add(m_edje, "color_class,set", "webkit/selection/active", applyColorCallback, this); 536 edje_object_signal_callback_add(m_edje, "color_class,set", "webkit/selection/inactive", applyColorCallback, this); 537 edje_object_signal_callback_add(m_edje, "color_class,set", "webkit/focus_ring", applyColorCallback, this); 538 539 applyPartDescriptionsFrom(m_themePath); 540 541 setColorFromThemeClass("webkit/selection/active"); 542 setColorFromThemeClass("webkit/selection/inactive"); 543 setColorFromThemeClass("webkit/focus_ring"); 544 545 platformColorsDidChange(); // Schedules a relayout, do last. 546 547 return true; 548 } 549 550 void RenderThemeEfl::applyPartDescriptionFallback(ThemePartDesc* desc) 551 { 552 desc->min.setWidth(Length(0, Fixed)); 553 desc->min.setHeight(Length(0, Fixed)); 554 555 desc->max.setWidth(Length(0, Fixed)); 556 desc->max.setHeight(Length(0, Fixed)); 557 558 desc->padding = LengthBox(0, 0, 0, 0); 559 } 560 561 void RenderThemeEfl::applyPartDescription(Evas_Object* object, ThemePartDesc* desc) 562 { 563 Evas_Coord minw, minh, maxw, maxh; 564 565 edje_object_size_min_get(object, &minw, &minh); 566 if (!minw && !minh) 567 edje_object_size_min_calc(object, &minw, &minh); 568 569 desc->min.setWidth(Length(minw, Fixed)); 570 desc->min.setHeight(Length(minh, Fixed)); 571 572 edje_object_size_max_get(object, &maxw, &maxh); 573 desc->max.setWidth(Length(maxw, Fixed)); 574 desc->max.setHeight(Length(maxh, Fixed)); 575 576 if (!edje_object_part_exists(object, "text_confinement")) 577 desc->padding = LengthBox(0, 0, 0, 0); 578 else { 579 Evas_Coord px, py, pw, ph; 580 Evas_Coord ox = 0, oy = 0, ow = 0, oh = 0; 581 int t, r, b, l; 582 583 if (minw > 0) 584 ow = minw; 585 else 586 ow = 100; 587 if (minh > 0) 588 oh = minh; 589 else 590 oh = 100; 591 if (maxw > 0 && ow > maxw) 592 ow = maxw; 593 if (maxh > 0 && oh > maxh) 594 oh = maxh; 595 596 evas_object_move(object, ox, oy); 597 evas_object_resize(object, ow, oh); 598 edje_object_calc_force(object); 599 edje_object_message_signal_process(object); 600 edje_object_part_geometry_get(object, "text_confinement", &px, &py, &pw, &ph); 601 602 t = py - oy; 603 b = (oh + oy) - (ph + py); 604 605 l = px - ox; 606 r = (ow + ox) - (pw + px); 607 608 desc->padding = LengthBox(t, r, b, l); 609 } 610 } 611 612 void RenderThemeEfl::applyPartDescriptionsFrom(const String& themePath) 613 { 614 Evas_Object* temp = edje_object_add(ecore_evas_get(m_canvas)); 615 _ASSERT_ON_RELEASE_RETURN(temp, "Could not create Edje object."); 616 617 for (size_t i = 0; i < FormTypeLast; i++) { 631 618 FormType type = static_cast<FormType>(i); 632 const char* group = edjeGroupFromFormType(type);633 619 m_partDescs[i].type = type; 634 if (!edje_object_file_set(object, file, group)) { 635 Edje_Load_Error err = edje_object_load_error_get(object); 636 const char* errmsg = edje_load_error_str(err); 637 EINA_LOG_ERR("Could not set theme group '%s' of file '%s': %s", 638 group, file, errmsg); 639 620 if (!setSourceGroupForEdjeObject(temp, themePath, toEdjeGroup(type))) 640 621 applyPartDescriptionFallback(m_partDescs + i); 641 } else 642 applyPartDescription(object, m_partDescs + i); 643 } 644 evas_object_del(object); 622 else 623 applyPartDescription(temp, m_partDescs + i); 624 } 625 626 evas_object_del(temp); 645 627 } 646 628 … … 665 647 RenderThemeEfl::~RenderThemeEfl() 666 648 { 667 cacheThemePartFlush();649 flushThemePartCache(); 668 650 669 651 if (m_canvas) { … … 835 817 836 818 adjustSizeConstraints(style, CheckBox); 837 ASSERT(m_edje);838 819 839 820 style->resetBorder(); 840 821 841 const struct ThemePartDesc *desc = m_partDescs + (size_t)CheckBox;822 const ThemePartDesc* desc = m_partDescs + (size_t)CheckBox; 842 823 if (style->width().value() < desc->min.width().value()) 843 824 style->setWidth(desc->min.width()); … … 859 840 860 841 adjustSizeConstraints(style, RadioButton); 861 ASSERT(m_edje);862 842 863 843 style->resetBorder(); 864 844 865 const struct ThemePartDesc *desc = m_partDescs + (size_t)RadioButton;845 const ThemePartDesc* desc = m_partDescs + (size_t)RadioButton; 866 846 if (style->width().value() < desc->min.width().value()) 867 847 style->setWidth(desc->min.width()); … … 1090 1070 _ASSERT_ON_RELEASE_RETURN_VAL(m_edje, false, "Could not paint native HTML part due to missing theme."); 1091 1071 1092 ThemePartCacheEntry* entry; 1093 1094 entry = cacheThemePartGet(formType, rect.size()); 1072 ThemePartCacheEntry* entry = getThemePartFromCache(formType, rect.size()); 1095 1073 _ASSERT_ON_RELEASE_RETURN_VAL(entry, false, "Could not paint native HTML part due to missing theme part."); 1096 1074 -
trunk/Source/WebCore/platform/efl/RenderThemeEfl.h
r128049 r128274 211 211 212 212 void setThemePath(const String&); 213 String themePath() { return m_themePath; }213 String themePath() const; 214 214 215 215 protected: … … 220 220 ALWAYS_INLINE bool loadThemeIfNeeded() const 221 221 { 222 return m_edje || const_cast<RenderThemeEfl*>(this)->loadTheme();222 return m_edje || (!m_themePath.isEmpty() && const_cast<RenderThemeEfl*>(this)->loadTheme()); 223 223 } 224 224 225 void applyPartDescriptionsFrom(Evas_Object*); 226 227 const char* edjeGroupFromFormType(FormType) const; 225 void applyPartDescriptionsFrom(const String& themePath); 226 228 227 void applyEdjeStateFromForm(Evas_Object*, ControlStates); 229 228 bool paintThemePart(RenderObject*, FormType, const PaintInfo&, const IntRect&); 230 bool isFormElementTooLargeToDisplay(const IntSize&);231 229 232 230 #if ENABLE(VIDEO) … … 261 259 262 260 struct ThemePartCacheEntry { 261 ThemePartCacheEntry(); 262 ~ThemePartCacheEntry(); 263 static ThemePartCacheEntry* create(const String& themePath, FormType, const IntSize&); 264 void reuse(const String& themePath, FormType, const IntSize& = IntSize()); 265 263 266 FormType type; 264 267 IntSize size; … … 272 275 // this should be small and not so frequently used, 273 276 // so use a vector and do linear searches 274 Vector<struct ThemePartCacheEntry *> m_partCache; 275 276 // get (use, create or replace) entry from cache 277 struct ThemePartCacheEntry* cacheThemePartGet(FormType, const IntSize&); 278 // flush cache, deleting all entries 279 void cacheThemePartFlush(); 280 281 // internal, used by cacheThemePartGet() 282 bool themePartCacheEntryReset(struct ThemePartCacheEntry*, FormType); 283 bool themePartCacheEntrySurfaceCreate(struct ThemePartCacheEntry*); 284 struct ThemePartCacheEntry* cacheThemePartNew(FormType, const IntSize&); 285 struct ThemePartCacheEntry* cacheThemePartReset(FormType, struct ThemePartCacheEntry*); 286 struct ThemePartCacheEntry* cacheThemePartResizeAndReset(FormType, const IntSize&, struct ThemePartCacheEntry*); 287 277 Vector<ThemePartCacheEntry*> m_partCache; 278 279 ThemePartCacheEntry* getThemePartFromCache(FormType, const IntSize&); 280 void flushThemePartCache(); 288 281 }; 289 282 }
Note: See TracChangeset
for help on using the changeset viewer.