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 "render_layer.h"
47 #include "DocumentImpl.h"
48 #include "EventNames.h"
49 #include "FrameView.h"
51 #include "GraphicsContext.h"
52 #include "dom2_eventsimpl.h"
53 #include "html_blockimpl.h"
54 #include "htmlnames.h"
55 #include "render_arena.h"
56 #include "render_canvas.h"
57 #include "render_inline.h"
58 #include "render_theme.h"
59 #include "SelectionController.h"
61 #include <kxmlcore/Vector.h>
62 #include <qscrollbar.h>
68 // These match the numbers we use over in WebKit (WebFrameView.m).
72 #define MIN_INTERSECT_FOR_REVEAL 32
76 using namespace EventNames;
77 using namespace HTMLNames;
79 QScrollBar* RenderLayer::gScrollBar = 0;
82 static bool inRenderLayerDestroy;
85 const RenderLayer::ScrollAlignment RenderLayer::gAlignCenterIfNeeded = { RenderLayer::noScroll, RenderLayer::alignCenter, RenderLayer::alignToClosestEdge };
86 const RenderLayer::ScrollAlignment RenderLayer::gAlignToEdgeIfNeeded = { RenderLayer::noScroll, RenderLayer::alignToClosestEdge, RenderLayer::alignToClosestEdge };
87 const RenderLayer::ScrollAlignment RenderLayer::gAlignCenterAlways = { RenderLayer::alignCenter, RenderLayer::alignCenter, RenderLayer::alignCenter };
88 const RenderLayer::ScrollAlignment RenderLayer::gAlignTopAlways = { RenderLayer::alignTop, RenderLayer::alignTop, RenderLayer::alignTop };
89 const RenderLayer::ScrollAlignment RenderLayer::gAlignBottomAlways = { RenderLayer::alignBottom, RenderLayer::alignBottom, RenderLayer::alignBottom };
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)
126 m_scrollLeftOverflow(0),
134 m_scrollDimensionsDirty(true),
135 m_zOrderListsDirty(true),
136 m_usedTransparency(false),
137 m_inOverflowRelayout(false),
142 RenderLayer::~RenderLayer()
144 // Child layers will be deleted by their corresponding render objects, so
145 // our destructor doesn't have to do anything.
148 delete m_posZOrderList;
149 delete m_negZOrderList;
152 // Make sure we have no lingering clip rects.
153 assert(!m_clipRects);
156 void RenderLayer::computeRepaintRects()
158 // FIXME: Child object could override visibility.
159 if (m_object->style()->visibility() == VISIBLE) {
160 m_object->getAbsoluteRepaintRectIncludingFloats(m_repaintRect, m_fullRepaintRect);
161 m_object->absolutePosition(m_repaintX, m_repaintY);
163 for (RenderLayer* child = firstChild(); child; child = child->nextSibling())
164 child->computeRepaintRects();
167 void RenderLayer::updateLayerPositions(bool doFullRepaint, bool checkForRepaint)
171 checkForRepaint = doFullRepaint = false;
174 updateLayerPosition(); // For relpositioned layers or non-positioned layers,
175 // we need to keep in sync, since we may have shifted relative
176 // to our parent layer.
178 if (m_hBar || m_vBar) {
179 // Need to position the scrollbars.
182 convertToLayerCoords(root(), x, y);
183 IntRect layerBounds = IntRect(x,y,width(),height());
184 positionScrollbars(layerBounds);
187 // FIXME: Child object could override visibility.
188 if (checkForRepaint && (m_object->style()->visibility() == VISIBLE)) {
190 m_object->absolutePosition(x, y);
191 if (x == m_repaintX && y == m_repaintY)
192 m_object->repaintAfterLayoutIfNeeded(m_repaintRect, m_fullRepaintRect);
194 RenderCanvas *c = m_object->canvas();
195 if (c && !c->printingMode()) {
196 c->repaintViewRectangle(m_fullRepaintRect);
197 IntRect newRect, newFullRect;
198 m_object->getAbsoluteRepaintRectIncludingFloats(newRect, newFullRect);
199 if (newRect != m_repaintRect)
200 c->repaintViewRectangle(newFullRect);
205 for (RenderLayer* child = firstChild(); child; child = child->nextSibling())
206 child->updateLayerPositions(doFullRepaint, checkForRepaint);
208 // With all our children positioned, now update our marquee if we need to.
210 m_marquee->updateMarqueePosition();
213 void RenderLayer::updateLayerPosition()
215 // Clear our cached clip rect information.
218 // The canvas is sized to the docWidth/Height over in RenderCanvas::layout, so we
219 // don't need to ever update our layer position here.
220 if (renderer()->isCanvas())
223 int x = m_object->xPos();
224 int y = m_object->yPos() - m_object->borderTopExtra();
226 if (!m_object->isPositioned()) {
227 // We must adjust our position by walking up the render tree looking for the
228 // nearest enclosing object with a layer.
229 RenderObject* curr = m_object->parent();
230 while (curr && !curr->layer()) {
231 if (!curr->isTableRow()) {
232 // Rows and cells share the same coordinate space (that of the section).
233 // Omit them when computing our xpos/ypos.
237 curr = curr->parent();
239 y += curr->borderTopExtra();
240 if (curr->isTableRow()) {
241 // Put ourselves into the row coordinate space.
248 if (m_object->isRelPositioned()) {
249 static_cast<RenderBox*>(m_object)->relativePositionOffset(m_relX, m_relY);
250 x += m_relX; y += m_relY;
253 // Subtract our parent's scroll offset.
254 if (m_object->isPositioned() && enclosingPositionedAncestor()) {
255 RenderLayer* positionedParent = enclosingPositionedAncestor();
257 // For positioned layers, we subtract out the enclosing positioned layer's scroll offset.
258 positionedParent->subtractScrollOffset(x, y);
260 if (m_object->isPositioned() && positionedParent->renderer()->isRelPositioned() &&
261 positionedParent->renderer()->isInlineFlow()) {
262 // When we have an enclosing relpositioned inline, we need to add in the offset of the first line
263 // box from the rest of the content, but only in the cases where we know we're positioned
264 // relative to the inline itself.
265 RenderFlow* flow = static_cast<RenderFlow*>(positionedParent->renderer());
267 if (flow->firstLineBox()) {
268 sx = flow->firstLineBox()->xPos();
269 sy = flow->firstLineBox()->yPos();
272 sx = flow->staticX();
273 sy = flow->staticY();
275 bool isInlineType = m_object->style()->isOriginalDisplayInlineType();
277 if (!m_object->hasStaticX())
280 // This is not terribly intuitive, but we have to match other browsers. Despite being a block display type inside
281 // an inline, we still keep our x locked to the left of the relative positioned inline. Arguably the correct
282 // behavior would be to go flush left to the block that contains the inline, but that isn't what other browsers
284 if (m_object->hasStaticX() && !isInlineType)
285 // Avoid adding in the left border/padding of the containing block twice. Subtract it out.
286 x += sx - (m_object->containingBlock()->borderLeft() + m_object->containingBlock()->paddingLeft());
288 if (!m_object->hasStaticY())
293 parent()->subtractScrollOffset(x, y);
297 setWidth(m_object->width());
298 setHeight(m_object->height() + m_object->borderTopExtra() + m_object->borderBottomExtra());
300 if (!m_object->hasOverflowClip()) {
301 if (m_object->overflowWidth() > m_object->width())
302 setWidth(m_object->overflowWidth());
303 if (m_object->overflowHeight() > m_object->height())
304 setHeight(m_object->overflowHeight());
308 RenderLayer *RenderLayer::stackingContext() const
310 RenderLayer* curr = parent();
311 for ( ; curr && !curr->m_object->isCanvas() && !curr->m_object->isRoot() &&
312 curr->m_object->style()->hasAutoZIndex();
313 curr = curr->parent());
318 RenderLayer::enclosingPositionedAncestor() const
320 RenderLayer* curr = parent();
321 for ( ; curr && !curr->m_object->isCanvas() && !curr->m_object->isRoot() &&
322 !curr->m_object->isPositioned() && !curr->m_object->isRelPositioned();
323 curr = curr->parent());
329 RenderLayer::isTransparent() const
332 if (m_object->node()->namespaceURI() == KSVG::SVGNames::svgNamespaceURI)
335 return m_object->isTransparent();
339 RenderLayer::transparentAncestor()
341 RenderLayer* curr = parent();
342 for ( ; curr && !curr->isTransparent(); curr = curr->parent());
346 static IntRect transparencyClipBox(RenderLayer* l)
348 // FIXME: Although this completely ignores clipping, we ultimately intersect with the
349 // paintDirtyRect, and that should cut down on the amount we have to paint. Still it
350 // would be better to respect clips.
351 IntRect clipRect = l->absoluteBoundingBox();
353 // Note: we don't have to walk z-order lists since transparent elements always establish
354 // a stacking context. This means we can just walk the layer tree directly.
355 for (RenderLayer* curr = l->firstChild(); curr; curr = curr->nextSibling()) {
356 // Transparent children paint in a different transparency layer, and so we exclude them.
357 if (!curr->isTransparent())
358 clipRect.unite(curr->absoluteBoundingBox());
364 void RenderLayer::beginTransparencyLayers(GraphicsContext* p, const IntRect& paintDirtyRect)
366 if (p->paintingDisabled() || (isTransparent() && m_usedTransparency))
369 RenderLayer* ancestor = transparentAncestor();
371 ancestor->beginTransparencyLayers(p, paintDirtyRect);
373 if (isTransparent()) {
374 m_usedTransparency = true;
375 IntRect clipRect = transparencyClipBox(this);
376 clipRect.intersect(paintDirtyRect);
378 p->addClip(clipRect);
379 p->beginTransparencyLayer(renderer()->opacity());
383 void* RenderLayer::operator new(size_t sz, RenderArena* renderArena) throw()
385 return renderArena->allocate(sz);
388 void RenderLayer::operator delete(void* ptr, size_t sz)
390 assert(inRenderLayerDestroy);
392 // Stash size where destroy can find it.
396 void RenderLayer::destroy(RenderArena* renderArena)
399 inRenderLayerDestroy = true;
403 inRenderLayerDestroy = false;
406 // Recover the size left there for us by operator delete and free the memory.
407 renderArena->free(*(size_t *)this, this);
410 void RenderLayer::addChild(RenderLayer *child, RenderLayer* beforeChild)
412 RenderLayer* prevSibling = beforeChild ? beforeChild->previousSibling() : lastChild();
414 child->setPreviousSibling(prevSibling);
415 prevSibling->setNextSibling(child);
418 setFirstChild(child);
421 beforeChild->setPreviousSibling(child);
422 child->setNextSibling(beforeChild);
427 child->setParent(this);
429 // Dirty the z-order list in which we are contained. The stackingContext() can be null in the
430 // case where we're building up generated content layers. This is ok, since the lists will start
431 // off dirty in that case anyway.
432 RenderLayer* stackingContext = child->stackingContext();
434 stackingContext->dirtyZOrderLists();
437 RenderLayer* RenderLayer::removeChild(RenderLayer* oldChild)
440 if (oldChild->previousSibling())
441 oldChild->previousSibling()->setNextSibling(oldChild->nextSibling());
442 if (oldChild->nextSibling())
443 oldChild->nextSibling()->setPreviousSibling(oldChild->previousSibling());
445 if (m_first == oldChild)
446 m_first = oldChild->nextSibling();
447 if (m_last == oldChild)
448 m_last = oldChild->previousSibling();
450 // Dirty the z-order list in which we are contained. When called via the
451 // reattachment process in removeOnlyThisLayer, the layer may already be disconnected
452 // from the main layer tree, so we need to null-check the |stackingContext| value.
453 RenderLayer* stackingContext = oldChild->stackingContext();
455 oldChild->stackingContext()->dirtyZOrderLists();
457 oldChild->setPreviousSibling(0);
458 oldChild->setNextSibling(0);
459 oldChild->setParent(0);
464 void RenderLayer::removeOnlyThisLayer()
469 // Dirty the clip rects.
472 // Remove us from the parent.
473 RenderLayer* parent = m_parent;
474 RenderLayer* nextSib = nextSibling();
475 parent->removeChild(this);
477 // Now walk our kids and reattach them to our parent.
478 RenderLayer* current = m_first;
480 RenderLayer* next = current->nextSibling();
481 removeChild(current);
482 parent->addChild(current, nextSib);
483 current->updateLayerPositions();
487 destroy(renderer()->renderArena());
490 void RenderLayer::insertOnlyThisLayer()
492 if (!m_parent && renderer()->parent()) {
493 // We need to connect ourselves when our renderer() has a parent.
494 // Find our enclosingLayer and add ourselves.
495 RenderLayer* parentLayer = renderer()->parent()->enclosingLayer();
497 parentLayer->addChild(this,
498 renderer()->parent()->findNextLayer(parentLayer, renderer()));
501 // Remove all descendant layers from the hierarchy and add them to the new position.
502 for (RenderObject* curr = renderer()->firstChild(); curr; curr = curr->nextSibling())
503 curr->moveLayers(m_parent, this);
505 // Clear out all the clip rects.
510 RenderLayer::convertToLayerCoords(const RenderLayer* ancestorLayer, int& x, int& y) const
512 if (ancestorLayer == this)
515 if (m_object->style()->position() == FixedPosition) {
516 // Add in the offset of the view. We can obtain this by calling
517 // absolutePosition() on the RenderCanvas.
519 m_object->absolutePosition(xOff, yOff, true);
525 RenderLayer* parentLayer;
526 if (m_object->style()->position() == AbsolutePosition)
527 parentLayer = enclosingPositionedAncestor();
529 parentLayer = parent();
531 if (!parentLayer) return;
533 parentLayer->convertToLayerCoords(ancestorLayer, x, y);
540 RenderLayer::scrollOffset(int& x, int& y)
542 x += scrollXOffset() + m_scrollLeftOverflow;
543 y += scrollYOffset();
547 RenderLayer::subtractScrollOffset(int& x, int& y)
549 x -= scrollXOffset() + m_scrollLeftOverflow;
550 y -= scrollYOffset();
553 void RenderLayer::scrollToOffset(int x, int y, bool updateScrollbars, bool repaint)
555 if (renderer()->style()->overflow() != OMARQUEE) {
559 // Call the scrollWidth/Height functions so that the dimensions will be computed if they need
560 // to be (for overflow:hidden blocks).
561 int maxX = scrollWidth() - m_object->clientWidth();
562 int maxY = scrollHeight() - m_object->clientHeight();
564 if (x > maxX) x = maxX;
565 if (y > maxY) y = maxY;
568 // FIXME: Eventually, we will want to perform a blit. For now never
569 // blit, since the check for blitting is going to be very
570 // complicated (since it will involve testing whether our layer
571 // is either occluded by another layer or clipped by an enclosing
572 // layer or contains fixed backgrounds, etc.).
573 m_scrollX = x - m_scrollOriginX;
576 // Update the positions of our child layers.
577 for (RenderLayer* child = firstChild(); child; child = child->nextSibling())
578 child->updateLayerPositions(false, false);
580 RenderCanvas *canvas = renderer()->canvas();
583 // Update dashboard regions, scrolling may change the clip of a
584 // particular region.
585 canvas->view()->updateDashboardRegions();
588 m_object->canvas()->updateWidgetPositions();
591 // Just schedule a full repaint of our object.
595 if (updateScrollbars) {
597 m_hBar->setValue(scrollXOffset());
599 m_vBar->setValue(m_scrollY);
602 // Fire the scroll DOM event.
603 m_object->element()->dispatchHTMLEvent(scrollEvent, true, false);
606 void RenderLayer::scrollRectToVisible(const IntRect &rect, const ScrollAlignment& alignX, const ScrollAlignment& alignY)
608 RenderLayer* parentLayer = 0;
609 IntRect newRect = rect;
610 int xOffset = 0, yOffset = 0;
612 if (m_object->hasOverflowClip()) {
613 IntRect layerBounds = IntRect(xPos() + scrollXOffset(), yPos() + scrollYOffset(),
614 width() - verticalScrollbarWidth(), height() - horizontalScrollbarHeight());
615 IntRect exposeRect = IntRect(rect.x() + scrollXOffset(), rect.y() + scrollYOffset(), rect.width(), rect.height());
616 IntRect r = getRectToExpose(layerBounds, exposeRect, alignX, alignY);
618 xOffset = r.x() - xPos();
619 yOffset = r.y() - yPos();
620 // Adjust offsets if they're outside of the allowable range.
621 xOffset = kMax(0, kMin(scrollWidth() - width(), xOffset));
622 yOffset = kMax(0, kMin(scrollHeight() - height(), yOffset));
624 if (xOffset != scrollXOffset() || yOffset != scrollYOffset()) {
625 int diffX = scrollXOffset();
626 int diffY = scrollYOffset();
627 scrollToOffset(xOffset, yOffset);
628 // FIXME: At this point a scroll event fired, which could have deleted this layer.
629 // Need to handle this case.
630 diffX = scrollXOffset() - diffX;
631 diffY = scrollYOffset() - diffY;
632 newRect.setX(rect.x() - diffX);
633 newRect.setY(rect.y() - diffY);
636 if (m_object->parent())
637 parentLayer = m_object->parent()->enclosingLayer();
639 FrameView* view = m_object->document()->view();
641 IntRect viewRect = IntRect(view->scrollXOffset(), view->scrollYOffset(), view->visibleWidth(), view->visibleHeight());
642 IntRect r = getRectToExpose(viewRect, rect, alignX, alignY);
646 // Adjust offsets if they're outside of the allowable range.
647 xOffset = kMax(0, kMin(view->contentsWidth(), xOffset));
648 yOffset = kMax(0, kMin(view->contentsHeight(), yOffset));
650 if (m_object->document() && m_object->document()->ownerElement() && m_object->document()->ownerElement()->renderer()) {
651 view->setContentsPos(xOffset, yOffset);
652 parentLayer = m_object->document()->ownerElement()->renderer()->enclosingLayer();
653 newRect.setX(rect.x() - view->contentsX() + view->x());
654 newRect.setY(rect.y() - view->contentsY() + view->y());
656 // If this is the outermost view that RenderLayer needs to scroll, then we should scroll the view recursively
657 // Other apps, like Mail, rely on this feature.
658 view->scrollPointRecursively(xOffset, yOffset);
664 parentLayer->scrollRectToVisible(newRect, alignX, alignY);
667 IntRect RenderLayer::getRectToExpose(const IntRect &visibleRect, const IntRect &exposeRect, const ScrollAlignment& alignX, const ScrollAlignment& alignY)
669 // Determine the appropriate X behavior.
670 ScrollBehavior scrollX;
671 IntRect exposeRectX(exposeRect.x(), visibleRect.y(), exposeRect.width(), visibleRect.height());
672 int intersectWidth = intersection(visibleRect, exposeRectX).width();
673 if (intersectWidth == exposeRect.width() || intersectWidth >= MIN_INTERSECT_FOR_REVEAL)
674 // If the rectangle is fully visible, use the specified visible behavior.
675 // If the rectangle is partially visible, but over a certain threshold,
676 // then treat it as fully visible to avoid unnecessary horizontal scrolling
677 scrollX = getVisibleBehavior(alignX);
678 else if (intersectWidth == visibleRect.width()) {
679 // If the rect is bigger than the visible area, don't bother trying to center. Other alignments will work.
680 scrollX = getVisibleBehavior(alignX);
681 if (scrollX == alignCenter)
683 } else if (intersectWidth > 0)
684 // If the rectangle is partially visible, but not above the minimum threshold, use the specified partial behavior
685 scrollX = getPartialBehavior(alignX);
687 scrollX = getHiddenBehavior(alignX);
688 // If we're trying to align to the closest edge, and the exposeRect is further right
689 // than the visibleRect, and not bigger than the visible area, then align with the right.
690 if (scrollX == alignToClosestEdge && exposeRect.right() > visibleRect.right() && exposeRect.width() < visibleRect.width())
691 scrollX = alignRight;
693 // Given the X behavior, compute the X coordinate.
695 if (scrollX == noScroll)
697 else if (scrollX == alignRight)
698 x = exposeRect.right() - visibleRect.width();
699 else if (scrollX == alignCenter)
700 x = exposeRect.x() + (exposeRect.width() - visibleRect.width()) / 2;
704 // Determine the appropriate Y behavior.
705 ScrollBehavior scrollY;
706 IntRect exposeRectY(visibleRect.x(), exposeRect.y(), visibleRect.width(), exposeRect.height());
707 int intersectHeight = intersection(visibleRect, exposeRectY).height();
708 if (intersectHeight == exposeRect.height())
709 // If the rectangle is fully visible, use the specified visible behavior.
710 scrollY = getVisibleBehavior(alignY);
711 else if (intersectHeight == visibleRect.height()) {
712 // If the rect is bigger than the visible area, don't bother trying to center. Other alignments will work.
713 scrollY = getVisibleBehavior(alignY);
714 if (scrollY == alignCenter)
716 } else if (intersectHeight > 0)
717 // If the rectangle is partially visible, use the specified partial behavior
718 scrollY = getPartialBehavior(alignY);
720 scrollY = getHiddenBehavior(alignY);
721 // If we're trying to align to the closest edge, and the exposeRect is further down
722 // than the visibleRect, and not bigger than the visible area, then align with the bottom.
723 if (scrollY == alignToClosestEdge && exposeRect.bottom() > visibleRect.bottom() && exposeRect.height() < visibleRect.height())
724 scrollY = alignBottom;
726 // Given the Y behavior, compute the Y coordinate.
728 if (scrollY == noScroll)
730 else if (scrollY == alignBottom)
731 y = exposeRect.bottom() - visibleRect.height();
732 else if (scrollY == alignCenter)
733 y = exposeRect.y() + (exposeRect.height() - visibleRect.height()) / 2;
737 return IntRect(IntPoint(x, y), visibleRect.size());
740 void RenderLayer::autoscroll()
742 int xOffset = scrollXOffset();
743 int yOffset = scrollYOffset();
745 // Get the rectangle for the extent of the selection
746 SelectionController sel = renderer()->document()->frame()->selection();
747 IntRect extentRect = SelectionController(sel.extent(), sel.affinity()).caretRect();
748 extentRect.move(xOffset, yOffset);
750 IntRect bounds = IntRect(xPos() + xOffset, yPos() + yOffset, width() - verticalScrollbarWidth(), height() - horizontalScrollbarHeight());
752 // Calculate how much the layer should scroll horizontally.
754 if (extentRect.right() > bounds.right())
755 diffX = extentRect.right() - bounds.right();
756 else if (extentRect.x() < bounds.x())
757 diffX = extentRect.x() - bounds.x();
759 // Calculate how much the layer should scroll vertically.
761 if (extentRect.bottom() > bounds.bottom())
762 diffY = extentRect.bottom() - bounds.bottom();
763 else if (extentRect.y() < bounds.y())
764 diffY = extentRect.y() - bounds.y();
766 scrollToOffset(xOffset + diffX, yOffset + diffY);
769 bool RenderLayer::shouldAutoscroll()
771 if (renderer()->hasOverflowClip() && (m_object->style()->overflow() != OHIDDEN || renderer()->node()->isContentEditable()))
776 void RenderLayer::valueChanged(Widget*)
778 // Update scroll position from scroll bars.
780 bool needUpdate = false;
781 int newX = scrollXOffset();
782 int newY = m_scrollY;
785 newX = m_hBar->value();
786 if (newX != scrollXOffset())
791 newY = m_vBar->value();
792 if (newY != m_scrollY)
797 scrollToOffset(newX, newY, false);
801 RenderLayer::setHasHorizontalScrollbar(bool hasScrollbar)
803 if (hasScrollbar && !m_hBar) {
804 FrameView* scrollView = m_object->element()->getDocument()->view();
805 m_hBar = new QScrollBar(HorizontalScrollBar);
806 m_hBar->setClient(this);
807 scrollView->addChild(m_hBar, 0, -50000);
808 } else if (!hasScrollbar && m_hBar) {
809 FrameView* scrollView = m_object->element()->getDocument()->view();
810 scrollView->removeChild(m_hBar);
817 RenderLayer::setHasVerticalScrollbar(bool hasScrollbar)
819 if (hasScrollbar && !m_vBar) {
820 FrameView* scrollView = m_object->element()->getDocument()->view();
821 m_vBar = new QScrollBar(VerticalScrollBar);
822 m_vBar->setClient(this);
823 scrollView->addChild(m_vBar, 0, -50000);
824 } else if (!hasScrollbar && m_vBar) {
825 FrameView* scrollView = m_object->element()->getDocument()->view();
826 scrollView->removeChild(m_vBar);
833 RenderLayer::verticalScrollbarWidth()
838 return m_vBar->width();
842 RenderLayer::horizontalScrollbarHeight()
847 return m_hBar->height();
851 RenderLayer::moveScrollbarsAside()
854 m_hBar->move(0, -50000);
856 m_vBar->move(0, -50000);
860 RenderLayer::positionScrollbars(const IntRect& absBounds)
863 m_vBar->move(absBounds.right() - m_object->borderRight() - m_vBar->width(),
864 absBounds.y() + m_object->borderTop());
865 m_vBar->resize(m_vBar->width(),
866 absBounds.height() - (m_object->borderTop() + m_object->borderBottom()) - (m_hBar ? m_hBar->height() - 1 : 0));
870 m_hBar->move(absBounds.x() + m_object->borderLeft(),
871 absBounds.bottom() - m_object->borderBottom() - m_hBar->height());
872 m_hBar->resize(absBounds.width() - (m_object->borderLeft() + m_object->borderRight()) - (m_vBar ? m_vBar->width() - 1 : 0),
877 int RenderLayer::scrollWidth()
879 if (m_scrollDimensionsDirty)
880 computeScrollDimensions();
881 return m_scrollWidth;
884 int RenderLayer::scrollHeight()
886 if (m_scrollDimensionsDirty)
887 computeScrollDimensions();
888 return m_scrollHeight;
891 void RenderLayer::computeScrollDimensions(bool* needHBar, bool* needVBar)
893 m_scrollDimensionsDirty = false;
895 bool ltr = m_object->style()->direction() == LTR;
897 int clientWidth = m_object->clientWidth();
898 int clientHeight = m_object->clientHeight();
900 m_scrollLeftOverflow = ltr ? 0 : kMin(0, m_object->leftmostPosition(true, false) - m_object->borderLeft());
903 m_object->rightmostPosition(true, false) - m_object->borderLeft() :
904 clientWidth - m_scrollLeftOverflow;
905 int bottomPos = m_object->lowestPosition(true, false) - m_object->borderTop();
907 m_scrollWidth = kMax(rightPos, clientWidth);
908 m_scrollHeight = kMax(bottomPos, clientHeight);
910 m_scrollOriginX = ltr ? 0 : m_scrollWidth - clientWidth;
913 *needHBar = rightPos > clientWidth;
915 *needVBar = bottomPos > clientHeight;
919 RenderLayer::updateScrollInfoAfterLayout()
921 m_scrollDimensionsDirty = true;
922 if (m_object->style()->overflow() == OHIDDEN)
923 return; // All we had to do was dirty.
925 bool needHorizontalBar, needVerticalBar;
926 computeScrollDimensions(&needHorizontalBar, &needVerticalBar);
928 if (m_object->style()->overflow() != OMARQUEE) {
929 // Layout may cause us to be in an invalid scroll position. In this case we need
930 // to pull our scroll offsets back to the max (or push them up to the min).
931 int newX = kMax(0, kMin(scrollXOffset(), scrollWidth() - m_object->clientWidth()));
932 int newY = kMax(0, kMin(m_scrollY, scrollHeight() - m_object->clientHeight()));
933 if (newX != scrollXOffset() || newY != m_scrollY)
934 scrollToOffset(newX, newY);
935 // FIXME: At this point a scroll event fired, which could have deleted this layer.
936 // Need to handle this case.
939 bool haveHorizontalBar = m_hBar;
940 bool haveVerticalBar = m_vBar;
942 // overflow:scroll should just enable/disable.
943 if (m_object->style()->overflow() == OSCROLL) {
944 m_hBar->setEnabled(needHorizontalBar);
945 m_vBar->setEnabled(needVerticalBar);
948 // overflow:auto may need to lay out again if scrollbars got added/removed.
949 bool scrollbarsChanged = (m_object->hasAutoScrollbars()) &&
950 (haveHorizontalBar != needHorizontalBar || haveVerticalBar != needVerticalBar);
951 if (scrollbarsChanged) {
952 setHasHorizontalScrollbar(needHorizontalBar);
953 setHasVerticalScrollbar(needVerticalBar);
956 // Force an update since we know the scrollbars have changed things.
957 if (m_object->document()->hasDashboardRegions())
958 m_object->document()->setDashboardRegionsDirty(true);
963 if (m_object->style()->overflow() == OAUTO) {
964 if (!m_inOverflowRelayout) {
965 // Our proprietary overflow: overlay value doesn't trigger a layout.
966 m_inOverflowRelayout = true;
967 m_object->setNeedsLayout(true);
968 if (m_object->isRenderBlock())
969 static_cast<RenderBlock*>(m_object)->layoutBlock(true);
972 m_inOverflowRelayout = false;
977 // Set up the range (and page step/line step).
979 int clientWidth = m_object->clientWidth();
980 int pageStep = (clientWidth-PAGE_KEEP);
981 if (pageStep < 0) pageStep = clientWidth;
982 m_hBar->setSteps(LINE_STEP, pageStep);
983 m_hBar->setKnobProportion(clientWidth, m_scrollWidth);
984 m_hBar->setValue(scrollXOffset());
987 int clientHeight = m_object->clientHeight();
988 int pageStep = (clientHeight-PAGE_KEEP);
989 if (pageStep < 0) pageStep = clientHeight;
990 m_vBar->setSteps(LINE_STEP, pageStep);
991 m_vBar->setKnobProportion(clientHeight, m_scrollHeight);
992 m_object->repaintRectangle(IntRect(m_object->borderLeft() + m_object->clientWidth(),
993 m_object->borderTop(), verticalScrollbarWidth(),
994 m_object->height() - m_object->borderTop() - m_object->borderBottom()));
998 // Force an update since we know the scrollbars have changed things.
999 if (m_object->document()->hasDashboardRegions())
1000 m_object->document()->setDashboardRegionsDirty(true);
1003 m_object->repaint();
1007 RenderLayer::paintScrollbars(GraphicsContext* p, const IntRect& damageRect)
1009 // Move the widgets if necessary. We normally move and resize widgets during layout, but sometimes
1010 // widgets can move without layout occurring (most notably when you scroll a document that
1011 // contains fixed positioned elements).
1012 if (m_hBar || m_vBar) {
1015 convertToLayerCoords(root(), x, y);
1016 IntRect layerBounds = IntRect(x, y, width(), height());
1017 positionScrollbars(layerBounds);
1020 // Now that we're sure the scrollbars are in the right place, paint them.
1022 m_hBar->paint(p, damageRect);
1024 m_vBar->paint(p, damageRect);
1027 bool RenderLayer::scroll(KWQScrollDirection direction, KWQScrollGranularity granularity, float multiplier)
1029 bool didHorizontalScroll = false;
1030 bool didVerticalScroll = false;
1033 if (granularity == KWQScrollDocument) {
1034 // Special-case for the KWQScrollDocument granularity. A document scroll can only be up
1035 // or down and in both cases the horizontal bar goes all the way to the left.
1036 didHorizontalScroll = m_hBar->scroll(KWQScrollLeft, KWQScrollDocument, multiplier);
1038 didHorizontalScroll = m_hBar->scroll(direction, granularity, multiplier);
1042 didVerticalScroll = m_vBar->scroll(direction, granularity, multiplier);
1045 return (didHorizontalScroll || didVerticalScroll);
1049 RenderLayer::paint(GraphicsContext* p, const IntRect& damageRect, bool selectionOnly, RenderObject *paintingRoot)
1051 paintLayer(this, p, damageRect, false, selectionOnly, paintingRoot);
1054 static void setClip(GraphicsContext* p, const IntRect& paintDirtyRect, const IntRect& clipRect)
1056 if (paintDirtyRect == clipRect)
1059 p->addClip(clipRect);
1062 static void restoreClip(GraphicsContext* p, const IntRect& paintDirtyRect, const IntRect& clipRect)
1064 if (paintDirtyRect == clipRect)
1070 RenderLayer::paintLayer(RenderLayer* rootLayer, GraphicsContext* p,
1071 const IntRect& paintDirtyRect, bool haveTransparency, bool selectionOnly,
1072 RenderObject *paintingRoot)
1074 // Calculate the clip rects we should use.
1075 IntRect layerBounds, damageRect, clipRectToApply, outlineRect;
1076 calculateRects(rootLayer, paintDirtyRect, layerBounds, damageRect, clipRectToApply, outlineRect);
1077 int x = layerBounds.x();
1078 int y = layerBounds.y();
1080 // Ensure our z-order lists are up-to-date.
1081 updateZOrderLists();
1083 if (isTransparent())
1084 haveTransparency = true;
1086 // If this layer's renderer is a child of the paintingRoot, we render unconditionally, which
1087 // is done by passing a nil paintingRoot down to our renderer (as if no paintingRoot was ever set).
1088 // Else, our renderer tree may or may not contain the painting root, so we pass that root along
1089 // so it will be tested against as we decend through the renderers.
1090 RenderObject *paintingRootForRenderer = 0;
1091 if (paintingRoot && !m_object->hasAncestor(paintingRoot))
1092 paintingRootForRenderer = paintingRoot;
1094 // We want to paint our layer, but only if we intersect the damage rect.
1095 bool shouldPaint = intersectsDamageRect(layerBounds, damageRect);
1096 if (shouldPaint && !selectionOnly && !damageRect.isEmpty()) {
1097 // Begin transparency layers lazily now that we know we have to paint something.
1098 if (haveTransparency)
1099 beginTransparencyLayers(p, paintDirtyRect);
1101 // Paint our background first, before painting any child layers.
1102 // Establish the clip used to paint our background.
1103 setClip(p, paintDirtyRect, damageRect);
1105 // Paint the background.
1106 RenderObject::PaintInfo info(p, damageRect, PaintActionBlockBackground, paintingRootForRenderer);
1107 renderer()->paint(info, x - renderer()->xPos(), y - renderer()->yPos() + renderer()->borderTopExtra());
1108 // Our scrollbar widgets paint exactly when we tell them to, so that they work properly with
1109 // z-index. We paint after we painted the background/border, so that the scrollbars will
1110 // sit above the background/border.
1111 paintScrollbars(p, damageRect);
1112 // Restore the clip.
1113 restoreClip(p, paintDirtyRect, damageRect);
1116 // Now walk the sorted list of children with negative z-indices.
1117 if (m_negZOrderList)
1118 for (Vector<RenderLayer*>::iterator it = m_negZOrderList->begin(); it != m_negZOrderList->end(); ++it)
1119 it[0]->paintLayer(rootLayer, p, paintDirtyRect, haveTransparency, selectionOnly, paintingRoot);
1121 // Now establish the appropriate clip and paint our child RenderObjects.
1122 if (shouldPaint && !clipRectToApply.isEmpty()) {
1123 // Begin transparency layers lazily now that we know we have to paint something.
1124 if (haveTransparency)
1125 beginTransparencyLayers(p, paintDirtyRect);
1127 // Set up the clip used when painting our children.
1128 setClip(p, paintDirtyRect, clipRectToApply);
1130 int tx = x - renderer()->xPos();
1131 int ty = y - renderer()->yPos() + renderer()->borderTopExtra();
1132 RenderObject::PaintInfo info(p, clipRectToApply,
1133 selectionOnly ? PaintActionSelection : PaintActionChildBlockBackgrounds,
1134 paintingRootForRenderer);
1135 renderer()->paint(info, tx, ty);
1136 if (!selectionOnly) {
1137 info.phase = PaintActionFloat;
1138 renderer()->paint(info, tx, ty);
1139 info.phase = PaintActionForeground;
1140 renderer()->paint(info, tx, ty);
1143 // Now restore our clip.
1144 restoreClip(p, paintDirtyRect, clipRectToApply);
1146 setClip(p, paintDirtyRect, outlineRect);
1147 info.phase = PaintActionOutline;
1148 renderer()->paint(info, tx, ty);
1149 restoreClip(p, paintDirtyRect, outlineRect);
1152 // Now walk the sorted list of children with positive z-indices.
1153 if (m_posZOrderList)
1154 for (Vector<RenderLayer*>::iterator it = m_posZOrderList->begin(); it != m_posZOrderList->end(); ++it)
1155 it[0]->paintLayer(rootLayer, p, paintDirtyRect, haveTransparency, selectionOnly, paintingRoot);
1157 // End our transparency layer
1158 if (isTransparent() && m_usedTransparency) {
1159 p->endTransparencyLayer();
1161 m_usedTransparency = false;
1166 RenderLayer::hitTest(RenderObject::NodeInfo& info, int x, int y)
1170 renderer()->document()->updateLayout();
1172 IntRect damageRect(m_x, m_y, width(), height());
1173 RenderLayer* insideLayer = hitTestLayer(this, info, x, y, damageRect);
1175 // Now determine if the result is inside an anchor; make sure an image map wins if
1176 // it already set URLElement and only use the innermost.
1177 NodeImpl* node = info.innerNode();
1179 if (node->isLink() && !info.URLElement())
1180 info.setURLElement(static_cast<ElementImpl*>(node));
1181 node = node->parentNode();
1184 // Next set up the correct :hover/:active state along the new chain.
1185 updateHoverActiveState(info);
1187 // Now return whether we were inside this layer (this will always be true for the root
1193 RenderLayer::hitTestLayer(RenderLayer* rootLayer, RenderObject::NodeInfo& info,
1194 int xMousePos, int yMousePos, const IntRect& hitTestRect)
1196 // Calculate the clip rects we should use.
1197 IntRect layerBounds, bgRect, fgRect, outlineRect;
1198 calculateRects(rootLayer, hitTestRect, layerBounds, bgRect, fgRect, outlineRect);
1200 // Ensure our z-order lists are up-to-date.
1201 updateZOrderLists();
1203 // This variable tracks which layer the mouse ends up being inside. The minute we find an insideLayer,
1204 // we are done and can return it.
1205 RenderLayer* insideLayer = 0;
1207 // Begin by walking our list of positive layers from highest z-index down to the lowest
1209 if (m_posZOrderList) {
1210 for (int i = m_posZOrderList->size() - 1; i >= 0; --i) {
1211 insideLayer = m_posZOrderList->at(i)->hitTestLayer(rootLayer, info, xMousePos, yMousePos, hitTestRect);
1217 // Next we want to see if the mouse pos is inside the child RenderObjects of the layer.
1218 if (containsPoint(xMousePos, yMousePos, fgRect) &&
1219 renderer()->hitTest(info, xMousePos, yMousePos,
1220 layerBounds.x() - renderer()->xPos(),
1221 layerBounds.y() - renderer()->yPos() + m_object->borderTopExtra(), HitTestDescendants)) {
1222 // for positioned generated content, we might still not have a
1223 // node by the time we get to the layer level, since none of
1224 // the content in the layer has an element. So just walk up
1226 if (!info.innerNode()) {
1227 for (RenderObject *r = renderer(); r != NULL; r = r->parent()) {
1229 info.setInnerNode(r->element());
1235 if (!info.innerNonSharedNode()) {
1236 for (RenderObject *r = renderer(); r != NULL; r = r->parent()) {
1238 info.setInnerNonSharedNode(r->element());
1246 // Now check our negative z-index children.
1247 if (m_negZOrderList) {
1248 for (int i = m_negZOrderList->size() - 1; i >= 0; --i) {
1249 insideLayer = m_negZOrderList->at(i)->hitTestLayer(rootLayer, info, xMousePos, yMousePos, hitTestRect);
1255 // Next we want to see if the mouse pos is inside this layer but not any of its children.
1256 if (containsPoint(xMousePos, yMousePos, bgRect) &&
1257 renderer()->hitTest(info, xMousePos, yMousePos,
1258 layerBounds.x() - renderer()->xPos(),
1259 layerBounds.y() - renderer()->yPos() + m_object->borderTopExtra(),
1267 void RenderLayer::calculateClipRects(const RenderLayer* rootLayer)
1270 return; // We have the correct cached value.
1273 // The root layer's clip rect is always just its dimensions.
1274 m_clipRects = new (m_object->renderArena()) ClipRects(IntRect(0,0,width(),height()));
1279 // Ensure that our parent's clip has been calculated so that we can examine the values.
1280 parent()->calculateClipRects(rootLayer);
1282 // Set up our three rects to initially match the parent rects.
1283 IntRect posClipRect(parent()->clipRects()->posClipRect());
1284 IntRect overflowClipRect(parent()->clipRects()->overflowClipRect());
1285 IntRect fixedClipRect(parent()->clipRects()->fixedClipRect());
1287 // A fixed object is essentially the root of its containing block hierarchy, so when
1288 // we encounter such an object, we reset our clip rects to the fixedClipRect.
1289 if (m_object->style()->position() == FixedPosition) {
1290 posClipRect = fixedClipRect;
1291 overflowClipRect = fixedClipRect;
1293 else if (m_object->style()->position() == RelativePosition)
1294 posClipRect = overflowClipRect;
1295 else if (m_object->style()->position() == AbsolutePosition)
1296 overflowClipRect = posClipRect;
1298 // Update the clip rects that will be passed to child layers.
1299 if (m_object->hasOverflowClip() || m_object->hasClip()) {
1300 // This layer establishes a clip of some kind.
1303 convertToLayerCoords(rootLayer, x, y);
1305 if (m_object->hasOverflowClip()) {
1306 IntRect newOverflowClip = m_object->getOverflowClipRect(x,y);
1307 overflowClipRect.intersect(newOverflowClip);
1308 if (m_object->isPositioned() || m_object->isRelPositioned())
1309 posClipRect.intersect(newOverflowClip);
1311 if (m_object->hasClip()) {
1312 IntRect newPosClip = m_object->getClipRect(x,y);
1313 posClipRect.intersect(newPosClip);
1314 overflowClipRect.intersect(newPosClip);
1315 fixedClipRect.intersect(newPosClip);
1319 // If our clip rects match our parent's clip, then we can just share its data structure and
1321 if (posClipRect == parent()->clipRects()->posClipRect() &&
1322 overflowClipRect == parent()->clipRects()->overflowClipRect() &&
1323 fixedClipRect == parent()->clipRects()->fixedClipRect())
1324 m_clipRects = parent()->clipRects();
1326 m_clipRects = new (m_object->renderArena()) ClipRects(overflowClipRect, fixedClipRect, posClipRect);
1330 void RenderLayer::calculateRects(const RenderLayer* rootLayer, const IntRect& paintDirtyRect, IntRect& layerBounds,
1331 IntRect& backgroundRect, IntRect& foregroundRect, IntRect& outlineRect)
1334 parent()->calculateClipRects(rootLayer);
1335 backgroundRect = m_object->style()->position() == FixedPosition ? parent()->clipRects()->fixedClipRect() :
1336 (m_object->isPositioned() ? parent()->clipRects()->posClipRect() :
1337 parent()->clipRects()->overflowClipRect());
1338 backgroundRect.intersect(paintDirtyRect);
1340 backgroundRect = paintDirtyRect;
1341 foregroundRect = backgroundRect;
1342 outlineRect = backgroundRect;
1346 convertToLayerCoords(rootLayer, x, y);
1347 layerBounds = IntRect(x,y,width(),height());
1349 // Update the clip rects that will be passed to child layers.
1350 if (m_object->hasOverflowClip() || m_object->hasClip()) {
1351 // This layer establishes a clip of some kind.
1352 if (m_object->hasOverflowClip())
1353 foregroundRect.intersect(m_object->getOverflowClipRect(x,y));
1354 if (m_object->hasClip()) {
1355 // Clip applies to *us* as well, so go ahead and update the damageRect.
1356 IntRect newPosClip = m_object->getClipRect(x,y);
1357 backgroundRect.intersect(newPosClip);
1358 foregroundRect.intersect(newPosClip);
1359 outlineRect.intersect(newPosClip);
1362 // If we establish a clip at all, then go ahead and make sure our background
1363 // rect is intersected with our layer's bounds.
1364 backgroundRect.intersect(layerBounds);
1368 bool RenderLayer::intersectsDamageRect(const IntRect& layerBounds, const IntRect& damageRect) const
1370 // Always examine the canvas and the root.
1371 if (renderer()->isCanvas() || renderer()->isRoot())
1374 // If we aren't an inline flow, and our layer bounds do intersect the damage rect, then we
1375 // can go ahead and return true.
1376 if (!renderer()->isInlineFlow() && layerBounds.intersects(damageRect))
1379 // Otherwise we need to compute the bounding box of this single layer and see if it intersects
1381 return absoluteBoundingBox().intersects(damageRect);
1384 IntRect RenderLayer::absoluteBoundingBox() const
1386 // There are three special cases we need to consider.
1387 // (1) Inline Flows. For inline flows we will create a bounding box that fully encompasses all of the lines occupied by the
1388 // inline. In other words, if some <span> wraps to three lines, we'll create a bounding box that fully encloses the root
1389 // line boxes of all three lines (including overflow on those lines).
1390 // (2) Left/Top Overflow. The width/height of layers already includes right/bottom overflow. However, in the case of left/top
1391 // overflow, we have to create a bounding box that will extend to include this overflow.
1392 // (3) Floats. When a layer has overhanging floats that it paints, we need to make sure to include these overhanging floats
1393 // as part of our bounding box. We do this because we are the responsible layer for both hit testing and painting those
1396 if (renderer()->isInlineFlow()) {
1397 // Go from our first line box to our last line box.
1398 RenderInline* inlineFlow = static_cast<RenderInline*>(renderer());
1399 InlineFlowBox* firstBox = inlineFlow->firstLineBox();
1402 int top = firstBox->root()->topOverflow();
1403 int bottom = inlineFlow->lastLineBox()->root()->bottomOverflow();
1404 int left = firstBox->xPos();
1405 for (InlineRunBox* curr = firstBox->nextLineBox(); curr; curr = curr->nextLineBox())
1406 left = kMin(left, curr->xPos());
1407 result = IntRect(m_x + left, m_y + (top - renderer()->yPos()), width(), bottom - top);
1408 } else if (renderer()->isTableRow()) {
1409 // Our bounding box is just the union of all of our cells' border/overflow rects.
1410 for (RenderObject* child = renderer()->firstChild(); child; child = child->nextSibling()) {
1411 if (child->isTableCell()) {
1412 IntRect bbox = child->borderBox();
1413 bbox.move(0, child->borderTopExtra());
1415 IntRect overflowRect = renderer()->overflowRect(false);
1416 overflowRect.move(0, child->borderTopExtra());
1417 if (bbox != overflowRect)
1418 result.unite(overflowRect);
1421 result.move(m_x, m_y);
1423 IntRect bbox = renderer()->borderBox();
1425 IntRect overflowRect = renderer()->overflowRect(false);
1426 if (bbox != overflowRect)
1427 result.unite(overflowRect);
1428 IntRect floatRect = renderer()->floatRect();
1429 if (bbox != floatRect)
1430 result.unite(floatRect);
1432 // We have to adjust the x/y of this result so that it is in the coordinate space of the layer.
1433 // We also have to add in borderTopExtra here, since borderBox(), in order to play well with methods like
1434 // floatRect that deal with child content, uses an origin of (0,0) that is at the child content box (so
1435 // border box returns a y coord of -borderTopExtra(). The layer, however, uses the outer box. This is all
1436 // really confusing.
1437 result.move(m_x, m_y + renderer()->borderTopExtra());
1440 // Convert the bounding box to an absolute position. We can do this easily by looking at the delta
1441 // between the bounding box's xpos and our layer's xpos and then applying that to the absolute layerBounds
1443 int absX = 0, absY = 0;
1444 convertToLayerCoords(root(), absX, absY);
1445 result.move(absX - m_x, absY - m_y);
1449 bool RenderLayer::containsPoint(int x, int y, const IntRect& damageRect) const
1451 // Always returning true for the root object to ensure that mouse events occurring
1452 // outside the window (when dragging) always target some node.
1453 return renderer()->isRoot() || damageRect.contains(x, y);
1456 void RenderLayer::clearClipRects()
1463 for (RenderLayer* l = firstChild(); l; l = l->nextSibling())
1464 l->clearClipRects();
1467 void RenderLayer::clearClipRect()
1470 m_clipRects->deref(m_object->renderArena());
1475 static RenderObject* commonAncestor(RenderObject* obj1, RenderObject* obj2)
1480 for (RenderObject* currObj1 = obj1; currObj1; currObj1 = currObj1->hoverAncestor())
1481 for (RenderObject* currObj2 = obj2; currObj2; currObj2 = currObj2->hoverAncestor())
1482 if (currObj1 == currObj2)
1488 void RenderLayer::updateHoverActiveState(RenderObject::NodeInfo& info)
1490 // We don't update :hover/:active state when the info is marked as readonly.
1491 if (info.readonly())
1494 DocumentImpl* doc = renderer()->document();
1497 NodeImpl* activeNode = doc->activeNode();
1498 if (activeNode && !info.active()) {
1499 // We are clearing the :active chain because the mouse has been released.
1500 for (RenderObject* curr = activeNode->renderer(); curr; curr = curr->parent()) {
1501 if (curr->element() && !curr->isText())
1502 curr->element()->setInActiveChain(false);
1504 doc->setActiveNode(0);
1506 NodeImpl* newActiveNode = info.innerNode();
1507 if (!activeNode && newActiveNode && info.active()) {
1508 // We are setting the :active chain and freezing it. If future moves happen, they
1509 // will need to reference this chain.
1510 for (RenderObject* curr = newActiveNode->renderer(); curr; curr = curr->parent()) {
1511 if (curr->element() && !curr->isText()) {
1512 curr->element()->setInActiveChain(true);
1515 doc->setActiveNode(newActiveNode);
1519 // If the mouse is down and if this is a mouse move event, we want to restrict changes in
1520 // :hover/:active to only apply to elements that are in the :active chain that we froze
1521 // at the time the mouse went down.
1522 bool mustBeInActiveChain = info.active() && info.mouseMove();
1524 // Check to see if the hovered node has changed. If not, then we don't need to
1526 DOM::NodeImpl* oldHoverNode = doc->hoverNode();
1527 DOM::NodeImpl* newHoverNode = info.innerNode();
1529 // Update our current hover node.
1530 doc->setHoverNode(newHoverNode);
1532 // We have two different objects. Fetch their renderers.
1533 RenderObject* oldHoverObj = oldHoverNode ? oldHoverNode->renderer() : 0;
1534 RenderObject* newHoverObj = newHoverNode ? newHoverNode->renderer() : 0;
1536 // Locate the common ancestor render object for the two renderers.
1537 RenderObject* ancestor = commonAncestor(oldHoverObj, newHoverObj);
1539 if (oldHoverObj != newHoverObj) {
1540 // The old hover path only needs to be cleared up to (and not including) the common ancestor;
1541 for (RenderObject* curr = oldHoverObj; curr && curr != ancestor; curr = curr->hoverAncestor()) {
1542 if (curr->element() && !curr->isText() && (!mustBeInActiveChain || curr->element()->inActiveChain())) {
1543 curr->element()->setActive(false);
1544 curr->element()->setHovered(false);
1549 // Now set the hover state for our new object up to the root.
1550 for (RenderObject* curr = newHoverObj; curr; curr = curr->hoverAncestor()) {
1551 if (curr->element() && !curr->isText() && (!mustBeInActiveChain || curr->element()->inActiveChain())) {
1552 curr->element()->setActive(info.active());
1553 curr->element()->setHovered(true);
1558 // Helpers for the sorting of layers by z-index.
1559 static inline bool isOverflowOnly(const RenderLayer* layer)
1561 return layer->renderer()->hasOverflowClip() &&
1562 !layer->renderer()->isPositioned() &&
1563 !layer->renderer()->isRelPositioned() &&
1564 !layer->isTransparent();
1567 static inline bool compareZIndex(RenderLayer* first, RenderLayer* second)
1569 return first->zIndex() < second->zIndex();
1573 void RenderLayer::dirtyZOrderLists()
1575 if (m_posZOrderList)
1576 m_posZOrderList->clear();
1577 if (m_negZOrderList)
1578 m_negZOrderList->clear();
1579 m_zOrderListsDirty = true;
1582 void RenderLayer::updateZOrderLists()
1584 if (!isStackingContext() || !m_zOrderListsDirty)
1587 for (RenderLayer* child = firstChild(); child; child = child->nextSibling())
1588 child->collectLayers(m_posZOrderList, m_negZOrderList);
1590 // Sort the two lists.
1591 if (m_posZOrderList)
1592 std::stable_sort(m_posZOrderList->begin(), m_posZOrderList->end(), compareZIndex);
1593 if (m_negZOrderList)
1594 std::stable_sort(m_negZOrderList->begin(), m_negZOrderList->end(), compareZIndex);
1596 m_zOrderListsDirty = false;
1599 void RenderLayer::collectLayers(Vector<RenderLayer*>*& posBuffer, Vector<RenderLayer*>*& negBuffer)
1601 // FIXME: A child render object or layer could override visibility. Don't remove this
1602 // optimization though until RenderObject's nodeAtPoint is patched to understand what to do
1603 // when visibility is overridden by a child.
1604 if (renderer()->style()->visibility() != VISIBLE)
1607 // Determine which buffer the child should be in.
1608 Vector<RenderLayer*>*& buffer = (zIndex() >= 0) ? posBuffer : negBuffer;
1610 // Create the buffer if it doesn't exist yet.
1612 buffer = new Vector<RenderLayer*>;
1614 // Append ourselves at the end of the appropriate buffer.
1615 buffer->append(this);
1617 // Recur into our children to collect more layers, but only if we don't establish
1618 // a stacking context.
1619 if (!isStackingContext())
1620 for (RenderLayer* child = firstChild(); child; child = child->nextSibling())
1621 child->collectLayers(posBuffer, negBuffer);
1624 void RenderLayer::repaintIncludingDescendants()
1626 m_object->repaint();
1627 for (RenderLayer* curr = firstChild(); curr; curr = curr->nextSibling())
1628 curr->repaintIncludingDescendants();
1631 void RenderLayer::styleChanged()
1633 if (m_object->style()->overflow() == OMARQUEE && m_object->style()->marqueeBehavior() != MNONE) {
1635 m_marquee = new Marquee(this);
1636 m_marquee->updateMarqueeStyle();
1638 else if (m_marquee) {
1644 void RenderLayer::suspendMarquees()
1647 m_marquee->suspend();
1649 for (RenderLayer* curr = firstChild(); curr; curr = curr->nextSibling())
1650 curr->suspendMarquees();
1653 // --------------------------------------------------------------------------
1654 // Marquee implementation
1656 Marquee::Marquee(RenderLayer* l)
1657 : m_layer(l), m_currentLoop(0)
1658 , m_timer(this, &Marquee::timerFired)
1659 , m_start(0), m_end(0), m_speed(0), m_unfurlPos(0), m_reset(false)
1660 , m_suspended(false), m_stopped(false), m_whiteSpace(NORMAL), m_direction(MAUTO)
1664 int Marquee::marqueeSpeed() const
1666 int result = m_layer->renderer()->style()->marqueeSpeed();
1667 DOM::NodeImpl* elt = m_layer->renderer()->element();
1668 if (elt && elt->hasTagName(marqueeTag)) {
1669 HTMLMarqueeElementImpl* marqueeElt = static_cast<HTMLMarqueeElementImpl*>(elt);
1670 result = kMax(result, marqueeElt->minimumDelay());
1675 EMarqueeDirection Marquee::direction() const
1677 // FIXME: Support the CSS3 "auto" value for determining the direction of the marquee.
1678 // For now just map MAUTO to MBACKWARD
1679 EMarqueeDirection result = m_layer->renderer()->style()->marqueeDirection();
1680 TextDirection dir = m_layer->renderer()->style()->direction();
1681 if (result == MAUTO)
1683 if (result == MFORWARD)
1684 result = (dir == LTR) ? MRIGHT : MLEFT;
1685 if (result == MBACKWARD)
1686 result = (dir == LTR) ? MLEFT : MRIGHT;
1688 // Now we have the real direction. Next we check to see if the increment is negative.
1689 // If so, then we reverse the direction.
1690 Length increment = m_layer->renderer()->style()->marqueeIncrement();
1691 if (increment.value() < 0)
1692 result = static_cast<EMarqueeDirection>(-result);
1697 bool Marquee::isHorizontal() const
1699 return direction() == MLEFT || direction() == MRIGHT;
1702 bool Marquee::isUnfurlMarquee() const
1704 EMarqueeBehavior behavior = m_layer->renderer()->style()->marqueeBehavior();
1705 return (behavior == MUNFURL);
1708 int Marquee::computePosition(EMarqueeDirection dir, bool stopAtContentEdge)
1710 RenderObject* o = m_layer->renderer();
1711 RenderStyle* s = o->style();
1712 if (isHorizontal()) {
1713 bool ltr = s->direction() == LTR;
1714 int clientWidth = o->clientWidth();
1715 int contentWidth = ltr ? o->rightmostPosition(true, false) : o->leftmostPosition(true, false);
1717 contentWidth += (o->paddingRight() - o->borderLeft());
1719 contentWidth = o->width() - contentWidth;
1720 contentWidth += (o->paddingLeft() - o->borderRight());
1722 if (dir == MRIGHT) {
1723 if (stopAtContentEdge)
1724 return kMax(0, ltr ? (contentWidth - clientWidth) : (clientWidth - contentWidth));
1726 return ltr ? contentWidth : clientWidth;
1729 if (stopAtContentEdge)
1730 return kMin(0, ltr ? (contentWidth - clientWidth) : (clientWidth - contentWidth));
1732 return ltr ? -clientWidth : -contentWidth;
1736 int contentHeight = m_layer->renderer()->lowestPosition(true, false) -
1737 m_layer->renderer()->borderTop() + m_layer->renderer()->paddingBottom();
1738 int clientHeight = m_layer->renderer()->clientHeight();
1740 if (stopAtContentEdge)
1741 return kMin(contentHeight - clientHeight, 0);
1743 return -clientHeight;
1746 if (stopAtContentEdge)
1747 return kMax(contentHeight - clientHeight, 0);
1749 return contentHeight;
1754 void Marquee::start()
1756 if (m_timer.isActive() || m_layer->renderer()->style()->marqueeIncrement().value() == 0)
1759 if (!m_suspended && !m_stopped) {
1760 if (isUnfurlMarquee()) {
1761 bool forward = direction() == MDOWN || direction() == MRIGHT;
1762 bool isReversed = (forward && m_currentLoop % 2) || (!forward && !(m_currentLoop % 2));
1763 m_unfurlPos = isReversed ? m_end : m_start;
1764 m_layer->renderer()->setChildNeedsLayout(true);
1768 m_layer->scrollToOffset(m_start, 0, false, false);
1770 m_layer->scrollToOffset(0, m_start, false, false);
1772 // FIXME: At this point a scroll event fired, which could have deleted this layer,
1773 // including the marquee. Need to handle this case.
1776 m_suspended = false;
1780 m_timer.startRepeating(speed() * 0.001);
1783 void Marquee::suspend()
1789 void Marquee::stop()
1795 void Marquee::updateMarqueePosition()
1797 bool activate = (m_totalLoops <= 0 || m_currentLoop < m_totalLoops);
1799 if (isUnfurlMarquee()) {
1800 if (m_unfurlPos < m_start) {
1801 m_unfurlPos = m_start;
1802 m_layer->renderer()->setChildNeedsLayout(true);
1804 else if (m_unfurlPos > m_end) {
1805 m_unfurlPos = m_end;
1806 m_layer->renderer()->setChildNeedsLayout(true);
1810 EMarqueeBehavior behavior = m_layer->renderer()->style()->marqueeBehavior();
1811 m_start = computePosition(direction(), behavior == MALTERNATE);
1812 m_end = computePosition(reverseDirection(), behavior == MALTERNATE || behavior == MSLIDE);
1819 void Marquee::updateMarqueeStyle()
1821 RenderStyle* s = m_layer->renderer()->style();
1823 if (m_direction != s->marqueeDirection() || (m_totalLoops != s->marqueeLoopCount() && m_currentLoop >= m_totalLoops))
1824 m_currentLoop = 0; // When direction changes or our loopCount is a smaller number than our current loop, reset our loop.
1826 m_totalLoops = s->marqueeLoopCount();
1827 m_direction = s->marqueeDirection();
1828 m_whiteSpace = s->whiteSpace();
1830 if (m_layer->renderer()->isHTMLMarquee()) {
1831 // Hack for WinIE. In WinIE, a value of 0 or lower for the loop count for SLIDE means to only do
1833 if (m_totalLoops <= 0 && (s->marqueeBehavior() == MSLIDE || s->marqueeBehavior() == MUNFURL))
1836 // Hack alert: Set the white-space value to nowrap for horizontal marquees with inline children, thus ensuring
1837 // all the text ends up on one line by default. Limit this hack to the <marquee> element to emulate
1838 // WinIE's behavior. Someone using CSS3 can use white-space: nowrap on their own to get this effect.
1839 // Second hack alert: Set the text-align back to auto. WinIE completely ignores text-align on the
1841 // FIXME: Bring these up with the CSS WG.
1842 if (isHorizontal() && m_layer->renderer()->childrenInline()) {
1843 s->setWhiteSpace(NOWRAP);
1844 s->setTextAlign(TAAUTO);
1848 // Marquee height hack!! Make sure that, if it is a horizontal marquee, the height attribute is overridden
1849 // if it is smaller than the font size. If it is a vertical marquee and height is not specified, we default
1850 // to a marquee of 200px.
1851 if (isHorizontal()) {
1852 if (s->height().isFixed() && s->height().value() < s->fontSize())
1853 s->setHeight(Length(s->fontSize(),Fixed));
1854 } else if (s->height().isAuto()) //vertical marquee with no specified height
1855 s->setHeight(Length(200, Fixed));
1857 if (speed() != marqueeSpeed()) {
1858 m_speed = marqueeSpeed();
1859 if (m_timer.isActive())
1860 m_timer.startRepeating(speed() * 0.001);
1863 // Check the loop count to see if we should now stop.
1864 bool activate = (m_totalLoops <= 0 || m_currentLoop < m_totalLoops);
1865 if (activate && !m_timer.isActive())
1866 m_layer->renderer()->setNeedsLayout(true);
1867 else if (!activate && m_timer.isActive())
1871 void Marquee::timerFired(Timer<Marquee>*)
1873 if (m_layer->renderer()->needsLayout())
1879 m_layer->scrollToXOffset(m_start);
1881 m_layer->scrollToYOffset(m_start);
1885 RenderStyle* s = m_layer->renderer()->style();
1887 int endPoint = m_end;
1888 int range = m_end - m_start;
1893 bool addIncrement = direction() == MUP || direction() == MLEFT;
1894 bool isReversed = s->marqueeBehavior() == MALTERNATE && m_currentLoop % 2;
1895 if (isUnfurlMarquee()) {
1896 isReversed = (!addIncrement && m_currentLoop % 2) || (addIncrement && !(m_currentLoop % 2));
1897 addIncrement = !isReversed;
1900 // We're going in the reverse direction.
1903 if (!isUnfurlMarquee())
1904 addIncrement = !addIncrement;
1906 bool positive = range > 0;
1907 int clientSize = isUnfurlMarquee() ? abs(range) :
1908 (isHorizontal() ? m_layer->renderer()->clientWidth() : m_layer->renderer()->clientHeight());
1909 int increment = kMax(1, abs(m_layer->renderer()->style()->marqueeIncrement().calcValue(clientSize)));
1910 int currentPos = isUnfurlMarquee() ? m_unfurlPos :
1911 (isHorizontal() ? m_layer->scrollXOffset() : m_layer->scrollYOffset());
1912 newPos = currentPos + (addIncrement ? increment : -increment);
1914 newPos = kMin(newPos, endPoint);
1916 newPos = kMax(newPos, endPoint);
1919 if (newPos == endPoint) {
1921 if (m_totalLoops > 0 && m_currentLoop >= m_totalLoops)
1923 else if (s->marqueeBehavior() != MALTERNATE && s->marqueeBehavior() != MUNFURL)
1927 if (isUnfurlMarquee()) {
1928 m_unfurlPos = newPos;
1929 m_layer->renderer()->setChildNeedsLayout(true);
1933 m_layer->scrollToXOffset(newPos);
1935 m_layer->scrollToYOffset(newPos);