1/* 2 * Copyright (C) 2008, 2009, 2013 Apple Inc. All Rights Reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27#include "RenderScrollbar.h" 28 29#include "Frame.h" 30#include "FrameView.h" 31#include "RenderScrollbarPart.h" 32#include "RenderScrollbarTheme.h" 33#include "RenderWidget.h" 34#include "StyleInheritedData.h" 35#include "StyleResolver.h" 36 37namespace WebCore { 38 39RefPtr<Scrollbar> RenderScrollbar::createCustomScrollbar(ScrollableArea* scrollableArea, ScrollbarOrientation orientation, Element* ownerElement, Frame* owningFrame) 40{ 41 return adoptRef(new RenderScrollbar(scrollableArea, orientation, ownerElement, owningFrame)); 42} 43 44RenderScrollbar::RenderScrollbar(ScrollableArea* scrollableArea, ScrollbarOrientation orientation, Element* ownerElement, Frame* owningFrame) 45 : Scrollbar(scrollableArea, orientation, RegularScrollbar, RenderScrollbarTheme::renderScrollbarTheme(), true) 46 , m_ownerElement(ownerElement) 47 , m_owningFrame(owningFrame) 48{ 49 ASSERT(ownerElement || owningFrame); 50 51 // FIXME: We need to do this because RenderScrollbar::styleChanged is called as soon as the scrollbar is created. 52 53 // Update the scrollbar size. 54 int width = 0; 55 int height = 0; 56 updateScrollbarPart(ScrollbarBGPart); 57 if (RenderScrollbarPart* part = m_parts.get(ScrollbarBGPart)) { 58 part->layout(); 59 width = part->width(); 60 height = part->height(); 61 } else if (this->orientation() == HorizontalScrollbar) 62 width = this->width(); 63 else 64 height = this->height(); 65 66 setFrameRect(IntRect(0, 0, width, height)); 67} 68 69RenderScrollbar::~RenderScrollbar() 70{ 71} 72 73RenderBox* RenderScrollbar::owningRenderer() const 74{ 75 if (m_owningFrame) { 76 RenderWidget* currentRenderer = m_owningFrame->ownerRenderer(); 77 return currentRenderer; 78 } 79 ASSERT(m_ownerElement); 80 if (m_ownerElement->renderer()) 81 return &m_ownerElement->renderer()->enclosingBox(); 82 return nullptr; 83} 84 85void RenderScrollbar::setParent(ScrollView* parent) 86{ 87 Scrollbar::setParent(parent); 88 if (!parent) 89 m_parts.clear(); 90} 91 92void RenderScrollbar::setEnabled(bool e) 93{ 94 bool wasEnabled = enabled(); 95 Scrollbar::setEnabled(e); 96 if (wasEnabled != e) 97 updateScrollbarParts(); 98} 99 100void RenderScrollbar::styleChanged() 101{ 102 updateScrollbarParts(); 103} 104 105void RenderScrollbar::paint(GraphicsContext* context, const IntRect& damageRect) 106{ 107 if (context->updatingControlTints()) { 108 updateScrollbarParts(); 109 return; 110 } 111 Scrollbar::paint(context, damageRect); 112} 113 114void RenderScrollbar::setHoveredPart(ScrollbarPart part) 115{ 116 if (part == m_hoveredPart) 117 return; 118 119 ScrollbarPart oldPart = m_hoveredPart; 120 m_hoveredPart = part; 121 122 updateScrollbarPart(oldPart); 123 updateScrollbarPart(m_hoveredPart); 124 125 updateScrollbarPart(ScrollbarBGPart); 126 updateScrollbarPart(TrackBGPart); 127} 128 129void RenderScrollbar::setPressedPart(ScrollbarPart part) 130{ 131 ScrollbarPart oldPart = m_pressedPart; 132 Scrollbar::setPressedPart(part); 133 134 updateScrollbarPart(oldPart); 135 updateScrollbarPart(part); 136 137 updateScrollbarPart(ScrollbarBGPart); 138 updateScrollbarPart(TrackBGPart); 139} 140 141PassRefPtr<RenderStyle> RenderScrollbar::getScrollbarPseudoStyle(ScrollbarPart partType, PseudoId pseudoId) 142{ 143 if (!owningRenderer()) 144 return 0; 145 146 RefPtr<RenderStyle> result = owningRenderer()->getUncachedPseudoStyle(PseudoStyleRequest(pseudoId, this, partType), &owningRenderer()->style()); 147 // Scrollbars for root frames should always have background color 148 // unless explicitly specified as transparent. So we force it. 149 // This is because WebKit assumes scrollbar to be always painted and missing background 150 // causes visual artifact like non-repainted dirty region. 151 if (result && m_owningFrame && m_owningFrame->view() && !m_owningFrame->view()->isTransparent() && !result->hasBackground()) 152 result->setBackgroundColor(Color::white); 153 154 return result; 155} 156 157void RenderScrollbar::updateScrollbarParts() 158{ 159 updateScrollbarPart(ScrollbarBGPart); 160 updateScrollbarPart(BackButtonStartPart); 161 updateScrollbarPart(ForwardButtonStartPart); 162 updateScrollbarPart(BackTrackPart); 163 updateScrollbarPart(ThumbPart); 164 updateScrollbarPart(ForwardTrackPart); 165 updateScrollbarPart(BackButtonEndPart); 166 updateScrollbarPart(ForwardButtonEndPart); 167 updateScrollbarPart(TrackBGPart); 168 169 // See if the scrollbar's thickness changed. If so, we need to mark our owning object as needing a layout. 170 bool isHorizontal = orientation() == HorizontalScrollbar; 171 int oldThickness = isHorizontal ? height() : width(); 172 int newThickness = 0; 173 RenderScrollbarPart* part = m_parts.get(ScrollbarBGPart); 174 if (part) { 175 part->layout(); 176 newThickness = isHorizontal ? part->height() : part->width(); 177 } 178 179 if (newThickness != oldThickness) { 180 setFrameRect(IntRect(location(), IntSize(isHorizontal ? width() : newThickness, isHorizontal ? newThickness : height()))); 181 if (RenderBox* box = owningRenderer()) 182 box->setChildNeedsLayout(); 183 } 184} 185 186static PseudoId pseudoForScrollbarPart(ScrollbarPart part) 187{ 188 switch (part) { 189 case BackButtonStartPart: 190 case ForwardButtonStartPart: 191 case BackButtonEndPart: 192 case ForwardButtonEndPart: 193 return SCROLLBAR_BUTTON; 194 case BackTrackPart: 195 case ForwardTrackPart: 196 return SCROLLBAR_TRACK_PIECE; 197 case ThumbPart: 198 return SCROLLBAR_THUMB; 199 case TrackBGPart: 200 return SCROLLBAR_TRACK; 201 case ScrollbarBGPart: 202 return SCROLLBAR; 203 case NoPart: 204 case AllParts: 205 break; 206 } 207 ASSERT_NOT_REACHED(); 208 return SCROLLBAR; 209} 210 211void RenderScrollbar::updateScrollbarPart(ScrollbarPart partType) 212{ 213 if (partType == NoPart) 214 return; 215 216 RefPtr<RenderStyle> partStyle = getScrollbarPseudoStyle(partType, pseudoForScrollbarPart(partType)); 217 bool needRenderer = partStyle && partStyle->display() != NONE; 218 219 if (needRenderer && partStyle->display() != BLOCK) { 220 // See if we are a button that should not be visible according to OS settings. 221 ScrollbarButtonsPlacement buttonsPlacement = theme()->buttonsPlacement(); 222 switch (partType) { 223 case BackButtonStartPart: 224 needRenderer = (buttonsPlacement == ScrollbarButtonsSingle || buttonsPlacement == ScrollbarButtonsDoubleStart || 225 buttonsPlacement == ScrollbarButtonsDoubleBoth); 226 break; 227 case ForwardButtonStartPart: 228 needRenderer = (buttonsPlacement == ScrollbarButtonsDoubleStart || buttonsPlacement == ScrollbarButtonsDoubleBoth); 229 break; 230 case BackButtonEndPart: 231 needRenderer = (buttonsPlacement == ScrollbarButtonsDoubleEnd || buttonsPlacement == ScrollbarButtonsDoubleBoth); 232 break; 233 case ForwardButtonEndPart: 234 needRenderer = (buttonsPlacement == ScrollbarButtonsSingle || buttonsPlacement == ScrollbarButtonsDoubleEnd || 235 buttonsPlacement == ScrollbarButtonsDoubleBoth); 236 break; 237 default: 238 break; 239 } 240 } 241 242 if (!needRenderer) { 243 m_parts.remove(partType); 244 return; 245 } 246 247 if (auto& partRendererSlot = m_parts.add(partType, nullptr).iterator->value) 248 partRendererSlot->setStyle(partStyle.releaseNonNull()); 249 else { 250 partRendererSlot = createRenderer<RenderScrollbarPart>(owningRenderer()->document(), partStyle.releaseNonNull(), this, partType); 251 partRendererSlot->initializeStyle(); 252 } 253} 254 255void RenderScrollbar::paintPart(GraphicsContext* graphicsContext, ScrollbarPart partType, const IntRect& rect) 256{ 257 RenderScrollbarPart* partRenderer = m_parts.get(partType); 258 if (!partRenderer) 259 return; 260 partRenderer->paintIntoRect(graphicsContext, location(), rect); 261} 262 263IntRect RenderScrollbar::buttonRect(ScrollbarPart partType) 264{ 265 RenderScrollbarPart* partRenderer = m_parts.get(partType); 266 if (!partRenderer) 267 return IntRect(); 268 269 partRenderer->layout(); 270 271 bool isHorizontal = orientation() == HorizontalScrollbar; 272 if (partType == BackButtonStartPart) 273 return IntRect(location(), IntSize(isHorizontal ? partRenderer->pixelSnappedWidth() : width(), isHorizontal ? height() : partRenderer->pixelSnappedHeight())); 274 if (partType == ForwardButtonEndPart) 275 return IntRect(isHorizontal ? x() + width() - partRenderer->pixelSnappedWidth() : x(), 276 isHorizontal ? y() : y() + height() - partRenderer->pixelSnappedHeight(), 277 isHorizontal ? partRenderer->pixelSnappedWidth() : width(), 278 isHorizontal ? height() : partRenderer->pixelSnappedHeight()); 279 280 if (partType == ForwardButtonStartPart) { 281 IntRect previousButton = buttonRect(BackButtonStartPart); 282 return IntRect(isHorizontal ? x() + previousButton.width() : x(), 283 isHorizontal ? y() : y() + previousButton.height(), 284 isHorizontal ? partRenderer->pixelSnappedWidth() : width(), 285 isHorizontal ? height() : partRenderer->pixelSnappedHeight()); 286 } 287 288 IntRect followingButton = buttonRect(ForwardButtonEndPart); 289 return IntRect(isHorizontal ? x() + width() - followingButton.width() - partRenderer->pixelSnappedWidth() : x(), 290 isHorizontal ? y() : y() + height() - followingButton.height() - partRenderer->pixelSnappedHeight(), 291 isHorizontal ? partRenderer->pixelSnappedWidth() : width(), 292 isHorizontal ? height() : partRenderer->pixelSnappedHeight()); 293} 294 295IntRect RenderScrollbar::trackRect(int startLength, int endLength) 296{ 297 RenderScrollbarPart* part = m_parts.get(TrackBGPart); 298 if (part) 299 part->layout(); 300 301 if (orientation() == HorizontalScrollbar) { 302 int marginLeft = part ? static_cast<int>(part->marginLeft()) : 0; 303 int marginRight = part ? static_cast<int>(part->marginRight()) : 0; 304 startLength += marginLeft; 305 endLength += marginRight; 306 int totalLength = startLength + endLength; 307 return IntRect(x() + startLength, y(), width() - totalLength, height()); 308 } 309 310 int marginTop = part ? static_cast<int>(part->marginTop()) : 0; 311 int marginBottom = part ? static_cast<int>(part->marginBottom()) : 0; 312 startLength += marginTop; 313 endLength += marginBottom; 314 int totalLength = startLength + endLength; 315 316 return IntRect(x(), y() + startLength, width(), height() - totalLength); 317} 318 319IntRect RenderScrollbar::trackPieceRectWithMargins(ScrollbarPart partType, const IntRect& oldRect) 320{ 321 RenderScrollbarPart* partRenderer = m_parts.get(partType); 322 if (!partRenderer) 323 return oldRect; 324 325 partRenderer->layout(); 326 327 IntRect rect = oldRect; 328 if (orientation() == HorizontalScrollbar) { 329 rect.setX(rect.x() + partRenderer->marginLeft()); 330 rect.setWidth(rect.width() - partRenderer->horizontalMarginExtent()); 331 } else { 332 rect.setY(rect.y() + partRenderer->marginTop()); 333 rect.setHeight(rect.height() - partRenderer->verticalMarginExtent()); 334 } 335 return rect; 336} 337 338int RenderScrollbar::minimumThumbLength() 339{ 340 RenderScrollbarPart* partRenderer = m_parts.get(ThumbPart); 341 if (!partRenderer) 342 return 0; 343 partRenderer->layout(); 344 return orientation() == HorizontalScrollbar ? partRenderer->width() : partRenderer->height(); 345} 346 347float RenderScrollbar::opacity() 348{ 349 RenderScrollbarPart* partRenderer = m_parts.get(ScrollbarBGPart); 350 if (!partRenderer) 351 return 1; 352 353 return partRenderer->style().opacity(); 354} 355 356} 357