summaryrefslogtreecommitdiffstats
path: root/src/3rdparty/webkit/WebCore/rendering/RenderLayer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/3rdparty/webkit/WebCore/rendering/RenderLayer.cpp')
-rw-r--r--src/3rdparty/webkit/WebCore/rendering/RenderLayer.cpp2609
1 files changed, 2609 insertions, 0 deletions
diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderLayer.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderLayer.cpp
new file mode 100644
index 0000000..9c9eff0
--- /dev/null
+++ b/src/3rdparty/webkit/WebCore/rendering/RenderLayer.cpp
@@ -0,0 +1,2609 @@
+/*
+ * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
+ *
+ * Portions are Copyright (C) 1998 Netscape Communications Corporation.
+ *
+ * Other contributors:
+ * Robert O'Callahan <roc+@cs.cmu.edu>
+ * David Baron <dbaron@fas.harvard.edu>
+ * Christian Biesinger <cbiesinger@web.de>
+ * Randall Jesup <rjesup@wgate.com>
+ * Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>
+ * Josh Soref <timeless@mac.com>
+ * Boris Zbarsky <bzbarsky@mit.edu>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Alternatively, the contents of this file may be used under the terms
+ * of either the Mozilla Public License Version 1.1, found at
+ * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public
+ * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html
+ * (the "GPL"), in which case the provisions of the MPL or the GPL are
+ * applicable instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of one of those two
+ * licenses (the MPL or the GPL) and not to allow others to use your
+ * version of this file under the LGPL, indicate your decision by
+ * deletingthe provisions above and replace them with the notice and
+ * other provisions required by the MPL or the GPL, as the case may be.
+ * If you do not delete the provisions above, a recipient may use your
+ * version of this file under any of the LGPL, the MPL or the GPL.
+ */
+
+#include "config.h"
+#include "RenderLayer.h"
+
+#include "CSSPropertyNames.h"
+#include "Document.h"
+#include "EventHandler.h"
+#include "EventNames.h"
+#include "FloatRect.h"
+#include "FocusController.h"
+#include "Frame.h"
+#include "FrameTree.h"
+#include "FrameView.h"
+#include "Gradient.h"
+#include "GraphicsContext.h"
+#include "HTMLNames.h"
+#include "HitTestRequest.h"
+#include "HitTestResult.h"
+#include "OverflowEvent.h"
+#include "Page.h"
+#include "PlatformMouseEvent.h"
+#include "RenderArena.h"
+#include "RenderInline.h"
+#include "RenderMarquee.h"
+#include "RenderReplica.h"
+#include "RenderScrollbar.h"
+#include "RenderScrollbarPart.h"
+#include "RenderTheme.h"
+#include "RenderView.h"
+#include "ScaleTransformOperation.h"
+#include "Scrollbar.h"
+#include "ScrollbarTheme.h"
+#include "SelectionController.h"
+#include "TranslateTransformOperation.h"
+#include <wtf/StdLibExtras.h>
+
+#if ENABLE(SVG)
+#include "SVGNames.h"
+#endif
+
+#define MIN_INTERSECT_FOR_REVEAL 32
+
+using namespace std;
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+const RenderLayer::ScrollAlignment RenderLayer::gAlignCenterIfNeeded = { RenderLayer::noScroll, RenderLayer::alignCenter, RenderLayer::alignToClosestEdge };
+const RenderLayer::ScrollAlignment RenderLayer::gAlignToEdgeIfNeeded = { RenderLayer::noScroll, RenderLayer::alignToClosestEdge, RenderLayer::alignToClosestEdge };
+const RenderLayer::ScrollAlignment RenderLayer::gAlignCenterAlways = { RenderLayer::alignCenter, RenderLayer::alignCenter, RenderLayer::alignCenter };
+const RenderLayer::ScrollAlignment RenderLayer::gAlignTopAlways = { RenderLayer::alignTop, RenderLayer::alignTop, RenderLayer::alignTop };
+const RenderLayer::ScrollAlignment RenderLayer::gAlignBottomAlways = { RenderLayer::alignBottom, RenderLayer::alignBottom, RenderLayer::alignBottom };
+
+const int MinimumWidthWhileResizing = 100;
+const int MinimumHeightWhileResizing = 40;
+
+void* ClipRects::operator new(size_t sz, RenderArena* renderArena) throw()
+{
+ return renderArena->allocate(sz);
+}
+
+void ClipRects::operator delete(void* ptr, size_t sz)
+{
+ // Stash size where destroy can find it.
+ *(size_t *)ptr = sz;
+}
+
+void ClipRects::destroy(RenderArena* renderArena)
+{
+ delete this;
+
+ // Recover the size left there for us by operator delete and free the memory.
+ renderArena->free(*(size_t *)this, this);
+}
+
+RenderLayer::RenderLayer(RenderObject* object)
+ : m_object(object)
+ , m_parent(0)
+ , m_previous(0)
+ , m_next(0)
+ , m_first(0)
+ , m_last(0)
+ , m_relX(0)
+ , m_relY(0)
+ , m_x(0)
+ , m_y(0)
+ , m_width(0)
+ , m_height(0)
+ , m_scrollX(0)
+ , m_scrollY(0)
+ , m_scrollOriginX(0)
+ , m_scrollLeftOverflow(0)
+ , m_scrollWidth(0)
+ , m_scrollHeight(0)
+ , m_inResizeMode(false)
+ , m_posZOrderList(0)
+ , m_negZOrderList(0)
+ , m_overflowList(0)
+ , m_clipRects(0)
+#ifndef NDEBUG
+ , m_clipRectsRoot(0)
+#endif
+ , m_scrollDimensionsDirty(true)
+ , m_zOrderListsDirty(true)
+ , m_overflowListDirty(true)
+ , m_isOverflowOnly(shouldBeOverflowOnly())
+ , m_usedTransparency(false)
+ , m_paintingInsideReflection(false)
+ , m_inOverflowRelayout(false)
+ , m_needsFullRepaint(false)
+ , m_overflowStatusDirty(true)
+ , m_visibleContentStatusDirty(true)
+ , m_hasVisibleContent(false)
+ , m_visibleDescendantStatusDirty(false)
+ , m_hasVisibleDescendant(false)
+ , m_marquee(0)
+ , m_staticX(0)
+ , m_staticY(0)
+ , m_transform(0)
+ , m_reflection(0)
+ , m_scrollCorner(0)
+ , m_resizer(0)
+{
+ if (!object->firstChild() && object->style()) {
+ m_visibleContentStatusDirty = false;
+ m_hasVisibleContent = object->style()->visibility() == VISIBLE;
+ }
+}
+
+RenderLayer::~RenderLayer()
+{
+ if (inResizeMode() && !renderer()->documentBeingDestroyed()) {
+ if (Frame* frame = renderer()->document()->frame())
+ frame->eventHandler()->resizeLayerDestroyed();
+ }
+
+ destroyScrollbar(HorizontalScrollbar);
+ destroyScrollbar(VerticalScrollbar);
+
+ // Child layers will be deleted by their corresponding render objects, so
+ // we don't need to delete them ourselves.
+
+ delete m_posZOrderList;
+ delete m_negZOrderList;
+ delete m_overflowList;
+ delete m_marquee;
+
+ // Make sure we have no lingering clip rects.
+ ASSERT(!m_clipRects);
+
+ if (m_reflection) {
+ if (!m_reflection->documentBeingDestroyed())
+ m_reflection->removeLayers(this);
+ m_reflection->setParent(0);
+ m_reflection->destroy();
+ }
+
+ if (m_scrollCorner)
+ m_scrollCorner->destroy();
+ if (m_resizer)
+ m_resizer->destroy();
+}
+
+void RenderLayer::updateLayerPositions(bool doFullRepaint, bool checkForRepaint)
+{
+ if (doFullRepaint) {
+ m_object->repaint();
+ checkForRepaint = doFullRepaint = false;
+ }
+
+ updateLayerPosition(); // For relpositioned layers or non-positioned layers,
+ // we need to keep in sync, since we may have shifted relative
+ // to our parent layer.
+
+ int x = 0;
+ int y = 0;
+ convertToLayerCoords(root(), x, y);
+ positionOverflowControls(x, y);
+
+ updateVisibilityStatus();
+
+ updateTransform();
+
+ if (m_hasVisibleContent) {
+ RenderView* view = m_object->view();
+ ASSERT(view);
+ // FIXME: Optimize using LayoutState and remove the disableLayoutState() call
+ // from updateScrollInfoAfterLayout().
+ ASSERT(!view->layoutState());
+
+ IntRect newRect = m_object->absoluteClippedOverflowRect();
+ IntRect newOutlineBox = m_object->absoluteOutlineBounds();
+ if (checkForRepaint) {
+ if (view && !view->printing()) {
+ if (m_needsFullRepaint) {
+ view->repaintViewRectangle(m_repaintRect);
+ if (newRect != m_repaintRect)
+ view->repaintViewRectangle(newRect);
+ } else
+ m_object->repaintAfterLayoutIfNeeded(m_repaintRect, m_outlineBox);
+ }
+ }
+ m_repaintRect = newRect;
+ m_outlineBox = newOutlineBox;
+ } else {
+ m_repaintRect = IntRect();
+ m_outlineBox = IntRect();
+ }
+
+ m_needsFullRepaint = false;
+
+ // Go ahead and update the reflection's position and size.
+ if (m_reflection)
+ m_reflection->layout();
+
+ for (RenderLayer* child = firstChild(); child; child = child->nextSibling())
+ child->updateLayerPositions(doFullRepaint, checkForRepaint);
+
+ // With all our children positioned, now update our marquee if we need to.
+ if (m_marquee)
+ m_marquee->updateMarqueePosition();
+}
+
+void RenderLayer::updateTransform()
+{
+ bool hasTransform = renderer()->hasTransform();
+ bool hadTransform = m_transform;
+ if (hasTransform != hadTransform) {
+ if (hasTransform)
+ m_transform.set(new TransformationMatrix);
+ else
+ m_transform.clear();
+ }
+
+ if (hasTransform) {
+ m_transform->reset();
+ renderer()->style()->applyTransform(*m_transform, renderer()->borderBox().size());
+ }
+}
+
+void RenderLayer::setHasVisibleContent(bool b)
+{
+ if (m_hasVisibleContent == b && !m_visibleContentStatusDirty)
+ return;
+ m_visibleContentStatusDirty = false;
+ m_hasVisibleContent = b;
+ if (m_hasVisibleContent) {
+ m_repaintRect = renderer()->absoluteClippedOverflowRect();
+ m_outlineBox = renderer()->absoluteOutlineBounds();
+ if (!isOverflowOnly())
+ dirtyStackingContextZOrderLists();
+ }
+ if (parent())
+ parent()->childVisibilityChanged(m_hasVisibleContent);
+}
+
+void RenderLayer::dirtyVisibleContentStatus()
+{
+ m_visibleContentStatusDirty = true;
+ if (parent())
+ parent()->dirtyVisibleDescendantStatus();
+}
+
+void RenderLayer::childVisibilityChanged(bool newVisibility)
+{
+ if (m_hasVisibleDescendant == newVisibility || m_visibleDescendantStatusDirty)
+ return;
+ if (newVisibility) {
+ RenderLayer* l = this;
+ while (l && !l->m_visibleDescendantStatusDirty && !l->m_hasVisibleDescendant) {
+ l->m_hasVisibleDescendant = true;
+ l = l->parent();
+ }
+ } else
+ dirtyVisibleDescendantStatus();
+}
+
+void RenderLayer::dirtyVisibleDescendantStatus()
+{
+ RenderLayer* l = this;
+ while (l && !l->m_visibleDescendantStatusDirty) {
+ l->m_visibleDescendantStatusDirty = true;
+ l = l->parent();
+ }
+}
+
+void RenderLayer::updateVisibilityStatus()
+{
+ if (m_visibleDescendantStatusDirty) {
+ m_hasVisibleDescendant = false;
+ for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) {
+ child->updateVisibilityStatus();
+ if (child->m_hasVisibleContent || child->m_hasVisibleDescendant) {
+ m_hasVisibleDescendant = true;
+ break;
+ }
+ }
+ m_visibleDescendantStatusDirty = false;
+ }
+
+ if (m_visibleContentStatusDirty) {
+ if (m_object->style()->visibility() == VISIBLE)
+ m_hasVisibleContent = true;
+ else {
+ // layer may be hidden but still have some visible content, check for this
+ m_hasVisibleContent = false;
+ RenderObject* r = m_object->firstChild();
+ while (r) {
+ if (r->style()->visibility() == VISIBLE && !r->hasLayer()) {
+ m_hasVisibleContent = true;
+ break;
+ }
+ if (r->firstChild() && !r->hasLayer())
+ r = r->firstChild();
+ else if (r->nextSibling())
+ r = r->nextSibling();
+ else {
+ do {
+ r = r->parent();
+ if (r==m_object)
+ r = 0;
+ } while (r && !r->nextSibling());
+ if (r)
+ r = r->nextSibling();
+ }
+ }
+ }
+ m_visibleContentStatusDirty = false;
+ }
+}
+
+void RenderLayer::updateLayerPosition()
+{
+ // Clear our cached clip rect information.
+ clearClipRects();
+
+ int x = m_object->xPos();
+ int y = m_object->yPos() - m_object->borderTopExtra();
+
+ if (!m_object->isPositioned() && m_object->parent()) {
+ // We must adjust our position by walking up the render tree looking for the
+ // nearest enclosing object with a layer.
+ RenderObject* curr = m_object->parent();
+ while (curr && !curr->hasLayer()) {
+ if (!curr->isTableRow()) {
+ // Rows and cells share the same coordinate space (that of the section).
+ // Omit them when computing our xpos/ypos.
+ x += curr->xPos();
+ y += curr->yPos();
+ }
+ curr = curr->parent();
+ }
+ y += curr->borderTopExtra();
+ if (curr->isTableRow()) {
+ // Put ourselves into the row coordinate space.
+ x -= curr->xPos();
+ y -= curr->yPos();
+ }
+ }
+
+ m_relX = m_relY = 0;
+ if (m_object->isRelPositioned()) {
+ m_relX = static_cast<RenderBox*>(m_object)->relativePositionOffsetX();
+ m_relY = static_cast<RenderBox*>(m_object)->relativePositionOffsetY();
+ x += m_relX; y += m_relY;
+ }
+
+ // Subtract our parent's scroll offset.
+ if (m_object->isPositioned() && enclosingPositionedAncestor()) {
+ RenderLayer* positionedParent = enclosingPositionedAncestor();
+
+ // For positioned layers, we subtract out the enclosing positioned layer's scroll offset.
+ positionedParent->subtractScrolledContentOffset(x, y);
+
+ if (m_object->isPositioned()) {
+ IntSize offset = static_cast<RenderBox*>(m_object)->offsetForPositionedInContainer(positionedParent->renderer());
+ x += offset.width();
+ y += offset.height();
+ }
+ } else if (parent())
+ parent()->subtractScrolledContentOffset(x, y);
+
+ setPos(x,y);
+
+ setWidth(m_object->width());
+ setHeight(m_object->height() + m_object->borderTopExtra() + m_object->borderBottomExtra());
+
+ if (!m_object->hasOverflowClip()) {
+ if (m_object->overflowWidth() > m_object->width())
+ setWidth(m_object->overflowWidth());
+ if (m_object->overflowHeight() > m_object->height())
+ setHeight(m_object->overflowHeight());
+ }
+}
+
+RenderLayer *RenderLayer::stackingContext() const
+{
+ RenderLayer* curr = parent();
+ for ( ; curr && !curr->m_object->isRenderView() && !curr->m_object->isRoot() &&
+ curr->m_object->style()->hasAutoZIndex();
+ curr = curr->parent()) { }
+ return curr;
+}
+
+RenderLayer* RenderLayer::enclosingPositionedAncestor() const
+{
+ RenderLayer* curr = parent();
+ for ( ; curr && !curr->m_object->isRenderView() && !curr->m_object->isPositioned() && !curr->m_object->isRelPositioned() && !curr->hasTransform();
+ curr = curr->parent()) { }
+ return curr;
+}
+
+RenderLayer* RenderLayer::enclosingTransformedAncestor() const
+{
+ RenderLayer* curr = parent();
+ for ( ; curr && !curr->m_object->isRenderView() && !curr->transform(); curr = curr->parent())
+ { }
+ return curr;
+}
+
+IntPoint RenderLayer::absoluteToContents(const IntPoint& absolutePoint) const
+{
+ // We don't use convertToLayerCoords because it doesn't know about transforms
+ return roundedIntPoint(renderer()->absoluteToLocal(absolutePoint, false, true));
+}
+
+bool RenderLayer::requiresSlowRepaints() const
+{
+ if (isTransparent() || hasReflection() || hasTransform())
+ return true;
+ if (!parent())
+ return false;
+ return parent()->requiresSlowRepaints();
+}
+
+bool RenderLayer::isTransparent() const
+{
+#if ENABLE(SVG)
+ if (m_object->node()->namespaceURI() == SVGNames::svgNamespaceURI)
+ return false;
+#endif
+ return m_object->isTransparent() || m_object->hasMask();
+}
+
+RenderLayer*
+RenderLayer::transparentAncestor()
+{
+ RenderLayer* curr = parent();
+ for ( ; curr && !curr->isTransparent(); curr = curr->parent()) { }
+ return curr;
+}
+
+static IntRect transparencyClipBox(const TransformationMatrix& enclosingTransform, const RenderLayer* l, const RenderLayer* rootLayer)
+{
+ // FIXME: Although this function completely ignores CSS-imposed clipping, we did already intersect with the
+ // paintDirtyRect, and that should cut down on the amount we have to paint. Still it
+ // would be better to respect clips.
+
+ TransformationMatrix* t = l->transform();
+ if (t && rootLayer != l) {
+ // The best we can do here is to use enclosed bounding boxes to establish a "fuzzy" enough clip to encompass
+ // the transformed layer and all of its children.
+ int x = 0;
+ int y = 0;
+ l->convertToLayerCoords(rootLayer, x, y);
+ TransformationMatrix transform;
+ transform.translate(x, y);
+ transform = *t * transform;
+ transform = transform * enclosingTransform;
+
+ // We now have a transform that will produce a rectangle in our view's space.
+ IntRect clipRect = transform.mapRect(l->boundingBox(l));
+
+ // Now shift the root layer to be us and pass down the new enclosing transform.
+ for (RenderLayer* curr = l->firstChild(); curr; curr = curr->nextSibling()) {
+ if (!l->reflection() || l->reflectionLayer() != curr)
+ clipRect.unite(transparencyClipBox(transform, curr, l));
+ }
+
+ return clipRect;
+ }
+
+ // Note: we don't have to walk z-order lists since transparent elements always establish
+ // a stacking context. This means we can just walk the layer tree directly.
+ IntRect clipRect = l->boundingBox(rootLayer);
+
+ // If we have a mask, then the clip is limited to the border box area (and there is
+ // no need to examine child layers).
+ if (!l->renderer()->hasMask()) {
+ for (RenderLayer* curr = l->firstChild(); curr; curr = curr->nextSibling()) {
+ if (!l->reflection() || l->reflectionLayer() != curr)
+ clipRect.unite(transparencyClipBox(enclosingTransform, curr, rootLayer));
+ }
+ }
+
+ // Now map the clipRect via the enclosing transform
+ return enclosingTransform.mapRect(clipRect);
+}
+
+void RenderLayer::beginTransparencyLayers(GraphicsContext* p, const RenderLayer* rootLayer)
+{
+ if (p->paintingDisabled() || (isTransparent() && m_usedTransparency))
+ return;
+
+ RenderLayer* ancestor = transparentAncestor();
+ if (ancestor)
+ ancestor->beginTransparencyLayers(p, rootLayer);
+
+ if (isTransparent()) {
+ m_usedTransparency = true;
+ p->save();
+ p->clip(transparencyClipBox(TransformationMatrix(), this, rootLayer));
+ p->beginTransparencyLayer(renderer()->opacity());
+ }
+}
+
+void* RenderLayer::operator new(size_t sz, RenderArena* renderArena) throw()
+{
+ return renderArena->allocate(sz);
+}
+
+void RenderLayer::operator delete(void* ptr, size_t sz)
+{
+ // Stash size where destroy can find it.
+ *(size_t *)ptr = sz;
+}
+
+void RenderLayer::destroy(RenderArena* renderArena)
+{
+ delete this;
+
+ // Recover the size left there for us by operator delete and free the memory.
+ renderArena->free(*(size_t *)this, this);
+}
+
+void RenderLayer::addChild(RenderLayer* child, RenderLayer* beforeChild)
+{
+ RenderLayer* prevSibling = beforeChild ? beforeChild->previousSibling() : lastChild();
+ if (prevSibling) {
+ child->setPreviousSibling(prevSibling);
+ prevSibling->setNextSibling(child);
+ } else
+ setFirstChild(child);
+
+ if (beforeChild) {
+ beforeChild->setPreviousSibling(child);
+ child->setNextSibling(beforeChild);
+ } else
+ setLastChild(child);
+
+ child->setParent(this);
+
+ if (child->isOverflowOnly())
+ dirtyOverflowList();
+
+ if (!child->isOverflowOnly() || child->firstChild()) {
+ // Dirty the z-order list in which we are contained. The stackingContext() can be null in the
+ // case where we're building up generated content layers. This is ok, since the lists will start
+ // off dirty in that case anyway.
+ child->dirtyStackingContextZOrderLists();
+ }
+
+ child->updateVisibilityStatus();
+ if (child->m_hasVisibleContent || child->m_hasVisibleDescendant)
+ childVisibilityChanged(true);
+}
+
+RenderLayer* RenderLayer::removeChild(RenderLayer* oldChild)
+{
+ // remove the child
+ if (oldChild->previousSibling())
+ oldChild->previousSibling()->setNextSibling(oldChild->nextSibling());
+ if (oldChild->nextSibling())
+ oldChild->nextSibling()->setPreviousSibling(oldChild->previousSibling());
+
+ if (m_first == oldChild)
+ m_first = oldChild->nextSibling();
+ if (m_last == oldChild)
+ m_last = oldChild->previousSibling();
+
+ if (oldChild->isOverflowOnly())
+ dirtyOverflowList();
+ if (!oldChild->isOverflowOnly() || oldChild->firstChild()) {
+ // Dirty the z-order list in which we are contained. When called via the
+ // reattachment process in removeOnlyThisLayer, the layer may already be disconnected
+ // from the main layer tree, so we need to null-check the |stackingContext| value.
+ oldChild->dirtyStackingContextZOrderLists();
+ }
+
+ oldChild->setPreviousSibling(0);
+ oldChild->setNextSibling(0);
+ oldChild->setParent(0);
+
+ oldChild->updateVisibilityStatus();
+ if (oldChild->m_hasVisibleContent || oldChild->m_hasVisibleDescendant)
+ childVisibilityChanged(false);
+
+ return oldChild;
+}
+
+void RenderLayer::removeOnlyThisLayer()
+{
+ if (!m_parent)
+ return;
+
+ // Dirty the clip rects.
+ clearClipRectsIncludingDescendants();
+
+ // Remove us from the parent.
+ RenderLayer* parent = m_parent;
+ RenderLayer* nextSib = nextSibling();
+ parent->removeChild(this);
+
+ if (reflection())
+ removeChild(reflectionLayer());
+
+ // Now walk our kids and reattach them to our parent.
+ RenderLayer* current = m_first;
+ while (current) {
+ RenderLayer* next = current->nextSibling();
+ removeChild(current);
+ parent->addChild(current, nextSib);
+ current->updateLayerPositions();
+ current = next;
+ }
+
+ destroy(renderer()->renderArena());
+}
+
+void RenderLayer::insertOnlyThisLayer()
+{
+ if (!m_parent && renderer()->parent()) {
+ // We need to connect ourselves when our renderer() has a parent.
+ // Find our enclosingLayer and add ourselves.
+ RenderLayer* parentLayer = renderer()->parent()->enclosingLayer();
+ RenderLayer* beforeChild = parentLayer->reflectionLayer() != this ? renderer()->parent()->findNextLayer(parentLayer, renderer()) : 0;
+ if (parentLayer)
+ parentLayer->addChild(this, beforeChild);
+ }
+
+ // Remove all descendant layers from the hierarchy and add them to the new position.
+ for (RenderObject* curr = renderer()->firstChild(); curr; curr = curr->nextSibling())
+ curr->moveLayers(m_parent, this);
+
+ // Clear out all the clip rects.
+ clearClipRectsIncludingDescendants();
+}
+
+void
+RenderLayer::convertToLayerCoords(const RenderLayer* ancestorLayer, int& x, int& y) const
+{
+ if (ancestorLayer == this)
+ return;
+
+ if (m_object->style()->position() == FixedPosition) {
+ // Add in the offset of the view. We can obtain this by calling
+ // localToAbsolute() on the RenderView.
+ FloatPoint absPos = m_object->localToAbsolute(FloatPoint(), true);
+ x += absPos.x();
+ y += absPos.y();
+ return;
+ }
+
+ RenderLayer* parentLayer;
+ if (m_object->style()->position() == AbsolutePosition)
+ parentLayer = enclosingPositionedAncestor();
+ else
+ parentLayer = parent();
+
+ if (!parentLayer) return;
+
+ parentLayer->convertToLayerCoords(ancestorLayer, x, y);
+
+ x += xPos();
+ y += yPos();
+}
+
+void RenderLayer::panScrollFromPoint(const IntPoint& sourcePoint)
+{
+ // We want to reduce the speed if we're close from the original point to improve the handleability of the scroll
+ const int shortDistanceLimit = 100; // We delimit a 200 pixels long square enclosing the original point
+ const int speedReducer = 2; // Within this square we divide the scrolling speed by 2
+
+ const int iconRadius = 10;
+ Frame* frame = renderer()->document()->frame();
+ if (!frame)
+ return;
+
+ IntPoint currentMousePosition = frame->eventHandler()->currentMousePosition();
+
+ // We need to check if the current mouse position is out of the window. When the mouse is out of the window, the position is incoherent
+ static IntPoint previousMousePosition;
+ if (currentMousePosition.x() < 0 || currentMousePosition.y() < 0)
+ currentMousePosition = previousMousePosition;
+ else
+ previousMousePosition = currentMousePosition;
+
+ int xDelta = currentMousePosition.x() - sourcePoint.x();
+ int yDelta = currentMousePosition.y() - sourcePoint.y();
+
+ if (abs(xDelta) < iconRadius) // at the center we let the space for the icon
+ xDelta = 0;
+ if (abs(yDelta) < iconRadius)
+ yDelta = 0;
+
+ // Let's attenuate the speed for the short distances
+ if (abs(xDelta) < shortDistanceLimit)
+ xDelta /= speedReducer;
+ if (abs(yDelta) < shortDistanceLimit)
+ yDelta /= speedReducer;
+
+ scrollByRecursively(xDelta, yDelta);
+}
+
+void RenderLayer::scrollByRecursively(int xDelta, int yDelta)
+{
+ bool restrictedByLineClamp = false;
+ if (m_object->parent())
+ restrictedByLineClamp = m_object->parent()->style()->lineClamp() >= 0;
+
+ if (m_object->hasOverflowClip() && !restrictedByLineClamp) {
+ int newOffsetX = scrollXOffset() + xDelta;
+ int newOffsetY = scrollYOffset() + yDelta;
+ scrollToOffset(newOffsetX, newOffsetY);
+
+ // If this layer can't do the scroll we ask its parent
+ int leftToScrollX = newOffsetX - scrollXOffset();
+ int leftToScrollY = newOffsetY - scrollYOffset();
+ if ((leftToScrollX || leftToScrollY) && m_object->parent()) {
+ m_object->parent()->enclosingLayer()->scrollByRecursively(leftToScrollX, leftToScrollY);
+ Frame* frame = renderer()->document()->frame();
+ if (frame)
+ frame->eventHandler()->updateAutoscrollRenderer();
+ }
+ } else if (m_object->view()->frameView())
+ m_object->view()->frameView()->scrollBy(IntSize(xDelta, yDelta));
+}
+
+
+void
+RenderLayer::addScrolledContentOffset(int& x, int& y) const
+{
+ x += scrollXOffset() + m_scrollLeftOverflow;
+ y += scrollYOffset();
+}
+
+void
+RenderLayer::subtractScrolledContentOffset(int& x, int& y) const
+{
+ x -= scrollXOffset() + m_scrollLeftOverflow;
+ y -= scrollYOffset();
+}
+
+void RenderLayer::scrollToOffset(int x, int y, bool updateScrollbars, bool repaint)
+{
+ if (renderer()->style()->overflowX() != OMARQUEE) {
+ if (x < 0) x = 0;
+ if (y < 0) y = 0;
+
+ // Call the scrollWidth/Height functions so that the dimensions will be computed if they need
+ // to be (for overflow:hidden blocks).
+ int maxX = scrollWidth() - m_object->clientWidth();
+ int maxY = scrollHeight() - m_object->clientHeight();
+
+ if (x > maxX) x = maxX;
+ if (y > maxY) y = maxY;
+ }
+
+ // FIXME: Eventually, we will want to perform a blit. For now never
+ // blit, since the check for blitting is going to be very
+ // complicated (since it will involve testing whether our layer
+ // is either occluded by another layer or clipped by an enclosing
+ // layer or contains fixed backgrounds, etc.).
+ int newScrollX = x - m_scrollOriginX;
+ if (m_scrollY == y && m_scrollX == newScrollX)
+ return;
+ m_scrollX = newScrollX;
+ m_scrollY = y;
+
+ // Update the positions of our child layers.
+ for (RenderLayer* child = firstChild(); child; child = child->nextSibling())
+ child->updateLayerPositions(false, false);
+
+ RenderView* view = renderer()->view();
+
+ // We should have a RenderView if we're trying to scroll.
+ ASSERT(view);
+ if (view) {
+#if ENABLE(DASHBOARD_SUPPORT)
+ // Update dashboard regions, scrolling may change the clip of a
+ // particular region.
+ view->frameView()->updateDashboardRegions();
+#endif
+
+ view->updateWidgetPositions();
+ }
+
+ // The caret rect needs to be invalidated after scrolling
+ Frame* frame = renderer()->document()->frame();
+ if (frame)
+ frame->invalidateSelection();
+
+ // Just schedule a full repaint of our object.
+ if (repaint)
+ m_object->repaint();
+
+ if (updateScrollbars) {
+ if (m_hBar)
+ m_hBar->setValue(scrollXOffset());
+ if (m_vBar)
+ m_vBar->setValue(m_scrollY);
+ }
+
+ // Schedule the scroll DOM event.
+ if (view) {
+ if (FrameView* frameView = view->frameView())
+ frameView->scheduleEvent(Event::create(eventNames().scrollEvent, false, false), EventTargetNodeCast(renderer()->element()));
+ }
+}
+
+void RenderLayer::scrollRectToVisible(const IntRect &rect, bool scrollToAnchor, const ScrollAlignment& alignX, const ScrollAlignment& alignY)
+{
+ RenderLayer* parentLayer = 0;
+ IntRect newRect = rect;
+ int xOffset = 0, yOffset = 0;
+
+ // We may end up propagating a scroll event. It is important that we suspend events until
+ // the end of the function since they could delete the layer or the layer's m_object.
+ FrameView* frameView = m_object->document()->view();
+ if (frameView)
+ frameView->pauseScheduledEvents();
+
+ bool restrictedByLineClamp = false;
+ if (m_object->parent()) {
+ parentLayer = m_object->parent()->enclosingLayer();
+ restrictedByLineClamp = m_object->parent()->style()->lineClamp() >= 0;
+ }
+
+ if (m_object->hasOverflowClip() && !restrictedByLineClamp) {
+ // Don't scroll to reveal an overflow layer that is restricted by the -webkit-line-clamp property.
+ // This will prevent us from revealing text hidden by the slider in Safari RSS.
+ FloatPoint absPos = m_object->localToAbsolute();
+ absPos.move(m_object->borderLeft(), m_object->borderTop());
+
+ IntRect layerBounds = IntRect(absPos.x() + scrollXOffset(), absPos.y() + scrollYOffset(), m_object->clientWidth(), m_object->clientHeight());
+ IntRect exposeRect = IntRect(rect.x() + scrollXOffset(), rect.y() + scrollYOffset(), rect.width(), rect.height());
+ IntRect r = getRectToExpose(layerBounds, exposeRect, alignX, alignY);
+
+ xOffset = r.x() - absPos.x();
+ yOffset = r.y() - absPos.y();
+ // Adjust offsets if they're outside of the allowable range.
+ xOffset = max(0, min(scrollWidth() - layerBounds.width(), xOffset));
+ yOffset = max(0, min(scrollHeight() - layerBounds.height(), yOffset));
+
+ if (xOffset != scrollXOffset() || yOffset != scrollYOffset()) {
+ int diffX = scrollXOffset();
+ int diffY = scrollYOffset();
+ scrollToOffset(xOffset, yOffset);
+ diffX = scrollXOffset() - diffX;
+ diffY = scrollYOffset() - diffY;
+ newRect.setX(rect.x() - diffX);
+ newRect.setY(rect.y() - diffY);
+ }
+ } else if (!parentLayer && renderer()->canBeProgramaticallyScrolled(scrollToAnchor)) {
+ if (frameView) {
+ if (m_object->document() && m_object->document()->ownerElement() && m_object->document()->ownerElement()->renderer()) {
+ IntRect viewRect = frameView->visibleContentRect();
+ IntRect r = getRectToExpose(viewRect, rect, alignX, alignY);
+
+ xOffset = r.x();
+ yOffset = r.y();
+ // Adjust offsets if they're outside of the allowable range.
+ xOffset = max(0, min(frameView->contentsWidth(), xOffset));
+ yOffset = max(0, min(frameView->contentsHeight(), yOffset));
+
+ frameView->setScrollPosition(IntPoint(xOffset, yOffset));
+ parentLayer = m_object->document()->ownerElement()->renderer()->enclosingLayer();
+ newRect.setX(rect.x() - frameView->scrollX() + frameView->x());
+ newRect.setY(rect.y() - frameView->scrollY() + frameView->y());
+ } else {
+ IntRect viewRect = frameView->visibleContentRect(true);
+ IntRect r = getRectToExpose(viewRect, rect, alignX, alignY);
+
+ // If this is the outermost view that RenderLayer needs to scroll, then we should scroll the view recursively
+ // Other apps, like Mail, rely on this feature.
+ frameView->scrollRectIntoViewRecursively(r);
+ }
+ }
+ }
+
+ if (parentLayer)
+ parentLayer->scrollRectToVisible(newRect, scrollToAnchor, alignX, alignY);
+
+ if (frameView)
+ frameView->resumeScheduledEvents();
+}
+
+IntRect RenderLayer::getRectToExpose(const IntRect &visibleRect, const IntRect &exposeRect, const ScrollAlignment& alignX, const ScrollAlignment& alignY)
+{
+ // Determine the appropriate X behavior.
+ ScrollBehavior scrollX;
+ IntRect exposeRectX(exposeRect.x(), visibleRect.y(), exposeRect.width(), visibleRect.height());
+ int intersectWidth = intersection(visibleRect, exposeRectX).width();
+ if (intersectWidth == exposeRect.width() || intersectWidth >= MIN_INTERSECT_FOR_REVEAL)
+ // If the rectangle is fully visible, use the specified visible behavior.
+ // If the rectangle is partially visible, but over a certain threshold,
+ // then treat it as fully visible to avoid unnecessary horizontal scrolling
+ scrollX = getVisibleBehavior(alignX);
+ else if (intersectWidth == visibleRect.width()) {
+ // If the rect is bigger than the visible area, don't bother trying to center. Other alignments will work.
+ scrollX = getVisibleBehavior(alignX);
+ if (scrollX == alignCenter)
+ scrollX = noScroll;
+ } else if (intersectWidth > 0)
+ // If the rectangle is partially visible, but not above the minimum threshold, use the specified partial behavior
+ scrollX = getPartialBehavior(alignX);
+ else
+ scrollX = getHiddenBehavior(alignX);
+ // If we're trying to align to the closest edge, and the exposeRect is further right
+ // than the visibleRect, and not bigger than the visible area, then align with the right.
+ if (scrollX == alignToClosestEdge && exposeRect.right() > visibleRect.right() && exposeRect.width() < visibleRect.width())
+ scrollX = alignRight;
+
+ // Given the X behavior, compute the X coordinate.
+ int x;
+ if (scrollX == noScroll)
+ x = visibleRect.x();
+ else if (scrollX == alignRight)
+ x = exposeRect.right() - visibleRect.width();
+ else if (scrollX == alignCenter)
+ x = exposeRect.x() + (exposeRect.width() - visibleRect.width()) / 2;
+ else
+ x = exposeRect.x();
+
+ // Determine the appropriate Y behavior.
+ ScrollBehavior scrollY;
+ IntRect exposeRectY(visibleRect.x(), exposeRect.y(), visibleRect.width(), exposeRect.height());
+ int intersectHeight = intersection(visibleRect, exposeRectY).height();
+ if (intersectHeight == exposeRect.height())
+ // If the rectangle is fully visible, use the specified visible behavior.
+ scrollY = getVisibleBehavior(alignY);
+ else if (intersectHeight == visibleRect.height()) {
+ // If the rect is bigger than the visible area, don't bother trying to center. Other alignments will work.
+ scrollY = getVisibleBehavior(alignY);
+ if (scrollY == alignCenter)
+ scrollY = noScroll;
+ } else if (intersectHeight > 0)
+ // If the rectangle is partially visible, use the specified partial behavior
+ scrollY = getPartialBehavior(alignY);
+ else
+ scrollY = getHiddenBehavior(alignY);
+ // If we're trying to align to the closest edge, and the exposeRect is further down
+ // than the visibleRect, and not bigger than the visible area, then align with the bottom.
+ if (scrollY == alignToClosestEdge && exposeRect.bottom() > visibleRect.bottom() && exposeRect.height() < visibleRect.height())
+ scrollY = alignBottom;
+
+ // Given the Y behavior, compute the Y coordinate.
+ int y;
+ if (scrollY == noScroll)
+ y = visibleRect.y();
+ else if (scrollY == alignBottom)
+ y = exposeRect.bottom() - visibleRect.height();
+ else if (scrollY == alignCenter)
+ y = exposeRect.y() + (exposeRect.height() - visibleRect.height()) / 2;
+ else
+ y = exposeRect.y();
+
+ return IntRect(IntPoint(x, y), visibleRect.size());
+}
+
+void RenderLayer::autoscroll()
+{
+ Frame* frame = renderer()->document()->frame();
+ if (!frame)
+ return;
+
+ FrameView* frameView = frame->view();
+ if (!frameView)
+ return;
+
+ frame->eventHandler()->updateSelectionForMouseDrag();
+
+ IntPoint currentDocumentPosition = frameView->windowToContents(frame->eventHandler()->currentMousePosition());
+ scrollRectToVisible(IntRect(currentDocumentPosition, IntSize(1, 1)), false, gAlignToEdgeIfNeeded, gAlignToEdgeIfNeeded);
+}
+
+void RenderLayer::resize(const PlatformMouseEvent& evt, const IntSize& oldOffset)
+{
+ if (!inResizeMode() || !m_object->hasOverflowClip())
+ return;
+
+ // Set the width and height of the shadow ancestor node if there is one.
+ // This is necessary for textarea elements since the resizable layer is in the shadow content.
+ Element* element = static_cast<Element*>(m_object->node()->shadowAncestorNode());
+ RenderBox* renderer = static_cast<RenderBox*>(element->renderer());
+
+ EResize resize = renderer->style()->resize();
+ if (resize == RESIZE_NONE)
+ return;
+
+ Document* document = element->document();
+ if (!document->frame()->eventHandler()->mousePressed())
+ return;
+
+ float zoomFactor = renderer->style()->effectiveZoom();
+
+ IntSize newOffset = offsetFromResizeCorner(document->view()->windowToContents(evt.pos()));
+ newOffset.setWidth(newOffset.width() / zoomFactor);
+ newOffset.setHeight(newOffset.height() / zoomFactor);
+
+ IntSize currentSize = IntSize(renderer->width() / zoomFactor, renderer->height() / zoomFactor);
+ IntSize minimumSize = element->minimumSizeForResizing().shrunkTo(currentSize);
+ element->setMinimumSizeForResizing(minimumSize);
+
+ IntSize adjustedOldOffset = IntSize(oldOffset.width() / zoomFactor, oldOffset.height() / zoomFactor);
+
+ IntSize difference = (currentSize + newOffset - adjustedOldOffset).expandedTo(minimumSize) - currentSize;
+
+ CSSStyleDeclaration* style = element->style();
+ bool isBoxSizingBorder = renderer->style()->boxSizing() == BORDER_BOX;
+
+ ExceptionCode ec;
+
+ if (difference.width()) {
+ if (element && element->isControl()) {
+ // Make implicit margins from the theme explicit (see <http://bugs.webkit.org/show_bug.cgi?id=9547>).
+ style->setProperty(CSSPropertyMarginLeft, String::number(renderer->marginLeft() / zoomFactor) + "px", false, ec);
+ style->setProperty(CSSPropertyMarginRight, String::number(renderer->marginRight() / zoomFactor) + "px", false, ec);
+ }
+ int baseWidth = renderer->width() - (isBoxSizingBorder ? 0
+ : renderer->borderLeft() + renderer->paddingLeft() + renderer->borderRight() + renderer->paddingRight());
+ baseWidth = baseWidth / zoomFactor;
+ style->setProperty(CSSPropertyWidth, String::number(baseWidth + difference.width()) + "px", false, ec);
+ }
+
+ if (difference.height()) {
+ if (element && element->isControl()) {
+ // Make implicit margins from the theme explicit (see <http://bugs.webkit.org/show_bug.cgi?id=9547>).
+ style->setProperty(CSSPropertyMarginTop, String::number(renderer->marginTop() / zoomFactor) + "px", false, ec);
+ style->setProperty(CSSPropertyMarginBottom, String::number(renderer->marginBottom() / zoomFactor) + "px", false, ec);
+ }
+ int baseHeight = renderer->height() - (isBoxSizingBorder ? 0
+ : renderer->borderTop() + renderer->paddingTop() + renderer->borderBottom() + renderer->paddingBottom());
+ baseHeight = baseHeight / zoomFactor;
+ style->setProperty(CSSPropertyHeight, String::number(baseHeight + difference.height()) + "px", false, ec);
+ }
+
+ document->updateLayout();
+
+ // FIXME (Radar 4118564): We should also autoscroll the window as necessary to keep the point under the cursor in view.
+}
+
+void RenderLayer::valueChanged(Scrollbar*)
+{
+ // Update scroll position from scrollbars.
+
+ bool needUpdate = false;
+ int newX = scrollXOffset();
+ int newY = m_scrollY;
+
+ if (m_hBar) {
+ newX = m_hBar->value();
+ if (newX != scrollXOffset())
+ needUpdate = true;
+ }
+
+ if (m_vBar) {
+ newY = m_vBar->value();
+ if (newY != m_scrollY)
+ needUpdate = true;
+ }
+
+ if (needUpdate)
+ scrollToOffset(newX, newY, false);
+}
+
+bool RenderLayer::isActive() const
+{
+ Page* page = renderer()->document()->frame()->page();
+ return page && page->focusController()->isActive();
+}
+
+
+static IntRect cornerRect(const RenderLayer* layer, const IntRect& bounds)
+{
+ int horizontalThickness;
+ int verticalThickness;
+ if (!layer->verticalScrollbar() && !layer->horizontalScrollbar()) {
+ // FIXME: This isn't right. We need to know the thickness of custom scrollbars
+ // even when they don't exist in order to set the resizer square size properly.
+ horizontalThickness = ScrollbarTheme::nativeTheme()->scrollbarThickness();
+ verticalThickness = horizontalThickness;
+ } else if (layer->verticalScrollbar() && !layer->horizontalScrollbar()) {
+ horizontalThickness = layer->verticalScrollbar()->width();
+ verticalThickness = horizontalThickness;
+ } else if (layer->horizontalScrollbar() && !layer->verticalScrollbar()) {
+ verticalThickness = layer->horizontalScrollbar()->height();
+ horizontalThickness = verticalThickness;
+ } else {
+ horizontalThickness = layer->verticalScrollbar()->width();
+ verticalThickness = layer->horizontalScrollbar()->height();
+ }
+ return IntRect(bounds.right() - horizontalThickness - layer->renderer()->style()->borderRightWidth(),
+ bounds.bottom() - verticalThickness - layer->renderer()->style()->borderBottomWidth(),
+ horizontalThickness, verticalThickness);
+}
+
+static IntRect scrollCornerRect(const RenderLayer* layer, const IntRect& bounds)
+{
+ // We have a scrollbar corner when a scrollbar is visible and not filling the entire length of the box.
+ // This happens when:
+ // (a) A resizer is present and at least one scrollbar is present
+ // (b) Both scrollbars are present.
+ bool hasHorizontalBar = layer->horizontalScrollbar();
+ bool hasVerticalBar = layer->verticalScrollbar();
+ bool hasResizer = layer->renderer()->style()->resize() != RESIZE_NONE;
+ if ((hasHorizontalBar && hasVerticalBar) || (hasResizer && (hasHorizontalBar || hasVerticalBar)))
+ return cornerRect(layer, bounds);
+ return IntRect();
+}
+
+static IntRect resizerCornerRect(const RenderLayer* layer, const IntRect& bounds)
+{
+ if (layer->renderer()->style()->resize() == RESIZE_NONE)
+ return IntRect();
+ return cornerRect(layer, bounds);
+}
+
+bool RenderLayer::scrollbarCornerPresent() const
+{
+ return !scrollCornerRect(this, m_object->borderBox()).isEmpty();
+}
+
+void RenderLayer::invalidateScrollbarRect(Scrollbar* scrollbar, const IntRect& rect)
+{
+ IntRect scrollRect = rect;
+ if (scrollbar == m_vBar.get())
+ scrollRect.move(renderer()->width() - renderer()->borderRight() - scrollbar->width(), renderer()->borderTop());
+ else
+ scrollRect.move(renderer()->borderLeft(), renderer()->height() - renderer()->borderBottom() - scrollbar->height());
+ renderer()->repaintRectangle(scrollRect);
+}
+
+PassRefPtr<Scrollbar> RenderLayer::createScrollbar(ScrollbarOrientation orientation)
+{
+ RefPtr<Scrollbar> widget;
+ bool hasCustomScrollbarStyle = m_object->node()->shadowAncestorNode()->renderer()->style()->hasPseudoStyle(RenderStyle::SCROLLBAR);
+ if (hasCustomScrollbarStyle)
+ widget = RenderScrollbar::createCustomScrollbar(this, orientation, m_object->node()->shadowAncestorNode()->renderer());
+ else
+ widget = Scrollbar::createNativeScrollbar(this, orientation, RegularScrollbar);
+ m_object->document()->view()->addChild(widget.get());
+ return widget.release();
+}
+
+void RenderLayer::destroyScrollbar(ScrollbarOrientation orientation)
+{
+ RefPtr<Scrollbar>& scrollbar = orientation == HorizontalScrollbar ? m_hBar : m_vBar;
+ if (scrollbar) {
+ scrollbar->removeFromParent();
+ scrollbar->setClient(0);
+ scrollbar = 0;
+ }
+}
+
+void RenderLayer::setHasHorizontalScrollbar(bool hasScrollbar)
+{
+ if (hasScrollbar == (m_hBar != 0))
+ return;
+
+ if (hasScrollbar)
+ m_hBar = createScrollbar(HorizontalScrollbar);
+ else
+ destroyScrollbar(HorizontalScrollbar);
+
+ // Destroying or creating one bar can cause our scrollbar corner to come and go. We need to update the opposite scrollbar's style.
+ if (m_hBar)
+ m_hBar->styleChanged();
+ if (m_vBar)
+ m_vBar->styleChanged();
+
+#if ENABLE(DASHBOARD_SUPPORT)
+ // Force an update since we know the scrollbars have changed things.
+ if (m_object->document()->hasDashboardRegions())
+ m_object->document()->setDashboardRegionsDirty(true);
+#endif
+}
+
+void RenderLayer::setHasVerticalScrollbar(bool hasScrollbar)
+{
+ if (hasScrollbar == (m_vBar != 0))
+ return;
+
+ if (hasScrollbar)
+ m_vBar = createScrollbar(VerticalScrollbar);
+ else
+ destroyScrollbar(VerticalScrollbar);
+
+ // Destroying or creating one bar can cause our scrollbar corner to come and go. We need to update the opposite scrollbar's style.
+ if (m_hBar)
+ m_hBar->styleChanged();
+ if (m_vBar)
+ m_vBar->styleChanged();
+
+#if ENABLE(DASHBOARD_SUPPORT)
+ // Force an update since we know the scrollbars have changed things.
+ if (m_object->document()->hasDashboardRegions())
+ m_object->document()->setDashboardRegionsDirty(true);
+#endif
+}
+
+int RenderLayer::verticalScrollbarWidth() const
+{
+ if (!m_vBar)
+ return 0;
+ return m_vBar->width();
+}
+
+int RenderLayer::horizontalScrollbarHeight() const
+{
+ if (!m_hBar)
+ return 0;
+ return m_hBar->height();
+}
+
+IntSize RenderLayer::offsetFromResizeCorner(const IntPoint& absolutePoint) const
+{
+ // Currently the resize corner is always the bottom right corner
+ IntPoint bottomRight(width(), height());
+ IntPoint localPoint = absoluteToContents(absolutePoint);
+ return localPoint - bottomRight;
+}
+
+void RenderLayer::positionOverflowControls(int tx, int ty)
+{
+ if (!m_hBar && !m_vBar && (!m_object->hasOverflowClip() || m_object->style()->resize() == RESIZE_NONE))
+ return;
+
+ IntRect borderBox = m_object->borderBox();
+ IntRect scrollCorner(scrollCornerRect(this, borderBox));
+ IntRect absBounds(borderBox.x() + tx, borderBox.y() + ty, borderBox.width(), borderBox.height());
+ if (m_vBar)
+ m_vBar->setFrameRect(IntRect(absBounds.right() - m_object->borderRight() - m_vBar->width(),
+ absBounds.y() + m_object->borderTop(),
+ m_vBar->width(),
+ absBounds.height() - (m_object->borderTop() + m_object->borderBottom()) - scrollCorner.height()));
+
+ if (m_hBar)
+ m_hBar->setFrameRect(IntRect(absBounds.x() + m_object->borderLeft(),
+ absBounds.bottom() - m_object->borderBottom() - m_hBar->height(),
+ absBounds.width() - (m_object->borderLeft() + m_object->borderRight()) - scrollCorner.width(),
+ m_hBar->height()));
+
+ if (m_scrollCorner)
+ m_scrollCorner->setRect(scrollCorner);
+ if (m_resizer)
+ m_resizer->setRect(resizerCornerRect(this, borderBox));
+}
+
+int RenderLayer::scrollWidth()
+{
+ if (m_scrollDimensionsDirty)
+ computeScrollDimensions();
+ return m_scrollWidth;
+}
+
+int RenderLayer::scrollHeight()
+{
+ if (m_scrollDimensionsDirty)
+ computeScrollDimensions();
+ return m_scrollHeight;
+}
+
+void RenderLayer::computeScrollDimensions(bool* needHBar, bool* needVBar)
+{
+ m_scrollDimensionsDirty = false;
+
+ bool ltr = m_object->style()->direction() == LTR;
+
+ int clientWidth = m_object->clientWidth();
+ int clientHeight = m_object->clientHeight();
+
+ m_scrollLeftOverflow = ltr ? 0 : min(0, m_object->leftmostPosition(true, false) - m_object->borderLeft());
+
+ int rightPos = ltr ?
+ m_object->rightmostPosition(true, false) - m_object->borderLeft() :
+ clientWidth - m_scrollLeftOverflow;
+ int bottomPos = m_object->lowestPosition(true, false) - m_object->borderTop();
+
+ m_scrollWidth = max(rightPos, clientWidth);
+ m_scrollHeight = max(bottomPos, clientHeight);
+
+ m_scrollOriginX = ltr ? 0 : m_scrollWidth - clientWidth;
+
+ if (needHBar)
+ *needHBar = rightPos > clientWidth;
+ if (needVBar)
+ *needVBar = bottomPos > clientHeight;
+}
+
+void RenderLayer::updateOverflowStatus(bool horizontalOverflow, bool verticalOverflow)
+{
+ if (m_overflowStatusDirty) {
+ m_horizontalOverflow = horizontalOverflow;
+ m_verticalOverflow = verticalOverflow;
+ m_overflowStatusDirty = false;
+
+ return;
+ }
+
+ bool horizontalOverflowChanged = (m_horizontalOverflow != horizontalOverflow);
+ bool verticalOverflowChanged = (m_verticalOverflow != verticalOverflow);
+
+ if (horizontalOverflowChanged || verticalOverflowChanged) {
+ m_horizontalOverflow = horizontalOverflow;
+ m_verticalOverflow = verticalOverflow;
+
+ if (FrameView* frameView = m_object->document()->view()) {
+ frameView->scheduleEvent(OverflowEvent::create(horizontalOverflowChanged, horizontalOverflow, verticalOverflowChanged, verticalOverflow),
+ EventTargetNodeCast(m_object->element()));
+ }
+ }
+}
+
+void
+RenderLayer::updateScrollInfoAfterLayout()
+{
+ m_scrollDimensionsDirty = true;
+
+ bool horizontalOverflow, verticalOverflow;
+ computeScrollDimensions(&horizontalOverflow, &verticalOverflow);
+
+ if (m_object->style()->overflowX() != OMARQUEE) {
+ // Layout may cause us to be in an invalid scroll position. In this case we need
+ // to pull our scroll offsets back to the max (or push them up to the min).
+ int newX = max(0, min(scrollXOffset(), scrollWidth() - m_object->clientWidth()));
+ int newY = max(0, min(m_scrollY, scrollHeight() - m_object->clientHeight()));
+ if (newX != scrollXOffset() || newY != m_scrollY) {
+ RenderView* view = m_object->view();
+ ASSERT(view);
+ // scrollToOffset() may call updateLayerPositions(), which doesn't work
+ // with LayoutState.
+ // FIXME: Remove the disableLayoutState/enableLayoutState if the above changes.
+ if (view)
+ view->disableLayoutState();
+ scrollToOffset(newX, newY);
+ if (view)
+ view->enableLayoutState();
+ }
+ }
+
+ bool haveHorizontalBar = m_hBar;
+ bool haveVerticalBar = m_vBar;
+
+ // overflow:scroll should just enable/disable.
+ if (m_object->style()->overflowX() == OSCROLL)
+ m_hBar->setEnabled(horizontalOverflow);
+ if (m_object->style()->overflowY() == OSCROLL)
+ m_vBar->setEnabled(verticalOverflow);
+
+ // A dynamic change from a scrolling overflow to overflow:hidden means we need to get rid of any
+ // scrollbars that may be present.
+ if (m_object->style()->overflowX() == OHIDDEN && haveHorizontalBar)
+ setHasHorizontalScrollbar(false);
+ if (m_object->style()->overflowY() == OHIDDEN && haveVerticalBar)
+ setHasVerticalScrollbar(false);
+
+ // overflow:auto may need to lay out again if scrollbars got added/removed.
+ bool scrollbarsChanged = (m_object->hasAutoHorizontalScrollbar() && haveHorizontalBar != horizontalOverflow) ||
+ (m_object->hasAutoVerticalScrollbar() && haveVerticalBar != verticalOverflow);
+ if (scrollbarsChanged) {
+ if (m_object->hasAutoHorizontalScrollbar())
+ setHasHorizontalScrollbar(horizontalOverflow);
+ if (m_object->hasAutoVerticalScrollbar())
+ setHasVerticalScrollbar(verticalOverflow);
+
+#if ENABLE(DASHBOARD_SUPPORT)
+ // Force an update since we know the scrollbars have changed things.
+ if (m_object->document()->hasDashboardRegions())
+ m_object->document()->setDashboardRegionsDirty(true);
+#endif
+
+ m_object->repaint();
+
+ if (m_object->style()->overflowX() == OAUTO || m_object->style()->overflowY() == OAUTO) {
+ if (!m_inOverflowRelayout) {
+ // Our proprietary overflow: overlay value doesn't trigger a layout.
+ m_inOverflowRelayout = true;
+ m_object->setNeedsLayout(true);
+ if (m_object->isRenderBlock())
+ static_cast<RenderBlock*>(m_object)->layoutBlock(true);
+ else
+ m_object->layout();
+ m_inOverflowRelayout = false;
+ }
+ }
+ }
+
+ // If overflow:scroll is turned into overflow:auto a bar might still be disabled (Bug 11985).
+ if (m_hBar && m_object->hasAutoHorizontalScrollbar())
+ m_hBar->setEnabled(true);
+ if (m_vBar && m_object->hasAutoVerticalScrollbar())
+ m_vBar->setEnabled(true);
+
+ // Set up the range (and page step/line step).
+ if (m_hBar) {
+ int clientWidth = m_object->clientWidth();
+ int pageStep = (clientWidth - cAmountToKeepWhenPaging);
+ if (pageStep < 0) pageStep = clientWidth;
+ m_hBar->setSteps(cScrollbarPixelsPerLineStep, pageStep);
+ m_hBar->setProportion(clientWidth, m_scrollWidth);
+ m_hBar->setValue(scrollXOffset());
+ }
+ if (m_vBar) {
+ int clientHeight = m_object->clientHeight();
+ int pageStep = (clientHeight - cAmountToKeepWhenPaging);
+ if (pageStep < 0) pageStep = clientHeight;
+ m_vBar->setSteps(cScrollbarPixelsPerLineStep, pageStep);
+ m_vBar->setProportion(clientHeight, m_scrollHeight);
+ }
+
+ if (m_object->element() && m_object->document()->hasListenerType(Document::OVERFLOWCHANGED_LISTENER))
+ updateOverflowStatus(horizontalOverflow, verticalOverflow);
+}
+
+void RenderLayer::paintOverflowControls(GraphicsContext* context, int tx, int ty, const IntRect& damageRect)
+{
+ // Don't do anything if we have no overflow.
+ if (!m_object->hasOverflowClip())
+ return;
+
+ // Move the scrollbar widgets if necessary. We normally move and resize widgets during layout, but sometimes
+ // widgets can move without layout occurring (most notably when you scroll a document that
+ // contains fixed positioned elements).
+ positionOverflowControls(tx, ty);
+
+ // Now that we're sure the scrollbars are in the right place, paint them.
+ if (m_hBar)
+ m_hBar->paint(context, damageRect);
+ if (m_vBar)
+ m_vBar->paint(context, damageRect);
+
+ // We fill our scroll corner with white if we have a scrollbar that doesn't run all the way up to the
+ // edge of the box.
+ paintScrollCorner(context, tx, ty, damageRect);
+
+ // Paint our resizer last, since it sits on top of the scroll corner.
+ paintResizer(context, tx, ty, damageRect);
+}
+
+void RenderLayer::paintScrollCorner(GraphicsContext* context, int tx, int ty, const IntRect& damageRect)
+{
+ IntRect cornerRect = scrollCornerRect(this, m_object->borderBox());
+ IntRect absRect = IntRect(cornerRect.x() + tx, cornerRect.y() + ty, cornerRect.width(), cornerRect.height());
+ if (!absRect.intersects(damageRect))
+ return;
+
+ if (context->updatingControlTints()) {
+ updateScrollCornerStyle();
+ return;
+ }
+
+ if (m_scrollCorner) {
+ m_scrollCorner->paintIntoRect(context, tx, ty, absRect);
+ return;
+ }
+
+ context->fillRect(absRect, Color::white);
+}
+
+void RenderLayer::paintResizer(GraphicsContext* context, int tx, int ty, const IntRect& damageRect)
+{
+ if (m_object->style()->resize() == RESIZE_NONE)
+ return;
+
+ IntRect cornerRect = resizerCornerRect(this, m_object->borderBox());
+ IntRect absRect = IntRect(cornerRect.x() + tx, cornerRect.y() + ty, cornerRect.width(), cornerRect.height());
+ if (!absRect.intersects(damageRect))
+ return;
+
+ if (context->updatingControlTints()) {
+ updateResizerStyle();
+ return;
+ }
+
+ if (m_resizer) {
+ m_resizer->paintIntoRect(context, tx, ty, absRect);
+ return;
+ }
+
+ // Paint the resizer control.
+ DEFINE_STATIC_LOCAL(RefPtr<Image>, resizeCornerImage, (Image::loadPlatformResource("textAreaResizeCorner")));
+ IntPoint imagePoint(absRect.right() - resizeCornerImage->width(), absRect.bottom() - resizeCornerImage->height());
+ context->drawImage(resizeCornerImage.get(), imagePoint);
+
+ // Draw a frame around the resizer (1px grey line) if there are any scrollbars present.
+ // Clipping will exclude the right and bottom edges of this frame.
+ if (m_hBar || m_vBar) {
+ context->save();
+ IntRect largerCorner = absRect;
+ largerCorner.setSize(IntSize(largerCorner.width() + 1, largerCorner.height() + 1));
+ context->setStrokeColor(Color(makeRGB(217, 217, 217)));
+ context->setStrokeThickness(1.0f);
+ context->setFillColor(Color::transparent);
+ context->drawRect(largerCorner);
+ context->restore();
+ }
+}
+
+bool RenderLayer::isPointInResizeControl(const IntPoint& absolutePoint) const
+{
+ if (!m_object->hasOverflowClip() || m_object->style()->resize() == RESIZE_NONE)
+ return false;
+
+ IntPoint localPoint = absoluteToContents(absolutePoint);
+
+ IntRect localBounds(0, 0, m_object->width(), m_object->height());
+ return resizerCornerRect(this, localBounds).contains(localPoint);
+}
+
+bool RenderLayer::hitTestOverflowControls(HitTestResult& result)
+{
+ if (!m_hBar && !m_vBar && (!renderer()->hasOverflowClip() || renderer()->style()->resize() == RESIZE_NONE))
+ return false;
+
+ int x = 0;
+ int y = 0;
+ convertToLayerCoords(root(), x, y);
+ IntRect absBounds(x, y, renderer()->width(), renderer()->height());
+
+ IntRect resizeControlRect;
+ if (renderer()->style()->resize() != RESIZE_NONE) {
+ resizeControlRect = resizerCornerRect(this, absBounds);
+ if (resizeControlRect.contains(result.point()))
+ return true;
+ }
+
+ int resizeControlSize = max(resizeControlRect.height(), 0);
+
+ if (m_vBar) {
+ IntRect vBarRect(absBounds.right() - renderer()->borderRight() - m_vBar->width(), absBounds.y() + renderer()->borderTop(), m_vBar->width(), absBounds.height() - (renderer()->borderTop() + renderer()->borderBottom()) - (m_hBar ? m_hBar->height() : resizeControlSize));
+ if (vBarRect.contains(result.point())) {
+ result.setScrollbar(m_vBar.get());
+ return true;
+ }
+ }
+
+ resizeControlSize = max(resizeControlRect.width(), 0);
+ if (m_hBar) {
+ IntRect hBarRect(absBounds.x() + renderer()->borderLeft(), absBounds.bottom() - renderer()->borderBottom() - m_hBar->height(), absBounds.width() - (renderer()->borderLeft() + renderer()->borderRight()) - (m_vBar ? m_vBar->width() : resizeControlSize), m_hBar->height());
+ if (hBarRect.contains(result.point())) {
+ result.setScrollbar(m_hBar.get());
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool RenderLayer::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier)
+{
+ bool didHorizontalScroll = false;
+ bool didVerticalScroll = false;
+
+ if (m_hBar) {
+ if (granularity == ScrollByDocument) {
+ // Special-case for the ScrollByDocument granularity. A document scroll can only be up
+ // or down and in both cases the horizontal bar goes all the way to the left.
+ didHorizontalScroll = m_hBar->scroll(ScrollLeft, ScrollByDocument, multiplier);
+ } else
+ didHorizontalScroll = m_hBar->scroll(direction, granularity, multiplier);
+ }
+
+ if (m_vBar)
+ didVerticalScroll = m_vBar->scroll(direction, granularity, multiplier);
+
+ return (didHorizontalScroll || didVerticalScroll);
+}
+
+void
+RenderLayer::paint(GraphicsContext* p, const IntRect& damageRect, PaintRestriction paintRestriction, RenderObject *paintingRoot)
+{
+ paintLayer(this, p, damageRect, false, paintRestriction, paintingRoot);
+}
+
+static void setClip(GraphicsContext* p, const IntRect& paintDirtyRect, const IntRect& clipRect)
+{
+ if (paintDirtyRect == clipRect)
+ return;
+ p->save();
+ p->clip(clipRect);
+}
+
+static void restoreClip(GraphicsContext* p, const IntRect& paintDirtyRect, const IntRect& clipRect)
+{
+ if (paintDirtyRect == clipRect)
+ return;
+ p->restore();
+}
+
+void
+RenderLayer::paintLayer(RenderLayer* rootLayer, GraphicsContext* p,
+ const IntRect& paintDirtyRect, bool haveTransparency, PaintRestriction paintRestriction,
+ RenderObject* paintingRoot, bool appliedTransform, bool temporaryClipRects)
+{
+ // Avoid painting layers when stylesheets haven't loaded. This eliminates FOUC.
+ // It's ok not to draw, because later on, when all the stylesheets do load, updateStyleSelector on the Document
+ // will do a full repaint().
+ if (renderer()->document()->didLayoutWithPendingStylesheets() && !renderer()->isRenderView() && !renderer()->isRoot())
+ return;
+
+ // If this layer is totally invisible then there is nothing to paint.
+ if (!m_object->opacity())
+ return;
+
+ if (isTransparent())
+ haveTransparency = true;
+
+ // Apply a transform if we have one. A reflection is considered to be a transform, since it is a flip and a translate.
+ if (m_transform && !appliedTransform) {
+ // If the transform can't be inverted, then don't paint anything.
+ if (!m_transform->isInvertible())
+ return;
+
+ // If we have a transparency layer enclosing us and we are the root of a transform, then we need to establish the transparency
+ // layer from the parent now.
+ if (haveTransparency)
+ parent()->beginTransparencyLayers(p, rootLayer);
+
+ // Make sure the parent's clip rects have been calculated.
+ IntRect clipRect = paintDirtyRect;
+ if (parent()) {
+ if (temporaryClipRects) {
+ ClipRects parentClipRects;
+ parent()->calculateClipRects(rootLayer, parentClipRects);
+ clipRect = parentClipRects.overflowClipRect();
+ } else {
+ parent()->updateClipRects(rootLayer);
+ clipRect = parent()->clipRects()->overflowClipRect();
+ }
+ clipRect.intersect(paintDirtyRect);
+ }
+
+ // Push the parent coordinate space's clip.
+ setClip(p, paintDirtyRect, clipRect);
+
+ // Adjust the transform such that the renderer's upper left corner will paint at (0,0) in user space.
+ // This involves subtracting out the position of the layer in our current coordinate space.
+ int x = 0;
+ int y = 0;
+ convertToLayerCoords(rootLayer, x, y);
+ TransformationMatrix transform;
+ transform.translate(x, y);
+ transform = *m_transform * transform;
+
+ // Apply the transform.
+ p->save();
+ p->concatCTM(transform);
+
+ // Now do a paint with the root layer shifted to be us.
+ paintLayer(this, p, transform.inverse().mapRect(paintDirtyRect), haveTransparency, paintRestriction, paintingRoot, true, temporaryClipRects);
+
+ p->restore();
+
+ // Restore the clip.
+ restoreClip(p, paintDirtyRect, clipRect);
+
+ return;
+ }
+
+ // Paint the reflection first if we have one.
+ if (m_reflection && !m_paintingInsideReflection && (!m_transform || appliedTransform)) {
+ // Mark that we are now inside replica painting.
+ m_paintingInsideReflection = true;
+ reflectionLayer()->paintLayer(rootLayer, p, paintDirtyRect, haveTransparency, paintRestriction, paintingRoot, false, temporaryClipRects);
+ m_paintingInsideReflection = false;
+ }
+
+ // Calculate the clip rects we should use.
+ IntRect layerBounds, damageRect, clipRectToApply, outlineRect;
+ calculateRects(rootLayer, paintDirtyRect, layerBounds, damageRect, clipRectToApply, outlineRect, temporaryClipRects);
+ int x = layerBounds.x();
+ int y = layerBounds.y();
+ int tx = x - renderer()->xPos();
+ int ty = y - renderer()->yPos() + renderer()->borderTopExtra();
+
+ // Ensure our lists are up-to-date.
+ updateZOrderLists();
+ updateOverflowList();
+
+ bool selectionOnly = paintRestriction == PaintRestrictionSelectionOnly || paintRestriction == PaintRestrictionSelectionOnlyBlackText;
+ bool forceBlackText = paintRestriction == PaintRestrictionSelectionOnlyBlackText;
+
+ // If this layer's renderer is a child of the paintingRoot, we render unconditionally, which
+ // is done by passing a nil paintingRoot down to our renderer (as if no paintingRoot was ever set).
+ // Else, our renderer tree may or may not contain the painting root, so we pass that root along
+ // so it will be tested against as we decend through the renderers.
+ RenderObject* paintingRootForRenderer = 0;
+ if (paintingRoot && !m_object->isDescendantOf(paintingRoot))
+ paintingRootForRenderer = paintingRoot;
+
+ // We want to paint our layer, but only if we intersect the damage rect.
+ bool shouldPaint = intersectsDamageRect(layerBounds, damageRect, rootLayer) && m_hasVisibleContent;
+ if (shouldPaint && !selectionOnly && !damageRect.isEmpty()) {
+ // Begin transparency layers lazily now that we know we have to paint something.
+ if (haveTransparency)
+ beginTransparencyLayers(p, rootLayer);
+
+ // Paint our background first, before painting any child layers.
+ // Establish the clip used to paint our background.
+ setClip(p, paintDirtyRect, damageRect);
+
+ // Paint the background.
+ RenderObject::PaintInfo paintInfo(p, damageRect, PaintPhaseBlockBackground, false, paintingRootForRenderer, 0);
+ renderer()->paint(paintInfo, tx, ty);
+
+ // Our scrollbar widgets paint exactly when we tell them to, so that they work properly with
+ // z-index. We paint after we painted the background/border, so that the scrollbars will
+ // sit above the background/border.
+ paintOverflowControls(p, x, y, damageRect);
+
+ // Restore the clip.
+ restoreClip(p, paintDirtyRect, damageRect);
+ }
+
+ // Now walk the sorted list of children with negative z-indices.
+ if (m_negZOrderList)
+ for (Vector<RenderLayer*>::iterator it = m_negZOrderList->begin(); it != m_negZOrderList->end(); ++it)
+ it[0]->paintLayer(rootLayer, p, paintDirtyRect, haveTransparency, paintRestriction, paintingRoot, false, temporaryClipRects);
+
+ // Now establish the appropriate clip and paint our child RenderObjects.
+ if (shouldPaint && !clipRectToApply.isEmpty()) {
+ // Begin transparency layers lazily now that we know we have to paint something.
+ if (haveTransparency)
+ beginTransparencyLayers(p, rootLayer);
+
+ // Set up the clip used when painting our children.
+ setClip(p, paintDirtyRect, clipRectToApply);
+ RenderObject::PaintInfo paintInfo(p, clipRectToApply,
+ selectionOnly ? PaintPhaseSelection : PaintPhaseChildBlockBackgrounds,
+ forceBlackText, paintingRootForRenderer, 0);
+ renderer()->paint(paintInfo, tx, ty);
+ if (!selectionOnly) {
+ paintInfo.phase = PaintPhaseFloat;
+ renderer()->paint(paintInfo, tx, ty);
+ paintInfo.phase = PaintPhaseForeground;
+ renderer()->paint(paintInfo, tx, ty);
+ paintInfo.phase = PaintPhaseChildOutlines;
+ renderer()->paint(paintInfo, tx, ty);
+ }
+
+ // Now restore our clip.
+ restoreClip(p, paintDirtyRect, clipRectToApply);
+ }
+
+ if (!outlineRect.isEmpty()) {
+ // Paint our own outline
+ RenderObject::PaintInfo paintInfo(p, outlineRect, PaintPhaseSelfOutline, false, paintingRootForRenderer, 0);
+ setClip(p, paintDirtyRect, outlineRect);
+ renderer()->paint(paintInfo, tx, ty);
+ restoreClip(p, paintDirtyRect, outlineRect);
+ }
+
+ // Paint any child layers that have overflow.
+ if (m_overflowList)
+ for (Vector<RenderLayer*>::iterator it = m_overflowList->begin(); it != m_overflowList->end(); ++it)
+ it[0]->paintLayer(rootLayer, p, paintDirtyRect, haveTransparency, paintRestriction, paintingRoot, false, temporaryClipRects);
+
+ // Now walk the sorted list of children with positive z-indices.
+ if (m_posZOrderList)
+ for (Vector<RenderLayer*>::iterator it = m_posZOrderList->begin(); it != m_posZOrderList->end(); ++it)
+ it[0]->paintLayer(rootLayer, p, paintDirtyRect, haveTransparency, paintRestriction, paintingRoot, false, temporaryClipRects);
+
+ if (renderer()->hasMask() && shouldPaint && !selectionOnly && !damageRect.isEmpty()) {
+ setClip(p, paintDirtyRect, damageRect);
+
+ // Paint the mask.
+ RenderObject::PaintInfo paintInfo(p, damageRect, PaintPhaseMask, false, paintingRootForRenderer, 0);
+ renderer()->paint(paintInfo, tx, ty);
+
+ // Restore the clip.
+ restoreClip(p, paintDirtyRect, damageRect);
+ }
+
+ // End our transparency layer
+ if (isTransparent() && m_usedTransparency) {
+ p->endTransparencyLayer();
+ p->restore();
+ m_usedTransparency = false;
+ }
+}
+
+static inline IntRect frameVisibleRect(RenderObject* renderer)
+{
+ FrameView* frameView = renderer->document()->view();
+ if (!frameView)
+ return IntRect();
+
+ return frameView->visibleContentRect();
+}
+
+bool RenderLayer::hitTest(const HitTestRequest& request, HitTestResult& result)
+{
+ renderer()->document()->updateLayout();
+
+ IntRect boundsRect(m_x, m_y, width(), height());
+ boundsRect.intersect(frameVisibleRect(renderer()));
+
+ RenderLayer* insideLayer = hitTestLayer(this, request, result, boundsRect, result.point());
+
+ // Now determine if the result is inside an anchor; make sure an image map wins if
+ // it already set URLElement and only use the innermost.
+ Node* node = result.innerNode();
+ while (node) {
+ // for imagemaps, URLElement is the associated area element not the image itself
+ if (node->isLink() && !result.URLElement() && !node->hasTagName(imgTag))
+ result.setURLElement(static_cast<Element*>(node));
+ node = node->eventParentNode();
+ }
+
+ // Next set up the correct :hover/:active state along the new chain.
+ updateHoverActiveState(request, result);
+
+ // Now return whether we were inside this layer (this will always be true for the root
+ // layer).
+ return insideLayer;
+}
+
+Node* RenderLayer::enclosingElement() const
+{
+ for (RenderObject* r = renderer(); r; r = r->parent()) {
+ if (Node* e = r->element())
+ return e;
+ }
+ ASSERT_NOT_REACHED();
+ return 0;
+}
+
+RenderLayer* RenderLayer::hitTestLayer(RenderLayer* rootLayer, const HitTestRequest& request, HitTestResult& result,
+ const IntRect& hitTestRect, const IntPoint& hitTestPoint, bool appliedTransform)
+{
+ // Apply a transform if we have one.
+ if (m_transform && !appliedTransform) {
+ // If the transform can't be inverted, then don't hit test this layer at all.
+ if (!m_transform->isInvertible())
+ return 0;
+
+ // Make sure the parent's clip rects have been calculated.
+ if (parent()) {
+ parent()->updateClipRects(rootLayer);
+
+ // Go ahead and test the enclosing clip now.
+ IntRect clipRect = parent()->clipRects()->overflowClipRect();
+ if (!clipRect.contains(hitTestPoint))
+ return 0;
+ }
+
+ // Adjust the transform such that the renderer's upper left corner is at (0,0) in user space.
+ // This involves subtracting out the position of the layer in our current coordinate space.
+ int x = 0;
+ int y = 0;
+ convertToLayerCoords(rootLayer, x, y);
+ TransformationMatrix transform;
+ transform.translate(x, y);
+ transform = *m_transform * transform;
+
+ // Map the hit test point into the transformed space and then do a hit test with the root layer shifted to be us.
+ return hitTestLayer(this, request, result, transform.inverse().mapRect(hitTestRect), transform.inverse().mapPoint(hitTestPoint), true);
+ }
+
+ // Calculate the clip rects we should use.
+ IntRect layerBounds;
+ IntRect bgRect;
+ IntRect fgRect;
+ IntRect outlineRect;
+ calculateRects(rootLayer, hitTestRect, layerBounds, bgRect, fgRect, outlineRect);
+
+ // Ensure our lists are up-to-date.
+ updateZOrderLists();
+ updateOverflowList();
+
+ // This variable tracks which layer the mouse ends up being inside. The minute we find an insideLayer,
+ // we are done and can return it.
+ RenderLayer* insideLayer = 0;
+
+ // Begin by walking our list of positive layers from highest z-index down to the lowest
+ // z-index.
+ if (m_posZOrderList) {
+ for (int i = m_posZOrderList->size() - 1; i >= 0; --i) {
+ insideLayer = m_posZOrderList->at(i)->hitTestLayer(rootLayer, request, result, hitTestRect, hitTestPoint);
+ if (insideLayer)
+ return insideLayer;
+ }
+ }
+
+ // Now check our overflow objects.
+ if (m_overflowList) {
+ for (int i = m_overflowList->size() - 1; i >= 0; --i) {
+ insideLayer = m_overflowList->at(i)->hitTestLayer(rootLayer, request, result, hitTestRect, hitTestPoint);
+ if (insideLayer)
+ return insideLayer;
+ }
+ }
+
+ // Next we want to see if the mouse pos is inside the child RenderObjects of the layer.
+ if (fgRect.contains(hitTestPoint) &&
+ renderer()->hitTest(request, result, hitTestPoint,
+ layerBounds.x() - renderer()->xPos(),
+ layerBounds.y() - renderer()->yPos() + m_object->borderTopExtra(),
+ HitTestDescendants)) {
+ // For positioned generated content, we might still not have a
+ // node by the time we get to the layer level, since none of
+ // the content in the layer has an element. So just walk up
+ // the tree.
+ if (!result.innerNode() || !result.innerNonSharedNode()) {
+ Node* e = enclosingElement();
+ if (!result.innerNode())
+ result.setInnerNode(e);
+ if (!result.innerNonSharedNode())
+ result.setInnerNonSharedNode(e);
+ }
+
+ return this;
+ }
+
+ // Now check our negative z-index children.
+ if (m_negZOrderList) {
+ for (int i = m_negZOrderList->size() - 1; i >= 0; --i) {
+ insideLayer = m_negZOrderList->at(i)->hitTestLayer(rootLayer, request, result, hitTestRect, hitTestPoint);
+ if (insideLayer)
+ return insideLayer;
+ }
+ }
+
+ // Next we want to see if the mouse is inside this layer but not any of its children.
+ if (bgRect.contains(hitTestPoint) &&
+ renderer()->hitTest(request, result, hitTestPoint,
+ layerBounds.x() - renderer()->xPos(),
+ layerBounds.y() - renderer()->yPos() + m_object->borderTopExtra(),
+ HitTestSelf)) {
+ if (!result.innerNode() || !result.innerNonSharedNode()) {
+ Node* e = enclosingElement();
+ if (!result.innerNode())
+ result.setInnerNode(e);
+ if (!result.innerNonSharedNode())
+ result.setInnerNonSharedNode(e);
+ }
+
+ return this;
+ }
+
+ // We didn't hit any layer. If we are the root layer and the mouse is -- or just was -- down,
+ // return ourselves. We do this so mouse events continue getting delivered after a drag has
+ // exited the WebView, and so hit testing over a scrollbar hits the content document.
+ if ((request.active || request.mouseUp) && renderer()->isRenderView()) {
+ renderer()->updateHitTestResult(result, hitTestPoint);
+ return this;
+ }
+
+ return 0;
+}
+
+void RenderLayer::updateClipRects(const RenderLayer* rootLayer)
+{
+ if (m_clipRects) {
+ ASSERT(rootLayer == m_clipRectsRoot);
+ return; // We have the correct cached value.
+ }
+
+ // For transformed layers, the root layer was shifted to be us, so there is no need to
+ // examine the parent. We want to cache clip rects with us as the root.
+ RenderLayer* parentLayer = rootLayer != this ? parent() : 0;
+ if (parentLayer)
+ parentLayer->updateClipRects(rootLayer);
+
+ ClipRects clipRects;
+ calculateClipRects(rootLayer, clipRects, true);
+
+ if (parentLayer && parentLayer->clipRects() && clipRects == *parentLayer->clipRects())
+ m_clipRects = parentLayer->clipRects();
+ else
+ m_clipRects = new (m_object->renderArena()) ClipRects(clipRects);
+ m_clipRects->ref();
+#ifndef NDEBUG
+ m_clipRectsRoot = rootLayer;
+#endif
+}
+
+void RenderLayer::calculateClipRects(const RenderLayer* rootLayer, ClipRects& clipRects, bool useCached) const
+{
+ IntRect infiniteRect(INT_MIN/2, INT_MIN/2, INT_MAX, INT_MAX);
+ if (!parent()) {
+ // The root layer's clip rect is always infinite.
+ clipRects.reset(infiniteRect);
+ return;
+ }
+
+ // For transformed layers, the root layer was shifted to be us, so there is no need to
+ // examine the parent. We want to cache clip rects with us as the root.
+ RenderLayer* parentLayer = rootLayer != this ? parent() : 0;
+
+ // Ensure that our parent's clip has been calculated so that we can examine the values.
+ if (parentLayer) {
+ if (useCached && parentLayer->clipRects())
+ clipRects = *parentLayer->clipRects();
+ else
+ parentLayer->calculateClipRects(rootLayer, clipRects);
+ }
+ else
+ clipRects.reset(infiniteRect);
+
+ // A fixed object is essentially the root of its containing block hierarchy, so when
+ // we encounter such an object, we reset our clip rects to the fixedClipRect.
+ if (m_object->style()->position() == FixedPosition) {
+ clipRects.setPosClipRect(clipRects.fixedClipRect());
+ clipRects.setOverflowClipRect(clipRects.fixedClipRect());
+ clipRects.setFixed(true);
+ }
+ else if (m_object->style()->position() == RelativePosition)
+ clipRects.setPosClipRect(clipRects.overflowClipRect());
+ else if (m_object->style()->position() == AbsolutePosition)
+ clipRects.setOverflowClipRect(clipRects.posClipRect());
+
+ // Update the clip rects that will be passed to child layers.
+ if (m_object->hasOverflowClip() || m_object->hasClip()) {
+ // This layer establishes a clip of some kind.
+ int x = 0;
+ int y = 0;
+ convertToLayerCoords(rootLayer, x, y);
+ RenderView* view = renderer()->view();
+ ASSERT(view);
+ if (view && clipRects.fixed() && rootLayer->renderer() == view) {
+ x -= view->frameView()->scrollX();
+ y -= view->frameView()->scrollY();
+ }
+
+ if (m_object->hasOverflowClip()) {
+ IntRect newOverflowClip = m_object->getOverflowClipRect(x,y);
+ clipRects.setOverflowClipRect(intersection(newOverflowClip, clipRects.overflowClipRect()));
+ if (m_object->isPositioned() || m_object->isRelPositioned())
+ clipRects.setPosClipRect(intersection(newOverflowClip, clipRects.posClipRect()));
+ }
+ if (m_object->hasClip()) {
+ IntRect newPosClip = m_object->getClipRect(x,y);
+ clipRects.setPosClipRect(intersection(newPosClip, clipRects.posClipRect()));
+ clipRects.setOverflowClipRect(intersection(newPosClip, clipRects.overflowClipRect()));
+ clipRects.setFixedClipRect(intersection(newPosClip, clipRects.fixedClipRect()));
+ }
+ }
+}
+
+void RenderLayer::calculateRects(const RenderLayer* rootLayer, const IntRect& paintDirtyRect, IntRect& layerBounds,
+ IntRect& backgroundRect, IntRect& foregroundRect, IntRect& outlineRect, bool temporaryClipRects) const
+{
+ if (rootLayer != this && parent()) {
+ ClipRects parentClipRects;
+ if (temporaryClipRects)
+ parent()->calculateClipRects(rootLayer, parentClipRects);
+ else {
+ parent()->updateClipRects(rootLayer);
+ parentClipRects = *parent()->clipRects();
+ }
+
+ backgroundRect = m_object->style()->position() == FixedPosition ? parentClipRects.fixedClipRect() :
+ (m_object->isPositioned() ? parentClipRects.posClipRect() :
+ parentClipRects.overflowClipRect());
+ RenderView* view = renderer()->view();
+ ASSERT(view);
+ if (view && parentClipRects.fixed() && rootLayer->renderer() == view)
+ backgroundRect.move(view->frameView()->scrollX(), view->frameView()->scrollY());
+
+ backgroundRect.intersect(paintDirtyRect);
+ } else
+ backgroundRect = paintDirtyRect;
+
+ foregroundRect = backgroundRect;
+ outlineRect = backgroundRect;
+
+ int x = 0;
+ int y = 0;
+ convertToLayerCoords(rootLayer, x, y);
+ layerBounds = IntRect(x,y,width(),height());
+
+ // Update the clip rects that will be passed to child layers.
+ if (m_object->hasOverflowClip() || m_object->hasClip()) {
+ // This layer establishes a clip of some kind.
+ if (m_object->hasOverflowClip())
+ foregroundRect.intersect(m_object->getOverflowClipRect(x,y));
+ if (m_object->hasClip()) {
+ // Clip applies to *us* as well, so go ahead and update the damageRect.
+ IntRect newPosClip = m_object->getClipRect(x,y);
+ backgroundRect.intersect(newPosClip);
+ foregroundRect.intersect(newPosClip);
+ outlineRect.intersect(newPosClip);
+ }
+
+ // If we establish a clip at all, then go ahead and make sure our background
+ // rect is intersected with our layer's bounds.
+ if (ShadowData* boxShadow = renderer()->style()->boxShadow()) {
+ IntRect overflow = layerBounds;
+ do {
+ IntRect shadowRect = layerBounds;
+ shadowRect.move(boxShadow->x, boxShadow->y);
+ shadowRect.inflate(boxShadow->blur);
+ overflow.unite(shadowRect);
+ boxShadow = boxShadow->next;
+ } while (boxShadow);
+ backgroundRect.intersect(overflow);
+ } else
+ backgroundRect.intersect(layerBounds);
+ }
+}
+
+IntRect RenderLayer::childrenClipRect() const
+{
+ RenderLayer* rootLayer = renderer()->document()->renderer()->layer();
+ IntRect layerBounds, backgroundRect, foregroundRect, outlineRect;
+ calculateRects(rootLayer, rootLayer->boundingBox(rootLayer), layerBounds, backgroundRect, foregroundRect, outlineRect);
+ return foregroundRect;
+}
+
+IntRect RenderLayer::selfClipRect() const
+{
+ RenderLayer* rootLayer = renderer()->document()->renderer()->layer();
+ IntRect layerBounds, backgroundRect, foregroundRect, outlineRect;
+ calculateRects(rootLayer, rootLayer->boundingBox(rootLayer), layerBounds, backgroundRect, foregroundRect, outlineRect);
+ return backgroundRect;
+}
+
+bool RenderLayer::intersectsDamageRect(const IntRect& layerBounds, const IntRect& damageRect, const RenderLayer* rootLayer) const
+{
+ // Always examine the canvas and the root.
+ // FIXME: Could eliminate the isRoot() check if we fix background painting so that the RenderView
+ // paints the root's background.
+ if (renderer()->isRenderView() || renderer()->isRoot())
+ return true;
+
+ // If we aren't an inline flow, and our layer bounds do intersect the damage rect, then we
+ // can go ahead and return true.
+ RenderView* view = renderer()->view();
+ ASSERT(view);
+ if (view && !renderer()->isInlineFlow()) {
+ IntRect b = layerBounds;
+ b.inflate(view->maximalOutlineSize());
+ if (b.intersects(damageRect))
+ return true;
+ }
+
+ // Otherwise we need to compute the bounding box of this single layer and see if it intersects
+ // the damage rect.
+ return boundingBox(rootLayer).intersects(damageRect);
+}
+
+IntRect RenderLayer::boundingBox(const RenderLayer* rootLayer) const
+{
+ // There are three special cases we need to consider.
+ // (1) Inline Flows. For inline flows we will create a bounding box that fully encompasses all of the lines occupied by the
+ // inline. In other words, if some <span> wraps to three lines, we'll create a bounding box that fully encloses the root
+ // line boxes of all three lines (including overflow on those lines).
+ // (2) Left/Top Overflow. The width/height of layers already includes right/bottom overflow. However, in the case of left/top
+ // overflow, we have to create a bounding box that will extend to include this overflow.
+ // (3) Floats. When a layer has overhanging floats that it paints, we need to make sure to include these overhanging floats
+ // as part of our bounding box. We do this because we are the responsible layer for both hit testing and painting those
+ // floats.
+ IntRect result;
+ if (renderer()->isInlineFlow()) {
+ // Go from our first line box to our last line box.
+ RenderInline* inlineFlow = static_cast<RenderInline*>(renderer());
+ InlineFlowBox* firstBox = inlineFlow->firstLineBox();
+ if (!firstBox)
+ return result;
+ int top = firstBox->root()->topOverflow();
+ int bottom = inlineFlow->lastLineBox()->root()->bottomOverflow();
+ int left = firstBox->xPos();
+ for (InlineRunBox* curr = firstBox->nextLineBox(); curr; curr = curr->nextLineBox())
+ left = min(left, curr->xPos());
+ result = IntRect(m_x + left, m_y + (top - renderer()->yPos()), width(), bottom - top);
+ } else if (renderer()->isTableRow()) {
+ // Our bounding box is just the union of all of our cells' border/overflow rects.
+ for (RenderObject* child = renderer()->firstChild(); child; child = child->nextSibling()) {
+ if (child->isTableCell()) {
+ IntRect bbox = child->borderBox();
+ bbox.move(0, child->borderTopExtra());
+ result.unite(bbox);
+ IntRect overflowRect = renderer()->overflowRect(false);
+ overflowRect.move(0, child->borderTopExtra());
+ if (bbox != overflowRect)
+ result.unite(overflowRect);
+ }
+ }
+ result.move(m_x, m_y);
+ } else {
+ if (renderer()->hasMask())
+ result = renderer()->maskClipRect();
+ else {
+ IntRect bbox = renderer()->borderBox();
+ result = bbox;
+ IntRect overflowRect = renderer()->overflowRect(false);
+ if (bbox != overflowRect)
+ result.unite(overflowRect);
+ }
+
+ // We have to adjust the x/y of this result so that it is in the coordinate space of the layer.
+ // We also have to add in borderTopExtra here, since borderBox(), in order to play well with methods like
+ // floatRect that deal with child content, uses an origin of (0,0) that is at the child content box (so
+ // border box returns a y coord of -borderTopExtra(). The layer, however, uses the outer box. This is all
+ // really confusing.
+ result.move(m_x, m_y + renderer()->borderTopExtra());
+ }
+
+ // Convert the bounding box to an absolute position. We can do this easily by looking at the delta
+ // between the bounding box's xpos and our layer's xpos and then applying that to the absolute layerBounds
+ // passed in.
+ int absX = 0, absY = 0;
+ convertToLayerCoords(rootLayer, absX, absY);
+ result.move(absX - m_x, absY - m_y);
+ RenderView* view = renderer()->view();
+ ASSERT(view);
+ if (view)
+ result.inflate(view->maximalOutlineSize());
+ return result;
+}
+
+void RenderLayer::clearClipRectsIncludingDescendants()
+{
+ if (!m_clipRects)
+ return;
+
+ clearClipRects();
+
+ for (RenderLayer* l = firstChild(); l; l = l->nextSibling())
+ l->clearClipRectsIncludingDescendants();
+}
+
+void RenderLayer::clearClipRects()
+{
+ if (m_clipRects) {
+ m_clipRects->deref(m_object->renderArena());
+ m_clipRects = 0;
+#ifndef NDEBUG
+ m_clipRectsRoot = 0;
+#endif
+ }
+}
+
+static RenderObject* commonAncestor(RenderObject* obj1, RenderObject* obj2)
+{
+ if (!obj1 || !obj2)
+ return 0;
+
+ for (RenderObject* currObj1 = obj1; currObj1; currObj1 = currObj1->hoverAncestor())
+ for (RenderObject* currObj2 = obj2; currObj2; currObj2 = currObj2->hoverAncestor())
+ if (currObj1 == currObj2)
+ return currObj1;
+
+ return 0;
+}
+
+void RenderLayer::updateHoverActiveState(const HitTestRequest& request, HitTestResult& result)
+{
+ // We don't update :hover/:active state when the result is marked as readonly.
+ if (request.readonly)
+ return;
+
+ Document* doc = renderer()->document();
+
+ Node* activeNode = doc->activeNode();
+ if (activeNode && !request.active) {
+ // We are clearing the :active chain because the mouse has been released.
+ for (RenderObject* curr = activeNode->renderer(); curr; curr = curr->parent()) {
+ if (curr->element() && !curr->isText())
+ curr->element()->setInActiveChain(false);
+ }
+ doc->setActiveNode(0);
+ } else {
+ Node* newActiveNode = result.innerNode();
+ if (!activeNode && newActiveNode && request.active) {
+ // We are setting the :active chain and freezing it. If future moves happen, they
+ // will need to reference this chain.
+ for (RenderObject* curr = newActiveNode->renderer(); curr; curr = curr->parent()) {
+ if (curr->element() && !curr->isText()) {
+ curr->element()->setInActiveChain(true);
+ }
+ }
+ doc->setActiveNode(newActiveNode);
+ }
+ }
+
+ // If the mouse is down and if this is a mouse move event, we want to restrict changes in
+ // :hover/:active to only apply to elements that are in the :active chain that we froze
+ // at the time the mouse went down.
+ bool mustBeInActiveChain = request.active && request.mouseMove;
+
+ // Check to see if the hovered node has changed. If not, then we don't need to
+ // do anything.
+ RefPtr<Node> oldHoverNode = doc->hoverNode();
+ Node* newHoverNode = result.innerNode();
+
+ // Update our current hover node.
+ doc->setHoverNode(newHoverNode);
+
+ // We have two different objects. Fetch their renderers.
+ RenderObject* oldHoverObj = oldHoverNode ? oldHoverNode->renderer() : 0;
+ RenderObject* newHoverObj = newHoverNode ? newHoverNode->renderer() : 0;
+
+ // Locate the common ancestor render object for the two renderers.
+ RenderObject* ancestor = commonAncestor(oldHoverObj, newHoverObj);
+
+ if (oldHoverObj != newHoverObj) {
+ // The old hover path only needs to be cleared up to (and not including) the common ancestor;
+ for (RenderObject* curr = oldHoverObj; curr && curr != ancestor; curr = curr->hoverAncestor()) {
+ if (curr->element() && !curr->isText() && (!mustBeInActiveChain || curr->element()->inActiveChain())) {
+ curr->element()->setActive(false);
+ curr->element()->setHovered(false);
+ }
+ }
+ }
+
+ // Now set the hover state for our new object up to the root.
+ for (RenderObject* curr = newHoverObj; curr; curr = curr->hoverAncestor()) {
+ if (curr->element() && !curr->isText() && (!mustBeInActiveChain || curr->element()->inActiveChain())) {
+ curr->element()->setActive(request.active);
+ curr->element()->setHovered(true);
+ }
+ }
+}
+
+// Helper for the sorting of layers by z-index.
+static inline bool compareZIndex(RenderLayer* first, RenderLayer* second)
+{
+ return first->zIndex() < second->zIndex();
+}
+
+void RenderLayer::dirtyZOrderLists()
+{
+ if (m_posZOrderList)
+ m_posZOrderList->clear();
+ if (m_negZOrderList)
+ m_negZOrderList->clear();
+ m_zOrderListsDirty = true;
+}
+
+void RenderLayer::dirtyStackingContextZOrderLists()
+{
+ RenderLayer* sc = stackingContext();
+ if (sc)
+ sc->dirtyZOrderLists();
+}
+
+void RenderLayer::dirtyOverflowList()
+{
+ if (m_overflowList)
+ m_overflowList->clear();
+ m_overflowListDirty = true;
+}
+
+void RenderLayer::updateZOrderLists()
+{
+ if (!isStackingContext() || !m_zOrderListsDirty)
+ return;
+
+ for (RenderLayer* child = firstChild(); child; child = child->nextSibling())
+ if (!m_reflection || reflectionLayer() != child)
+ child->collectLayers(m_posZOrderList, m_negZOrderList);
+
+ // Sort the two lists.
+ if (m_posZOrderList)
+ std::stable_sort(m_posZOrderList->begin(), m_posZOrderList->end(), compareZIndex);
+ if (m_negZOrderList)
+ std::stable_sort(m_negZOrderList->begin(), m_negZOrderList->end(), compareZIndex);
+
+ m_zOrderListsDirty = false;
+}
+
+void RenderLayer::updateOverflowList()
+{
+ if (!m_overflowListDirty)
+ return;
+
+ for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) {
+ // Ignore non-overflow layers and reflections.
+ if (child->isOverflowOnly() && (!m_reflection || reflectionLayer() != child)) {
+ if (!m_overflowList)
+ m_overflowList = new Vector<RenderLayer*>;
+ m_overflowList->append(child);
+ }
+ }
+
+ m_overflowListDirty = false;
+}
+
+void RenderLayer::collectLayers(Vector<RenderLayer*>*& posBuffer, Vector<RenderLayer*>*& negBuffer)
+{
+ updateVisibilityStatus();
+
+ // Overflow layers are just painted by their enclosing layers, so they don't get put in zorder lists.
+ if ((m_hasVisibleContent || (m_hasVisibleDescendant && isStackingContext())) && !isOverflowOnly()) {
+ // Determine which buffer the child should be in.
+ Vector<RenderLayer*>*& buffer = (zIndex() >= 0) ? posBuffer : negBuffer;
+
+ // Create the buffer if it doesn't exist yet.
+ if (!buffer)
+ buffer = new Vector<RenderLayer*>;
+
+ // Append ourselves at the end of the appropriate buffer.
+ buffer->append(this);
+ }
+
+ // Recur into our children to collect more layers, but only if we don't establish
+ // a stacking context.
+ if (m_hasVisibleDescendant && !isStackingContext()) {
+ for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) {
+ // Ignore reflections.
+ if (!m_reflection || reflectionLayer() != child)
+ child->collectLayers(posBuffer, negBuffer);
+ }
+ }
+}
+
+void RenderLayer::repaintIncludingDescendants()
+{
+ m_object->repaint();
+ for (RenderLayer* curr = firstChild(); curr; curr = curr->nextSibling())
+ curr->repaintIncludingDescendants();
+}
+
+bool RenderLayer::shouldBeOverflowOnly() const
+{
+ return (renderer()->hasOverflowClip() || renderer()->hasReflection()) &&
+ !renderer()->isPositioned() &&
+ !renderer()->isRelPositioned() &&
+ !renderer()->hasTransform() &&
+ !isTransparent();
+}
+
+void RenderLayer::styleChanged(RenderStyle::Diff, const RenderStyle*)
+{
+ bool isOverflowOnly = shouldBeOverflowOnly();
+ if (isOverflowOnly != m_isOverflowOnly) {
+ m_isOverflowOnly = isOverflowOnly;
+ RenderLayer* p = parent();
+ if (p)
+ p->dirtyOverflowList();
+ dirtyStackingContextZOrderLists();
+ }
+
+ if (m_object->style()->overflowX() == OMARQUEE && m_object->style()->marqueeBehavior() != MNONE) {
+ if (!m_marquee)
+ m_marquee = new RenderMarquee(this);
+ m_marquee->updateMarqueeStyle();
+ }
+ else if (m_marquee) {
+ delete m_marquee;
+ m_marquee = 0;
+ }
+
+ if (!hasReflection() && m_reflection) {
+ m_reflection->destroy();
+ m_reflection = 0;
+ } else if (hasReflection()) {
+ if (!m_reflection)
+ createReflection();
+ updateReflectionStyle();
+ }
+
+ // FIXME: Need to detect a swap from custom to native scrollbars (and vice versa).
+ if (m_hBar)
+ m_hBar->styleChanged();
+ if (m_vBar)
+ m_vBar->styleChanged();
+
+ updateScrollCornerStyle();
+ updateResizerStyle();
+}
+
+void RenderLayer::updateScrollCornerStyle()
+{
+ RenderObject* actualRenderer = m_object->node()->isElementNode() ? m_object->node()->shadowAncestorNode()->renderer() : m_object;
+ RefPtr<RenderStyle> corner = m_object->hasOverflowClip() ? actualRenderer->getUncachedPseudoStyle(RenderStyle::SCROLLBAR_CORNER, actualRenderer->style()) : 0;
+ if (corner) {
+ if (!m_scrollCorner) {
+ m_scrollCorner = new (m_object->renderArena()) RenderScrollbarPart(m_object->document());
+ m_scrollCorner->setParent(m_object);
+ }
+ m_scrollCorner->setStyle(corner.release());
+ } else if (m_scrollCorner) {
+ m_scrollCorner->destroy();
+ m_scrollCorner = 0;
+ }
+}
+
+void RenderLayer::updateResizerStyle()
+{
+ RenderObject* actualRenderer = m_object->node()->isElementNode() ? m_object->node()->shadowAncestorNode()->renderer() : m_object;
+ RefPtr<RenderStyle> resizer = m_object->hasOverflowClip() ? actualRenderer->getUncachedPseudoStyle(RenderStyle::RESIZER, actualRenderer->style()) : 0;
+ if (resizer) {
+ if (!m_resizer) {
+ m_resizer = new (m_object->renderArena()) RenderScrollbarPart(m_object->document());
+ m_resizer->setParent(m_object);
+ }
+ m_resizer->setStyle(resizer.release());
+ } else if (m_resizer) {
+ m_resizer->destroy();
+ m_resizer = 0;
+ }
+}
+
+RenderLayer* RenderLayer::reflectionLayer() const
+{
+ return m_reflection ? m_reflection->layer() : 0;
+}
+
+void RenderLayer::createReflection()
+{
+ ASSERT(!m_reflection);
+ m_reflection = new (renderer()->renderArena()) RenderReplica(renderer()->document());
+ m_reflection->setParent(renderer()); // We create a 1-way connection.
+}
+
+void RenderLayer::updateReflectionStyle()
+{
+ RefPtr<RenderStyle> newStyle = RenderStyle::create();
+ newStyle->inheritFrom(renderer()->style());
+
+ // Map in our transform.
+ TransformOperations transform;
+ switch (renderer()->style()->boxReflect()->direction()) {
+ case ReflectionBelow:
+ transform.operations().append(TranslateTransformOperation::create(Length(0, Fixed), Length(100., Percent), TransformOperation::TRANSLATE));
+ transform.operations().append(TranslateTransformOperation::create(Length(0, Fixed), renderer()->style()->boxReflect()->offset(), TransformOperation::TRANSLATE));
+ transform.operations().append(ScaleTransformOperation::create(1.0, -1.0, ScaleTransformOperation::SCALE));
+ break;
+ case ReflectionAbove:
+ transform.operations().append(ScaleTransformOperation::create(1.0, -1.0, ScaleTransformOperation::SCALE));
+ transform.operations().append(TranslateTransformOperation::create(Length(0, Fixed), Length(100., Percent), TransformOperation::TRANSLATE));
+ transform.operations().append(TranslateTransformOperation::create(Length(0, Fixed), renderer()->style()->boxReflect()->offset(), TransformOperation::TRANSLATE));
+ break;
+ case ReflectionRight:
+ transform.operations().append(TranslateTransformOperation::create(Length(100., Percent), Length(0, Fixed), TransformOperation::TRANSLATE));
+ transform.operations().append(TranslateTransformOperation::create(renderer()->style()->boxReflect()->offset(), Length(0, Fixed), TransformOperation::TRANSLATE));
+ transform.operations().append(ScaleTransformOperation::create(-1.0, 1.0, ScaleTransformOperation::SCALE));
+ break;
+ case ReflectionLeft:
+ transform.operations().append(ScaleTransformOperation::create(-1.0, 1.0, ScaleTransformOperation::SCALE));
+ transform.operations().append(TranslateTransformOperation::create(Length(100., Percent), Length(0, Fixed), TransformOperation::TRANSLATE));
+ transform.operations().append(TranslateTransformOperation::create(renderer()->style()->boxReflect()->offset(), Length(0, Fixed), TransformOperation::TRANSLATE));
+ break;
+ }
+ newStyle->setTransform(transform);
+
+ // Map in our mask.
+ newStyle->setMaskBoxImage(renderer()->style()->boxReflect()->mask());
+
+ m_reflection->setStyle(newStyle.release());
+}
+
+void RenderLayer::suspendMarquees()
+{
+ if (m_marquee)
+ m_marquee->suspend();
+
+ for (RenderLayer* curr = firstChild(); curr; curr = curr->nextSibling())
+ curr->suspendMarquees();
+}
+
+} // namespace WebCore