1 /* 2 * Copyright (C) 2008, 2009 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 "RenderPart.h" 32 #include "RenderScrollbarPart.h" 33 #include "RenderScrollbarTheme.h" 34 35 namespace WebCore { 36 37 PassRefPtr<Scrollbar> RenderScrollbar::createCustomScrollbar(ScrollableArea* scrollableArea, ScrollbarOrientation orientation, RenderBox* renderer, Frame* owningFrame) 38 { 39 return adoptRef(new RenderScrollbar(scrollableArea, orientation, renderer, owningFrame)); 40 } 41 42 RenderScrollbar::RenderScrollbar(ScrollableArea* scrollableArea, ScrollbarOrientation orientation, RenderBox* renderer, Frame* owningFrame) 43 : Scrollbar(scrollableArea, orientation, RegularScrollbar, RenderScrollbarTheme::renderScrollbarTheme()) 44 , m_owner(renderer) 45 , m_owningFrame(owningFrame) 46 { 47 // FIXME: We need to do this because RenderScrollbar::styleChanged is called as soon as the scrollbar is created. 48 49 // Update the scrollbar size. 50 int width = 0; 51 int height = 0; 52 updateScrollbarPart(ScrollbarBGPart); 53 if (RenderScrollbarPart* part = m_parts.get(ScrollbarBGPart)) { 54 part->layout(); 55 width = part->width(); 56 height = part->height(); 57 } else if (this->orientation() == HorizontalScrollbar) 58 width = this->width(); 59 else 60 height = this->height(); 61 62 setFrameRect(IntRect(0, 0, width, height)); 63 } 64 65 RenderScrollbar::~RenderScrollbar() 66 { 67 ASSERT(m_parts.isEmpty()); 68 } 69 70 RenderBox* RenderScrollbar::owningRenderer() const 71 { 72 if (m_owningFrame) { 73 RenderBox* currentRenderer = m_owningFrame->ownerRenderer(); 74 return currentRenderer; 75 } 76 return m_owner; 77 } 78 79 void RenderScrollbar::setParent(ScrollView* parent) 80 { 81 Scrollbar::setParent(parent); 82 if (!parent) { 83 // Destroy all of the scrollbar's RenderBoxes. 84 updateScrollbarParts(true); 85 } 86 } 87 88 void RenderScrollbar::setEnabled(bool e) 89 { 90 bool wasEnabled = enabled(); 91 Scrollbar::setEnabled(e); 92 if (wasEnabled != e) 93 updateScrollbarParts(); 94 } 95 96 void RenderScrollbar::styleChanged() 97 { 98 updateScrollbarParts(); 99 } 100 101 void RenderScrollbar::paint(GraphicsContext* context, const IntRect& damageRect) 102 { 103 if (context->updatingControlTints()) { 104 updateScrollbarParts(); 105 return; 106 } 107 Scrollbar::paint(context, damageRect); 108 } 109 110 void RenderScrollbar::setHoveredPart(ScrollbarPart part) 111 { 112 if (part == m_hoveredPart) 113 return; 114 115 ScrollbarPart oldPart = m_hoveredPart; 116 m_hoveredPart = part; 117 118 updateScrollbarPart(oldPart); 119 updateScrollbarPart(m_hoveredPart); 120 121 updateScrollbarPart(ScrollbarBGPart); 122 updateScrollbarPart(TrackBGPart); 123 } 124 125 void RenderScrollbar::setPressedPart(ScrollbarPart part) 126 { 127 ScrollbarPart oldPart = m_pressedPart; 128 Scrollbar::setPressedPart(part); 129 130 updateScrollbarPart(oldPart); 131 updateScrollbarPart(part); 132 133 updateScrollbarPart(ScrollbarBGPart); 134 updateScrollbarPart(TrackBGPart); 135 } 136 137 static ScrollbarPart s_styleResolvePart; 138 static RenderScrollbar* s_styleResolveScrollbar; 139 140 RenderScrollbar* RenderScrollbar::scrollbarForStyleResolve() 141 { 142 return s_styleResolveScrollbar; 143 } 144 145 ScrollbarPart RenderScrollbar::partForStyleResolve() 146 { 147 return s_styleResolvePart; 148 } 149 150 PassRefPtr<RenderStyle> RenderScrollbar::getScrollbarPseudoStyle(ScrollbarPart partType, PseudoId pseudoId) 151 { 152 if (!m_owner) 153 return 0; 154 155 s_styleResolvePart = partType; 156 s_styleResolveScrollbar = this; 157 RefPtr<RenderStyle> result = owningRenderer()->getUncachedPseudoStyle(pseudoId, owningRenderer()->style()); 158 s_styleResolvePart = NoPart; 159 s_styleResolveScrollbar = 0; 160 161 // Scrollbars for root frames should always have background color 162 // unless explicitly specified as transparent. So we force it. 163 // This is because WebKit assumes scrollbar to be always painted and missing background 164 // causes visual artifact like non-repainted dirty region. 165 if (result && m_owningFrame && m_owningFrame->view() && !m_owningFrame->view()->isTransparent() && !result->hasBackground()) 166 result->setBackgroundColor(Color::white); 167 168 return result; 169 } 170 171 void RenderScrollbar::updateScrollbarParts(bool destroy) 172 { 173 updateScrollbarPart(ScrollbarBGPart, destroy); 174 updateScrollbarPart(BackButtonStartPart, destroy); 175 updateScrollbarPart(ForwardButtonStartPart, destroy); 176 updateScrollbarPart(BackTrackPart, destroy); 177 updateScrollbarPart(ThumbPart, destroy); 178 updateScrollbarPart(ForwardTrackPart, destroy); 179 updateScrollbarPart(BackButtonEndPart, destroy); 180 updateScrollbarPart(ForwardButtonEndPart, destroy); 181 updateScrollbarPart(TrackBGPart, destroy); 182 183 if (destroy) 184 return; 185 186 // See if the scrollbar's thickness changed. If so, we need to mark our owning object as needing a layout. 187 bool isHorizontal = orientation() == HorizontalScrollbar; 188 int oldThickness = isHorizontal ? height() : width(); 189 int newThickness = 0; 190 RenderScrollbarPart* part = m_parts.get(ScrollbarBGPart); 191 if (part) { 192 part->layout(); 193 newThickness = isHorizontal ? part->height() : part->width(); 194 } 195 196 if (newThickness != oldThickness) { 197 setFrameRect(IntRect(x(), y(), isHorizontal ? width() : newThickness, isHorizontal ? newThickness : height())); 198 owningRenderer()->setChildNeedsLayout(true); 199 } 200 } 201 202 static PseudoId pseudoForScrollbarPart(ScrollbarPart part) 203 { 204 switch (part) { 205 case BackButtonStartPart: 206 case ForwardButtonStartPart: 207 case BackButtonEndPart: 208 case ForwardButtonEndPart: 209 return SCROLLBAR_BUTTON; 210 case BackTrackPart: 211 case ForwardTrackPart: 212 return SCROLLBAR_TRACK_PIECE; 213 case ThumbPart: 214 return SCROLLBAR_THUMB; 215 case TrackBGPart: 216 return SCROLLBAR_TRACK; 217 case ScrollbarBGPart: 218 return SCROLLBAR; 219 case NoPart: 220 case AllParts: 221 break; 222 } 223 ASSERT_NOT_REACHED(); 224 return SCROLLBAR; 225 } 226 227 void RenderScrollbar::updateScrollbarPart(ScrollbarPart partType, bool destroy) 228 { 229 if (partType == NoPart) 230 return; 231 232 RefPtr<RenderStyle> partStyle = !destroy ? getScrollbarPseudoStyle(partType, pseudoForScrollbarPart(partType)) : 0; 233 234 bool needRenderer = !destroy && partStyle && partStyle->display() != NONE && partStyle->visibility() == VISIBLE; 235 236 if (needRenderer && partStyle->display() != BLOCK) { 237 // See if we are a button that should not be visible according to OS settings. 238 ScrollbarButtonsPlacement buttonsPlacement = theme()->buttonsPlacement(); 239 switch (partType) { 240 case BackButtonStartPart: 241 needRenderer = (buttonsPlacement == ScrollbarButtonsSingle || buttonsPlacement == ScrollbarButtonsDoubleStart || 242 buttonsPlacement == ScrollbarButtonsDoubleBoth); 243 break; 244 case ForwardButtonStartPart: 245 needRenderer = (buttonsPlacement == ScrollbarButtonsDoubleStart || buttonsPlacement == ScrollbarButtonsDoubleBoth); 246 break; 247 case BackButtonEndPart: 248 needRenderer = (buttonsPlacement == ScrollbarButtonsDoubleEnd || buttonsPlacement == ScrollbarButtonsDoubleBoth); 249 break; 250 case ForwardButtonEndPart: 251 needRenderer = (buttonsPlacement == ScrollbarButtonsSingle || buttonsPlacement == ScrollbarButtonsDoubleEnd || 252 buttonsPlacement == ScrollbarButtonsDoubleBoth); 253 break; 254 default: 255 break; 256 } 257 } 258 259 RenderScrollbarPart* partRenderer = m_parts.get(partType); 260 if (!partRenderer && needRenderer) { 261 partRenderer = new (owningRenderer()->renderArena()) RenderScrollbarPart(owningRenderer()->document(), this, partType); 262 m_parts.set(partType, partRenderer); 263 } else if (partRenderer && !needRenderer) { 264 m_parts.remove(partType); 265 partRenderer->destroy(); 266 partRenderer = 0; 267 } 268 269 if (partRenderer) 270 partRenderer->setStyle(partStyle.release()); 271 } 272 273 void RenderScrollbar::paintPart(GraphicsContext* graphicsContext, ScrollbarPart partType, const IntRect& rect) 274 { 275 RenderScrollbarPart* partRenderer = m_parts.get(partType); 276 if (!partRenderer) 277 return; 278 partRenderer->paintIntoRect(graphicsContext, x(), y(), rect); 279 } 280 281 IntRect RenderScrollbar::buttonRect(ScrollbarPart partType) 282 { 283 RenderScrollbarPart* partRenderer = m_parts.get(partType); 284 if (!partRenderer) 285 return IntRect(); 286 287 partRenderer->layout(); 288 289 bool isHorizontal = orientation() == HorizontalScrollbar; 290 if (partType == BackButtonStartPart) 291 return IntRect(x(), y(), isHorizontal ? partRenderer->width() : width(), isHorizontal ? height() : partRenderer->height()); 292 if (partType == ForwardButtonEndPart) 293 return IntRect(isHorizontal ? x() + width() - partRenderer->width() : x(), 294 295 isHorizontal ? y() : y() + height() - partRenderer->height(), 296 isHorizontal ? partRenderer->width() : width(), 297 isHorizontal ? height() : partRenderer->height()); 298 299 if (partType == ForwardButtonStartPart) { 300 IntRect previousButton = buttonRect(BackButtonStartPart); 301 return IntRect(isHorizontal ? x() + previousButton.width() : x(), 302 isHorizontal ? y() : y() + previousButton.height(), 303 isHorizontal ? partRenderer->width() : width(), 304 isHorizontal ? height() : partRenderer->height()); 305 } 306 307 IntRect followingButton = buttonRect(ForwardButtonEndPart); 308 return IntRect(isHorizontal ? x() + width() - followingButton.width() - partRenderer->width() : x(), 309 isHorizontal ? y() : y() + height() - followingButton.height() - partRenderer->height(), 310 isHorizontal ? partRenderer->width() : width(), 311 isHorizontal ? height() : partRenderer->height()); 312 } 313 314 IntRect RenderScrollbar::trackRect(int startLength, int endLength) 315 { 316 RenderScrollbarPart* part = m_parts.get(TrackBGPart); 317 if (part) 318 part->layout(); 319 320 if (orientation() == HorizontalScrollbar) { 321 int marginLeft = part ? part->marginLeft() : 0; 322 int marginRight = part ? part->marginRight() : 0; 323 startLength += marginLeft; 324 endLength += marginRight; 325 int totalLength = startLength + endLength; 326 return IntRect(x() + startLength, y(), width() - totalLength, height()); 327 } 328 329 int marginTop = part ? part->marginTop() : 0; 330 int marginBottom = part ? part->marginBottom() : 0; 331 startLength += marginTop; 332 endLength += marginBottom; 333 int totalLength = startLength + endLength; 334 335 return IntRect(x(), y() + startLength, width(), height() - totalLength); 336 } 337 338 IntRect RenderScrollbar::trackPieceRectWithMargins(ScrollbarPart partType, const IntRect& oldRect) 339 { 340 RenderScrollbarPart* partRenderer = m_parts.get(partType); 341 if (!partRenderer) 342 return oldRect; 343 344 partRenderer->layout(); 345 346 IntRect rect = oldRect; 347 if (orientation() == HorizontalScrollbar) { 348 rect.setX(rect.x() + partRenderer->marginLeft()); 349 rect.setWidth(rect.width() - (partRenderer->marginLeft() + partRenderer->marginRight())); 350 } else { 351 rect.setY(rect.y() + partRenderer->marginTop()); 352 rect.setHeight(rect.height() - (partRenderer->marginTop() + partRenderer->marginBottom())); 353 } 354 return rect; 355 } 356 357 int RenderScrollbar::minimumThumbLength() 358 { 359 RenderScrollbarPart* partRenderer = m_parts.get(ThumbPart); 360 if (!partRenderer) 361 return 0; 362 partRenderer->layout(); 363 return orientation() == HorizontalScrollbar ? partRenderer->width() : partRenderer->height(); 364 } 365 366 } 367