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.
44 #include "render_layer.h"
47 #include "khtmlview.h"
48 #include "render_canvas.h"
49 #include "render_arena.h"
50 #include "xml/dom_docimpl.h"
51 #include "xml/dom2_eventsimpl.h"
52 #include "html/html_blockimpl.h"
54 #include <qscrollbar.h>
55 #include <qptrvector.h>
58 #include "KWQKHTMLPart.h" // For Dashboard.
61 // These match the numbers we use over in WebKit (WebFrameView.m).
66 using namespace khtml;
69 QScrollBar* RenderLayer::gScrollBar = 0;
73 static bool inRenderLayerDetach;
76 void* ClipRects::operator new(size_t sz, RenderArena* renderArena) throw()
78 return renderArena->allocate(sz);
81 void ClipRects::operator delete(void* ptr, size_t sz)
83 // Stash size where detach can find it.
87 void ClipRects::detach(RenderArena* renderArena)
91 // Recover the size left there for us by operator delete and free the memory.
92 renderArena->free(*(size_t *)this, this);
96 RenderScrollMediator::slotValueChanged(int val)
98 m_layer->updateScrollPositionFromScrollbars();
101 RenderLayer::RenderLayer(RenderObject* object)
102 : m_object( object ),
120 m_scrollMediator( 0 ),
121 m_posZOrderList( 0 ),
122 m_negZOrderList( 0 ),
124 m_scrollDimensionsDirty( true ),
125 m_zOrderListsDirty( true ),
126 m_usedTransparency( false ),
131 RenderLayer::~RenderLayer()
133 // Child layers will be deleted by their corresponding render objects, so
134 // our destructor doesn't have to do anything.
137 delete m_scrollMediator;
138 delete m_posZOrderList;
139 delete m_negZOrderList;
142 // Make sure we have no lingering clip rects.
143 assert(!m_clipRects);
146 void RenderLayer::computeRepaintRects()
148 // FIXME: Child object could override visibility.
149 if (m_object->style()->visibility() == VISIBLE)
150 m_object->getAbsoluteRepaintRectIncludingFloats(m_repaintRect, m_fullRepaintRect);
151 for (RenderLayer* child = firstChild(); child; child = child->nextSibling())
152 child->computeRepaintRects();
155 void RenderLayer::updateLayerPositions(bool doFullRepaint, bool checkForRepaint)
159 checkForRepaint = doFullRepaint = false;
162 updateLayerPosition(); // For relpositioned layers or non-positioned layers,
163 // we need to keep in sync, since we may have shifted relative
164 // to our parent layer.
166 if (m_hBar || m_vBar) {
167 // Need to position the scrollbars.
170 convertToLayerCoords(root(), x, y);
171 QRect layerBounds = QRect(x,y,width(),height());
172 positionScrollbars(layerBounds);
175 // FIXME: Child object could override visibility.
176 if (checkForRepaint && (m_object->style()->visibility() == VISIBLE))
177 m_object->repaintAfterLayoutIfNeeded(m_repaintRect, m_fullRepaintRect);
179 for (RenderLayer* child = firstChild(); child; child = child->nextSibling())
180 child->updateLayerPositions(doFullRepaint, checkForRepaint);
182 // With all our children positioned, now update our marquee if we need to.
184 m_marquee->updateMarqueePosition();
187 void RenderLayer::updateLayerPosition()
189 // Clear our cached clip rect information.
192 // The canvas is sized to the docWidth/Height over in RenderCanvas::layout, so we
193 // don't need to ever update our layer position here.
194 if (renderer()->isCanvas())
197 int x = m_object->xPos();
198 int y = m_object->yPos();
200 if (!m_object->isPositioned()) {
201 // We must adjust our position by walking up the render tree looking for the
202 // nearest enclosing object with a layer.
203 RenderObject* curr = m_object->parent();
204 while (curr && !curr->layer()) {
207 curr = curr->parent();
212 if (m_object->isRelPositioned()) {
213 static_cast<RenderBox*>(m_object)->relativePositionOffset(m_relX, m_relY);
214 x += m_relX; y += m_relY;
217 // Subtract our parent's scroll offset.
218 if (m_object->isPositioned() && enclosingPositionedAncestor()) {
219 RenderLayer* positionedParent = enclosingPositionedAncestor();
221 // For positioned layers, we subtract out the enclosing positioned layer's scroll offset.
222 positionedParent->subtractScrollOffset(x, y);
224 if (m_object->isPositioned() && positionedParent->renderer()->isRelPositioned() &&
225 positionedParent->renderer()->isInlineFlow()) {
226 // When we have an enclosing relpositioned inline, we need to add in the offset of the first line
227 // box from the rest of the content, but only in the cases where we know we're positioned
228 // relative to the inline itself.
229 RenderFlow* flow = static_cast<RenderFlow*>(positionedParent->renderer());
231 if (flow->firstLineBox()) {
232 sx = flow->firstLineBox()->xPos();
233 sy = flow->firstLineBox()->yPos();
236 sx = flow->staticX();
237 sy = flow->staticY();
239 bool isInlineType = m_object->style()->isOriginalDisplayInlineType();
241 if (!m_object->hasStaticX())
244 // This is not terribly intuitive, but we have to match other browsers. Despite being a block display type inside
245 // an inline, we still keep our x locked to the left of the relative positioned inline. Arguably the correct
246 // behavior would be to go flush left to the block that contains the inline, but that isn't what other browsers
248 if (m_object->hasStaticX() && !isInlineType)
249 // Avoid adding in the left border/padding of the containing block twice. Subtract it out.
250 x += sx - (m_object->containingBlock()->borderLeft() + m_object->containingBlock()->paddingLeft());
252 if (!m_object->hasStaticY())
257 parent()->subtractScrollOffset(x, y);
261 setWidth(m_object->width());
262 setHeight(m_object->height());
264 if (!m_object->hasOverflowClip()) {
265 if (m_object->overflowWidth() > m_object->width())
266 setWidth(m_object->overflowWidth());
267 if (m_object->overflowHeight() > m_object->height())
268 setHeight(m_object->overflowHeight());
272 RenderLayer *RenderLayer::stackingContext() const
274 RenderLayer* curr = parent();
275 for ( ; curr && !curr->m_object->isCanvas() && !curr->m_object->isRoot() &&
276 curr->m_object->style()->hasAutoZIndex();
277 curr = curr->parent());
282 RenderLayer::enclosingPositionedAncestor() const
284 RenderLayer* curr = parent();
285 for ( ; curr && !curr->m_object->isCanvas() && !curr->m_object->isRoot() &&
286 !curr->m_object->isPositioned() && !curr->m_object->isRelPositioned();
287 curr = curr->parent());
294 RenderLayer::isTransparent()
296 return m_object->style()->opacity() < 1.0f;
300 RenderLayer::transparentAncestor()
302 RenderLayer* curr = parent();
303 for ( ; curr && curr->m_object->style()->opacity() == 1.0f; curr = curr->parent());
307 void RenderLayer::beginTransparencyLayers(QPainter* p)
309 if (isTransparent() && m_usedTransparency)
312 RenderLayer* ancestor = transparentAncestor();
314 ancestor->beginTransparencyLayers(p);
316 if (isTransparent()) {
317 m_usedTransparency = true;
318 p->beginTransparencyLayer(renderer()->style()->opacity());
324 void* RenderLayer::operator new(size_t sz, RenderArena* renderArena) throw()
326 return renderArena->allocate(sz);
329 void RenderLayer::operator delete(void* ptr, size_t sz)
331 assert(inRenderLayerDetach);
333 // Stash size where detach can find it.
337 void RenderLayer::detach(RenderArena* renderArena)
340 inRenderLayerDetach = true;
344 inRenderLayerDetach = false;
347 // Recover the size left there for us by operator delete and free the memory.
348 renderArena->free(*(size_t *)this, this);
351 void RenderLayer::addChild(RenderLayer *child, RenderLayer* beforeChild)
353 RenderLayer* prevSibling = beforeChild ? beforeChild->previousSibling() : lastChild();
355 child->setPreviousSibling(prevSibling);
356 prevSibling->setNextSibling(child);
359 setFirstChild(child);
362 beforeChild->setPreviousSibling(child);
363 child->setNextSibling(beforeChild);
368 child->setParent(this);
370 // Dirty the z-order list in which we are contained. The stackingContext() can be null in the
371 // case where we're building up generated content layers. This is ok, since the lists will start
372 // off dirty in that case anyway.
373 RenderLayer* stackingContext = child->stackingContext();
375 stackingContext->dirtyZOrderLists();
378 RenderLayer* RenderLayer::removeChild(RenderLayer* oldChild)
381 if (oldChild->previousSibling())
382 oldChild->previousSibling()->setNextSibling(oldChild->nextSibling());
383 if (oldChild->nextSibling())
384 oldChild->nextSibling()->setPreviousSibling(oldChild->previousSibling());
386 if (m_first == oldChild)
387 m_first = oldChild->nextSibling();
388 if (m_last == oldChild)
389 m_last = oldChild->previousSibling();
391 // Dirty the z-order list in which we are contained. When called via the
392 // reattachment process in removeOnlyThisLayer, the layer may already be disconnected
393 // from the main layer tree, so we need to null-check the |stackingContext| value.
394 RenderLayer* stackingContext = oldChild->stackingContext();
396 oldChild->stackingContext()->dirtyZOrderLists();
398 oldChild->setPreviousSibling(0);
399 oldChild->setNextSibling(0);
400 oldChild->setParent(0);
405 void RenderLayer::removeOnlyThisLayer()
410 // Dirty the clip rects.
413 // Remove us from the parent.
414 RenderLayer* parent = m_parent;
415 RenderLayer* nextSib = nextSibling();
416 parent->removeChild(this);
418 // Now walk our kids and reattach them to our parent.
419 RenderLayer* current = m_first;
421 RenderLayer* next = current->nextSibling();
422 removeChild(current);
423 parent->addChild(current, nextSib);
427 detach(renderer()->renderArena());
430 void RenderLayer::insertOnlyThisLayer()
432 if (!m_parent && renderer()->parent()) {
433 // We need to connect ourselves when our renderer() has a parent.
434 // Find our enclosingLayer and add ourselves.
435 RenderLayer* parentLayer = renderer()->parent()->enclosingLayer();
437 parentLayer->addChild(this,
438 renderer()->parent()->findNextLayer(parentLayer, renderer()));
441 // Remove all descendant layers from the hierarchy and add them to the new position.
442 for (RenderObject* curr = renderer()->firstChild(); curr; curr = curr->nextSibling())
443 curr->moveLayers(m_parent, this);
445 // Clear out all the clip rects.
450 RenderLayer::convertToLayerCoords(const RenderLayer* ancestorLayer, int& x, int& y) const
452 if (ancestorLayer == this)
455 if (m_object->style()->position() == FIXED) {
456 // Add in the offset of the view. We can obtain this by calling
457 // absolutePosition() on the RenderCanvas.
459 m_object->absolutePosition(xOff, yOff, true);
465 RenderLayer* parentLayer;
466 if (m_object->style()->position() == ABSOLUTE)
467 parentLayer = enclosingPositionedAncestor();
469 parentLayer = parent();
471 if (!parentLayer) return;
473 parentLayer->convertToLayerCoords(ancestorLayer, x, y);
480 RenderLayer::scrollOffset(int& x, int& y)
482 x += scrollXOffset();
483 y += scrollYOffset();
487 RenderLayer::subtractScrollOffset(int& x, int& y)
489 x -= scrollXOffset();
490 y -= scrollYOffset();
494 RenderLayer::scrollToOffset(int x, int y, bool updateScrollbars, bool repaint)
496 if (renderer()->style()->overflow() != OMARQUEE) {
500 // Call the scrollWidth/Height functions so that the dimensions will be computed if they need
501 // to be (for overflow:hidden blocks).
502 int maxX = scrollWidth() - m_object->clientWidth();
503 int maxY = scrollHeight() - m_object->clientHeight();
505 if (x > maxX) x = maxX;
506 if (y > maxY) y = maxY;
509 // FIXME: Eventually, we will want to perform a blit. For now never
510 // blit, since the check for blitting is going to be very
511 // complicated (since it will involve testing whether our layer
512 // is either occluded by another layer or clipped by an enclosing
513 // layer or contains fixed backgrounds, etc.).
517 // Update the positions of our child layers.
518 for (RenderLayer* child = firstChild(); child; child = child->nextSibling())
519 child->updateLayerPositions(false, false);
523 m_object->updateWidgetPositions();
525 // Update dashboard regions, scrolling may change the clip of a
526 // particular region.
527 RenderCanvas *canvas = renderer()->canvas();
529 canvas->view()->updateDashboardRegions();
532 // Fire the scroll DOM event.
533 m_object->element()->dispatchHTMLEvent(EventImpl::SCROLL_EVENT, true, false);
535 // Just schedule a full repaint of our object.
539 if (updateScrollbars) {
541 m_hBar->setValue(m_scrollX);
543 m_vBar->setValue(m_scrollY);
548 RenderLayer::updateScrollPositionFromScrollbars()
550 bool needUpdate = false;
551 int newX = m_scrollX;
552 int newY = m_scrollY;
555 newX = m_hBar->value();
556 if (newX != m_scrollX)
561 newY = m_vBar->value();
562 if (newY != m_scrollY)
567 scrollToOffset(newX, newY, false);
571 RenderLayer::setHasHorizontalScrollbar(bool hasScrollbar)
573 if (hasScrollbar && !m_hBar) {
574 QScrollView* scrollView = m_object->element()->getDocument()->view();
575 m_hBar = new QScrollBar(Qt::Horizontal, 0);
576 scrollView->addChild(m_hBar, 0, -50000);
577 if (!m_scrollMediator)
578 m_scrollMediator = new RenderScrollMediator(this);
579 m_scrollMediator->connect(m_hBar, SIGNAL(valueChanged(int)), SLOT(slotValueChanged(int)));
581 else if (!hasScrollbar && m_hBar) {
582 QScrollView* scrollView = m_object->element()->getDocument()->view();
583 scrollView->removeChild (m_hBar);
585 m_scrollMediator->disconnect(m_hBar, SIGNAL(valueChanged(int)),
586 m_scrollMediator, SLOT(slotValueChanged(int)));
593 RenderLayer::setHasVerticalScrollbar(bool hasScrollbar)
595 if (hasScrollbar && !m_vBar) {
596 QScrollView* scrollView = m_object->element()->getDocument()->view();
597 m_vBar = new QScrollBar(Qt::Vertical, 0);
598 scrollView->addChild(m_vBar, 0, -50000);
599 if (!m_scrollMediator)
600 m_scrollMediator = new RenderScrollMediator(this);
601 m_scrollMediator->connect(m_vBar, SIGNAL(valueChanged(int)), SLOT(slotValueChanged(int)));
603 else if (!hasScrollbar && m_vBar) {
604 QScrollView* scrollView = m_object->element()->getDocument()->view();
605 scrollView->removeChild (m_vBar);
607 m_scrollMediator->disconnect(m_vBar, SIGNAL(valueChanged(int)),
608 m_scrollMediator, SLOT(slotValueChanged(int)));
615 RenderLayer::verticalScrollbarWidth()
620 return m_vBar->width();
624 RenderLayer::horizontalScrollbarHeight()
629 return m_hBar->height();
633 RenderLayer::moveScrollbarsAside()
636 m_hBar->move(0, -50000);
638 m_vBar->move(0, -50000);
642 RenderLayer::positionScrollbars(const QRect& absBounds)
645 m_vBar->move(absBounds.x()+absBounds.width()-m_object->borderRight()-m_vBar->width(),
646 absBounds.y()+m_object->borderTop());
647 m_vBar->resize(m_vBar->width(), absBounds.height() -
648 (m_object->borderTop()+m_object->borderBottom()) -
649 (m_hBar ? m_hBar->height()-1 : 0));
653 m_hBar->move(absBounds.x()+m_object->borderLeft(),
654 absBounds.y()+absBounds.height()-m_object->borderBottom()-m_hBar->height());
655 m_hBar->resize(absBounds.width() - (m_object->borderLeft()+m_object->borderRight()) -
656 (m_vBar ? m_vBar->width()-1 : 0),
661 int RenderLayer::scrollWidth()
663 if (m_scrollDimensionsDirty)
664 computeScrollDimensions();
665 return m_scrollWidth;
668 int RenderLayer::scrollHeight()
670 if (m_scrollDimensionsDirty)
671 computeScrollDimensions();
672 return m_scrollHeight;
675 void RenderLayer::computeScrollDimensions(bool* needHBar, bool* needVBar)
677 m_scrollDimensionsDirty = false;
679 int rightPos = m_object->rightmostPosition(true, false) - m_object->borderLeft();
680 int bottomPos = m_object->lowestPosition(true, false) - m_object->borderTop();
682 int clientWidth = m_object->clientWidth();
683 int clientHeight = m_object->clientHeight();
685 m_scrollWidth = kMax(rightPos, clientWidth);
686 m_scrollHeight = kMax(bottomPos, clientHeight);
689 *needHBar = rightPos > clientWidth;
691 *needVBar = bottomPos > clientHeight;
695 RenderLayer::updateScrollInfoAfterLayout()
697 m_scrollDimensionsDirty = true;
698 if (m_object->style()->overflow() == OHIDDEN)
699 return; // All we had to do was dirty.
701 bool needHorizontalBar, needVerticalBar;
702 computeScrollDimensions(&needHorizontalBar, &needVerticalBar);
704 if (m_object->style()->overflow() != OMARQUEE) {
705 // Layout may cause us to be in an invalid scroll position. In this case we need
706 // to pull our scroll offsets back to the max (or push them up to the min).
707 int newX = kMax(0, kMin(m_scrollX, scrollWidth() - m_object->clientWidth()));
708 int newY = kMax(0, kMin(m_scrollY, scrollHeight() - m_object->clientHeight()));
709 if (newX != m_scrollX || newY != m_scrollY)
710 scrollToOffset(newX, newY);
713 bool haveHorizontalBar = m_hBar;
714 bool haveVerticalBar = m_vBar;
716 // overflow:scroll should just enable/disable.
717 if (m_object->style()->overflow() == OSCROLL) {
718 m_hBar->setEnabled(needHorizontalBar);
719 m_vBar->setEnabled(needVerticalBar);
722 // overflow:auto may need to lay out again if scrollbars got added/removed.
723 bool scrollbarsChanged = (m_object->hasAutoScrollbars()) &&
724 (haveHorizontalBar != needHorizontalBar || haveVerticalBar != needVerticalBar);
725 if (scrollbarsChanged) {
726 setHasHorizontalScrollbar(needHorizontalBar);
727 setHasVerticalScrollbar(needVerticalBar);
730 // Force an update since we know the scrollbars have changed things.
731 if (m_object->document()->hasDashboardRegions())
732 m_object->document()->setDashboardRegionsDirty(true);
737 if (m_object->style()->overflow() == OAUTO) {
738 // Our proprietary overflow: overlay value doesn't trigger a layout.
739 m_object->setNeedsLayout(true);
740 if (m_object->isRenderBlock())
741 static_cast<RenderBlock*>(m_object)->layoutBlock(true);
747 // Set up the range (and page step/line step).
749 int clientWidth = m_object->clientWidth();
750 int pageStep = (clientWidth-PAGE_KEEP);
751 if (pageStep < 0) pageStep = clientWidth;
752 m_hBar->setSteps(LINE_STEP, pageStep);
754 m_hBar->setKnobProportion(clientWidth, m_scrollWidth);
756 m_hBar->setRange(0, m_scrollWidth-clientWidth);
757 m_object->repaintRectangle(QRect(m_object->borderLeft(), m_object->borderTop() + clientHeight(),
758 horizontalScrollbarHeight(),
759 m_object->width() - m_object->borderLeft() - m_object->borderRight()));
763 int clientHeight = m_object->clientHeight();
764 int pageStep = (clientHeight-PAGE_KEEP);
765 if (pageStep < 0) pageStep = clientHeight;
766 m_vBar->setSteps(LINE_STEP, pageStep);
768 m_vBar->setKnobProportion(clientHeight, m_scrollHeight);
770 m_vBar->setRange(0, m_scrollHeight-clientHeight);
772 m_object->repaintRectangle(QRect(m_object->borderLeft() + m_object->clientWidth(),
773 m_object->borderTop(), verticalScrollbarWidth(),
774 m_object->height() - m_object->borderTop() - m_object->borderBottom()));
778 // Force an update since we know the scrollbars have changed things.
779 if (m_object->document()->hasDashboardRegions())
780 m_object->document()->setDashboardRegionsDirty(true);
788 RenderLayer::paintScrollbars(QPainter* p, const QRect& damageRect)
790 // Move the widgets if necessary. We normally move and resize widgets during layout, but sometimes
791 // widgets can move without layout occurring (most notably when you scroll a document that
792 // contains fixed positioned elements).
793 if (m_hBar || m_vBar) {
796 convertToLayerCoords(root(), x, y);
797 QRect layerBounds = QRect(x, y, width(), height());
798 positionScrollbars(layerBounds);
801 // Now that we're sure the scrollbars are in the right place, paint them.
803 m_hBar->paint(p, damageRect);
805 m_vBar->paint(p, damageRect);
809 bool RenderLayer::scroll(KWQScrollDirection direction, KWQScrollGranularity granularity, float multiplier)
811 bool didHorizontalScroll = false;
812 bool didVerticalScroll = false;
815 if (granularity == KWQScrollDocument) {
816 // Special-case for the KWQScrollDocument granularity. A document scroll can only be up
817 // or down and in both cases the horizontal bar goes all the way to the left.
818 didHorizontalScroll = m_hBar->scroll(KWQScrollLeft, KWQScrollDocument, multiplier);
820 didHorizontalScroll = m_hBar->scroll(direction, granularity, multiplier);
824 didVerticalScroll = m_vBar->scroll(direction, granularity, multiplier);
827 return (didHorizontalScroll || didVerticalScroll);
831 RenderLayer::paint(QPainter *p, const QRect& damageRect, bool selectionOnly, RenderObject *paintingRoot)
833 paintLayer(this, p, damageRect, false, selectionOnly, paintingRoot);
836 static void setClip(QPainter* p, const QRect& paintDirtyRect, const QRect& clipRect)
838 if (paintDirtyRect == clipRect)
844 p->addClip(clipRect);
846 QRect clippedRect = p->xForm(clipRect);
847 QRegion creg(clippedRect);
848 QRegion old = p->clipRegion();
850 creg = old.intersect(creg);
851 p->setClipRegion(creg);
856 static void restoreClip(QPainter* p, const QRect& paintDirtyRect, const QRect& clipRect)
858 if (paintDirtyRect == clipRect)
864 RenderLayer::paintLayer(RenderLayer* rootLayer, QPainter *p,
865 const QRect& paintDirtyRect, bool haveTransparency, bool selectionOnly,
866 RenderObject *paintingRoot)
868 // Calculate the clip rects we should use.
869 QRect layerBounds, damageRect, clipRectToApply;
870 calculateRects(rootLayer, paintDirtyRect, layerBounds, damageRect, clipRectToApply);
871 int x = layerBounds.x();
872 int y = layerBounds.y();
874 // Ensure our z-order lists are up-to-date.
879 haveTransparency = true;
882 // If this layer's renderer is a child of the paintingRoot, we render unconditionally, which
883 // is done by passing a nil paintingRoot down to our renderer (as if no paintingRoot was ever set).
884 // Else, our renderer tree may or may not contain the painting root, so we pass that root along
885 // so it will be tested against as we decend through the renderers.
886 RenderObject *paintingRootForRenderer = 0;
887 if (paintingRoot && !m_object->hasAncestor(paintingRoot)) {
888 paintingRootForRenderer = paintingRoot;
891 // We want to paint our layer, but only if we intersect the damage rect.
892 bool shouldPaint = intersectsDamageRect(layerBounds, damageRect);
893 if (shouldPaint && !selectionOnly && !damageRect.isEmpty()) {
895 // Begin transparency layers lazily now that we know we have to paint something.
896 if (haveTransparency)
897 beginTransparencyLayers(p);
900 // Paint our background first, before painting any child layers.
901 // Establish the clip used to paint our background.
902 setClip(p, paintDirtyRect, damageRect);
904 // Paint the background.
905 RenderObject::PaintInfo info(p, damageRect, PaintActionBlockBackground, paintingRootForRenderer);
906 renderer()->paint(info, x - renderer()->xPos(), y - renderer()->yPos());
908 // Our scrollbar widgets paint exactly when we tell them to, so that they work properly with
909 // z-index. We paint after we painted the background/border, so that the scrollbars will
910 // sit above the background/border.
911 paintScrollbars(p, damageRect);
914 restoreClip(p, paintDirtyRect, damageRect);
917 // Now walk the sorted list of children with negative z-indices.
918 if (m_negZOrderList) {
919 uint count = m_negZOrderList->count();
920 for (uint i = 0; i < count; i++) {
921 RenderLayer* child = m_negZOrderList->at(i);
922 child->paintLayer(rootLayer, p, paintDirtyRect, haveTransparency, selectionOnly, paintingRoot);
926 // Now establish the appropriate clip and paint our child RenderObjects.
927 if (shouldPaint && !clipRectToApply.isEmpty()) {
929 // Begin transparency layers lazily now that we know we have to paint something.
930 if (haveTransparency)
931 beginTransparencyLayers(p);
934 // Set up the clip used when painting our children.
935 setClip(p, paintDirtyRect, clipRectToApply);
937 int tx = x - renderer()->xPos();
938 int ty = y - renderer()->yPos();
939 RenderObject::PaintInfo info(p, clipRectToApply,
940 selectionOnly ? PaintActionSelection : PaintActionChildBlockBackgrounds,
941 paintingRootForRenderer);
942 renderer()->paint(info, tx, ty);
943 if (!selectionOnly) {
944 info.phase = PaintActionFloat;
945 renderer()->paint(info, tx, ty);
946 info.phase = PaintActionForeground;
947 renderer()->paint(info, tx, ty);
948 info.phase = PaintActionOutline;
949 renderer()->paint(info, tx, ty);
952 // Now restore our clip.
953 restoreClip(p, paintDirtyRect, clipRectToApply);
956 // Now walk the sorted list of children with positive z-indices.
957 if (m_posZOrderList) {
958 uint count = m_posZOrderList->count();
959 for (uint i = 0; i < count; i++) {
960 RenderLayer* child = m_posZOrderList->at(i);
961 child->paintLayer(rootLayer, p, paintDirtyRect, haveTransparency, selectionOnly, paintingRoot);
966 // End our transparency layer
967 if (isTransparent() && m_usedTransparency) {
968 p->endTransparencyLayer();
969 m_usedTransparency = false;
975 RenderLayer::hitTest(RenderObject::NodeInfo& info, int x, int y)
978 // Clear our our scrollbar variable
979 RenderLayer::gScrollBar = 0;
982 QRect damageRect(m_x, m_y, width(), height());
983 RenderLayer* insideLayer = hitTestLayer(this, info, x, y, damageRect);
985 // Now determine if the result is inside an anchor; make sure an image map wins if
986 // it already set URLElement and only use the innermost.
987 DOM::NodeImpl* node = info.innerNode();
989 if (node->isLink() && !info.URLElement())
990 info.setURLElement(node);
991 node = node->parentNode();
994 // Next set up the correct :hover/:active state along the new chain.
995 updateHoverActiveState(info);
997 // Now return whether we were inside this layer (this will always be true for the root
1003 RenderLayer::hitTestLayer(RenderLayer* rootLayer, RenderObject::NodeInfo& info,
1004 int xMousePos, int yMousePos, const QRect& hitTestRect)
1006 // Calculate the clip rects we should use.
1007 QRect layerBounds, bgRect, fgRect;
1008 calculateRects(rootLayer, hitTestRect, layerBounds, bgRect, fgRect);
1010 // Ensure our z-order lists are up-to-date.
1011 updateZOrderLists();
1013 // This variable tracks which layer the mouse ends up being inside. The minute we find an insideLayer,
1014 // we are done and can return it.
1015 RenderLayer* insideLayer = 0;
1017 // Begin by walking our list of positive layers from highest z-index down to the lowest
1019 if (m_posZOrderList) {
1020 uint count = m_posZOrderList->count();
1021 for (int i = count-1; i >= 0; i--) {
1022 RenderLayer* child = m_posZOrderList->at(i);
1023 insideLayer = child->hitTestLayer(rootLayer, info, xMousePos, yMousePos, hitTestRect);
1029 // Next we want to see if the mouse pos is inside the child RenderObjects of the layer.
1030 if (containsPoint(xMousePos, yMousePos, fgRect) &&
1031 renderer()->hitTest(info, xMousePos, yMousePos,
1032 layerBounds.x() - renderer()->xPos(),
1033 layerBounds.y() - renderer()->yPos(), HitTestDescendants)) {
1034 // for positioned generated content, we might still not have a
1035 // node by the time we get to the layer level, since none of
1036 // the content in the layer has an element. So just walk up
1038 if (!info.innerNode()) {
1039 for (RenderObject *r = renderer(); r != NULL; r = r->parent()) {
1041 info.setInnerNode(r->element());
1047 if (!info.innerNonSharedNode()) {
1048 for (RenderObject *r = renderer(); r != NULL; r = r->parent()) {
1050 info.setInnerNonSharedNode(r->element());
1058 // Now check our negative z-index children.
1059 if (m_negZOrderList) {
1060 uint count = m_negZOrderList->count();
1061 for (int i = count-1; i >= 0; i--) {
1062 RenderLayer* child = m_negZOrderList->at(i);
1063 insideLayer = child->hitTestLayer(rootLayer, info, xMousePos, yMousePos, hitTestRect);
1069 // Next we want to see if the mouse pos is inside this layer but not any of its children.
1070 if (containsPoint(xMousePos, yMousePos, bgRect) &&
1071 renderer()->hitTest(info, xMousePos, yMousePos,
1072 layerBounds.x() - renderer()->xPos(),
1073 layerBounds.y() - renderer()->yPos(),
1081 void RenderLayer::calculateClipRects(const RenderLayer* rootLayer)
1084 return; // We have the correct cached value.
1087 // The root layer's clip rect is always just its dimensions.
1088 m_clipRects = new (m_object->renderArena()) ClipRects(QRect(0,0,width(),height()));
1093 // Ensure that our parent's clip has been calculated so that we can examine the values.
1094 parent()->calculateClipRects(rootLayer);
1096 // Set up our three rects to initially match the parent rects.
1097 QRect posClipRect(parent()->clipRects()->posClipRect());
1098 QRect overflowClipRect(parent()->clipRects()->overflowClipRect());
1099 QRect fixedClipRect(parent()->clipRects()->fixedClipRect());
1101 // A fixed object is essentially the root of its containing block hierarchy, so when
1102 // we encounter such an object, we reset our clip rects to the fixedClipRect.
1103 if (m_object->style()->position() == FIXED) {
1104 posClipRect = fixedClipRect;
1105 overflowClipRect = fixedClipRect;
1107 else if (m_object->style()->position() == RELATIVE)
1108 posClipRect = overflowClipRect;
1110 // Update the clip rects that will be passed to child layers.
1111 if (m_object->hasOverflowClip() || m_object->hasClip()) {
1112 // This layer establishes a clip of some kind.
1115 convertToLayerCoords(rootLayer, x, y);
1117 if (m_object->hasOverflowClip()) {
1118 QRect newOverflowClip = m_object->getOverflowClipRect(x,y);
1119 overflowClipRect = newOverflowClip.intersect(overflowClipRect);
1120 if (m_object->isPositioned() || m_object->isRelPositioned())
1121 posClipRect = newOverflowClip.intersect(posClipRect);
1123 if (m_object->hasClip()) {
1124 QRect newPosClip = m_object->getClipRect(x,y);
1125 posClipRect = posClipRect.intersect(newPosClip);
1126 overflowClipRect = overflowClipRect.intersect(newPosClip);
1127 fixedClipRect = fixedClipRect.intersect(newPosClip);
1131 // If our clip rects match our parent's clip, then we can just share its data structure and
1133 if (posClipRect == parent()->clipRects()->posClipRect() &&
1134 overflowClipRect == parent()->clipRects()->overflowClipRect() &&
1135 fixedClipRect == parent()->clipRects()->fixedClipRect())
1136 m_clipRects = parent()->clipRects();
1138 m_clipRects = new (m_object->renderArena()) ClipRects(overflowClipRect, fixedClipRect, posClipRect);
1142 void RenderLayer::calculateRects(const RenderLayer* rootLayer, const QRect& paintDirtyRect, QRect& layerBounds,
1143 QRect& backgroundRect, QRect& foregroundRect)
1146 parent()->calculateClipRects(rootLayer);
1147 backgroundRect = m_object->style()->position() == FIXED ? parent()->clipRects()->fixedClipRect() :
1148 (m_object->isPositioned() ? parent()->clipRects()->posClipRect() :
1149 parent()->clipRects()->overflowClipRect());
1150 backgroundRect = backgroundRect.intersect(paintDirtyRect);
1152 backgroundRect = paintDirtyRect;
1153 foregroundRect = backgroundRect;
1157 convertToLayerCoords(rootLayer, x, y);
1158 layerBounds = QRect(x,y,width(),height());
1160 // Update the clip rects that will be passed to child layers.
1161 if (m_object->hasOverflowClip() || m_object->hasClip()) {
1162 // This layer establishes a clip of some kind.
1163 if (m_object->hasOverflowClip())
1164 foregroundRect = foregroundRect.intersect(m_object->getOverflowClipRect(x,y));
1165 if (m_object->hasClip()) {
1166 // Clip applies to *us* as well, so go ahead and update the damageRect.
1167 QRect newPosClip = m_object->getClipRect(x,y);
1168 backgroundRect = backgroundRect.intersect(newPosClip);
1169 foregroundRect = foregroundRect.intersect(newPosClip);
1172 // If we establish a clip at all, then go ahead and make sure our background
1173 // rect is intersected with our layer's bounds.
1174 backgroundRect = backgroundRect.intersect(layerBounds);
1178 static bool mustExamineRenderer(RenderObject* renderer)
1180 if (renderer->isCanvas() || renderer->isRoot() || renderer->isInlineFlow())
1183 QRect bbox = renderer->borderBox();
1184 QRect overflowRect = renderer->overflowRect(false);
1185 if (bbox != overflowRect)
1187 QRect floatRect = renderer->floatRect();
1188 if (bbox != floatRect)
1194 bool RenderLayer::intersectsDamageRect(const QRect& layerBounds, const QRect& damageRect) const
1196 return mustExamineRenderer(renderer()) || layerBounds.intersects(damageRect);
1199 bool RenderLayer::containsPoint(int x, int y, const QRect& damageRect) const
1201 return mustExamineRenderer(renderer()) || damageRect.contains(x, y);
1204 void RenderLayer::clearClipRects()
1211 for (RenderLayer* l = firstChild(); l; l = l->nextSibling())
1212 l->clearClipRects();
1215 void RenderLayer::clearClipRect()
1218 m_clipRects->deref(m_object->renderArena());
1223 // This code has been written to anticipate the addition of CSS3-::outside and ::inside generated
1224 // content (and perhaps XBL). That's why it uses the render tree and not the DOM tree.
1225 static RenderObject* hoverAncestor(RenderObject* obj)
1227 return (!obj->isInline() && obj->continuation()) ? obj->continuation() : obj->parent();
1230 static RenderObject* commonAncestor(RenderObject* obj1, RenderObject* obj2)
1235 for (RenderObject* currObj1 = obj1; currObj1; currObj1 = hoverAncestor(currObj1))
1236 for (RenderObject* currObj2 = obj2; currObj2; currObj2 = hoverAncestor(currObj2))
1237 if (currObj1 == currObj2)
1243 void RenderLayer::updateHoverActiveState(RenderObject::NodeInfo& info)
1245 // We don't update :hover/:active state when the info is marked as readonly.
1246 if (info.readonly())
1249 // Check to see if the hovered node has changed. If not, then we don't need to
1250 // do anything. An exception is if we just went from :hover into :hover:active,
1251 // in which case we need to update to get the new :active state.
1252 DOM::DocumentImpl* doc = renderer()->document();
1253 DOM::NodeImpl* oldHoverNode = doc ? doc->hoverNode() : 0;
1254 DOM::NodeImpl* newHoverNode = info.innerNode();
1256 // Update our current hover node.
1258 doc->setHoverNode(newHoverNode);
1260 // We have two different objects. Fetch their renderers.
1261 RenderObject* oldHoverObj = oldHoverNode ? oldHoverNode->renderer() : 0;
1262 RenderObject* newHoverObj = newHoverNode ? newHoverNode->renderer() : 0;
1264 // Locate the common ancestor render object for the two renderers.
1265 RenderObject* ancestor = commonAncestor(oldHoverObj, newHoverObj);
1267 if (oldHoverObj != newHoverObj) {
1268 // The old hover path only needs to be cleared up to (and not including) the common ancestor;
1269 for (RenderObject* curr = oldHoverObj; curr && curr != ancestor; curr = hoverAncestor(curr)) {
1270 curr->setMouseInside(false);
1271 if (curr->element() && !curr->isText()) {
1272 bool oldActive = curr->element()->active();
1273 curr->element()->setActive(false);
1274 if (curr->style()->affectedByHoverRules() ||
1275 (curr->style()->affectedByActiveRules() && oldActive))
1276 curr->element()->setChanged();
1281 // Now set the hover state for our new object up to the root.
1282 for (RenderObject* curr = newHoverObj; curr; curr = hoverAncestor(curr)) {
1283 bool oldInside = curr->mouseInside();
1284 curr->setMouseInside(true);
1285 if (curr->element() && !curr->isText()) {
1286 bool oldActive = curr->element()->active();
1287 curr->element()->setActive(info.active());
1288 if ((curr->style()->affectedByHoverRules() && !oldInside) ||
1289 (curr->style()->affectedByActiveRules() && oldActive != info.active()))
1290 curr->element()->setChanged();
1295 // Sort the buffer from lowest z-index to highest. The common scenario will have
1296 // most z-indices equal, so we optimize for that case (i.e., the list will be mostly
1298 static void sortByZOrder(QPtrVector<RenderLayer>* buffer,
1299 QPtrVector<RenderLayer>* mergeBuffer,
1300 uint start, uint end)
1303 return; // Sanity check.
1305 if (end - start <= 6) {
1306 // Apply a bubble sort for smaller lists.
1307 for (uint i = end-1; i > start; i--) {
1309 for (uint j = start; j < i; j++) {
1310 RenderLayer* elt = buffer->at(j);
1311 RenderLayer* elt2 = buffer->at(j+1);
1312 if (elt->zIndex() > elt2->zIndex()) {
1314 buffer->insert(j, elt2);
1315 buffer->insert(j+1, elt);
1323 // Peform a merge sort for larger lists.
1324 uint mid = (start+end)/2;
1325 sortByZOrder(buffer, mergeBuffer, start, mid);
1326 sortByZOrder(buffer, mergeBuffer, mid, end);
1328 RenderLayer* elt = buffer->at(mid-1);
1329 RenderLayer* elt2 = buffer->at(mid);
1331 // Handle the fast common case (of equal z-indices). The list may already
1332 // be completely sorted.
1333 if (elt->zIndex() <= elt2->zIndex())
1336 // We have to merge sort. Ensure our merge buffer is big enough to hold
1338 mergeBuffer->resize(end - start);
1342 elt = buffer->at(i1);
1343 elt2 = buffer->at(i2);
1345 while (i1 < mid || i2 < end) {
1346 if (i1 < mid && (i2 == end || elt->zIndex() <= elt2->zIndex())) {
1347 mergeBuffer->insert(mergeBuffer->count(), elt);
1350 elt = buffer->at(i1);
1353 mergeBuffer->insert(mergeBuffer->count(), elt2);
1356 elt2 = buffer->at(i2);
1360 for (uint i = start; i < end; i++)
1361 buffer->insert(i, mergeBuffer->at(i-start));
1363 mergeBuffer->clear();
1367 void RenderLayer::dirtyZOrderLists()
1369 if (m_posZOrderList)
1370 m_posZOrderList->clear();
1371 if (m_negZOrderList)
1372 m_negZOrderList->clear();
1373 m_zOrderListsDirty = true;
1376 void RenderLayer::updateZOrderLists()
1378 if (!isStackingContext() || !m_zOrderListsDirty)
1381 for (RenderLayer* child = firstChild(); child; child = child->nextSibling())
1382 child->collectLayers(m_posZOrderList, m_negZOrderList);
1384 // Sort the two lists.
1385 if (m_posZOrderList) {
1386 QPtrVector<RenderLayer> mergeBuffer;
1387 sortByZOrder(m_posZOrderList, &mergeBuffer, 0, m_posZOrderList->count());
1389 if (m_negZOrderList) {
1390 QPtrVector<RenderLayer> mergeBuffer;
1391 sortByZOrder(m_negZOrderList, &mergeBuffer, 0, m_negZOrderList->count());
1394 m_zOrderListsDirty = false;
1397 void RenderLayer::collectLayers(QPtrVector<RenderLayer>*& posBuffer, QPtrVector<RenderLayer>*& negBuffer)
1399 // FIXME: A child render object or layer could override visibility. Don't remove this
1400 // optimization though until RenderObject's nodeAtPoint is patched to understand what to do
1401 // when visibility is overridden by a child.
1402 if (renderer()->style()->visibility() != VISIBLE)
1405 // Determine which buffer the child should be in.
1406 QPtrVector<RenderLayer>*& buffer = (zIndex() >= 0) ? posBuffer : negBuffer;
1408 // Create the buffer if it doesn't exist yet.
1410 buffer = new QPtrVector<RenderLayer>();
1412 // Resize by a power of 2 when our buffer fills up.
1413 if (buffer->count() == buffer->size())
1414 buffer->resize(2*(buffer->size()+1));
1416 // Append ourselves at the end of the appropriate buffer.
1417 buffer->insert(buffer->count(), this);
1419 // Recur into our children to collect more layers, but only if we don't establish
1420 // a stacking context.
1421 if (!isStackingContext()) {
1422 for (RenderLayer* child = firstChild(); child; child = child->nextSibling())
1423 child->collectLayers(posBuffer, negBuffer);
1427 void RenderLayer::repaintIncludingDescendants()
1429 m_object->repaint();
1430 for (RenderLayer* curr = firstChild(); curr; curr = curr->nextSibling())
1431 curr->repaintIncludingDescendants();
1434 void RenderLayer::styleChanged()
1436 if (m_object->style()->overflow() == OMARQUEE && m_object->style()->marqueeBehavior() != MNONE) {
1438 m_marquee = new Marquee(this);
1439 m_marquee->updateMarqueeStyle();
1441 else if (m_marquee) {
1447 void RenderLayer::suspendMarquees()
1450 m_marquee->suspend();
1452 for (RenderLayer* curr = firstChild(); curr; curr = curr->nextSibling())
1453 curr->suspendMarquees();
1456 // --------------------------------------------------------------------------
1457 // Marquee implementation
1459 Marquee::Marquee(RenderLayer* l)
1460 :m_layer(l), m_currentLoop(0), m_timerId(0), m_start(0), m_end(0), m_speed(0), m_unfurlPos(0), m_reset(false),
1461 m_suspended(false), m_stopped(false), m_whiteSpace(NORMAL), m_direction(MAUTO)
1465 int Marquee::marqueeSpeed() const
1467 int result = m_layer->renderer()->style()->marqueeSpeed();
1468 DOM::NodeImpl* elt = m_layer->renderer()->element();
1469 if (elt && elt->hasTagName(HTMLNames::marquee())) {
1470 HTMLMarqueeElementImpl* marqueeElt = static_cast<HTMLMarqueeElementImpl*>(elt);
1471 result = kMax(result, marqueeElt->minimumDelay());
1476 EMarqueeDirection Marquee::direction() const
1478 // FIXME: Support the CSS3 "auto" value for determining the direction of the marquee.
1479 // For now just map MAUTO to MBACKWARD
1480 EMarqueeDirection result = m_layer->renderer()->style()->marqueeDirection();
1481 EDirection dir = m_layer->renderer()->style()->direction();
1482 if (result == MAUTO)
1484 if (result == MFORWARD)
1485 result = (dir == LTR) ? MRIGHT : MLEFT;
1486 if (result == MBACKWARD)
1487 result = (dir == LTR) ? MLEFT : MRIGHT;
1489 // Now we have the real direction. Next we check to see if the increment is negative.
1490 // If so, then we reverse the direction.
1491 Length increment = m_layer->renderer()->style()->marqueeIncrement();
1492 if (increment.value < 0)
1493 result = static_cast<EMarqueeDirection>(-result);
1498 bool Marquee::isHorizontal() const
1500 return direction() == MLEFT || direction() == MRIGHT;
1503 bool Marquee::isUnfurlMarquee() const
1505 EMarqueeBehavior behavior = m_layer->renderer()->style()->marqueeBehavior();
1506 return (behavior == MUNFURL);
1509 int Marquee::computePosition(EMarqueeDirection dir, bool stopAtContentEdge)
1511 RenderObject* o = m_layer->renderer();
1512 RenderStyle* s = o->style();
1513 if (isHorizontal()) {
1514 bool ltr = s->direction() == LTR;
1515 int clientWidth = o->clientWidth();
1516 int contentWidth = ltr ? o->rightmostPosition(true, false) : o->leftmostPosition(true, false);
1518 contentWidth += (o->paddingRight() - o->borderLeft());
1520 contentWidth = o->width() - contentWidth;
1521 contentWidth += (o->paddingLeft() - o->borderRight());
1523 if (dir == MRIGHT) {
1524 if (stopAtContentEdge)
1525 return kMax(0, ltr ? (contentWidth - clientWidth) : (clientWidth - contentWidth));
1527 return ltr ? contentWidth : clientWidth;
1530 if (stopAtContentEdge)
1531 return kMin(0, ltr ? (contentWidth - clientWidth) : (clientWidth - contentWidth));
1533 return ltr ? -clientWidth : -contentWidth;
1537 int contentHeight = m_layer->renderer()->lowestPosition(true, false) -
1538 m_layer->renderer()->borderTop() + m_layer->renderer()->paddingBottom();
1539 int clientHeight = m_layer->renderer()->clientHeight();
1541 if (stopAtContentEdge)
1542 return kMin(contentHeight - clientHeight, 0);
1544 return -clientHeight;
1547 if (stopAtContentEdge)
1548 return kMax(contentHeight - clientHeight, 0);
1550 return contentHeight;
1555 void Marquee::start()
1557 if (m_timerId || m_layer->renderer()->style()->marqueeIncrement().value == 0)
1560 if (!m_suspended && !m_stopped) {
1561 if (isUnfurlMarquee()) {
1562 bool forward = direction() == MDOWN || direction() == MRIGHT;
1563 bool isReversed = (forward && m_currentLoop % 2) || (!forward && !(m_currentLoop % 2));
1564 m_unfurlPos = isReversed ? m_end : m_start;
1565 m_layer->renderer()->setChildNeedsLayout(true);
1569 m_layer->scrollToOffset(m_start, 0, false, false);
1571 m_layer->scrollToOffset(0, m_start, false, false);
1575 m_suspended = false;
1579 m_timerId = startTimer(speed());
1582 void Marquee::suspend()
1585 killTimer(m_timerId);
1592 void Marquee::stop()
1595 killTimer(m_timerId);
1602 void Marquee::updateMarqueePosition()
1604 bool activate = (m_totalLoops <= 0 || m_currentLoop < m_totalLoops);
1606 if (isUnfurlMarquee()) {
1607 if (m_unfurlPos < m_start) {
1608 m_unfurlPos = m_start;
1609 m_layer->renderer()->setChildNeedsLayout(true);
1611 else if (m_unfurlPos > m_end) {
1612 m_unfurlPos = m_end;
1613 m_layer->renderer()->setChildNeedsLayout(true);
1617 EMarqueeBehavior behavior = m_layer->renderer()->style()->marqueeBehavior();
1618 m_start = computePosition(direction(), behavior == MALTERNATE);
1619 m_end = computePosition(reverseDirection(), behavior == MALTERNATE || behavior == MSLIDE);
1626 void Marquee::updateMarqueeStyle()
1628 RenderStyle* s = m_layer->renderer()->style();
1630 if (m_direction != s->marqueeDirection() || (m_totalLoops != s->marqueeLoopCount() && m_currentLoop >= m_totalLoops))
1631 m_currentLoop = 0; // When direction changes or our loopCount is a smaller number than our current loop, reset our loop.
1633 m_totalLoops = s->marqueeLoopCount();
1634 m_direction = s->marqueeDirection();
1635 m_whiteSpace = s->whiteSpace();
1637 if (m_layer->renderer()->isHTMLMarquee()) {
1638 // Hack for WinIE. In WinIE, a value of 0 or lower for the loop count for SLIDE means to only do
1640 if (m_totalLoops <= 0 && (s->marqueeBehavior() == MSLIDE || s->marqueeBehavior() == MUNFURL))
1643 // Hack alert: Set the white-space value to nowrap for horizontal marquees with inline children, thus ensuring
1644 // all the text ends up on one line by default. Limit this hack to the <marquee> element to emulate
1645 // WinIE's behavior. Someone using CSS3 can use white-space: nowrap on their own to get this effect.
1646 // Second hack alert: Set the text-align back to auto. WinIE completely ignores text-align on the
1648 // FIXME: Bring these up with the CSS WG.
1649 if (isHorizontal() && m_layer->renderer()->childrenInline()) {
1650 s->setWhiteSpace(NOWRAP);
1651 s->setTextAlign(TAAUTO);
1655 if (speed() != marqueeSpeed()) {
1656 m_speed = marqueeSpeed();
1658 killTimer(m_timerId);
1659 m_timerId = startTimer(speed());
1663 // Check the loop count to see if we should now stop.
1664 bool activate = (m_totalLoops <= 0 || m_currentLoop < m_totalLoops);
1665 if (activate && !m_timerId)
1666 m_layer->renderer()->setNeedsLayout(true);
1667 else if (!activate && m_timerId) {
1668 // Destroy the timer.
1669 killTimer(m_timerId);
1674 void Marquee::timerEvent(QTimerEvent* evt)
1676 if (m_layer->renderer()->needsLayout())
1682 m_layer->scrollToXOffset(m_start);
1684 m_layer->scrollToYOffset(m_start);
1688 RenderStyle* s = m_layer->renderer()->style();
1690 int endPoint = m_end;
1691 int range = m_end - m_start;
1696 bool addIncrement = direction() == MUP || direction() == MLEFT;
1697 bool isReversed = s->marqueeBehavior() == MALTERNATE && m_currentLoop % 2;
1698 if (isUnfurlMarquee()) {
1699 isReversed = (!addIncrement && m_currentLoop % 2) || (addIncrement && !(m_currentLoop % 2));
1700 addIncrement = !isReversed;
1703 // We're going in the reverse direction.
1706 if (!isUnfurlMarquee())
1707 addIncrement = !addIncrement;
1709 bool positive = range > 0;
1710 int clientSize = isUnfurlMarquee() ? abs(range) :
1711 (isHorizontal() ? m_layer->renderer()->clientWidth() : m_layer->renderer()->clientHeight());
1712 int increment = kMax(1, abs(m_layer->renderer()->style()->marqueeIncrement().width(clientSize)));
1713 int currentPos = isUnfurlMarquee() ? m_unfurlPos :
1714 (isHorizontal() ? m_layer->scrollXOffset() : m_layer->scrollYOffset());
1715 newPos = currentPos + (addIncrement ? increment : -increment);
1717 newPos = kMin(newPos, endPoint);
1719 newPos = kMax(newPos, endPoint);
1722 if (newPos == endPoint) {
1724 if (m_totalLoops > 0 && m_currentLoop >= m_totalLoops) {
1725 killTimer(m_timerId);
1728 else if (s->marqueeBehavior() != MALTERNATE && s->marqueeBehavior() != MUNFURL)
1732 if (isUnfurlMarquee()) {
1733 m_unfurlPos = newPos;
1734 m_layer->renderer()->setChildNeedsLayout(true);
1738 m_layer->scrollToXOffset(newPos);
1740 m_layer->scrollToYOffset(newPos);