2 * Copyright (C) 2003 Apple Computer, Inc.
4 * Portions are Copyright (C) 1998 Netscape Communications Corporation.
7 * Robert O'Callahan <roc+@cs.cmu.edu>
8 * David Baron <dbaron@fas.harvard.edu>
9 * Christian Biesinger <cbiesinger@web.de>
10 * Randall Jesup <rjesup@wgate.com>
11 * Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>
12 * Josh Soref <timeless@mac.com>
13 * Boris Zbarsky <bzbarsky@mit.edu>
15 * This library is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU Lesser General Public
17 * License as published by the Free Software Foundation; either
18 * version 2.1 of the License, or (at your option) any later version.
20 * This library is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * Lesser General Public License for more details.
25 * You should have received a copy of the GNU Lesser General Public
26 * License along with this library; if not, write to the Free Software
27 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 * Alternatively, the contents of this file may be used under the terms
30 * of either the Mozilla Public License Version 1.1, found at
31 * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public
32 * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html
33 * (the "GPL"), in which case the provisions of the MPL or the GPL are
34 * applicable instead of those above. If you wish to allow use of your
35 * version of this file only under the terms of one of those two
36 * licenses (the MPL or the GPL) and not to allow others to use your
37 * version of this file under the LGPL, indicate your decision by
38 * deletingthe provisions above and replace them with the notice and
39 * other provisions required by the MPL or the GPL, as the case may be.
40 * If you do not delete the provisions above, a recipient may use your
41 * version of this file under any of the LGPL, the MPL or the GPL.
45 #include "RenderLayer.h"
47 #include "CSSPropertyNames.h"
49 #include "EventNames.h"
50 #include "FloatRect.h"
52 #include "FrameTree.h"
53 #include "GraphicsContext.h"
54 #include "HTMLMarqueeElement.h"
55 #include "HTMLNames.h"
56 #include "OverflowEvent.h"
57 #include "PlatformMouseEvent.h"
58 #include "RenderArena.h"
59 #include "RenderInline.h"
60 #include "RenderTheme.h"
61 #include "RenderView.h"
62 #include "SelectionController.h"
63 #include "PlatformScrollBar.h"
69 #define MIN_INTERSECT_FOR_REVEAL 32
75 using namespace EventNames;
76 using namespace HTMLNames;
79 static bool inRenderLayerDestroy;
82 const RenderLayer::ScrollAlignment RenderLayer::gAlignCenterIfNeeded = { RenderLayer::noScroll, RenderLayer::alignCenter, RenderLayer::alignToClosestEdge };
83 const RenderLayer::ScrollAlignment RenderLayer::gAlignToEdgeIfNeeded = { RenderLayer::noScroll, RenderLayer::alignToClosestEdge, RenderLayer::alignToClosestEdge };
84 const RenderLayer::ScrollAlignment RenderLayer::gAlignCenterAlways = { RenderLayer::alignCenter, RenderLayer::alignCenter, RenderLayer::alignCenter };
85 const RenderLayer::ScrollAlignment RenderLayer::gAlignTopAlways = { RenderLayer::alignTop, RenderLayer::alignTop, RenderLayer::alignTop };
86 const RenderLayer::ScrollAlignment RenderLayer::gAlignBottomAlways = { RenderLayer::alignBottom, RenderLayer::alignBottom, RenderLayer::alignBottom };
88 const int MinimumWidthWhileResizing = 100;
89 const int MinimumHeightWhileResizing = 40;
91 void* ClipRects::operator new(size_t sz, RenderArena* renderArena) throw()
93 return renderArena->allocate(sz);
96 void ClipRects::operator delete(void* ptr, size_t sz)
98 // Stash size where destroy can find it.
102 void ClipRects::destroy(RenderArena* renderArena)
106 // Recover the size left there for us by operator delete and free the memory.
107 renderArena->free(*(size_t *)this, this);
110 RenderLayer::RenderLayer(RenderObject* object)
128 m_scrollLeftOverflow(0),
131 m_inResizeMode(false),
132 m_resizeCornerImage(0),
137 m_scrollDimensionsDirty(true),
138 m_zOrderListsDirty(true),
139 m_overflowListDirty(true),
140 m_isOverflowOnly(shouldBeOverflowOnly()),
141 m_usedTransparency(false),
142 m_inOverflowRelayout(false),
143 m_repaintOverflowOnResize(false),
144 m_overflowStatusDirty(true),
149 RenderLayer::~RenderLayer()
151 destroyScrollbar(HorizontalScrollbar);
152 destroyScrollbar(VerticalScrollbar);
154 // Child layers will be deleted by their corresponding render objects, so
155 // we don't need to delete them ourselves.
157 delete m_resizeCornerImage;
158 delete m_posZOrderList;
159 delete m_negZOrderList;
160 delete m_overflowList;
163 // Make sure we have no lingering clip rects.
164 assert(!m_clipRects);
167 void RenderLayer::checkForRepaintOnResize()
169 m_repaintOverflowOnResize = m_object->selfNeedsLayout() || m_object->hasOverflowClip() && m_object->normalChildNeedsLayout();
170 for (RenderLayer* child = firstChild(); child; child = child->nextSibling())
171 child->checkForRepaintOnResize();
174 void RenderLayer::updateLayerPositions(bool doFullRepaint, bool checkForRepaint)
178 checkForRepaint = doFullRepaint = false;
181 updateLayerPosition(); // For relpositioned layers or non-positioned layers,
182 // we need to keep in sync, since we may have shifted relative
183 // to our parent layer.
185 positionResizeControl();
186 if (m_hBar || m_vBar) {
187 // Need to position the scrollbars.
190 convertToLayerCoords(root(), x, y);
191 IntRect layerBounds = IntRect(x,y,width(),height());
192 positionScrollbars(layerBounds);
195 // FIXME: Child object could override visibility.
196 if (m_object->style()->visibility() == VISIBLE) {
198 m_object->absolutePosition(x, y);
199 IntRect newRect, newFullRect;
200 m_object->getAbsoluteRepaintRectIncludingFloats(newRect, newFullRect);
201 if (checkForRepaint) {
202 RenderView *c = m_object->view();
203 if (c && !c->printingMode()) {
204 bool didMove = x != m_repaintX || y != m_repaintY;
205 if (!didMove && !m_repaintOverflowOnResize)
206 m_object->repaintAfterLayoutIfNeeded(m_repaintRect, m_fullRepaintRect);
207 else if (didMove || newRect != m_repaintRect) {
208 c->repaintViewRectangle(m_fullRepaintRect);
209 c->repaintViewRectangle(newFullRect);
213 m_repaintRect = newRect;
214 m_fullRepaintRect = newFullRect;
218 m_repaintRect = IntRect();
219 m_fullRepaintRect = IntRect();
222 for (RenderLayer* child = firstChild(); child; child = child->nextSibling())
223 child->updateLayerPositions(doFullRepaint, checkForRepaint);
225 // With all our children positioned, now update our marquee if we need to.
227 m_marquee->updateMarqueePosition();
230 void RenderLayer::updateLayerPosition()
232 // Clear our cached clip rect information.
235 // The canvas is sized to the docWidth/Height over in RenderView::layout, so we
236 // don't need to ever update our layer position here.
237 if (renderer()->isRenderView())
240 int x = m_object->xPos();
241 int y = m_object->yPos() - m_object->borderTopExtra();
243 if (!m_object->isPositioned()) {
244 // We must adjust our position by walking up the render tree looking for the
245 // nearest enclosing object with a layer.
246 RenderObject* curr = m_object->parent();
247 while (curr && !curr->layer()) {
248 if (!curr->isTableRow()) {
249 // Rows and cells share the same coordinate space (that of the section).
250 // Omit them when computing our xpos/ypos.
254 curr = curr->parent();
256 y += curr->borderTopExtra();
257 if (curr->isTableRow()) {
258 // Put ourselves into the row coordinate space.
265 if (m_object->isRelPositioned()) {
266 m_relX = static_cast<RenderBox*>(m_object)->relativePositionOffsetX();
267 m_relY = static_cast<RenderBox*>(m_object)->relativePositionOffsetY();
268 x += m_relX; y += m_relY;
271 // Subtract our parent's scroll offset.
272 if (m_object->isPositioned() && enclosingPositionedAncestor()) {
273 RenderLayer* positionedParent = enclosingPositionedAncestor();
275 // For positioned layers, we subtract out the enclosing positioned layer's scroll offset.
276 positionedParent->subtractScrollOffset(x, y);
278 if (m_object->isPositioned() && positionedParent->renderer()->isRelPositioned() &&
279 positionedParent->renderer()->isInlineFlow()) {
280 // When we have an enclosing relpositioned inline, we need to add in the offset of the first line
281 // box from the rest of the content, but only in the cases where we know we're positioned
282 // relative to the inline itself.
283 RenderFlow* flow = static_cast<RenderFlow*>(positionedParent->renderer());
285 if (flow->firstLineBox()) {
286 sx = flow->firstLineBox()->xPos();
287 sy = flow->firstLineBox()->yPos();
290 sx = flow->staticX();
291 sy = flow->staticY();
293 bool isInlineType = m_object->style()->isOriginalDisplayInlineType();
295 if (!m_object->hasStaticX())
298 // This is not terribly intuitive, but we have to match other browsers. Despite being a block display type inside
299 // an inline, we still keep our x locked to the left of the relative positioned inline. Arguably the correct
300 // behavior would be to go flush left to the block that contains the inline, but that isn't what other browsers
302 if (m_object->hasStaticX() && !isInlineType)
303 // Avoid adding in the left border/padding of the containing block twice. Subtract it out.
304 x += sx - (m_object->containingBlock()->borderLeft() + m_object->containingBlock()->paddingLeft());
306 if (!m_object->hasStaticY())
311 parent()->subtractScrollOffset(x, y);
315 setWidth(m_object->width());
316 setHeight(m_object->height() + m_object->borderTopExtra() + m_object->borderBottomExtra());
318 if (!m_object->hasOverflowClip()) {
319 if (m_object->overflowWidth() > m_object->width())
320 setWidth(m_object->overflowWidth());
321 if (m_object->overflowHeight() > m_object->height())
322 setHeight(m_object->overflowHeight());
326 RenderLayer *RenderLayer::stackingContext() const
328 RenderLayer* curr = parent();
329 for ( ; curr && !curr->m_object->isRenderView() && !curr->m_object->isRoot() &&
330 curr->m_object->style()->hasAutoZIndex();
331 curr = curr->parent());
336 RenderLayer::enclosingPositionedAncestor() const
338 RenderLayer* curr = parent();
339 for ( ; curr && !curr->m_object->isRenderView() && !curr->m_object->isRoot() &&
340 !curr->m_object->isPositioned() && !curr->m_object->isRelPositioned();
341 curr = curr->parent());
347 RenderLayer::isTransparent() const
350 if (m_object->node()->namespaceURI() == SVGNames::svgNamespaceURI)
353 return m_object->isTransparent();
357 RenderLayer::transparentAncestor()
359 RenderLayer* curr = parent();
360 for ( ; curr && !curr->isTransparent(); curr = curr->parent());
364 static IntRect transparencyClipBox(RenderLayer* l)
366 // FIXME: Although this completely ignores clipping, we ultimately intersect with the
367 // paintDirtyRect, and that should cut down on the amount we have to paint. Still it
368 // would be better to respect clips.
369 IntRect clipRect = l->absoluteBoundingBox();
371 // Note: we don't have to walk z-order lists since transparent elements always establish
372 // a stacking context. This means we can just walk the layer tree directly.
373 for (RenderLayer* curr = l->firstChild(); curr; curr = curr->nextSibling()) {
374 // Transparent children paint in a different transparency layer, and so we exclude them.
375 if (!curr->isTransparent())
376 clipRect.unite(curr->absoluteBoundingBox());
382 void RenderLayer::beginTransparencyLayers(GraphicsContext* p, const IntRect& paintDirtyRect)
384 if (p->paintingDisabled() || (isTransparent() && m_usedTransparency))
387 RenderLayer* ancestor = transparentAncestor();
389 ancestor->beginTransparencyLayers(p, paintDirtyRect);
391 if (isTransparent()) {
392 m_usedTransparency = true;
393 IntRect clipRect = transparencyClipBox(this);
394 clipRect.intersect(paintDirtyRect);
397 p->beginTransparencyLayer(renderer()->opacity());
401 void* RenderLayer::operator new(size_t sz, RenderArena* renderArena) throw()
403 return renderArena->allocate(sz);
406 void RenderLayer::operator delete(void* ptr, size_t sz)
408 assert(inRenderLayerDestroy);
410 // Stash size where destroy can find it.
414 void RenderLayer::destroy(RenderArena* renderArena)
417 inRenderLayerDestroy = true;
421 inRenderLayerDestroy = false;
424 // Recover the size left there for us by operator delete and free the memory.
425 renderArena->free(*(size_t *)this, this);
428 void RenderLayer::addChild(RenderLayer *child, RenderLayer* beforeChild)
430 RenderLayer* prevSibling = beforeChild ? beforeChild->previousSibling() : lastChild();
432 child->setPreviousSibling(prevSibling);
433 prevSibling->setNextSibling(child);
436 setFirstChild(child);
439 beforeChild->setPreviousSibling(child);
440 child->setNextSibling(beforeChild);
445 child->setParent(this);
447 if (child->isOverflowOnly())
450 // Dirty the z-order list in which we are contained. The stackingContext() can be null in the
451 // case where we're building up generated content layers. This is ok, since the lists will start
452 // off dirty in that case anyway.
453 RenderLayer* stackingContext = child->stackingContext();
455 stackingContext->dirtyZOrderLists();
459 RenderLayer* RenderLayer::removeChild(RenderLayer* oldChild)
462 if (oldChild->previousSibling())
463 oldChild->previousSibling()->setNextSibling(oldChild->nextSibling());
464 if (oldChild->nextSibling())
465 oldChild->nextSibling()->setPreviousSibling(oldChild->previousSibling());
467 if (m_first == oldChild)
468 m_first = oldChild->nextSibling();
469 if (m_last == oldChild)
470 m_last = oldChild->previousSibling();
472 if (oldChild->isOverflowOnly())
475 // Dirty the z-order list in which we are contained. When called via the
476 // reattachment process in removeOnlyThisLayer, the layer may already be disconnected
477 // from the main layer tree, so we need to null-check the |stackingContext| value.
478 RenderLayer* stackingContext = oldChild->stackingContext();
480 stackingContext->dirtyZOrderLists();
483 oldChild->setPreviousSibling(0);
484 oldChild->setNextSibling(0);
485 oldChild->setParent(0);
490 void RenderLayer::removeOnlyThisLayer()
495 // Dirty the clip rects.
498 // Remove us from the parent.
499 RenderLayer* parent = m_parent;
500 RenderLayer* nextSib = nextSibling();
501 parent->removeChild(this);
503 // Now walk our kids and reattach them to our parent.
504 RenderLayer* current = m_first;
506 RenderLayer* next = current->nextSibling();
507 removeChild(current);
508 parent->addChild(current, nextSib);
509 current->updateLayerPositions();
513 destroy(renderer()->renderArena());
516 void RenderLayer::insertOnlyThisLayer()
518 if (!m_parent && renderer()->parent()) {
519 // We need to connect ourselves when our renderer() has a parent.
520 // Find our enclosingLayer and add ourselves.
521 RenderLayer* parentLayer = renderer()->parent()->enclosingLayer();
523 parentLayer->addChild(this,
524 renderer()->parent()->findNextLayer(parentLayer, renderer()));
527 // Remove all descendant layers from the hierarchy and add them to the new position.
528 for (RenderObject* curr = renderer()->firstChild(); curr; curr = curr->nextSibling())
529 curr->moveLayers(m_parent, this);
531 // Clear out all the clip rects.
536 RenderLayer::convertToLayerCoords(const RenderLayer* ancestorLayer, int& x, int& y) const
538 if (ancestorLayer == this)
541 if (m_object->style()->position() == FixedPosition) {
542 // Add in the offset of the view. We can obtain this by calling
543 // absolutePosition() on the RenderView.
545 m_object->absolutePosition(xOff, yOff, true);
551 RenderLayer* parentLayer;
552 if (m_object->style()->position() == AbsolutePosition)
553 parentLayer = enclosingPositionedAncestor();
555 parentLayer = parent();
557 if (!parentLayer) return;
559 parentLayer->convertToLayerCoords(ancestorLayer, x, y);
566 RenderLayer::scrollOffset(int& x, int& y)
568 x += scrollXOffset() + m_scrollLeftOverflow;
569 y += scrollYOffset();
573 RenderLayer::subtractScrollOffset(int& x, int& y)
575 x -= scrollXOffset() + m_scrollLeftOverflow;
576 y -= scrollYOffset();
579 void RenderLayer::scrollToOffset(int x, int y, bool updateScrollbars, bool repaint)
581 if (renderer()->style()->overflowX() != OMARQUEE) {
585 // Call the scrollWidth/Height functions so that the dimensions will be computed if they need
586 // to be (for overflow:hidden blocks).
587 int maxX = scrollWidth() - m_object->clientWidth();
588 int maxY = scrollHeight() - m_object->clientHeight();
590 if (x > maxX) x = maxX;
591 if (y > maxY) y = maxY;
594 // FIXME: Eventually, we will want to perform a blit. For now never
595 // blit, since the check for blitting is going to be very
596 // complicated (since it will involve testing whether our layer
597 // is either occluded by another layer or clipped by an enclosing
598 // layer or contains fixed backgrounds, etc.).
599 m_scrollX = x - m_scrollOriginX;
602 // Update the positions of our child layers.
603 for (RenderLayer* child = firstChild(); child; child = child->nextSibling())
604 child->updateLayerPositions(false, false);
606 RenderView* view = renderer()->view();
609 // Update dashboard regions, scrolling may change the clip of a
610 // particular region.
611 view->frameView()->updateDashboardRegions();
614 m_object->view()->updateWidgetPositions();
617 // Just schedule a full repaint of our object.
621 if (updateScrollbars) {
623 m_hBar->setValue(scrollXOffset());
625 m_vBar->setValue(m_scrollY);
628 // Fire the scroll DOM event.
629 EventTargetNodeCast(m_object->element())->dispatchHTMLEvent(scrollEvent, true, false);
632 void RenderLayer::scrollRectToVisible(const IntRect &rect, const ScrollAlignment& alignX, const ScrollAlignment& alignY)
634 RenderLayer* parentLayer = 0;
635 IntRect newRect = rect;
636 int xOffset = 0, yOffset = 0;
638 if (m_object->hasOverflowClip()) {
639 IntRect layerBounds = IntRect(xPos() + scrollXOffset(), yPos() + scrollYOffset(),
640 width() - verticalScrollbarWidth(), height() - horizontalScrollbarHeight());
641 IntRect exposeRect = IntRect(rect.x() + scrollXOffset(), rect.y() + scrollYOffset(), rect.width(), rect.height());
642 IntRect r = getRectToExpose(layerBounds, exposeRect, alignX, alignY);
644 xOffset = r.x() - xPos();
645 yOffset = r.y() - yPos();
646 // Adjust offsets if they're outside of the allowable range.
647 xOffset = max(0, min(scrollWidth() - width(), xOffset));
648 yOffset = max(0, min(scrollHeight() - height(), yOffset));
650 if (xOffset != scrollXOffset() || yOffset != scrollYOffset()) {
651 int diffX = scrollXOffset();
652 int diffY = scrollYOffset();
653 scrollToOffset(xOffset, yOffset);
654 // FIXME: At this point a scroll event fired, which could have deleted this layer.
655 // Need to handle this case.
656 diffX = scrollXOffset() - diffX;
657 diffY = scrollYOffset() - diffY;
658 newRect.setX(rect.x() - diffX);
659 newRect.setY(rect.y() - diffY);
662 if (m_object->parent())
663 parentLayer = m_object->parent()->enclosingLayer();
665 FrameView* view = m_object->document()->view();
667 IntRect viewRect = enclosingIntRect(view->visibleContentRect());
668 IntRect r = getRectToExpose(viewRect, rect, alignX, alignY);
672 // Adjust offsets if they're outside of the allowable range.
673 xOffset = max(0, min(view->contentsWidth(), xOffset));
674 yOffset = max(0, min(view->contentsHeight(), yOffset));
676 if (m_object->document() && m_object->document()->ownerElement() && m_object->document()->ownerElement()->renderer()) {
677 view->setContentsPos(xOffset, yOffset);
678 parentLayer = m_object->document()->ownerElement()->renderer()->enclosingLayer();
679 newRect.setX(rect.x() - view->contentsX() + view->x());
680 newRect.setY(rect.y() - view->contentsY() + view->y());
682 // If this is the outermost view that RenderLayer needs to scroll, then we should scroll the view recursively
683 // Other apps, like Mail, rely on this feature.
684 view->scrollPointRecursively(xOffset, yOffset);
690 parentLayer->scrollRectToVisible(newRect, alignX, alignY);
693 IntRect RenderLayer::getRectToExpose(const IntRect &visibleRect, const IntRect &exposeRect, const ScrollAlignment& alignX, const ScrollAlignment& alignY)
695 // Determine the appropriate X behavior.
696 ScrollBehavior scrollX;
697 IntRect exposeRectX(exposeRect.x(), visibleRect.y(), exposeRect.width(), visibleRect.height());
698 int intersectWidth = intersection(visibleRect, exposeRectX).width();
699 if (intersectWidth == exposeRect.width() || intersectWidth >= MIN_INTERSECT_FOR_REVEAL)
700 // If the rectangle is fully visible, use the specified visible behavior.
701 // If the rectangle is partially visible, but over a certain threshold,
702 // then treat it as fully visible to avoid unnecessary horizontal scrolling
703 scrollX = getVisibleBehavior(alignX);
704 else if (intersectWidth == visibleRect.width()) {
705 // If the rect is bigger than the visible area, don't bother trying to center. Other alignments will work.
706 scrollX = getVisibleBehavior(alignX);
707 if (scrollX == alignCenter)
709 } else if (intersectWidth > 0)
710 // If the rectangle is partially visible, but not above the minimum threshold, use the specified partial behavior
711 scrollX = getPartialBehavior(alignX);
713 scrollX = getHiddenBehavior(alignX);
714 // If we're trying to align to the closest edge, and the exposeRect is further right
715 // than the visibleRect, and not bigger than the visible area, then align with the right.
716 if (scrollX == alignToClosestEdge && exposeRect.right() > visibleRect.right() && exposeRect.width() < visibleRect.width())
717 scrollX = alignRight;
719 // Given the X behavior, compute the X coordinate.
721 if (scrollX == noScroll)
723 else if (scrollX == alignRight)
724 x = exposeRect.right() - visibleRect.width();
725 else if (scrollX == alignCenter)
726 x = exposeRect.x() + (exposeRect.width() - visibleRect.width()) / 2;
730 // Determine the appropriate Y behavior.
731 ScrollBehavior scrollY;
732 IntRect exposeRectY(visibleRect.x(), exposeRect.y(), visibleRect.width(), exposeRect.height());
733 int intersectHeight = intersection(visibleRect, exposeRectY).height();
734 if (intersectHeight == exposeRect.height())
735 // If the rectangle is fully visible, use the specified visible behavior.
736 scrollY = getVisibleBehavior(alignY);
737 else if (intersectHeight == visibleRect.height()) {
738 // If the rect is bigger than the visible area, don't bother trying to center. Other alignments will work.
739 scrollY = getVisibleBehavior(alignY);
740 if (scrollY == alignCenter)
742 } else if (intersectHeight > 0)
743 // If the rectangle is partially visible, use the specified partial behavior
744 scrollY = getPartialBehavior(alignY);
746 scrollY = getHiddenBehavior(alignY);
747 // If we're trying to align to the closest edge, and the exposeRect is further down
748 // than the visibleRect, and not bigger than the visible area, then align with the bottom.
749 if (scrollY == alignToClosestEdge && exposeRect.bottom() > visibleRect.bottom() && exposeRect.height() < visibleRect.height())
750 scrollY = alignBottom;
752 // Given the Y behavior, compute the Y coordinate.
754 if (scrollY == noScroll)
756 else if (scrollY == alignBottom)
757 y = exposeRect.bottom() - visibleRect.height();
758 else if (scrollY == alignCenter)
759 y = exposeRect.y() + (exposeRect.height() - visibleRect.height()) / 2;
763 return IntRect(IntPoint(x, y), visibleRect.size());
766 void RenderLayer::autoscroll()
768 int xOffset = scrollXOffset();
769 int yOffset = scrollYOffset();
771 // Get the rectangle for the extent of the selection
772 Selection selection(renderer()->document()->frame()->selectionController()->selection());
773 IntRect extentRect = VisiblePosition(selection.extent()).caretRect();
774 extentRect.move(xOffset, yOffset);
776 IntRect bounds = IntRect(xPos() + xOffset, yPos() + yOffset, width() - verticalScrollbarWidth(), height() - horizontalScrollbarHeight());
778 // Calculate how much the layer should scroll horizontally.
780 if (extentRect.right() > bounds.right())
781 diffX = extentRect.right() - bounds.right();
782 else if (extentRect.x() < bounds.x())
783 diffX = extentRect.x() - bounds.x();
785 // Calculate how much the layer should scroll vertically.
787 if (extentRect.bottom() > bounds.bottom())
788 diffY = extentRect.bottom() - bounds.bottom();
789 else if (extentRect.y() < bounds.y())
790 diffY = extentRect.y() - bounds.y();
792 scrollToOffset(xOffset + diffX, yOffset + diffY);
795 void RenderLayer::resize(const PlatformMouseEvent& evt, const IntSize& offsetFromResizeCorner)
797 if (!inResizeMode() || !renderer()->hasOverflowClip() || m_object->style()->resize() == RESIZE_NONE)
800 if (!m_object->document()->frame()->view()->mousePressed())
803 // FIXME Radar 4118559: This behaves very oddly for textareas that are in blocks with right-aligned text; you have
804 // to drag the bottom-right corner to make the bottom-left corner move.
805 // FIXME Radar 4118564: ideally we'd autoscroll the window as necessary to keep the point under
806 // the cursor in view.
808 IntPoint currentPoint = m_object->document()->view()->windowToContents(evt.pos());
809 currentPoint += offsetFromResizeCorner;
813 m_object->absolutePosition(x, y);
814 int right = x + m_object->width();
815 int bottom = y + m_object->height();
816 int diffWidth = max(currentPoint.x() - right, min(0, MinimumWidthWhileResizing - m_object->width()));
817 int diffHeight = max(currentPoint.y() - bottom, min(0, MinimumHeightWhileResizing - m_object->height()));
819 ExceptionCode ec = 0;
820 // Set the width and height for the shadow ancestor node. This is necessary for textareas since the resizable layer is on the inner div.
821 // For non-shadow content, this will set the width and height on the original node.
822 RenderObject* renderer = m_object->node()->shadowAncestorNode()->renderer();
823 if (diffWidth && (m_object->style()->resize() == RESIZE_HORIZONTAL || m_object->style()->resize() == RESIZE_BOTH)) {
824 CSSStyleDeclaration* style = static_cast<Element*>(m_object->node()->shadowAncestorNode())->style();
825 if (renderer->element() && renderer->element()->isControl()) {
826 style->setProperty(CSS_PROP_MARGIN_LEFT, String::number(renderer->marginLeft()) + "px", false, ec);
827 style->setProperty(CSS_PROP_MARGIN_RIGHT, String::number(renderer->marginRight()) + "px", false, ec);
829 int baseWidth = renderer->width() - (renderer->style()->boxSizing() == BORDER_BOX ? 0 : renderer->borderLeft() + renderer->paddingLeft() + renderer->borderRight() + renderer->paddingRight());
830 style->setProperty(CSS_PROP_WIDTH, String::number(baseWidth + diffWidth) + "px", false, ec);
833 if (diffHeight && (m_object->style()->resize() == RESIZE_VERTICAL || m_object->style()->resize() == RESIZE_BOTH)) {
834 CSSStyleDeclaration* style = static_cast<Element*>(m_object->node()->shadowAncestorNode())->style();
835 if (renderer->element() && renderer->element()->isControl()) {
836 style->setProperty(CSS_PROP_MARGIN_TOP, String::number(renderer->marginTop()) + "px", false, ec);
837 style->setProperty(CSS_PROP_MARGIN_BOTTOM, String::number(renderer->marginBottom()) + "px", false, ec);
839 int baseHeight = renderer->height() - (renderer->style()->boxSizing() == BORDER_BOX ? 0 : renderer->borderTop() + renderer->paddingTop() + renderer->borderBottom() + renderer->paddingBottom());
840 style->setProperty(CSS_PROP_HEIGHT, String::number(baseHeight + diffHeight) + "px", false, ec);
845 if (m_object->style()->resize() != RESIZE_NONE) {
846 m_object->setNeedsLayout(true);
847 m_object->node()->shadowAncestorNode()->renderer()->setNeedsLayout(true);
848 m_object->document()->updateLayout();
852 PlatformScrollbar* RenderLayer::horizontaScrollbarWidget() const
854 if (m_hBar && m_hBar->isWidget())
855 return static_cast<PlatformScrollbar*>(m_hBar.get());
859 PlatformScrollbar* RenderLayer::verticalScrollbarWidget() const
861 if (m_vBar && m_vBar->isWidget())
862 return static_cast<PlatformScrollbar*>(m_vBar.get());
866 void RenderLayer::valueChanged(Scrollbar*)
868 // Update scroll position from scrollbars.
870 bool needUpdate = false;
871 int newX = scrollXOffset();
872 int newY = m_scrollY;
875 newX = m_hBar->value();
876 if (newX != scrollXOffset())
881 newY = m_vBar->value();
882 if (newY != m_scrollY)
887 scrollToOffset(newX, newY, false);
890 PassRefPtr<Scrollbar> RenderLayer::createScrollbar(ScrollbarOrientation orientation)
892 if (Scrollbar::hasPlatformScrollbars()) {
893 RefPtr<PlatformScrollbar> widget = new PlatformScrollbar(this, orientation, RegularScrollbar);
894 m_object->element()->document()->view()->addChild(widget.get());
895 return widget.release();
898 // FIXME: Create scrollbars using the engine.
902 void RenderLayer::destroyScrollbar(ScrollbarOrientation orientation)
904 RefPtr<Scrollbar>& scrollbar = orientation == HorizontalScrollbar ? m_hBar : m_vBar;
906 if (scrollbar->isWidget())
907 static_cast<PlatformScrollbar*>(scrollbar.get())->removeFromParent();
909 // FIXME: Destroy the engine scrollbar.
914 void RenderLayer::setHasHorizontalScrollbar(bool hasScrollbar)
916 if (hasScrollbar == (m_hBar != 0))
920 m_hBar = createScrollbar(HorizontalScrollbar);
922 destroyScrollbar(HorizontalScrollbar);
925 // Force an update since we know the scrollbars have changed things.
926 if (m_object->document()->hasDashboardRegions())
927 m_object->document()->setDashboardRegionsDirty(true);
932 void RenderLayer::setHasVerticalScrollbar(bool hasScrollbar)
934 if (hasScrollbar == (m_vBar != 0))
938 m_vBar = createScrollbar(VerticalScrollbar);
940 destroyScrollbar(VerticalScrollbar);
943 // Force an update since we know the scrollbars have changed things.
944 if (m_object->document()->hasDashboardRegions())
945 m_object->document()->setDashboardRegionsDirty(true);
950 int RenderLayer::verticalScrollbarWidth() const
954 return m_vBar->width();
957 int RenderLayer::horizontalScrollbarHeight() const
961 return m_hBar->height();
964 bool RenderLayer::isPointInResizeControl(const IntPoint& p)
966 return resizeControlRect().contains(IntRect(p, IntSize()));
969 IntSize RenderLayer::offsetFromResizeCorner(const IntPoint& p) const
971 // Currently the resize corner is always the bottom right corner
974 convertToLayerCoords(root(), x, y);
975 return IntSize(x - p.x(), y - p.y());
978 void RenderLayer::positionResizeControl()
980 if (m_object->hasOverflowClip() && m_object->style()->resize() != RESIZE_NONE) {
981 // FIXME: needs to be updated for RTL.
984 convertToLayerCoords(root(), x, y);
985 // FIXME: get the potential scrollbar size from the theme
986 int scrollbarSize = 15;
987 setResizeControlRect(IntRect(x + width() - scrollbarSize - m_object->style()->borderRightWidth() - 1,
988 y + height() - scrollbarSize - m_object->style()->borderBottomWidth() - 1, scrollbarSize, scrollbarSize));
993 RenderLayer::positionScrollbars(const IntRect& absBounds)
995 int resizeControlSize = max(resizeControlRect().height(), 0);
997 m_vBar->setRect(IntRect(absBounds.right() - m_object->borderRight() - m_vBar->width(),
998 absBounds.y() + m_object->borderTop(),
1000 absBounds.height() - (m_object->borderTop() + m_object->borderBottom()) - (m_hBar ? m_hBar->height() : resizeControlSize)));
1002 resizeControlSize = max(resizeControlRect().width(), 0);
1004 m_hBar->setRect(IntRect(absBounds.x() + m_object->borderLeft(),
1005 absBounds.bottom() - m_object->borderBottom() - m_hBar->height(),
1006 absBounds.width() - (m_object->borderLeft() + m_object->borderRight()) - (m_vBar ? m_vBar->width() : resizeControlSize),
1010 int RenderLayer::scrollWidth()
1012 if (m_scrollDimensionsDirty)
1013 computeScrollDimensions();
1014 return m_scrollWidth;
1017 int RenderLayer::scrollHeight()
1019 if (m_scrollDimensionsDirty)
1020 computeScrollDimensions();
1021 return m_scrollHeight;
1024 void RenderLayer::computeScrollDimensions(bool* needHBar, bool* needVBar)
1026 m_scrollDimensionsDirty = false;
1028 bool ltr = m_object->style()->direction() == LTR;
1030 int clientWidth = m_object->clientWidth();
1031 int clientHeight = m_object->clientHeight();
1033 m_scrollLeftOverflow = ltr ? 0 : min(0, m_object->leftmostPosition(true, false) - m_object->borderLeft());
1035 int rightPos = ltr ?
1036 m_object->rightmostPosition(true, false) - m_object->borderLeft() :
1037 clientWidth - m_scrollLeftOverflow;
1038 int bottomPos = m_object->lowestPosition(true, false) - m_object->borderTop();
1040 m_scrollWidth = max(rightPos, clientWidth);
1041 m_scrollHeight = max(bottomPos, clientHeight);
1043 m_scrollOriginX = ltr ? 0 : m_scrollWidth - clientWidth;
1046 *needHBar = rightPos > clientWidth;
1048 *needVBar = bottomPos > clientHeight;
1051 void RenderLayer::updateOverflowStatus(bool horizontalOverflow, bool verticalOverflow)
1053 if (m_overflowStatusDirty) {
1054 m_horizontalOverflow = horizontalOverflow;
1055 m_verticalOverflow = verticalOverflow;
1056 m_overflowStatusDirty = false;
1061 bool horizontalOverflowChanged = (m_horizontalOverflow != horizontalOverflow);
1062 bool verticalOverflowChanged = (m_verticalOverflow != verticalOverflow);
1064 if (horizontalOverflowChanged || verticalOverflowChanged) {
1065 m_horizontalOverflow = horizontalOverflow;
1066 m_verticalOverflow = verticalOverflow;
1068 m_object->element()->document()->frame()->view()->scheduleEvent(new OverflowEvent(horizontalOverflowChanged, horizontalOverflow, verticalOverflowChanged, verticalOverflow),
1069 EventTargetNodeCast(m_object->element()), true);
1074 RenderLayer::updateScrollInfoAfterLayout()
1076 m_scrollDimensionsDirty = true;
1078 bool horizontalOverflow, verticalOverflow;
1079 computeScrollDimensions(&horizontalOverflow, &verticalOverflow);
1081 if (m_object->style()->overflowX() != OMARQUEE) {
1082 // Layout may cause us to be in an invalid scroll position. In this case we need
1083 // to pull our scroll offsets back to the max (or push them up to the min).
1084 int newX = max(0, min(scrollXOffset(), scrollWidth() - m_object->clientWidth()));
1085 int newY = max(0, min(m_scrollY, scrollHeight() - m_object->clientHeight()));
1086 if (newX != scrollXOffset() || newY != m_scrollY)
1087 scrollToOffset(newX, newY);
1088 // FIXME: At this point a scroll event fired, which could have deleted this layer.
1089 // Need to handle this case.
1092 bool haveHorizontalBar = m_hBar;
1093 bool haveVerticalBar = m_vBar;
1095 // overflow:scroll should just enable/disable.
1096 if (m_object->style()->overflowX() == OSCROLL)
1097 m_hBar->setEnabled(horizontalOverflow);
1098 if (m_object->style()->overflowY() == OSCROLL)
1099 m_vBar->setEnabled(verticalOverflow);
1101 // A dynamic change from a scrolling overflow to overflow:hidden means we need to get rid of any
1102 // scrollbars that may be present.
1103 if (m_object->style()->overflowX() == OHIDDEN && haveHorizontalBar)
1104 setHasHorizontalScrollbar(false);
1105 if (m_object->style()->overflowY() == OHIDDEN && haveVerticalBar)
1106 setHasVerticalScrollbar(false);
1108 // overflow:auto may need to lay out again if scrollbars got added/removed.
1109 bool scrollbarsChanged = (m_object->hasAutoHorizontalScrollbar() && haveHorizontalBar != horizontalOverflow) ||
1110 (m_object->hasAutoVerticalScrollbar() && haveVerticalBar != verticalOverflow);
1111 if (scrollbarsChanged) {
1112 if (m_object->hasAutoHorizontalScrollbar())
1113 setHasHorizontalScrollbar(horizontalOverflow);
1114 if (m_object->hasAutoVerticalScrollbar())
1115 setHasVerticalScrollbar(verticalOverflow);
1118 // Force an update since we know the scrollbars have changed things.
1119 if (m_object->document()->hasDashboardRegions())
1120 m_object->document()->setDashboardRegionsDirty(true);
1123 m_object->repaint();
1125 if (m_object->style()->overflowX() == OAUTO || m_object->style()->overflowY() == OAUTO) {
1126 if (!m_inOverflowRelayout) {
1127 // Our proprietary overflow: overlay value doesn't trigger a layout.
1128 m_inOverflowRelayout = true;
1129 m_object->setNeedsLayout(true);
1130 if (m_object->isRenderBlock())
1131 static_cast<RenderBlock*>(m_object)->layoutBlock(true);
1134 m_inOverflowRelayout = false;
1139 // Set up the range (and page step/line step).
1141 int clientWidth = m_object->clientWidth();
1142 int pageStep = (clientWidth-PAGE_KEEP);
1143 if (pageStep < 0) pageStep = clientWidth;
1144 m_hBar->setSteps(LINE_STEP, pageStep);
1145 m_hBar->setProportion(clientWidth, m_scrollWidth);
1146 m_hBar->setValue(scrollXOffset());
1149 int clientHeight = m_object->clientHeight();
1150 int pageStep = (clientHeight-PAGE_KEEP);
1151 if (pageStep < 0) pageStep = clientHeight;
1152 m_vBar->setSteps(LINE_STEP, pageStep);
1153 m_vBar->setProportion(clientHeight, m_scrollHeight);
1154 m_object->repaintRectangle(IntRect(m_object->borderLeft() + m_object->clientWidth(),
1155 m_object->borderTop(), verticalScrollbarWidth(),
1156 m_object->height() - m_object->borderTop() - m_object->borderBottom()));
1159 if (m_object->element() && m_object->document()->hasListenerType(Document::OVERFLOWCHANGED_LISTENER))
1160 updateOverflowStatus(horizontalOverflow, verticalOverflow);
1164 RenderLayer::paintScrollbars(GraphicsContext* p, const IntRect& damageRect)
1166 // Move the widgets if necessary. We normally move and resize widgets during layout, but sometimes
1167 // widgets can move without layout occurring (most notably when you scroll a document that
1168 // contains fixed positioned elements).
1169 positionResizeControl();
1170 if (m_hBar || m_vBar) {
1173 convertToLayerCoords(root(), x, y);
1174 IntRect layerBounds = IntRect(x, y, width(), height());
1175 positionScrollbars(layerBounds);
1178 // Now that we're sure the scrollbars are in the right place, paint them.
1180 m_hBar->paint(p, damageRect);
1182 m_vBar->paint(p, damageRect);
1185 void RenderLayer::paintResizeControl(GraphicsContext* c)
1187 if (resizeControlRect().isEmpty())
1190 if (!m_resizeCornerImage)
1191 m_resizeCornerImage = Image::loadPlatformResource("textAreaResizeCorner");
1193 IntPoint imagePoint(resizeControlRect().right() - m_resizeCornerImage->width(), resizeControlRect().bottom() - m_resizeCornerImage->height());
1194 c->drawImage(m_resizeCornerImage, imagePoint);
1197 bool RenderLayer::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier)
1199 bool didHorizontalScroll = false;
1200 bool didVerticalScroll = false;
1203 if (granularity == ScrollByDocument) {
1204 // Special-case for the ScrollByDocument granularity. A document scroll can only be up
1205 // or down and in both cases the horizontal bar goes all the way to the left.
1206 didHorizontalScroll = m_hBar->scroll(ScrollLeft, ScrollByDocument, multiplier);
1208 didHorizontalScroll = m_hBar->scroll(direction, granularity, multiplier);
1212 didVerticalScroll = m_vBar->scroll(direction, granularity, multiplier);
1214 return (didHorizontalScroll || didVerticalScroll);
1218 RenderLayer::paint(GraphicsContext* p, const IntRect& damageRect, PaintRestriction paintRestriction, RenderObject *paintingRoot)
1220 paintLayer(this, p, damageRect, false, paintRestriction, paintingRoot);
1223 static void setClip(GraphicsContext* p, const IntRect& paintDirtyRect, const IntRect& clipRect)
1225 // Work around bugs in focus ring clipping on Mac.
1226 p->setFocusRingClip(clipRect);
1227 if (paintDirtyRect == clipRect)
1233 static void restoreClip(GraphicsContext* p, const IntRect& paintDirtyRect, const IntRect& clipRect)
1235 // Work around bugs in focus ring clipping on Mac.
1236 p->clearFocusRingClip();
1237 if (paintDirtyRect == clipRect)
1243 RenderLayer::paintLayer(RenderLayer* rootLayer, GraphicsContext* p,
1244 const IntRect& paintDirtyRect, bool haveTransparency, PaintRestriction paintRestriction,
1245 RenderObject *paintingRoot)
1247 // Avoid painting layers when stylesheets haven't loaded. This eliminates FOUC.
1248 // It's ok not to draw, because later on, when all the stylesheets do load, updateStyleSelector on the Document
1249 // will do a full repaint().
1250 if (renderer()->document()->didLayoutWithPendingStylesheets() && !renderer()->isRenderView() && !renderer()->isRoot())
1253 // Calculate the clip rects we should use.
1254 IntRect layerBounds, damageRect, clipRectToApply, outlineRect;
1255 calculateRects(rootLayer, paintDirtyRect, layerBounds, damageRect, clipRectToApply, outlineRect);
1256 int x = layerBounds.x();
1257 int y = layerBounds.y();
1258 int tx = x - renderer()->xPos();
1259 int ty = y - renderer()->yPos() + renderer()->borderTopExtra();
1261 // Ensure our lists are up-to-date.
1262 updateZOrderLists();
1263 updateOverflowList();
1265 // If this layer is totally invisible then there is nothing to paint.
1266 if (!m_object->opacity())
1269 bool selectionOnly = paintRestriction == PaintRestrictionSelectionOnly || paintRestriction == PaintRestrictionSelectionOnlyWhiteText;
1270 bool forceWhiteText = paintRestriction == PaintRestrictionSelectionOnlyWhiteText;
1272 if (isTransparent())
1273 haveTransparency = true;
1275 // If this layer's renderer is a child of the paintingRoot, we render unconditionally, which
1276 // is done by passing a nil paintingRoot down to our renderer (as if no paintingRoot was ever set).
1277 // Else, our renderer tree may or may not contain the painting root, so we pass that root along
1278 // so it will be tested against as we decend through the renderers.
1279 RenderObject *paintingRootForRenderer = 0;
1280 if (paintingRoot && !m_object->isDescendantOf(paintingRoot))
1281 paintingRootForRenderer = paintingRoot;
1283 // We want to paint our layer, but only if we intersect the damage rect.
1284 bool shouldPaint = intersectsDamageRect(layerBounds, damageRect);
1285 if (shouldPaint && !selectionOnly && !damageRect.isEmpty()) {
1286 // Begin transparency layers lazily now that we know we have to paint something.
1287 if (haveTransparency)
1288 beginTransparencyLayers(p, paintDirtyRect);
1290 // Paint our background first, before painting any child layers.
1291 // Establish the clip used to paint our background.
1292 setClip(p, paintDirtyRect, damageRect);
1294 // Paint the background.
1295 RenderObject::PaintInfo info(p, damageRect, PaintPhaseBlockBackground, false, paintingRootForRenderer, 0);
1296 renderer()->paint(info, tx, ty);
1298 // Our scrollbar widgets paint exactly when we tell them to, so that they work properly with
1299 // z-index. We paint after we painted the background/border, so that the scrollbars will
1300 // sit above the background/border.
1301 paintScrollbars(p, damageRect);
1302 paintResizeControl(p);
1303 // Restore the clip.
1304 restoreClip(p, paintDirtyRect, damageRect);
1307 // Now walk the sorted list of children with negative z-indices.
1308 if (m_negZOrderList)
1309 for (Vector<RenderLayer*>::iterator it = m_negZOrderList->begin(); it != m_negZOrderList->end(); ++it)
1310 it[0]->paintLayer(rootLayer, p, paintDirtyRect, haveTransparency, paintRestriction, paintingRoot);
1312 // Now establish the appropriate clip and paint our child RenderObjects.
1313 if (shouldPaint && !clipRectToApply.isEmpty()) {
1314 // Begin transparency layers lazily now that we know we have to paint something.
1315 if (haveTransparency)
1316 beginTransparencyLayers(p, paintDirtyRect);
1318 // Set up the clip used when painting our children.
1319 setClip(p, paintDirtyRect, clipRectToApply);
1320 RenderObject::PaintInfo info(p, clipRectToApply,
1321 selectionOnly ? PaintPhaseSelection : PaintPhaseChildBlockBackgrounds,
1323 paintingRootForRenderer, 0);
1324 renderer()->paint(info, tx, ty);
1325 if (!selectionOnly) {
1326 info.phase = PaintPhaseFloat;
1327 renderer()->paint(info, tx, ty);
1328 info.phase = PaintPhaseForeground;
1329 renderer()->paint(info, tx, ty);
1330 info.phase = PaintPhaseChildOutlines;
1331 renderer()->paint(info, tx, ty);
1334 // Now restore our clip.
1335 restoreClip(p, paintDirtyRect, clipRectToApply);
1338 if (!outlineRect.isEmpty()) {
1339 // Paint our own outline
1340 RenderObject::PaintInfo info(p, outlineRect, PaintPhaseSelfOutline, false, paintingRootForRenderer, 0);
1341 setClip(p, paintDirtyRect, outlineRect);
1342 renderer()->paint(info, tx, ty);
1343 restoreClip(p, paintDirtyRect, outlineRect);
1346 // Paint any child layers that have overflow.
1348 for (Vector<RenderLayer*>::iterator it = m_overflowList->begin(); it != m_overflowList->end(); ++it)
1349 it[0]->paintLayer(rootLayer, p, paintDirtyRect, haveTransparency, paintRestriction, paintingRoot);
1351 // Now walk the sorted list of children with positive z-indices.
1352 if (m_posZOrderList)
1353 for (Vector<RenderLayer*>::iterator it = m_posZOrderList->begin(); it != m_posZOrderList->end(); ++it)
1354 it[0]->paintLayer(rootLayer, p, paintDirtyRect, haveTransparency, paintRestriction, paintingRoot);
1356 // End our transparency layer
1357 if (isTransparent() && m_usedTransparency) {
1358 p->endTransparencyLayer();
1360 m_usedTransparency = false;
1364 static inline bool isSubframe(RenderObject* renderer)
1366 return renderer->isRenderView() && renderer->node()->document()->frame()->tree()->parent();
1369 static inline IntRect frameVisibleRect(RenderObject* renderer)
1371 return enclosingIntRect(renderer->node()->document()->frame()->view()->visibleContentRect());
1375 RenderLayer::hitTest(RenderObject::NodeInfo& info, const IntPoint& point)
1377 renderer()->document()->updateLayout();
1379 IntRect boundsRect(m_x, m_y, width(), height());
1380 if (isSubframe(renderer()))
1381 boundsRect.intersect(frameVisibleRect(renderer()));
1383 RenderLayer* insideLayer = hitTestLayer(this, info, point, boundsRect);
1385 // Now determine if the result is inside an anchor; make sure an image map wins if
1386 // it already set URLElement and only use the innermost.
1387 Node* node = info.innerNode();
1389 if (node->isLink() && !info.URLElement())
1390 info.setURLElement(static_cast<Element*>(node));
1391 node = node->parentNode();
1394 // Next set up the correct :hover/:active state along the new chain.
1395 updateHoverActiveState(info);
1397 // Now return whether we were inside this layer (this will always be true for the root
1402 static inline bool shouldApplyImplicitCapture(RenderObject* renderer, bool mouseDown)
1404 return mouseDown && renderer->isRoot();
1408 RenderLayer::hitTestLayer(RenderLayer* rootLayer, RenderObject::NodeInfo& info,
1409 const IntPoint& mousePos, const IntRect& hitTestRect)
1411 // Calculate the clip rects we should use.
1412 IntRect layerBounds;
1415 IntRect outlineRect;
1416 calculateRects(rootLayer, hitTestRect, layerBounds, bgRect, fgRect, outlineRect);
1418 // Ensure our lists are up-to-date.
1419 updateZOrderLists();
1420 updateOverflowList();
1422 // This variable tracks which layer the mouse ends up being inside. The minute we find an insideLayer,
1423 // we are done and can return it.
1424 RenderLayer* insideLayer = 0;
1426 // Begin by walking our list of positive layers from highest z-index down to the lowest
1428 if (m_posZOrderList) {
1429 for (int i = m_posZOrderList->size() - 1; i >= 0; --i) {
1430 insideLayer = m_posZOrderList->at(i)->hitTestLayer(rootLayer, info, mousePos, hitTestRect);
1436 // Now check our overflow objects.
1437 if (m_overflowList) {
1438 for (int i = m_overflowList->size() - 1; i >= 0; --i) {
1439 insideLayer = m_overflowList->at(i)->hitTestLayer(rootLayer, info, mousePos, hitTestRect);
1445 // Next we want to see if the mouse pos is inside the child RenderObjects of the layer.
1446 if (fgRect.contains(mousePos) &&
1447 renderer()->hitTest(info, mousePos.x(), mousePos.y(),
1448 layerBounds.x() - renderer()->xPos(),
1449 layerBounds.y() - renderer()->yPos() + m_object->borderTopExtra(), HitTestDescendants)) {
1450 // for positioned generated content, we might still not have a
1451 // node by the time we get to the layer level, since none of
1452 // the content in the layer has an element. So just walk up
1454 if (!info.innerNode()) {
1455 for (RenderObject *r = renderer(); r != NULL; r = r->parent()) {
1457 info.setInnerNode(r->element());
1463 if (!info.innerNonSharedNode()) {
1464 for (RenderObject *r = renderer(); r != NULL; r = r->parent()) {
1466 info.setInnerNonSharedNode(r->element());
1474 // Now check our negative z-index children.
1475 if (m_negZOrderList) {
1476 for (int i = m_negZOrderList->size() - 1; i >= 0; --i) {
1477 insideLayer = m_negZOrderList->at(i)->hitTestLayer(rootLayer, info, mousePos, hitTestRect);
1483 // Next we want to see if the mouse pos is inside this layer but not any of its children.
1484 // If this is the root layer and the mouse is down, we want to do this even if it doesn't
1485 // contain the point so mouse move events keep getting delivered when dragging outside the
1487 if ((bgRect.contains(mousePos) || shouldApplyImplicitCapture(renderer(), info.active())) &&
1488 renderer()->hitTest(info, mousePos.x(), mousePos.y(),
1489 layerBounds.x() - renderer()->xPos(),
1490 layerBounds.y() - renderer()->yPos() + m_object->borderTopExtra(),
1498 void RenderLayer::calculateClipRects(const RenderLayer* rootLayer)
1501 return; // We have the correct cached value.
1504 // The root layer's clip rect is always just its dimensions.
1505 m_clipRects = new (m_object->renderArena()) ClipRects(IntRect(0,0,width(),height()));
1510 // Ensure that our parent's clip has been calculated so that we can examine the values.
1511 parent()->calculateClipRects(rootLayer);
1513 // Set up our three rects to initially match the parent rects.
1514 IntRect posClipRect(parent()->clipRects()->posClipRect());
1515 IntRect overflowClipRect(parent()->clipRects()->overflowClipRect());
1516 IntRect fixedClipRect(parent()->clipRects()->fixedClipRect());
1518 // A fixed object is essentially the root of its containing block hierarchy, so when
1519 // we encounter such an object, we reset our clip rects to the fixedClipRect.
1520 if (m_object->style()->position() == FixedPosition) {
1521 posClipRect = fixedClipRect;
1522 overflowClipRect = fixedClipRect;
1524 else if (m_object->style()->position() == RelativePosition)
1525 posClipRect = overflowClipRect;
1526 else if (m_object->style()->position() == AbsolutePosition)
1527 overflowClipRect = posClipRect;
1529 // Update the clip rects that will be passed to child layers.
1530 if (m_object->hasOverflowClip() || m_object->hasClip()) {
1531 // This layer establishes a clip of some kind.
1534 convertToLayerCoords(rootLayer, x, y);
1536 if (m_object->hasOverflowClip()) {
1537 IntRect newOverflowClip = m_object->getOverflowClipRect(x,y);
1538 overflowClipRect.intersect(newOverflowClip);
1539 if (m_object->isPositioned() || m_object->isRelPositioned())
1540 posClipRect.intersect(newOverflowClip);
1542 if (m_object->hasClip()) {
1543 IntRect newPosClip = m_object->getClipRect(x,y);
1544 posClipRect.intersect(newPosClip);
1545 overflowClipRect.intersect(newPosClip);
1546 fixedClipRect.intersect(newPosClip);
1550 // If our clip rects match our parent's clip, then we can just share its data structure and
1552 if (posClipRect == parent()->clipRects()->posClipRect() &&
1553 overflowClipRect == parent()->clipRects()->overflowClipRect() &&
1554 fixedClipRect == parent()->clipRects()->fixedClipRect())
1555 m_clipRects = parent()->clipRects();
1557 m_clipRects = new (m_object->renderArena()) ClipRects(overflowClipRect, fixedClipRect, posClipRect);
1561 void RenderLayer::calculateRects(const RenderLayer* rootLayer, const IntRect& paintDirtyRect, IntRect& layerBounds,
1562 IntRect& backgroundRect, IntRect& foregroundRect, IntRect& outlineRect)
1565 parent()->calculateClipRects(rootLayer);
1566 backgroundRect = m_object->style()->position() == FixedPosition ? parent()->clipRects()->fixedClipRect() :
1567 (m_object->isPositioned() ? parent()->clipRects()->posClipRect() :
1568 parent()->clipRects()->overflowClipRect());
1569 backgroundRect.intersect(paintDirtyRect);
1571 backgroundRect = paintDirtyRect;
1572 foregroundRect = backgroundRect;
1573 outlineRect = backgroundRect;
1577 convertToLayerCoords(rootLayer, x, y);
1578 layerBounds = IntRect(x,y,width(),height());
1580 // Update the clip rects that will be passed to child layers.
1581 if (m_object->hasOverflowClip() || m_object->hasClip()) {
1582 // This layer establishes a clip of some kind.
1583 if (m_object->hasOverflowClip())
1584 foregroundRect.intersect(m_object->getOverflowClipRect(x,y));
1585 if (m_object->hasClip()) {
1586 // Clip applies to *us* as well, so go ahead and update the damageRect.
1587 IntRect newPosClip = m_object->getClipRect(x,y);
1588 backgroundRect.intersect(newPosClip);
1589 foregroundRect.intersect(newPosClip);
1590 outlineRect.intersect(newPosClip);
1593 // If we establish a clip at all, then go ahead and make sure our background
1594 // rect is intersected with our layer's bounds.
1595 backgroundRect.intersect(layerBounds);
1599 bool RenderLayer::intersectsDamageRect(const IntRect& layerBounds, const IntRect& damageRect) const
1601 // Always examine the canvas and the root.
1602 if (renderer()->isRenderView() || renderer()->isRoot())
1605 // If we aren't an inline flow, and our layer bounds do intersect the damage rect, then we
1606 // can go ahead and return true.
1607 if (!renderer()->isInlineFlow()) {
1608 IntRect b = layerBounds;
1609 b.inflate(renderer()->view()->maximalOutlineSize());
1610 if (b.intersects(damageRect))
1614 // Otherwise we need to compute the bounding box of this single layer and see if it intersects
1616 return absoluteBoundingBox().intersects(damageRect);
1619 IntRect RenderLayer::absoluteBoundingBox() const
1621 // There are three special cases we need to consider.
1622 // (1) Inline Flows. For inline flows we will create a bounding box that fully encompasses all of the lines occupied by the
1623 // inline. In other words, if some <span> wraps to three lines, we'll create a bounding box that fully encloses the root
1624 // line boxes of all three lines (including overflow on those lines).
1625 // (2) Left/Top Overflow. The width/height of layers already includes right/bottom overflow. However, in the case of left/top
1626 // overflow, we have to create a bounding box that will extend to include this overflow.
1627 // (3) Floats. When a layer has overhanging floats that it paints, we need to make sure to include these overhanging floats
1628 // as part of our bounding box. We do this because we are the responsible layer for both hit testing and painting those
1631 if (renderer()->isInlineFlow()) {
1632 // Go from our first line box to our last line box.
1633 RenderInline* inlineFlow = static_cast<RenderInline*>(renderer());
1634 InlineFlowBox* firstBox = inlineFlow->firstLineBox();
1637 int top = firstBox->root()->topOverflow();
1638 int bottom = inlineFlow->lastLineBox()->root()->bottomOverflow();
1639 int left = firstBox->xPos();
1640 for (InlineRunBox* curr = firstBox->nextLineBox(); curr; curr = curr->nextLineBox())
1641 left = min(left, curr->xPos());
1642 result = IntRect(m_x + left, m_y + (top - renderer()->yPos()), width(), bottom - top);
1643 } else if (renderer()->isTableRow()) {
1644 // Our bounding box is just the union of all of our cells' border/overflow rects.
1645 for (RenderObject* child = renderer()->firstChild(); child; child = child->nextSibling()) {
1646 if (child->isTableCell()) {
1647 IntRect bbox = child->borderBox();
1648 bbox.move(0, child->borderTopExtra());
1650 IntRect overflowRect = renderer()->overflowRect(false);
1651 overflowRect.move(0, child->borderTopExtra());
1652 if (bbox != overflowRect)
1653 result.unite(overflowRect);
1656 result.move(m_x, m_y);
1658 IntRect bbox = renderer()->borderBox();
1660 IntRect overflowRect = renderer()->overflowRect(false);
1661 if (bbox != overflowRect)
1662 result.unite(overflowRect);
1663 IntRect floatRect = renderer()->floatRect();
1664 if (bbox != floatRect)
1665 result.unite(floatRect);
1667 // We have to adjust the x/y of this result so that it is in the coordinate space of the layer.
1668 // We also have to add in borderTopExtra here, since borderBox(), in order to play well with methods like
1669 // floatRect that deal with child content, uses an origin of (0,0) that is at the child content box (so
1670 // border box returns a y coord of -borderTopExtra(). The layer, however, uses the outer box. This is all
1671 // really confusing.
1672 result.move(m_x, m_y + renderer()->borderTopExtra());
1675 // Convert the bounding box to an absolute position. We can do this easily by looking at the delta
1676 // between the bounding box's xpos and our layer's xpos and then applying that to the absolute layerBounds
1678 int absX = 0, absY = 0;
1679 convertToLayerCoords(root(), absX, absY);
1680 result.move(absX - m_x, absY - m_y);
1681 result.inflate(renderer()->view()->maximalOutlineSize());
1685 void RenderLayer::clearClipRects()
1692 for (RenderLayer* l = firstChild(); l; l = l->nextSibling())
1693 l->clearClipRects();
1696 void RenderLayer::clearClipRect()
1699 m_clipRects->deref(m_object->renderArena());
1704 static RenderObject* commonAncestor(RenderObject* obj1, RenderObject* obj2)
1709 for (RenderObject* currObj1 = obj1; currObj1; currObj1 = currObj1->hoverAncestor())
1710 for (RenderObject* currObj2 = obj2; currObj2; currObj2 = currObj2->hoverAncestor())
1711 if (currObj1 == currObj2)
1717 void RenderLayer::updateHoverActiveState(RenderObject::NodeInfo& info)
1719 // We don't update :hover/:active state when the info is marked as readonly.
1720 if (info.readonly())
1723 Document* doc = renderer()->document();
1726 Node* activeNode = doc->activeNode();
1727 if (activeNode && !info.active()) {
1728 // We are clearing the :active chain because the mouse has been released.
1729 for (RenderObject* curr = activeNode->renderer(); curr; curr = curr->parent()) {
1730 if (curr->element() && !curr->isText())
1731 curr->element()->setInActiveChain(false);
1733 doc->setActiveNode(0);
1735 Node* newActiveNode = info.innerNode();
1736 if (!activeNode && newActiveNode && info.active()) {
1737 // We are setting the :active chain and freezing it. If future moves happen, they
1738 // will need to reference this chain.
1739 for (RenderObject* curr = newActiveNode->renderer(); curr; curr = curr->parent()) {
1740 if (curr->element() && !curr->isText()) {
1741 curr->element()->setInActiveChain(true);
1744 doc->setActiveNode(newActiveNode);
1748 // If the mouse is down and if this is a mouse move event, we want to restrict changes in
1749 // :hover/:active to only apply to elements that are in the :active chain that we froze
1750 // at the time the mouse went down.
1751 bool mustBeInActiveChain = info.active() && info.mouseMove();
1753 // Check to see if the hovered node has changed. If not, then we don't need to
1755 Node* oldHoverNode = doc->hoverNode();
1756 Node* newHoverNode = info.innerNode();
1758 // Update our current hover node.
1759 doc->setHoverNode(newHoverNode);
1761 // We have two different objects. Fetch their renderers.
1762 RenderObject* oldHoverObj = oldHoverNode ? oldHoverNode->renderer() : 0;
1763 RenderObject* newHoverObj = newHoverNode ? newHoverNode->renderer() : 0;
1765 // Locate the common ancestor render object for the two renderers.
1766 RenderObject* ancestor = commonAncestor(oldHoverObj, newHoverObj);
1768 if (oldHoverObj != newHoverObj) {
1769 // The old hover path only needs to be cleared up to (and not including) the common ancestor;
1770 for (RenderObject* curr = oldHoverObj; curr && curr != ancestor; curr = curr->hoverAncestor()) {
1771 if (curr->element() && !curr->isText() && (!mustBeInActiveChain || curr->element()->inActiveChain())) {
1772 curr->element()->setActive(false);
1773 curr->element()->setHovered(false);
1778 // Now set the hover state for our new object up to the root.
1779 for (RenderObject* curr = newHoverObj; curr; curr = curr->hoverAncestor()) {
1780 if (curr->element() && !curr->isText() && (!mustBeInActiveChain || curr->element()->inActiveChain())) {
1781 curr->element()->setActive(info.active());
1782 curr->element()->setHovered(true);
1787 // Helper for the sorting of layers by z-index.
1788 static inline bool compareZIndex(RenderLayer* first, RenderLayer* second)
1790 return first->zIndex() < second->zIndex();
1793 void RenderLayer::dirtyZOrderLists()
1795 if (m_posZOrderList)
1796 m_posZOrderList->clear();
1797 if (m_negZOrderList)
1798 m_negZOrderList->clear();
1799 m_zOrderListsDirty = true;
1802 void RenderLayer::dirtyOverflowList()
1805 m_overflowList->clear();
1806 m_overflowListDirty = true;
1809 void RenderLayer::updateZOrderLists()
1811 if (!isStackingContext() || !m_zOrderListsDirty)
1814 for (RenderLayer* child = firstChild(); child; child = child->nextSibling())
1815 child->collectLayers(m_posZOrderList, m_negZOrderList);
1817 // Sort the two lists.
1818 if (m_posZOrderList)
1819 std::stable_sort(m_posZOrderList->begin(), m_posZOrderList->end(), compareZIndex);
1820 if (m_negZOrderList)
1821 std::stable_sort(m_negZOrderList->begin(), m_negZOrderList->end(), compareZIndex);
1823 m_zOrderListsDirty = false;
1826 void RenderLayer::updateOverflowList()
1828 if (!m_overflowListDirty)
1831 for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) {
1832 if (child->isOverflowOnly()) {
1833 if (!m_overflowList)
1834 m_overflowList = new Vector<RenderLayer*>;
1835 m_overflowList->append(child);
1839 m_overflowListDirty = false;
1842 void RenderLayer::collectLayers(Vector<RenderLayer*>*& posBuffer, Vector<RenderLayer*>*& negBuffer)
1844 // FIXME: A child render object or layer could override visibility. Don't remove this
1845 // optimization though until RenderObject's nodeAtPoint is patched to understand what to do
1846 // when visibility is overridden by a child.
1847 if (renderer()->style()->visibility() != VISIBLE)
1850 // Overflow layers are just painted by their enclosing layers, so they don't get put in zorder lists.
1851 if (!isOverflowOnly()) {
1852 // Determine which buffer the child should be in.
1853 Vector<RenderLayer*>*& buffer = (zIndex() >= 0) ? posBuffer : negBuffer;
1855 // Create the buffer if it doesn't exist yet.
1857 buffer = new Vector<RenderLayer*>;
1859 // Append ourselves at the end of the appropriate buffer.
1860 buffer->append(this);
1863 // Recur into our children to collect more layers, but only if we don't establish
1864 // a stacking context.
1865 if (!isStackingContext())
1866 for (RenderLayer* child = firstChild(); child; child = child->nextSibling())
1867 child->collectLayers(posBuffer, negBuffer);
1870 void RenderLayer::repaintIncludingDescendants()
1872 m_object->repaint();
1873 for (RenderLayer* curr = firstChild(); curr; curr = curr->nextSibling())
1874 curr->repaintIncludingDescendants();
1877 bool RenderLayer::shouldBeOverflowOnly() const
1879 return renderer()->hasOverflowClip() &&
1880 !renderer()->isPositioned() &&
1881 !renderer()->isRelPositioned() &&
1885 void RenderLayer::styleChanged()
1887 bool isOverflowOnly = shouldBeOverflowOnly();
1888 if (isOverflowOnly != m_isOverflowOnly) {
1889 m_isOverflowOnly = isOverflowOnly;
1890 RenderLayer* p = parent();
1891 RenderLayer* sc = stackingContext();
1893 p->dirtyOverflowList();
1895 sc->dirtyZOrderLists();
1898 if (m_object->style()->overflowX() == OMARQUEE && m_object->style()->marqueeBehavior() != MNONE) {
1900 m_marquee = new Marquee(this);
1901 m_marquee->updateMarqueeStyle();
1903 else if (m_marquee) {
1909 void RenderLayer::suspendMarquees()
1912 m_marquee->suspend();
1914 for (RenderLayer* curr = firstChild(); curr; curr = curr->nextSibling())
1915 curr->suspendMarquees();
1918 // --------------------------------------------------------------------------
1919 // Marquee implementation
1921 Marquee::Marquee(RenderLayer* l)
1922 : m_layer(l), m_currentLoop(0)
1923 , m_timer(this, &Marquee::timerFired)
1924 , m_start(0), m_end(0), m_speed(0), m_unfurlPos(0), m_reset(false)
1925 , m_suspended(false), m_stopped(false), m_whiteSpace(NORMAL), m_direction(MAUTO)
1929 int Marquee::marqueeSpeed() const
1931 int result = m_layer->renderer()->style()->marqueeSpeed();
1932 Node* elt = m_layer->renderer()->element();
1933 if (elt && elt->hasTagName(marqueeTag)) {
1934 HTMLMarqueeElement* marqueeElt = static_cast<HTMLMarqueeElement*>(elt);
1935 result = max(result, marqueeElt->minimumDelay());
1940 EMarqueeDirection Marquee::direction() const
1942 // FIXME: Support the CSS3 "auto" value for determining the direction of the marquee.
1943 // For now just map MAUTO to MBACKWARD
1944 EMarqueeDirection result = m_layer->renderer()->style()->marqueeDirection();
1945 TextDirection dir = m_layer->renderer()->style()->direction();
1946 if (result == MAUTO)
1948 if (result == MFORWARD)
1949 result = (dir == LTR) ? MRIGHT : MLEFT;
1950 if (result == MBACKWARD)
1951 result = (dir == LTR) ? MLEFT : MRIGHT;
1953 // Now we have the real direction. Next we check to see if the increment is negative.
1954 // If so, then we reverse the direction.
1955 Length increment = m_layer->renderer()->style()->marqueeIncrement();
1956 if (increment.value() < 0)
1957 result = static_cast<EMarqueeDirection>(-result);
1962 bool Marquee::isHorizontal() const
1964 return direction() == MLEFT || direction() == MRIGHT;
1967 bool Marquee::isUnfurlMarquee() const
1969 EMarqueeBehavior behavior = m_layer->renderer()->style()->marqueeBehavior();
1970 return (behavior == MUNFURL);
1973 int Marquee::computePosition(EMarqueeDirection dir, bool stopAtContentEdge)
1975 RenderObject* o = m_layer->renderer();
1976 RenderStyle* s = o->style();
1977 if (isHorizontal()) {
1978 bool ltr = s->direction() == LTR;
1979 int clientWidth = o->clientWidth();
1980 int contentWidth = ltr ? o->rightmostPosition(true, false) : o->leftmostPosition(true, false);
1982 contentWidth += (o->paddingRight() - o->borderLeft());
1984 contentWidth = o->width() - contentWidth;
1985 contentWidth += (o->paddingLeft() - o->borderRight());
1987 if (dir == MRIGHT) {
1988 if (stopAtContentEdge)
1989 return max(0, ltr ? (contentWidth - clientWidth) : (clientWidth - contentWidth));
1991 return ltr ? contentWidth : clientWidth;
1994 if (stopAtContentEdge)
1995 return min(0, ltr ? (contentWidth - clientWidth) : (clientWidth - contentWidth));
1997 return ltr ? -clientWidth : -contentWidth;
2001 int contentHeight = m_layer->renderer()->lowestPosition(true, false) -
2002 m_layer->renderer()->borderTop() + m_layer->renderer()->paddingBottom();
2003 int clientHeight = m_layer->renderer()->clientHeight();
2005 if (stopAtContentEdge)
2006 return min(contentHeight - clientHeight, 0);
2008 return -clientHeight;
2011 if (stopAtContentEdge)
2012 return max(contentHeight - clientHeight, 0);
2014 return contentHeight;
2019 void Marquee::start()
2021 if (m_timer.isActive() || m_layer->renderer()->style()->marqueeIncrement().value() == 0)
2024 if (!m_suspended && !m_stopped) {
2025 if (isUnfurlMarquee()) {
2026 bool forward = direction() == MDOWN || direction() == MRIGHT;
2027 bool isReversed = (forward && m_currentLoop % 2) || (!forward && !(m_currentLoop % 2));
2028 m_unfurlPos = isReversed ? m_end : m_start;
2029 m_layer->renderer()->setChildNeedsLayout(true);
2033 m_layer->scrollToOffset(m_start, 0, false, false);
2035 m_layer->scrollToOffset(0, m_start, false, false);
2037 // FIXME: At this point a scroll event fired, which could have deleted this layer,
2038 // including the marquee. Need to handle this case.
2041 m_suspended = false;
2045 m_timer.startRepeating(speed() * 0.001);
2048 void Marquee::suspend()
2054 void Marquee::stop()
2060 void Marquee::updateMarqueePosition()
2062 bool activate = (m_totalLoops <= 0 || m_currentLoop < m_totalLoops);
2064 if (isUnfurlMarquee()) {
2065 if (m_unfurlPos < m_start) {
2066 m_unfurlPos = m_start;
2067 m_layer->renderer()->setChildNeedsLayout(true);
2069 else if (m_unfurlPos > m_end) {
2070 m_unfurlPos = m_end;
2071 m_layer->renderer()->setChildNeedsLayout(true);
2075 EMarqueeBehavior behavior = m_layer->renderer()->style()->marqueeBehavior();
2076 m_start = computePosition(direction(), behavior == MALTERNATE);
2077 m_end = computePosition(reverseDirection(), behavior == MALTERNATE || behavior == MSLIDE);
2084 void Marquee::updateMarqueeStyle()
2086 RenderStyle* s = m_layer->renderer()->style();
2088 if (m_direction != s->marqueeDirection() || (m_totalLoops != s->marqueeLoopCount() && m_currentLoop >= m_totalLoops))
2089 m_currentLoop = 0; // When direction changes or our loopCount is a smaller number than our current loop, reset our loop.
2091 m_totalLoops = s->marqueeLoopCount();
2092 m_direction = s->marqueeDirection();
2093 m_whiteSpace = s->whiteSpace();
2095 if (m_layer->renderer()->isHTMLMarquee()) {
2096 // Hack for WinIE. In WinIE, a value of 0 or lower for the loop count for SLIDE means to only do
2098 if (m_totalLoops <= 0 && (s->marqueeBehavior() == MSLIDE || s->marqueeBehavior() == MUNFURL))
2101 // Hack alert: Set the white-space value to nowrap for horizontal marquees with inline children, thus ensuring
2102 // all the text ends up on one line by default. Limit this hack to the <marquee> element to emulate
2103 // WinIE's behavior. Someone using CSS3 can use white-space: nowrap on their own to get this effect.
2104 // Second hack alert: Set the text-align back to auto. WinIE completely ignores text-align on the
2106 // FIXME: Bring these up with the CSS WG.
2107 if (isHorizontal() && m_layer->renderer()->childrenInline()) {
2108 s->setWhiteSpace(NOWRAP);
2109 s->setTextAlign(TAAUTO);
2113 // Marquee height hack!! Make sure that, if it is a horizontal marquee, the height attribute is overridden
2114 // if it is smaller than the font size. If it is a vertical marquee and height is not specified, we default
2115 // to a marquee of 200px.
2116 if (isHorizontal()) {
2117 if (s->height().isFixed() && s->height().value() < s->fontSize())
2118 s->setHeight(Length(s->fontSize(),Fixed));
2119 } else if (s->height().isAuto()) //vertical marquee with no specified height
2120 s->setHeight(Length(200, Fixed));
2122 if (speed() != marqueeSpeed()) {
2123 m_speed = marqueeSpeed();
2124 if (m_timer.isActive())
2125 m_timer.startRepeating(speed() * 0.001);
2128 // Check the loop count to see if we should now stop.
2129 bool activate = (m_totalLoops <= 0 || m_currentLoop < m_totalLoops);
2130 if (activate && !m_timer.isActive())
2131 m_layer->renderer()->setNeedsLayout(true);
2132 else if (!activate && m_timer.isActive())
2136 void Marquee::timerFired(Timer<Marquee>*)
2138 if (m_layer->renderer()->needsLayout())
2144 m_layer->scrollToXOffset(m_start);
2146 m_layer->scrollToYOffset(m_start);
2150 RenderStyle* s = m_layer->renderer()->style();
2152 int endPoint = m_end;
2153 int range = m_end - m_start;
2158 bool addIncrement = direction() == MUP || direction() == MLEFT;
2159 bool isReversed = s->marqueeBehavior() == MALTERNATE && m_currentLoop % 2;
2160 if (isUnfurlMarquee()) {
2161 isReversed = (!addIncrement && m_currentLoop % 2) || (addIncrement && !(m_currentLoop % 2));
2162 addIncrement = !isReversed;
2165 // We're going in the reverse direction.
2168 if (!isUnfurlMarquee())
2169 addIncrement = !addIncrement;
2171 bool positive = range > 0;
2172 int clientSize = isUnfurlMarquee() ? abs(range) :
2173 (isHorizontal() ? m_layer->renderer()->clientWidth() : m_layer->renderer()->clientHeight());
2174 int increment = max(1, abs(m_layer->renderer()->style()->marqueeIncrement().calcValue(clientSize)));
2175 int currentPos = isUnfurlMarquee() ? m_unfurlPos :
2176 (isHorizontal() ? m_layer->scrollXOffset() : m_layer->scrollYOffset());
2177 newPos = currentPos + (addIncrement ? increment : -increment);
2179 newPos = min(newPos, endPoint);
2181 newPos = max(newPos, endPoint);
2184 if (newPos == endPoint) {
2186 if (m_totalLoops > 0 && m_currentLoop >= m_totalLoops)
2188 else if (s->marqueeBehavior() != MALTERNATE && s->marqueeBehavior() != MUNFURL)
2192 if (isUnfurlMarquee()) {
2193 m_unfurlPos = newPos;
2194 m_layer->renderer()->setChildNeedsLayout(true);
2198 m_layer->scrollToXOffset(newPos);
2200 m_layer->scrollToYOffset(newPos);