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) 2009 Kenneth Rohde Christiansen 6 * Copyright (C) 2010 Igalia S.L. 7 * 8 * This library is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU Library General Public 10 * License as published by the Free Software Foundation; either 11 * version 2 of the License, or (at your option) any later version. 12 * 13 * This library is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * Library General Public License for more details. 17 * 18 * You should have received a copy of the GNU Library General Public License 19 * along with this library; see the file COPYING.LIB. If not, write to 20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 21 * Boston, MA 02110-1301, USA. 22 * 23 */ 24 25#include "config.h" 26#include "RenderThemeGtk.h" 27 28#include "CSSValueKeywords.h" 29#include "ExceptionCodePlaceholder.h" 30#include "FileList.h" 31#include "FileSystem.h" 32#include "FontDescription.h" 33#include <wtf/gobject/GOwnPtr.h> 34#include "Gradient.h" 35#include "GraphicsContext.h" 36#include "GtkVersioning.h" 37#include "HTMLMediaElement.h" 38#include "LocalizedStrings.h" 39#include "MediaControlElements.h" 40#include "PaintInfo.h" 41#include "PlatformContextCairo.h" 42#include "RenderBox.h" 43#include "RenderObject.h" 44#include "StringTruncator.h" 45#include "TimeRanges.h" 46#include "UserAgentStyleSheets.h" 47#include <cmath> 48#include <gdk/gdk.h> 49#include <glib.h> 50#include <gtk/gtk.h> 51#include <wtf/text/CString.h> 52 53#if ENABLE(PROGRESS_ELEMENT) 54#include "RenderProgress.h" 55#endif 56 57namespace WebCore { 58 59// This would be a static method, except that forward declaring GType is tricky, since its 60// definition depends on including glib.h, negating the benefit of using a forward declaration. 61extern GRefPtr<GdkPixbuf> getStockIconForWidgetType(GType, const char* iconName, gint direction, gint state, gint iconSize); 62extern GRefPtr<GdkPixbuf> getStockSymbolicIconForWidgetType(GType widgetType, const char* symbolicIconName, const char *fallbackStockIconName, gint direction, gint state, gint iconSize); 63 64#if ENABLE(VIDEO) 65static HTMLMediaElement* getMediaElementFromRenderObject(RenderObject* o) 66{ 67 Node* node = o->node(); 68 Node* mediaNode = node ? node->shadowHost() : 0; 69 if (!mediaNode) 70 mediaNode = node; 71 if (!mediaNode || !mediaNode->isElementNode() || !toElement(mediaNode)->isMediaElement()) 72 return 0; 73 74 return static_cast<HTMLMediaElement*>(mediaNode); 75} 76 77void RenderThemeGtk::initMediaButtons() 78{ 79 static bool iconsInitialized = false; 80 81 if (iconsInitialized) 82 return; 83 84 GRefPtr<GtkIconFactory> iconFactory = adoptGRef(gtk_icon_factory_new()); 85 GtkIconSource* iconSource = gtk_icon_source_new(); 86 const char* icons[] = { "audio-volume-high", "audio-volume-muted" }; 87 88 gtk_icon_factory_add_default(iconFactory.get()); 89 90 for (size_t i = 0; i < G_N_ELEMENTS(icons); ++i) { 91 gtk_icon_source_set_icon_name(iconSource, icons[i]); 92 GtkIconSet* iconSet = gtk_icon_set_new(); 93 gtk_icon_set_add_source(iconSet, iconSource); 94 gtk_icon_factory_add(iconFactory.get(), icons[i], iconSet); 95 gtk_icon_set_unref(iconSet); 96 } 97 98 gtk_icon_source_free(iconSource); 99 100 iconsInitialized = true; 101} 102#endif 103 104PassRefPtr<RenderTheme> RenderThemeGtk::create() 105{ 106 return adoptRef(new RenderThemeGtk()); 107} 108 109PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page) 110{ 111 static RenderTheme* rt = RenderThemeGtk::create().leakRef(); 112 return rt; 113} 114 115RenderThemeGtk::RenderThemeGtk() 116 : m_panelColor(Color::white) 117 , m_sliderColor(Color::white) 118 , m_sliderThumbColor(Color::white) 119 , m_mediaIconSize(16) 120 , m_mediaSliderHeight(14) 121{ 122 platformInit(); 123#if ENABLE(VIDEO) 124 initMediaColors(); 125 initMediaButtons(); 126#endif 127} 128 129static bool supportsFocus(ControlPart appearance) 130{ 131 switch (appearance) { 132 case PushButtonPart: 133 case ButtonPart: 134 case TextFieldPart: 135 case TextAreaPart: 136 case SearchFieldPart: 137 case MenulistPart: 138 case RadioPart: 139 case CheckboxPart: 140 case SliderHorizontalPart: 141 case SliderVerticalPart: 142 case MediaPlayButtonPart: 143 case MediaVolumeSliderPart: 144 case MediaMuteButtonPart: 145 case MediaEnterFullscreenButtonPart: 146 case MediaSliderPart: 147 return true; 148 default: 149 return false; 150 } 151} 152 153bool RenderThemeGtk::supportsFocusRing(const RenderStyle* style) const 154{ 155 return supportsFocus(style->appearance()); 156} 157 158bool RenderThemeGtk::controlSupportsTints(const RenderObject* o) const 159{ 160 return isEnabled(o); 161} 162 163int RenderThemeGtk::baselinePosition(const RenderObject* o) const 164{ 165 if (!o->isBox()) 166 return 0; 167 168 // FIXME: This strategy is possibly incorrect for the GTK+ port. 169 if (o->style()->appearance() == CheckboxPart 170 || o->style()->appearance() == RadioPart) { 171 const RenderBox* box = toRenderBox(o); 172 return box->marginTop() + box->height() - 2; 173 } 174 175 return RenderTheme::baselinePosition(o); 176} 177 178// This is used in RenderThemeGtk2 and RenderThemeGtk3. Normally, it would be in 179// the RenderThemeGtk header (perhaps as a static method), but we want to avoid 180// having to include GTK+ headers only for the GtkTextDirection enum. 181GtkTextDirection gtkTextDirection(TextDirection direction) 182{ 183 switch (direction) { 184 case RTL: 185 return GTK_TEXT_DIR_RTL; 186 case LTR: 187 return GTK_TEXT_DIR_LTR; 188 default: 189 return GTK_TEXT_DIR_NONE; 190 } 191} 192 193static GtkStateType gtkIconState(RenderTheme* theme, RenderObject* renderObject) 194{ 195 if (!theme->isEnabled(renderObject)) 196 return GTK_STATE_INSENSITIVE; 197 if (theme->isPressed(renderObject)) 198 return GTK_STATE_ACTIVE; 199 if (theme->isHovered(renderObject)) 200 return GTK_STATE_PRELIGHT; 201 202 return GTK_STATE_NORMAL; 203} 204 205void RenderThemeGtk::adjustButtonStyle(StyleResolver*, RenderStyle* style, WebCore::Element*) const 206{ 207 // Some layout tests check explicitly that buttons ignore line-height. 208 if (style->appearance() == PushButtonPart) 209 style->setLineHeight(RenderStyle::initialLineHeight()); 210} 211 212void RenderThemeGtk::adjustMenuListStyle(StyleResolver*, RenderStyle* style, Element*) const 213{ 214 // The tests check explicitly that select menu buttons ignore line height. 215 style->setLineHeight(RenderStyle::initialLineHeight()); 216 217 // We cannot give a proper rendering when border radius is active, unfortunately. 218 style->resetBorderRadius(); 219} 220 221void RenderThemeGtk::adjustMenuListButtonStyle(StyleResolver* styleResolver, RenderStyle* style, Element* e) const 222{ 223 adjustMenuListStyle(styleResolver, style, e); 224} 225 226bool RenderThemeGtk::paintMenuListButton(RenderObject* object, const PaintInfo& info, const IntRect& rect) 227{ 228 return paintMenuList(object, info, rect); 229} 230 231bool RenderThemeGtk::paintTextArea(RenderObject* o, const PaintInfo& i, const IntRect& r) 232{ 233 return paintTextField(o, i, r); 234} 235 236static void paintGdkPixbuf(GraphicsContext* context, const GdkPixbuf* icon, const IntRect& iconRect) 237{ 238 IntSize iconSize(gdk_pixbuf_get_width(icon), gdk_pixbuf_get_height(icon)); 239 GRefPtr<GdkPixbuf> scaledIcon; 240 if (iconRect.size() != iconSize) { 241 // We could use cairo_scale() here but cairo/pixman downscale quality is quite bad. 242 scaledIcon = adoptGRef(gdk_pixbuf_scale_simple(icon, iconRect.width(), iconRect.height(), 243 GDK_INTERP_BILINEAR)); 244 icon = scaledIcon.get(); 245 } 246 247 cairo_t* cr = context->platformContext()->cr(); 248 cairo_save(cr); 249 gdk_cairo_set_source_pixbuf(cr, icon, iconRect.x(), iconRect.y()); 250 cairo_paint(cr); 251 cairo_restore(cr); 252} 253 254// Defined in GTK+ (gtk/gtkiconfactory.c) 255static const gint gtkIconSizeMenu = 16; 256static const gint gtkIconSizeSmallToolbar = 18; 257static const gint gtkIconSizeButton = 20; 258static const gint gtkIconSizeLargeToolbar = 24; 259static const gint gtkIconSizeDnd = 32; 260static const gint gtkIconSizeDialog = 48; 261 262static GtkIconSize getIconSizeForPixelSize(gint pixelSize) 263{ 264 if (pixelSize < gtkIconSizeSmallToolbar) 265 return GTK_ICON_SIZE_MENU; 266 if (pixelSize >= gtkIconSizeSmallToolbar && pixelSize < gtkIconSizeButton) 267 return GTK_ICON_SIZE_SMALL_TOOLBAR; 268 if (pixelSize >= gtkIconSizeButton && pixelSize < gtkIconSizeLargeToolbar) 269 return GTK_ICON_SIZE_BUTTON; 270 if (pixelSize >= gtkIconSizeLargeToolbar && pixelSize < gtkIconSizeDnd) 271 return GTK_ICON_SIZE_LARGE_TOOLBAR; 272 if (pixelSize >= gtkIconSizeDnd && pixelSize < gtkIconSizeDialog) 273 return GTK_ICON_SIZE_DND; 274 275 return GTK_ICON_SIZE_DIALOG; 276} 277 278void RenderThemeGtk::adjustSearchFieldResultsButtonStyle(StyleResolver* styleResolver, RenderStyle* style, Element* e) const 279{ 280 adjustSearchFieldCancelButtonStyle(styleResolver, style, e); 281} 282 283bool RenderThemeGtk::paintSearchFieldResultsButton(RenderObject* o, const PaintInfo& i, const IntRect& rect) 284{ 285 return paintSearchFieldResultsDecoration(o, i, rect); 286} 287 288static void adjustSearchFieldIconStyle(RenderStyle* style) 289{ 290 style->resetBorder(); 291 style->resetPadding(); 292 293 // Get the icon size based on the font size. 294 int fontSize = style->fontSize(); 295 if (fontSize < gtkIconSizeMenu) { 296 style->setWidth(Length(fontSize, Fixed)); 297 style->setHeight(Length(fontSize, Fixed)); 298 return; 299 } 300 gint width = 0, height = 0; 301 gtk_icon_size_lookup(getIconSizeForPixelSize(fontSize), &width, &height); 302 style->setWidth(Length(width, Fixed)); 303 style->setHeight(Length(height, Fixed)); 304} 305 306void RenderThemeGtk::adjustSearchFieldResultsDecorationStyle(StyleResolver*, RenderStyle* style, Element*) const 307{ 308 adjustSearchFieldIconStyle(style); 309} 310 311static IntRect centerRectVerticallyInParentInputElement(RenderObject* renderObject, const IntRect& rect) 312{ 313 // Get the renderer of <input> element. 314 Node* input = renderObject->node()->shadowHost(); 315 if (!input) 316 input = renderObject->node(); 317 if (!input->renderer()->isBox()) 318 return IntRect(); 319 320 // If possible center the y-coordinate of the rect vertically in the parent input element. 321 // We also add one pixel here to ensure that the y coordinate is rounded up for box heights 322 // that are even, which looks in relation to the box text. 323 IntRect inputContentBox = toRenderBox(input->renderer())->absoluteContentBox(); 324 325 // Make sure the scaled decoration stays square and will fit in its parent's box. 326 int iconSize = std::min(inputContentBox.width(), std::min(inputContentBox.height(), rect.height())); 327 IntRect scaledRect(rect.x(), inputContentBox.y() + (inputContentBox.height() - iconSize + 1) / 2, iconSize, iconSize); 328 return scaledRect; 329} 330 331bool RenderThemeGtk::paintSearchFieldResultsDecoration(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) 332{ 333 IntRect iconRect = centerRectVerticallyInParentInputElement(renderObject, rect); 334 if (iconRect.isEmpty()) 335 return false; 336 337 GRefPtr<GdkPixbuf> icon = getStockIconForWidgetType(GTK_TYPE_ENTRY, GTK_STOCK_FIND, 338 gtkTextDirection(renderObject->style()->direction()), 339 gtkIconState(this, renderObject), 340 getIconSizeForPixelSize(rect.height())); 341 paintGdkPixbuf(paintInfo.context, icon.get(), iconRect); 342 return false; 343} 344 345void RenderThemeGtk::adjustSearchFieldCancelButtonStyle(StyleResolver*, RenderStyle* style, Element*) const 346{ 347 adjustSearchFieldIconStyle(style); 348} 349 350bool RenderThemeGtk::paintSearchFieldCancelButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) 351{ 352 IntRect iconRect = centerRectVerticallyInParentInputElement(renderObject, rect); 353 if (iconRect.isEmpty()) 354 return false; 355 356 GRefPtr<GdkPixbuf> icon = getStockIconForWidgetType(GTK_TYPE_ENTRY, GTK_STOCK_CLEAR, 357 gtkTextDirection(renderObject->style()->direction()), 358 gtkIconState(this, renderObject), 359 getIconSizeForPixelSize(rect.height())); 360 paintGdkPixbuf(paintInfo.context, icon.get(), iconRect); 361 return false; 362} 363 364void RenderThemeGtk::adjustSearchFieldStyle(StyleResolver*, RenderStyle* style, Element*) const 365{ 366 // We cannot give a proper rendering when border radius is active, unfortunately. 367 style->resetBorderRadius(); 368 style->setLineHeight(RenderStyle::initialLineHeight()); 369} 370 371bool RenderThemeGtk::paintSearchField(RenderObject* o, const PaintInfo& i, const IntRect& rect) 372{ 373 return paintTextField(o, i, rect); 374} 375 376bool RenderThemeGtk::paintCapsLockIndicator(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) 377{ 378 // The other paint methods don't need to check whether painting is disabled because RenderTheme already checks it 379 // before calling them, but paintCapsLockIndicator() is called by RenderTextControlSingleLine which doesn't check it. 380 if (paintInfo.context->paintingDisabled()) 381 return true; 382 383 int iconSize = std::min(rect.width(), rect.height()); 384 GRefPtr<GdkPixbuf> icon = getStockIconForWidgetType(GTK_TYPE_ENTRY, GTK_STOCK_CAPS_LOCK_WARNING, 385 gtkTextDirection(renderObject->style()->direction()), 386 0, getIconSizeForPixelSize(iconSize)); 387 388 // Only re-scale the icon when it's smaller than the minimum icon size. 389 if (iconSize >= gtkIconSizeMenu) 390 iconSize = gdk_pixbuf_get_height(icon.get()); 391 392 // GTK+ locates the icon right aligned in the entry. The given rectangle is already 393 // centered vertically by RenderTextControlSingleLine. 394 IntRect iconRect(rect.x() + rect.width() - iconSize, 395 rect.y() + (rect.height() - iconSize) / 2, 396 iconSize, iconSize); 397 paintGdkPixbuf(paintInfo.context, icon.get(), iconRect); 398 return true; 399} 400 401void RenderThemeGtk::adjustSliderTrackStyle(StyleResolver*, RenderStyle* style, Element*) const 402{ 403 style->setBoxShadow(nullptr); 404} 405 406void RenderThemeGtk::adjustSliderThumbStyle(StyleResolver* styleResolver, RenderStyle* style, Element* element) const 407{ 408 RenderTheme::adjustSliderThumbStyle(styleResolver, style, element); 409 style->setBoxShadow(nullptr); 410} 411 412double RenderThemeGtk::caretBlinkInterval() const 413{ 414 GtkSettings* settings = gtk_settings_get_default(); 415 416 gboolean shouldBlink; 417 gint time; 418 419 g_object_get(settings, "gtk-cursor-blink", &shouldBlink, "gtk-cursor-blink-time", &time, NULL); 420 421 if (!shouldBlink) 422 return 0; 423 424 return time / 2000.; 425} 426 427double RenderThemeGtk::getScreenDPI() 428{ 429 // FIXME: Really this should be the widget's screen. 430 GdkScreen* screen = gdk_screen_get_default(); 431 if (!screen) 432 return 96; // Default to 96 DPI. 433 434 float dpi = gdk_screen_get_resolution(screen); 435 if (dpi <= 0) 436 return 96; 437 return dpi; 438} 439 440void RenderThemeGtk::systemFont(int, FontDescription& fontDescription) const 441{ 442 GtkSettings* settings = gtk_settings_get_default(); 443 if (!settings) 444 return; 445 446 // This will be a font selection string like "Sans 10" so we cannot use it as the family name. 447 GOwnPtr<gchar> fontName; 448 g_object_get(settings, "gtk-font-name", &fontName.outPtr(), NULL); 449 450 PangoFontDescription* pangoDescription = pango_font_description_from_string(fontName.get()); 451 if (!pangoDescription) 452 return; 453 454 fontDescription.setOneFamily(pango_font_description_get_family(pangoDescription)); 455 456 int size = pango_font_description_get_size(pangoDescription) / PANGO_SCALE; 457 // If the size of the font is in points, we need to convert it to pixels. 458 if (!pango_font_description_get_size_is_absolute(pangoDescription)) 459 size = size * (getScreenDPI() / 72.0); 460 461 fontDescription.setSpecifiedSize(size); 462 fontDescription.setIsAbsoluteSize(true); 463 fontDescription.setGenericFamily(FontDescription::NoFamily); 464 fontDescription.setWeight(FontWeightNormal); 465 fontDescription.setItalic(false); 466 pango_font_description_free(pangoDescription); 467} 468 469void RenderThemeGtk::platformColorsDidChange() 470{ 471#if ENABLE(VIDEO) 472 initMediaColors(); 473#endif 474 RenderTheme::platformColorsDidChange(); 475} 476 477#if ENABLE(VIDEO) 478String RenderThemeGtk::extraMediaControlsStyleSheet() 479{ 480 return String(mediaControlsGtkUserAgentStyleSheet, sizeof(mediaControlsGtkUserAgentStyleSheet)); 481} 482 483#if ENABLE(FULLSCREEN_API) 484String RenderThemeGtk::extraFullScreenStyleSheet() 485{ 486 return String(); 487} 488#endif 489 490bool RenderThemeGtk::paintMediaButton(RenderObject* renderObject, GraphicsContext* context, const IntRect& rect, const char* symbolicIconName, const char* fallbackStockIconName) 491{ 492 IntRect iconRect(rect.x() + (rect.width() - m_mediaIconSize) / 2, 493 rect.y() + (rect.height() - m_mediaIconSize) / 2, 494 m_mediaIconSize, m_mediaIconSize); 495 GRefPtr<GdkPixbuf> icon = getStockSymbolicIconForWidgetType(GTK_TYPE_CONTAINER, symbolicIconName, fallbackStockIconName, 496 gtkTextDirection(renderObject->style()->direction()), gtkIconState(this, renderObject), iconRect.width()); 497 paintGdkPixbuf(context, icon.get(), iconRect); 498 return false; 499} 500 501bool RenderThemeGtk::hasOwnDisabledStateHandlingFor(ControlPart part) const 502{ 503 return (part != MediaMuteButtonPart); 504} 505 506bool RenderThemeGtk::paintMediaFullscreenButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) 507{ 508 return paintMediaButton(renderObject, paintInfo.context, rect, "view-fullscreen-symbolic", GTK_STOCK_FULLSCREEN); 509} 510 511bool RenderThemeGtk::paintMediaMuteButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) 512{ 513 HTMLMediaElement* mediaElement = getMediaElementFromRenderObject(renderObject); 514 if (!mediaElement) 515 return false; 516 517 bool muted = mediaElement->muted(); 518 return paintMediaButton(renderObject, paintInfo.context, rect, 519 muted ? "audio-volume-muted-symbolic" : "audio-volume-high-symbolic", 520 muted ? "audio-volume-muted" : "audio-volume-high"); 521} 522 523bool RenderThemeGtk::paintMediaPlayButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) 524{ 525 Node* node = renderObject->node(); 526 if (!node) 527 return false; 528 if (!node->isMediaControlElement()) 529 return false; 530 531 bool play = mediaControlElementType(node) == MediaPlayButton; 532 return paintMediaButton(renderObject, paintInfo.context, rect, 533 play ? "media-playback-start-symbolic" : "media-playback-pause-symbolic", 534 play ? GTK_STOCK_MEDIA_PLAY : GTK_STOCK_MEDIA_PAUSE); 535} 536 537bool RenderThemeGtk::paintMediaSeekBackButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) 538{ 539 return paintMediaButton(renderObject, paintInfo.context, rect, "media-seek-backward-symbolic", GTK_STOCK_MEDIA_REWIND); 540} 541 542bool RenderThemeGtk::paintMediaSeekForwardButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) 543{ 544 return paintMediaButton(renderObject, paintInfo.context, rect, "media-seek-forward-symbolic", GTK_STOCK_MEDIA_FORWARD); 545} 546 547static RoundedRect::Radii borderRadiiFromStyle(RenderStyle* style) 548{ 549 return RoundedRect::Radii( 550 IntSize(style->borderTopLeftRadius().width().intValue(), style->borderTopLeftRadius().height().intValue()), 551 IntSize(style->borderTopRightRadius().width().intValue(), style->borderTopRightRadius().height().intValue()), 552 IntSize(style->borderBottomLeftRadius().width().intValue(), style->borderBottomLeftRadius().height().intValue()), 553 IntSize(style->borderBottomRightRadius().width().intValue(), style->borderBottomRightRadius().height().intValue())); 554} 555 556bool RenderThemeGtk::paintMediaSliderTrack(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) 557{ 558 HTMLMediaElement* mediaElement = toParentMediaElement(o); 559 if (!mediaElement) 560 return false; 561 562 GraphicsContext* context = paintInfo.context; 563 context->save(); 564 context->setStrokeStyle(NoStroke); 565 566 float mediaDuration = mediaElement->duration(); 567 float totalTrackWidth = r.width(); 568 RenderStyle* style = o->style(); 569 RefPtr<TimeRanges> timeRanges = mediaElement->buffered(); 570 for (unsigned index = 0; index < timeRanges->length(); ++index) { 571 float start = timeRanges->start(index, IGNORE_EXCEPTION); 572 float end = timeRanges->end(index, IGNORE_EXCEPTION); 573 float startRatio = start / mediaDuration; 574 float lengthRatio = (end - start) / mediaDuration; 575 if (!lengthRatio) 576 continue; 577 578 IntRect rangeRect(r); 579 rangeRect.setWidth(lengthRatio * totalTrackWidth); 580 if (index) 581 rangeRect.move(startRatio * totalTrackWidth, 0); 582 context->fillRoundedRect(RoundedRect(rangeRect, borderRadiiFromStyle(style)), style->visitedDependentColor(CSSPropertyColor), style->colorSpace()); 583 } 584 585 context->restore(); 586 return false; 587} 588 589bool RenderThemeGtk::paintMediaSliderThumb(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) 590{ 591 RenderStyle* style = o->style(); 592 paintInfo.context->fillRoundedRect(RoundedRect(r, borderRadiiFromStyle(style)), style->visitedDependentColor(CSSPropertyColor), style->colorSpace()); 593 return false; 594} 595 596bool RenderThemeGtk::paintMediaVolumeSliderContainer(RenderObject*, const PaintInfo& paintInfo, const IntRect& rect) 597{ 598 return true; 599} 600 601bool RenderThemeGtk::paintMediaVolumeSliderTrack(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) 602{ 603 HTMLMediaElement* mediaElement = toParentMediaElement(renderObject); 604 if (!mediaElement) 605 return true; 606 607 float volume = mediaElement->volume(); 608 if (!volume) 609 return true; 610 611 GraphicsContext* context = paintInfo.context; 612 context->save(); 613 context->setStrokeStyle(NoStroke); 614 615 int rectHeight = rect.height(); 616 float trackHeight = rectHeight * volume; 617 RenderStyle* style = renderObject->style(); 618 IntRect volumeRect(rect); 619 volumeRect.move(0, rectHeight - trackHeight); 620 volumeRect.setHeight(ceil(trackHeight)); 621 622 context->fillRoundedRect(RoundedRect(volumeRect, borderRadiiFromStyle(style)), 623 style->visitedDependentColor(CSSPropertyColor), style->colorSpace()); 624 context->restore(); 625 626 return false; 627} 628 629bool RenderThemeGtk::paintMediaVolumeSliderThumb(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) 630{ 631 return paintMediaSliderThumb(renderObject, paintInfo, rect); 632} 633 634String RenderThemeGtk::formatMediaControlsCurrentTime(float currentTime, float duration) const 635{ 636 return formatMediaControlsTime(currentTime) + " / " + formatMediaControlsTime(duration); 637} 638 639bool RenderThemeGtk::paintMediaCurrentTime(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) 640{ 641 return false; 642} 643#endif 644 645#if ENABLE(PROGRESS_ELEMENT) 646void RenderThemeGtk::adjustProgressBarStyle(StyleResolver*, RenderStyle* style, Element*) const 647{ 648 style->setBoxShadow(nullptr); 649} 650 651// These values have been copied from RenderThemeChromiumSkia.cpp 652static const int progressActivityBlocks = 5; 653static const int progressAnimationFrames = 10; 654static const double progressAnimationInterval = 0.125; 655double RenderThemeGtk::animationRepeatIntervalForProgressBar(RenderProgress*) const 656{ 657 return progressAnimationInterval; 658} 659 660double RenderThemeGtk::animationDurationForProgressBar(RenderProgress*) const 661{ 662 return progressAnimationInterval * progressAnimationFrames * 2; // "2" for back and forth; 663} 664 665IntRect RenderThemeGtk::calculateProgressRect(RenderObject* renderObject, const IntRect& fullBarRect) 666{ 667 IntRect progressRect(fullBarRect); 668 RenderProgress* renderProgress = toRenderProgress(renderObject); 669 if (renderProgress->isDeterminate()) { 670 int progressWidth = progressRect.width() * renderProgress->position(); 671 if (renderObject->style()->direction() == RTL) 672 progressRect.setX(progressRect.x() + progressRect.width() - progressWidth); 673 progressRect.setWidth(progressWidth); 674 return progressRect; 675 } 676 677 double animationProgress = renderProgress->animationProgress(); 678 679 // Never let the progress rect shrink smaller than 2 pixels. 680 int newWidth = max(2, progressRect.width() / progressActivityBlocks); 681 int movableWidth = progressRect.width() - newWidth; 682 progressRect.setWidth(newWidth); 683 684 // We want the first 0.5 units of the animation progress to represent the 685 // forward motion and the second 0.5 units to represent the backward motion, 686 // thus we multiply by two here to get the full sweep of the progress bar with 687 // each direction. 688 if (animationProgress < 0.5) 689 progressRect.setX(progressRect.x() + (animationProgress * 2 * movableWidth)); 690 else 691 progressRect.setX(progressRect.x() + ((1.0 - animationProgress) * 2 * movableWidth)); 692 return progressRect; 693} 694#endif 695 696String RenderThemeGtk::fileListNameForWidth(const FileList* fileList, const Font& font, int width, bool multipleFilesAllowed) const 697{ 698 if (width <= 0) 699 return String(); 700 701 if (fileList->length() > 1) 702 return StringTruncator::rightTruncate(multipleFileUploadText(fileList->length()), width, font, StringTruncator::EnableRoundingHacks); 703 704 String string; 705 if (fileList->length()) 706 string = pathGetFileName(fileList->item(0)->path()); 707 else if (multipleFilesAllowed) 708 string = fileButtonNoFilesSelectedLabel(); 709 else 710 string = fileButtonNoFileSelectedLabel(); 711 712 return StringTruncator::centerTruncate(string, width, font, StringTruncator::EnableRoundingHacks); 713} 714 715#if ENABLE(DATALIST_ELEMENT) 716IntSize RenderThemeGtk::sliderTickSize() const 717{ 718 // FIXME: We need to set this to the size of one tick mark. 719 return IntSize(0, 0); 720} 721 722int RenderThemeGtk::sliderTickOffsetFromTrackCenter() const 723{ 724 // FIXME: We need to set this to the position of the tick marks. 725 return 0; 726} 727#endif 728 729} 730