diff options
| author | Allan Sandfeld Jensen <allan.jensen@digia.com> | 2013-09-13 12:51:20 +0200 |
|---|---|---|
| committer | The Qt Project <gerrit-noreply@qt-project.org> | 2013-09-19 20:50:05 +0200 |
| commit | d441d6f39bb846989d95bcf5caf387b42414718d (patch) | |
| tree | e367e64a75991c554930278175d403c072de6bb8 /Source/WebCore/rendering | |
| parent | 0060b2994c07842f4c59de64b5e3e430525c4b90 (diff) | |
| download | qtwebkit-d441d6f39bb846989d95bcf5caf387b42414718d.tar.gz | |
Import Qt5x2 branch of QtWebkit for Qt 5.2
Importing a new snapshot of webkit.
Change-Id: I2d01ad12cdc8af8cb015387641120a9d7ea5f10c
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@digia.com>
Diffstat (limited to 'Source/WebCore/rendering')
380 files changed, 22913 insertions, 16653 deletions
diff --git a/Source/WebCore/rendering/AutoTableLayout.cpp b/Source/WebCore/rendering/AutoTableLayout.cpp index 8a712c74f..4f6d432c0 100644 --- a/Source/WebCore/rendering/AutoTableLayout.cpp +++ b/Source/WebCore/rendering/AutoTableLayout.cpp @@ -50,9 +50,12 @@ void AutoTableLayout::recalcColumn(unsigned effCol) RenderTableCell* maxContributor = 0; for (RenderObject* child = m_table->children()->firstChild(); child; child = child->nextSibling()) { - if (child->isRenderTableCol()) - toRenderTableCol(child)->computePreferredLogicalWidths(); - else if (child->isTableSection()) { + if (child->isRenderTableCol()){ + // RenderTableCols don't have the concept of preferred logical width, but we need to clear their dirty bits + // so that if we call setPreferredWidthsDirty(true) on a col or one of its descendants, we'll mark it's + // ancestors as dirty. + toRenderTableCol(child)->clearPreferredLogicalWidthsDirtyBits(); + } else if (child->isTableSection()) { RenderTableSection* section = toRenderTableSection(child); unsigned numRows = section->numRows(); for (unsigned i = 0; i < numRows; i++) { @@ -72,8 +75,6 @@ void AutoTableLayout::recalcColumn(unsigned effCol) columnLayout.maxLogicalWidth = max<int>(columnLayout.maxLogicalWidth, 1); if (cell->colSpan() == 1) { - if (cell->preferredLogicalWidthsDirty()) - cell->computePreferredLogicalWidths(); columnLayout.minLogicalWidth = max<int>(cell->minPreferredLogicalWidth(), columnLayout.minLogicalWidth); if (cell->maxPreferredLogicalWidth() > columnLayout.maxLogicalWidth) { columnLayout.maxLogicalWidth = cell->maxPreferredLogicalWidth(); @@ -93,7 +94,7 @@ void AutoTableLayout::recalcColumn(unsigned effCol) case Fixed: // ignore width=0 if (cellLogicalWidth.isPositive() && !columnLayout.logicalWidth.isPercent()) { - LayoutUnit logicalWidth = cell->adjustBorderBoxLogicalWidthForBoxSizing(cellLogicalWidth.value()); + int logicalWidth = cell->adjustBorderBoxLogicalWidthForBoxSizing(cellLogicalWidth.value()); if (columnLayout.logicalWidth.isFixed()) { // Nav/IE weirdness if ((logicalWidth > columnLayout.logicalWidth.value()) @@ -209,7 +210,7 @@ static bool shouldScaleColumns(RenderTable* table) return scale; } -void AutoTableLayout::computePreferredLogicalWidths(LayoutUnit& minWidth, LayoutUnit& maxWidth) +void AutoTableLayout::computeIntrinsicLogicalWidths(LayoutUnit& minWidth, LayoutUnit& maxWidth) { fullRecalc(); @@ -246,25 +247,13 @@ void AutoTableLayout::computePreferredLogicalWidths(LayoutUnit& minWidth, Layout } maxWidth = max<int>(maxWidth, spanMaxLogicalWidth); +} - int bordersPaddingAndSpacing = m_table->bordersPaddingAndSpacingInRowDirection(); - minWidth += bordersPaddingAndSpacing; - maxWidth += bordersPaddingAndSpacing; - +void AutoTableLayout::applyPreferredLogicalWidthQuirks(LayoutUnit& minWidth, LayoutUnit& maxWidth) const +{ Length tableLogicalWidth = m_table->style()->logicalWidth(); - if (tableLogicalWidth.isFixed() && tableLogicalWidth.isPositive()) { - minWidth = max<int>(minWidth, tableLogicalWidth.value()); - maxWidth = minWidth; - } else if (!remainingPercent && maxNonPercent) { - // if there was no remaining percent, maxWidth is invalid - maxWidth = tableMaxWidth; - } - - Length tableLogicalMinWidth = m_table->style()->logicalMinWidth(); - if (tableLogicalMinWidth.isFixed() && tableLogicalMinWidth.isPositive()) { - minWidth = max<int>(minWidth, tableLogicalMinWidth.value()); - maxWidth = max<int>(minWidth, maxWidth); - } + if (tableLogicalWidth.isFixed() && tableLogicalWidth.isPositive()) + minWidth = maxWidth = max<int>(minWidth, tableLogicalWidth.value()); } /* diff --git a/Source/WebCore/rendering/AutoTableLayout.h b/Source/WebCore/rendering/AutoTableLayout.h index 9d0adc1cd..a1fdad048 100644 --- a/Source/WebCore/rendering/AutoTableLayout.h +++ b/Source/WebCore/rendering/AutoTableLayout.h @@ -36,7 +36,8 @@ public: AutoTableLayout(RenderTable*); ~AutoTableLayout(); - virtual void computePreferredLogicalWidths(LayoutUnit& minWidth, LayoutUnit& maxWidth); + virtual void computeIntrinsicLogicalWidths(LayoutUnit& minWidth, LayoutUnit& maxWidth) OVERRIDE; + virtual void applyPreferredLogicalWidthQuirks(LayoutUnit& minWidth, LayoutUnit& maxWidth) const OVERRIDE; virtual void layout(); private: diff --git a/Source/WebCore/rendering/BidiRun.h b/Source/WebCore/rendering/BidiRun.h index 694cbca77..e1c725223 100644 --- a/Source/WebCore/rendering/BidiRun.h +++ b/Source/WebCore/rendering/BidiRun.h @@ -39,7 +39,12 @@ struct BidiRun : BidiCharacterRun { , m_object(object) , m_box(0) { - m_hasHyphen = false; // Stored in base class to save space. + ASSERT(!object->isText() || static_cast<unsigned>(stop) <= toRenderText(object)->textLength()); + // Stored in base class to save space. + m_hasHyphen = false; +#if ENABLE(CSS_SHAPES) + m_startsSegment = false; +#endif } void destroy(); diff --git a/Source/WebCore/rendering/EllipsisBox.cpp b/Source/WebCore/rendering/EllipsisBox.cpp index 902df2b0a..6d90e386c 100644 --- a/Source/WebCore/rendering/EllipsisBox.cpp +++ b/Source/WebCore/rendering/EllipsisBox.cpp @@ -36,13 +36,13 @@ void EllipsisBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, La { GraphicsContext* context = paintInfo.context; RenderStyle* style = m_renderer->style(isFirstLineStyle()); - Color textColor = style->visitedDependentColor(CSSPropertyColor); + Color textColor = style->visitedDependentColor(CSSPropertyWebkitTextFillColor); if (textColor != context->fillColor()) context->setFillColor(textColor, style->colorSpace()); bool setShadow = false; if (style->textShadow()) { context->setShadow(LayoutSize(style->textShadow()->x(), style->textShadow()->y()), - style->textShadow()->blur(), style->textShadow()->color(), style->colorSpace()); + style->textShadow()->radius(), style->textShadow()->color(), style->colorSpace()); setShadow = true; } @@ -51,7 +51,7 @@ void EllipsisBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, La paintSelection(context, paintOffset, style, font); // Select the correct color for painting the text. - Color foreground = paintInfo.forceBlackText ? Color::black : renderer()->selectionForegroundColor(); + Color foreground = paintInfo.forceBlackText() ? Color::black : renderer()->selectionForegroundColor(); if (foreground.isValid() && foreground != textColor) context->setFillColor(foreground, style->colorSpace()); } @@ -69,26 +69,35 @@ void EllipsisBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, La paintMarkupBox(paintInfo, paintOffset, lineTop, lineBottom, style); } -void EllipsisBox::paintMarkupBox(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit lineTop, LayoutUnit lineBottom, RenderStyle* style) +InlineBox* EllipsisBox::markupBox() const { if (!m_shouldPaintMarkupBox || !m_renderer->isRenderBlock()) - return; + return 0; RenderBlock* block = toRenderBlock(m_renderer); RootInlineBox* lastLine = block->lineAtIndex(block->lineCount() - 1); if (!lastLine) - return; + return 0; // If the last line-box on the last line of a block is a link, -webkit-line-clamp paints that box after the ellipsis. // It does not actually move the link. InlineBox* anchorBox = lastLine->lastChild(); if (!anchorBox || !anchorBox->renderer()->style()->isLink()) + return 0; + + return anchorBox; +} + +void EllipsisBox::paintMarkupBox(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit lineTop, LayoutUnit lineBottom, RenderStyle* style) +{ + InlineBox* markupBox = this->markupBox(); + if (!markupBox) return; LayoutPoint adjustedPaintOffset = paintOffset; - adjustedPaintOffset.move(x() + m_logicalWidth - anchorBox->x(), - y() + style->fontMetrics().ascent() - (anchorBox->y() + anchorBox->renderer()->style(isFirstLineStyle())->fontMetrics().ascent())); - anchorBox->paint(paintInfo, adjustedPaintOffset, lineTop, lineBottom); + adjustedPaintOffset.move(x() + m_logicalWidth - markupBox->x(), + y() + style->fontMetrics().ascent() - (markupBox->y() + markupBox->renderer()->style(isFirstLineStyle())->fontMetrics().ascent())); + markupBox->paint(paintInfo, adjustedPaintOffset, lineTop, lineBottom); } IntRect EllipsisBox::selectionRect() @@ -121,8 +130,28 @@ void EllipsisBox::paintSelection(GraphicsContext* context, const LayoutPoint& pa context->drawHighlightForText(font, RenderBlock::constructTextRun(renderer(), font, m_str, style, TextRun::AllowTrailingExpansion), roundedIntPoint(LayoutPoint(x() + paintOffset.x(), y() + paintOffset.y() + top)), h, c, style->colorSpace()); } -bool EllipsisBox::nodeAtPoint(const HitTestRequest&, HitTestResult&, const HitTestLocation&, const LayoutPoint&, LayoutUnit, LayoutUnit) +bool EllipsisBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, LayoutUnit lineTop, LayoutUnit lineBottom) { + LayoutPoint adjustedLocation = accumulatedOffset + roundedLayoutPoint(topLeft()); + + // Hit test the markup box. + if (InlineBox* markupBox = this->markupBox()) { + RenderStyle* style = m_renderer->style(isFirstLineStyle()); + LayoutUnit mtx = adjustedLocation.x() + m_logicalWidth - markupBox->x(); + LayoutUnit mty = adjustedLocation.y() + style->fontMetrics().ascent() - (markupBox->y() + markupBox->renderer()->style(isFirstLineStyle())->fontMetrics().ascent()); + if (markupBox->nodeAtPoint(request, result, locationInContainer, LayoutPoint(mtx, mty), lineTop, lineBottom)) { + renderer()->updateHitTestResult(result, locationInContainer.point() - LayoutSize(mtx, mty)); + return true; + } + } + + LayoutRect boundsRect(adjustedLocation, LayoutSize(m_logicalWidth, m_height)); + if (visibleToHitTesting() && boundsRect.intersects(HitTestLocation::rectForPoint(locationInContainer.point(), 0, 0, 0, 0))) { + renderer()->updateHitTestResult(result, locationInContainer.point() - toLayoutSize(adjustedLocation)); + if (!result.addNodeToRectBasedTestResult(renderer()->node(), request, locationInContainer, boundsRect)) + return true; + } + return false; } diff --git a/Source/WebCore/rendering/EllipsisBox.h b/Source/WebCore/rendering/EllipsisBox.h index 699767847..7c60b18bd 100644 --- a/Source/WebCore/rendering/EllipsisBox.h +++ b/Source/WebCore/rendering/EllipsisBox.h @@ -27,7 +27,7 @@ namespace WebCore { class HitTestRequest; class HitTestResult; -class EllipsisBox : public InlineBox { +class EllipsisBox FINAL : public InlineBox { public: EllipsisBox(RenderObject* obj, const AtomicString& ellipsisStr, InlineFlowBox* parent, int width, int height, int y, bool firstLine, bool isVertical, InlineBox* markupBox) @@ -49,6 +49,7 @@ private: virtual int height() const { return m_height; } virtual RenderObject::SelectionState selectionState() { return m_selectionState; } void paintSelection(GraphicsContext*, const LayoutPoint&, RenderStyle*, const Font&); + InlineBox* markupBox() const; bool m_shouldPaintMarkupBox; int m_height; diff --git a/Source/WebCore/rendering/ExclusionPolygon.cpp b/Source/WebCore/rendering/ExclusionPolygon.cpp deleted file mode 100644 index 21beac1ec..000000000 --- a/Source/WebCore/rendering/ExclusionPolygon.cpp +++ /dev/null @@ -1,379 +0,0 @@ -/* - * Copyright (C) 2012 Adobe Systems Incorporated. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above - * copyright notice, this list of conditions and the following - * disclaimer. - * 2. Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "ExclusionPolygon.h" - -#include <wtf/MathExtras.h> - -namespace WebCore { - -enum EdgeIntersectionType { - Normal, - VertexMinY, - VertexMaxY, - VertexYBoth -}; - -struct EdgeIntersection { - const ExclusionPolygonEdge* edge; - FloatPoint point; - EdgeIntersectionType type; -}; - -static inline float determinant(const FloatSize& a, const FloatSize& b) -{ - return a.width() * b.height() - a.height() * b.width(); -} - -static inline bool areCollinearPoints(const FloatPoint& p0, const FloatPoint& p1, const FloatPoint& p2) -{ - return !determinant(p1 - p0, p2 - p0); -} - -static inline bool areCoincidentPoints(const FloatPoint& p0, const FloatPoint& p1) -{ - return p0.x() == p1.x() && p0.y() == p1.y(); -} - -static inline unsigned nextVertexIndex(unsigned vertexIndex, unsigned nVertices, bool clockwise) -{ - return ((clockwise) ? vertexIndex + 1 : vertexIndex - 1 + nVertices) % nVertices; -} - -unsigned ExclusionPolygon::findNextEdgeVertexIndex(unsigned vertexIndex1, bool clockwise) const -{ - unsigned nVertices = numberOfVertices(); - unsigned vertexIndex2 = nextVertexIndex(vertexIndex1, nVertices, clockwise); - - while (vertexIndex2 && areCoincidentPoints(vertexAt(vertexIndex1), vertexAt(vertexIndex2))) - vertexIndex2 = nextVertexIndex(vertexIndex2, nVertices, clockwise); - - while (vertexIndex2) { - unsigned vertexIndex3 = nextVertexIndex(vertexIndex2, nVertices, clockwise); - if (!areCollinearPoints(vertexAt(vertexIndex1), vertexAt(vertexIndex2), vertexAt(vertexIndex3))) - break; - vertexIndex2 = vertexIndex3; - } - - return vertexIndex2; -} - -ExclusionPolygon::ExclusionPolygon(PassOwnPtr<Vector<FloatPoint> > vertices, WindRule fillRule) - : ExclusionShape() - , m_vertices(vertices) - , m_fillRule(fillRule) -{ - unsigned nVertices = numberOfVertices(); - m_edges.resize(nVertices); - m_empty = nVertices < 3; - - if (nVertices) - m_boundingBox.setLocation(vertexAt(0)); - - if (m_empty) - return; - - unsigned minVertexIndex = 0; - for (unsigned i = 1; i < nVertices; ++i) { - const FloatPoint& vertex = vertexAt(i); - if (vertex.y() < vertexAt(minVertexIndex).y() || (vertex.y() == vertexAt(minVertexIndex).y() && vertex.x() < vertexAt(minVertexIndex).x())) - minVertexIndex = i; - } - FloatPoint nextVertex = vertexAt((minVertexIndex + 1) % nVertices); - FloatPoint prevVertex = vertexAt((minVertexIndex + nVertices - 1) % nVertices); - bool clockwise = determinant(vertexAt(minVertexIndex) - prevVertex, nextVertex - prevVertex) > 0; - - unsigned edgeIndex = 0; - unsigned vertexIndex1 = 0; - do { - m_boundingBox.extend(vertexAt(vertexIndex1)); - unsigned vertexIndex2 = findNextEdgeVertexIndex(vertexIndex1, clockwise); - m_edges[edgeIndex].polygon = this; - m_edges[edgeIndex].vertexIndex1 = vertexIndex1; - m_edges[edgeIndex].vertexIndex2 = vertexIndex2; - m_edges[edgeIndex].edgeIndex = edgeIndex; - edgeIndex++; - vertexIndex1 = vertexIndex2; - } while (vertexIndex1); - - if (edgeIndex > 3) { - const ExclusionPolygonEdge& firstEdge = m_edges[0]; - const ExclusionPolygonEdge& lastEdge = m_edges[edgeIndex - 1]; - if (areCollinearPoints(lastEdge.vertex1(), lastEdge.vertex2(), firstEdge.vertex2())) { - m_edges[0].vertexIndex1 = lastEdge.vertexIndex1; - edgeIndex--; - } - } - - m_edges.resize(edgeIndex); - m_empty = m_edges.size() < 3; - - if (m_empty) - return; - - for (unsigned i = 0; i < m_edges.size(); i++) { - ExclusionPolygonEdge* edge = &m_edges[i]; - m_edgeTree.add(EdgeInterval(edge->minY(), edge->maxY(), edge)); - } -} - -static bool computeXIntersection(const ExclusionPolygonEdge* edgePointer, float y, EdgeIntersection& result) -{ - const ExclusionPolygonEdge& edge = *edgePointer; - - if (edge.minY() > y || edge.maxY() < y) - return false; - - const FloatPoint& vertex1 = edge.vertex1(); - const FloatPoint& vertex2 = edge.vertex2(); - float dy = vertex2.y() - vertex1.y(); - - float intersectionX; - EdgeIntersectionType intersectionType; - - if (!dy) { - intersectionType = VertexYBoth; - intersectionX = edge.minX(); - } else if (y == edge.minY()) { - intersectionType = VertexMinY; - intersectionX = (vertex1.y() < vertex2.y()) ? vertex1.x() : vertex2.x(); - } else if (y == edge.maxY()) { - intersectionType = VertexMaxY; - intersectionX = (vertex1.y() > vertex2.y()) ? vertex1.x() : vertex2.x(); - } else { - intersectionType = Normal; - intersectionX = ((y - vertex1.y()) * (vertex2.x() - vertex1.x()) / dy) + vertex1.x(); - } - - result.edge = edgePointer; - result.type = intersectionType; - result.point.set(intersectionX, y); - - return true; -} - -static inline bool getVertexIntersectionVertices(const EdgeIntersection& intersection, FloatPoint& prevVertex, FloatPoint& thisVertex, FloatPoint& nextVertex) -{ - if (intersection.type != VertexMinY && intersection.type != VertexMaxY) - return false; - - ASSERT(intersection.edge && intersection.edge->polygon); - const ExclusionPolygon& polygon = *(intersection.edge->polygon); - const ExclusionPolygonEdge& thisEdge = *(intersection.edge); - - if ((intersection.type == VertexMinY && (thisEdge.vertex1().y() < thisEdge.vertex2().y())) - || (intersection.type == VertexMaxY && (thisEdge.vertex1().y() > thisEdge.vertex2().y()))) { - prevVertex = polygon.vertexAt(thisEdge.previousEdge().vertexIndex1); - thisVertex = polygon.vertexAt(thisEdge.vertexIndex1); - nextVertex = polygon.vertexAt(thisEdge.vertexIndex2); - } else { - prevVertex = polygon.vertexAt(thisEdge.vertexIndex1); - thisVertex = polygon.vertexAt(thisEdge.vertexIndex2); - nextVertex = polygon.vertexAt(thisEdge.nextEdge().vertexIndex2); - } - - return true; -} - -static inline bool appendIntervalX(float x, bool inside, Vector<ExclusionInterval>& result) -{ - if (!inside) - result.append(ExclusionInterval(x)); - else - result[result.size() - 1].x2 = x; - - return !inside; -} - -static bool compareEdgeIntersectionX(const EdgeIntersection& intersection1, const EdgeIntersection& intersection2) -{ - float x1 = intersection1.point.x(); - float x2 = intersection2.point.x(); - return (x1 == x2) ? intersection1.type < intersection2.type : x1 < x2; -} - -void ExclusionPolygon::computeXIntersections(float y, bool isMinY, Vector<ExclusionInterval>& result) const -{ - Vector<ExclusionPolygon::EdgeInterval> overlappingEdges; - m_edgeTree.allOverlaps(ExclusionPolygon::EdgeInterval(y, y, 0), overlappingEdges); - - Vector<EdgeIntersection> intersections; - EdgeIntersection intersection; - for (unsigned i = 0; i < overlappingEdges.size(); i++) { - ExclusionPolygonEdge* edge = static_cast<ExclusionPolygonEdge*>(overlappingEdges[i].data()); - if (computeXIntersection(edge, y, intersection) && intersection.type != VertexYBoth) - intersections.append(intersection); - } - if (intersections.size() < 2) - return; - - std::sort(intersections.begin(), intersections.end(), WebCore::compareEdgeIntersectionX); - - unsigned index = 0; - int windCount = 0; - bool inside = false; - - while (index < intersections.size()) { - const EdgeIntersection& thisIntersection = intersections[index]; - if (index + 1 < intersections.size()) { - const EdgeIntersection& nextIntersection = intersections[index + 1]; - if ((thisIntersection.point.x() == nextIntersection.point.x()) && (thisIntersection.type == VertexMinY || thisIntersection.type == VertexMaxY)) { - if (thisIntersection.type == nextIntersection.type) { - // Skip pairs of intersections whose types are VertexMaxY,VertexMaxY and VertexMinY,VertexMinY. - index += 2; - } else { - // Replace pairs of intersections whose types are VertexMinY,VertexMaxY or VertexMaxY,VertexMinY with one intersection. - index++; - } - continue; - } - } - - const ExclusionPolygonEdge& thisEdge = *thisIntersection.edge; - bool evenOddCrossing = !windCount; - - if (fillRule() == RULE_EVENODD) { - windCount += (thisEdge.vertex2().y() > thisEdge.vertex1().y()) ? 1 : -1; - evenOddCrossing = evenOddCrossing || !windCount; - } - - if (evenOddCrossing) { - bool edgeCrossing = thisIntersection.type == Normal; - if (!edgeCrossing) { - FloatPoint prevVertex; - FloatPoint thisVertex; - FloatPoint nextVertex; - - if (getVertexIntersectionVertices(thisIntersection, prevVertex, thisVertex, nextVertex)) { - if (nextVertex.y() == y) - edgeCrossing = (isMinY) ? prevVertex.y() > y : prevVertex.y() < y; - else if (prevVertex.y() == y) - edgeCrossing = (isMinY) ? nextVertex.y() > y : nextVertex.y() < y; - else - edgeCrossing = true; - } - } - if (edgeCrossing) - inside = appendIntervalX(thisIntersection.point.x(), inside, result); - } - - index++; - } -} - -// Return the X projections of the edges that overlap y1,y2. -void ExclusionPolygon::computeEdgeIntersections(float y1, float y2, Vector<ExclusionInterval>& result) const -{ - Vector<ExclusionPolygon::EdgeInterval> overlappingEdges; - m_edgeTree.allOverlaps(ExclusionPolygon::EdgeInterval(y1, y2, 0), overlappingEdges); - - EdgeIntersection intersection; - for (unsigned i = 0; i < overlappingEdges.size(); i++) { - const ExclusionPolygonEdge *edge = static_cast<ExclusionPolygonEdge*>(overlappingEdges[i].data()); - float x1; - float x2; - - if (edge->minY() < y1) { - computeXIntersection(edge, y1, intersection); - x1 = intersection.point.x(); - } else - x1 = (edge->vertex1().y() < edge->vertex2().y()) ? edge->vertex1().x() : edge->vertex2().x(); - - if (edge->maxY() > y2) { - computeXIntersection(edge, y2, intersection); - x2 = intersection.point.x(); - } else - x2 = (edge->vertex1().y() > edge->vertex2().y()) ? edge->vertex1().x() : edge->vertex2().x(); - - if (x1 > x2) - std::swap(x1, x2); - - if (x2 > x1) - result.append(ExclusionInterval(x1, x2)); - } - - sortExclusionIntervals(result); -} - -void ExclusionPolygon::getExcludedIntervals(float logicalTop, float logicalHeight, SegmentList& result) const -{ - if (isEmpty()) - return; - - float y1 = minYForLogicalLine(logicalTop, logicalHeight); - float y2 = maxYForLogicalLine(logicalTop, logicalHeight); - - Vector<ExclusionInterval> y1XIntervals, y2XIntervals; - computeXIntersections(y1, true, y1XIntervals); - computeXIntersections(y2, false, y2XIntervals); - - Vector<ExclusionInterval> mergedIntervals; - mergeExclusionIntervals(y1XIntervals, y2XIntervals, mergedIntervals); - - Vector<ExclusionInterval> edgeIntervals; - computeEdgeIntersections(y1, y2, edgeIntervals); - - Vector<ExclusionInterval> excludedIntervals; - mergeExclusionIntervals(mergedIntervals, edgeIntervals, excludedIntervals); - - for (unsigned i = 0; i < excludedIntervals.size(); i++) { - ExclusionInterval interval = excludedIntervals[i]; - result.append(LineSegment(interval.x1, interval.x2)); - } -} - -void ExclusionPolygon::getIncludedIntervals(float logicalTop, float logicalHeight, SegmentList& result) const -{ - if (isEmpty()) - return; - - float y1 = minYForLogicalLine(logicalTop, logicalHeight); - float y2 = maxYForLogicalLine(logicalTop, logicalHeight); - - Vector<ExclusionInterval> y1XIntervals, y2XIntervals; - computeXIntersections(y1, true, y1XIntervals); - computeXIntersections(y2, false, y2XIntervals); - - Vector<ExclusionInterval> commonIntervals; - intersectExclusionIntervals(y1XIntervals, y2XIntervals, commonIntervals); - - Vector<ExclusionInterval> edgeIntervals; - computeEdgeIntersections(y1, y2, edgeIntervals); - - Vector<ExclusionInterval> includedIntervals; - subtractExclusionIntervals(commonIntervals, edgeIntervals, includedIntervals); - - for (unsigned i = 0; i < includedIntervals.size(); i++) { - ExclusionInterval interval = includedIntervals[i]; - result.append(LineSegment(interval.x1, interval.x2)); - } -} - -} // namespace WebCore diff --git a/Source/WebCore/rendering/ExclusionPolygon.h b/Source/WebCore/rendering/ExclusionPolygon.h deleted file mode 100644 index 647eb172e..000000000 --- a/Source/WebCore/rendering/ExclusionPolygon.h +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (C) 2012 Adobe Systems Incorporated. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above - * copyright notice, this list of conditions and the following - * disclaimer. - * 2. Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef ExclusionPolygon_h -#define ExclusionPolygon_h - -#include "ExclusionInterval.h" -#include "ExclusionShape.h" -#include "FloatPoint.h" -#include "FloatRect.h" -#include "PODIntervalTree.h" -#include "WindRule.h" -#include <wtf/MathExtras.h> -#include <wtf/OwnPtr.h> -#include <wtf/PassOwnPtr.h> -#include <wtf/Vector.h> - -namespace WebCore { - -struct ExclusionPolygonEdge; - -// This class is used by PODIntervalTree for debugging. -#ifndef NDEBUG -template <class> struct ValueToString; -#endif - -class ExclusionPolygon : public ExclusionShape { - WTF_MAKE_NONCOPYABLE(ExclusionPolygon); -public: - ExclusionPolygon(PassOwnPtr<Vector<FloatPoint> > vertices, WindRule fillRule); - - const FloatPoint& vertexAt(unsigned index) const { return (*m_vertices)[index]; } - unsigned numberOfVertices() const { return m_vertices->size(); } - WindRule fillRule() const { return m_fillRule; } - - const ExclusionPolygonEdge& edgeAt(unsigned index) const { return m_edges[index]; } - unsigned numberOfEdges() const { return m_edges.size(); } - - virtual FloatRect shapeLogicalBoundingBox() const OVERRIDE { return internalToLogicalBoundingBox(m_boundingBox); } - virtual bool isEmpty() const OVERRIDE { return m_empty; } - virtual void getExcludedIntervals(float logicalTop, float logicalHeight, SegmentList&) const OVERRIDE; - virtual void getIncludedIntervals(float logicalTop, float logicalHeight, SegmentList&) const OVERRIDE; - -private: - void computeXIntersections(float y, bool isMinY, Vector<ExclusionInterval>&) const; - void computeEdgeIntersections(float minY, float maxY, Vector<ExclusionInterval>&) const; - unsigned findNextEdgeVertexIndex(unsigned vertexIndex1, bool clockwise) const; - - typedef PODInterval<float, ExclusionPolygonEdge*> EdgeInterval; - typedef PODIntervalTree<float, ExclusionPolygonEdge*> EdgeIntervalTree; - - OwnPtr<Vector<FloatPoint> > m_vertices; - WindRule m_fillRule; - FloatRect m_boundingBox; - Vector<ExclusionPolygonEdge> m_edges; - EdgeIntervalTree m_edgeTree; - bool m_empty; -}; - -// EdgeIntervalTree nodes store minY, maxY, and a ("UserData") pointer to an ExclusionPolygonEdge. Edge vertex -// index1 is less than index2, except the last edge, where index2 is 0. When a polygon edge is defined by 3 -// or more colinear vertices, index2 can be the the index of the last colinear vertex. -struct ExclusionPolygonEdge { - const FloatPoint& vertex1() const - { - ASSERT(polygon); - return polygon->vertexAt(vertexIndex1); - } - - const FloatPoint& vertex2() const - { - ASSERT(polygon); - return polygon->vertexAt(vertexIndex2); - } - - const ExclusionPolygonEdge& previousEdge() const - { - ASSERT(polygon && polygon->numberOfEdges() > 1); - return polygon->edgeAt((edgeIndex + polygon->numberOfEdges() - 1) % polygon->numberOfEdges()); - } - - const ExclusionPolygonEdge& nextEdge() const - { - ASSERT(polygon && polygon->numberOfEdges() > 1); - return polygon->edgeAt((edgeIndex + 1) % polygon->numberOfEdges()); - } - - float minX() const { return std::min(vertex1().x(), vertex2().x()); } - float minY() const { return std::min(vertex1().y(), vertex2().y()); } - float maxX() const { return std::max(vertex1().x(), vertex2().x()); } - float maxY() const { return std::max(vertex1().y(), vertex2().y()); } - - const ExclusionPolygon* polygon; - unsigned vertexIndex1; - unsigned vertexIndex2; - unsigned edgeIndex; -}; - -// These structures are used by PODIntervalTree for debugging.1 -#ifndef NDEBUG -template <> struct ValueToString<float> { - static String string(const float value) { return String::number(value); } -}; - -template<> struct ValueToString<ExclusionPolygonEdge*> { - static String string(const ExclusionPolygonEdge* edge) { return String::format("%p (%f,%f %f,%f)", edge, edge->vertex1().x(), edge->vertex1().y(), edge->vertex2().x(), edge->vertex2().y()); } -}; -#endif - -} // namespace WebCore - -#endif // ExclusionPolygon_h diff --git a/Source/WebCore/rendering/ExclusionRectangle.cpp b/Source/WebCore/rendering/ExclusionRectangle.cpp deleted file mode 100644 index 328cba2f3..000000000 --- a/Source/WebCore/rendering/ExclusionRectangle.cpp +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright (C) 2012 Adobe Systems Incorporated. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above - * copyright notice, this list of conditions and the following - * disclaimer. - * 2. Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "ExclusionRectangle.h" - -#include <wtf/MathExtras.h> - -namespace WebCore { - -static inline float ellipseXIntercept(float y, float rx, float ry) -{ - ASSERT(ry > 0); - return rx * sqrt(1 - (y*y) / (ry*ry)); -} - -void ExclusionRectangle::getExcludedIntervals(float logicalTop, float logicalHeight, SegmentList& result) const -{ - if (isEmpty()) - return; - - float y1 = minYForLogicalLine(logicalTop, logicalHeight); - float y2 = maxYForLogicalLine(logicalTop, logicalHeight); - - if (y2 < m_y || y1 >= m_y + m_height) - return; - - float x1 = m_x; - float x2 = m_x + m_width; - - if (m_ry > 0) { - if (y2 < m_y + m_ry) { - float yi = y2 - m_y - m_ry; - float xi = ellipseXIntercept(yi, m_rx, m_ry); - x1 = m_x + m_rx - xi; - x2 = m_x + m_width - m_rx + xi; - } else if (y1 > m_y + m_height - m_ry) { - float yi = y1 - (m_y + m_height - m_ry); - float xi = ellipseXIntercept(yi, m_rx, m_ry); - x1 = m_x + m_rx - xi; - x2 = m_x + m_width - m_rx + xi; - } - } - - result.append(LineSegment(x1, x2)); -} - -void ExclusionRectangle::getIncludedIntervals(float logicalTop, float logicalHeight, SegmentList& result) const -{ - if (isEmpty()) - return; - - float y1 = minYForLogicalLine(logicalTop, logicalHeight); - float y2 = maxYForLogicalLine(logicalTop, logicalHeight); - - if (y1 < m_y || y2 > m_y + m_height) - return; - - float x1 = m_x; - float x2 = m_x + m_width; - - if (m_ry > 0) { - bool y1InterceptsCorner = y1 < m_y + m_ry; - bool y2InterceptsCorner = y2 > m_y + m_height - m_ry; - float xi = 0; - - if (y1InterceptsCorner && y2InterceptsCorner) { - if (y1 < m_height + 2*m_y - y2) { - float yi = y1 - m_y - m_ry; - xi = ellipseXIntercept(yi, m_rx, m_ry); - } else { - float yi = y2 - (m_y + m_height - m_ry); - xi = ellipseXIntercept(yi, m_rx, m_ry); - } - } else if (y1InterceptsCorner) { - float yi = y1 - m_y - m_ry; - xi = ellipseXIntercept(yi, m_rx, m_ry); - } else if (y2InterceptsCorner) { - float yi = y2 - (m_y + m_height - m_ry); - xi = ellipseXIntercept(yi, m_rx, m_ry); - } - - if (y1InterceptsCorner || y2InterceptsCorner) { - x1 = m_x + m_rx - xi; - x2 = m_x + m_width - m_rx + xi; - } - } - - result.append(LineSegment(x1, x2)); -} - -} // namespace WebCore diff --git a/Source/WebCore/rendering/ExclusionRectangle.h b/Source/WebCore/rendering/ExclusionRectangle.h deleted file mode 100644 index 5839eec47..000000000 --- a/Source/WebCore/rendering/ExclusionRectangle.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) 2012 Adobe Systems Incorporated. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above - * copyright notice, this list of conditions and the following - * disclaimer. - * 2. Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef ExclusionRectangle_h -#define ExclusionRectangle_h - -#include "ExclusionShape.h" -#include "FloatSize.h" -#include <wtf/Assertions.h> -#include <wtf/Vector.h> - -namespace WebCore { - -class ExclusionRectangle : public ExclusionShape { -public: - ExclusionRectangle(const FloatRect& bounds, const FloatSize& radii) - : ExclusionShape() - , m_x(bounds.x()) - , m_y(bounds.y()) - , m_width(bounds.width()) - , m_height(bounds.height()) - , m_rx(radii.width()) - , m_ry(radii.height()) - { - } - - virtual FloatRect shapeLogicalBoundingBox() const OVERRIDE { return internalToLogicalBoundingBox(FloatRect(m_x, m_y, m_width, m_height)); } - virtual bool isEmpty() const OVERRIDE { return m_width <= 0 || m_height <= 0; } - virtual void getExcludedIntervals(float logicalTop, float logicalHeight, SegmentList&) const OVERRIDE; - virtual void getIncludedIntervals(float logicalTop, float logicalHeight, SegmentList&) const OVERRIDE; - -private: - float m_x; - float m_y; - float m_width; - float m_height; - float m_rx; // corner X radius - float m_ry; // corner Y radius -}; - -} // namespace WebCore - -#endif // ExclusionRectangle_h diff --git a/Source/WebCore/rendering/ExclusionShape.cpp b/Source/WebCore/rendering/ExclusionShape.cpp deleted file mode 100644 index 5e0ea8bf1..000000000 --- a/Source/WebCore/rendering/ExclusionShape.cpp +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright (C) 2012 Adobe Systems Incorporated. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above - * copyright notice, this list of conditions and the following - * disclaimer. - * 2. Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "ExclusionShape.h" - -#include "BasicShapeFunctions.h" -#include "ExclusionPolygon.h" -#include "ExclusionRectangle.h" -#include "FloatSize.h" -#include "LengthFunctions.h" -#include "WindRule.h" -#include <wtf/MathExtras.h> -#include <wtf/OwnPtr.h> -#include <wtf/PassOwnPtr.h> - -namespace WebCore { - -static PassOwnPtr<ExclusionShape> createExclusionRectangle(const FloatRect& bounds, const FloatSize& radii) -{ - ASSERT(bounds.width() >= 0 && bounds.height() >= 0 && radii.width() >= 0 && radii.height() >= 0); - return adoptPtr(new ExclusionRectangle(bounds, radii)); -} - -static PassOwnPtr<ExclusionShape> createExclusionCircle(const FloatPoint& center, float radius) -{ - ASSERT(radius >= 0); - return adoptPtr(new ExclusionRectangle(FloatRect(center.x() - radius, center.y() - radius, radius*2, radius*2), FloatSize(radius, radius))); -} - -static PassOwnPtr<ExclusionShape> createExclusionEllipse(const FloatPoint& center, const FloatSize& radii) -{ - ASSERT(radii.width() >= 0 && radii.height() >= 0); - return adoptPtr(new ExclusionRectangle(FloatRect(center.x() - radii.width(), center.y() - radii.height(), radii.width()*2, radii.height()*2), radii)); -} - -static PassOwnPtr<ExclusionShape> createExclusionPolygon(PassOwnPtr<Vector<FloatPoint> > vertices, WindRule fillRule) -{ - return adoptPtr(new ExclusionPolygon(vertices, fillRule)); -} - -// If the writingMode is vertical, then the BasicShape's (physical) x and y coordinates are swapped, so that -// line segments are parallel to the internal coordinate system's X axis. - -PassOwnPtr<ExclusionShape> ExclusionShape::createExclusionShape(const BasicShape* basicShape, float logicalBoxWidth, float logicalBoxHeight, WritingMode writingMode) -{ - ASSERT(basicShape); - - bool horizontalWritingMode = isHorizontalWritingMode(writingMode); - float boxWidth = horizontalWritingMode ? logicalBoxWidth : logicalBoxHeight; - float boxHeight = horizontalWritingMode ? logicalBoxHeight : logicalBoxWidth; - OwnPtr<ExclusionShape> exclusionShape; - - switch (basicShape->type()) { - - case BasicShape::BASIC_SHAPE_RECTANGLE: { - const BasicShapeRectangle* rectangle = static_cast<const BasicShapeRectangle*>(basicShape); - FloatRect bounds( - floatValueForLength(rectangle->x(), boxWidth), - floatValueForLength(rectangle->y(), boxHeight), - floatValueForLength(rectangle->width(), boxWidth), - floatValueForLength(rectangle->height(), boxHeight)); - Length radiusXLength = rectangle->cornerRadiusX(); - Length radiusYLength = rectangle->cornerRadiusY(); - FloatSize cornerRadii( - radiusXLength.isUndefined() ? 0 : floatValueForLength(radiusXLength, boxWidth), - radiusYLength.isUndefined() ? 0 : floatValueForLength(radiusYLength, boxHeight)); - - exclusionShape = horizontalWritingMode - ? createExclusionRectangle(bounds, cornerRadii) - : createExclusionRectangle(bounds.transposedRect(), cornerRadii.transposedSize()); - exclusionShape->m_boundingBox = bounds; - break; - } - - case BasicShape::BASIC_SHAPE_CIRCLE: { - const BasicShapeCircle* circle = static_cast<const BasicShapeCircle*>(basicShape); - float centerX = floatValueForLength(circle->centerX(), boxWidth); - float centerY = floatValueForLength(circle->centerY(), boxHeight); - float radius = floatValueForLength(circle->radius(), std::max(boxHeight, boxWidth)); - - exclusionShape = horizontalWritingMode - ? createExclusionCircle(FloatPoint(centerX, centerY), radius) - : createExclusionCircle(FloatPoint(centerY, centerX), radius); - exclusionShape->m_boundingBox = FloatRect(centerX - radius, centerY - radius, radius * 2, radius * 2); - break; - } - - case BasicShape::BASIC_SHAPE_ELLIPSE: { - const BasicShapeEllipse* ellipse = static_cast<const BasicShapeEllipse*>(basicShape); - float centerX = floatValueForLength(ellipse->centerX(), boxWidth); - float centerY = floatValueForLength(ellipse->centerY(), boxHeight); - float radiusX = floatValueForLength(ellipse->radiusX(), boxWidth); - float radiusY = floatValueForLength(ellipse->radiusY(), boxHeight); - - exclusionShape = horizontalWritingMode - ? createExclusionEllipse(FloatPoint(centerX, centerY), FloatSize(radiusX, radiusY)) - : createExclusionEllipse(FloatPoint(centerY, centerX), FloatSize(radiusY, radiusX)); - exclusionShape->m_boundingBox = FloatRect(centerX - radiusX, centerY - radiusY, radiusX * 2, radiusY * 2); - break; - } - - case BasicShape::BASIC_SHAPE_POLYGON: { - const BasicShapePolygon* polygon = static_cast<const BasicShapePolygon*>(basicShape); - const Vector<Length>& values = polygon->values(); - size_t valuesSize = values.size(); - ASSERT(!(valuesSize % 2)); - FloatRect boundingBox; - Vector<FloatPoint>* vertices = new Vector<FloatPoint>(valuesSize / 2); - for (unsigned i = 0; i < valuesSize; i += 2) { - FloatPoint vertex( - floatValueForLength(values.at(i), boxWidth), - floatValueForLength(values.at(i + 1), boxHeight)); - (*vertices)[i / 2] = horizontalWritingMode ? vertex : vertex.transposedPoint(); - if (!i) - boundingBox.setLocation(vertex); - else - boundingBox.extend(vertex); - } - exclusionShape = createExclusionPolygon(adoptPtr(vertices), polygon->windRule()); - exclusionShape->m_boundingBox = boundingBox; - break; - } - - default: - ASSERT_NOT_REACHED(); - } - - exclusionShape->m_logicalBoxWidth = logicalBoxWidth; - exclusionShape->m_logicalBoxHeight = logicalBoxHeight; - exclusionShape->m_writingMode = writingMode; - - return exclusionShape.release(); -} - -} // namespace WebCore diff --git a/Source/WebCore/rendering/ExclusionShapeInsideInfo.cpp b/Source/WebCore/rendering/ExclusionShapeInsideInfo.cpp deleted file mode 100644 index 934e3f299..000000000 --- a/Source/WebCore/rendering/ExclusionShapeInsideInfo.cpp +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (C) 2012 Adobe Systems Incorporated. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above - * copyright notice, this list of conditions and the following - * disclaimer. - * 2. Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, - * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF - * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include "config.h" -#include "ExclusionShapeInsideInfo.h" - -#if ENABLE(CSS_EXCLUSIONS) - -#include "NotImplemented.h" -#include "RenderBlock.h" -#include <wtf/HashMap.h> -#include <wtf/OwnPtr.h> - -namespace WebCore { - -typedef HashMap<const RenderBlock*, OwnPtr<ExclusionShapeInsideInfo> > ExclusionShapeInsideInfoMap; - -static ExclusionShapeInsideInfoMap& exclusionShapeInsideInfoMap() -{ - DEFINE_STATIC_LOCAL(ExclusionShapeInsideInfoMap, staticExclusionShapeInsideInfoMap, ()); - return staticExclusionShapeInsideInfoMap; -} - -ExclusionShapeInsideInfo::ExclusionShapeInsideInfo(RenderBlock* block) - : m_block(block) - , m_shapeSizeDirty(true) -{ -} - -ExclusionShapeInsideInfo::~ExclusionShapeInsideInfo() -{ -} - -ExclusionShapeInsideInfo* ExclusionShapeInsideInfo::ensureExclusionShapeInsideInfoForRenderBlock(RenderBlock* block) -{ - ExclusionShapeInsideInfoMap::AddResult result = exclusionShapeInsideInfoMap().add(block, create(block)); - return result.iterator->value.get(); -} - -ExclusionShapeInsideInfo* ExclusionShapeInsideInfo::exclusionShapeInsideInfoForRenderBlock(const RenderBlock* block) -{ - ASSERT(block->style()->shapeInside()); - return exclusionShapeInsideInfoMap().get(block); -} - -bool ExclusionShapeInsideInfo::isExclusionShapeInsideInfoEnabledForRenderBlock(const RenderBlock* block) -{ - // FIXME: Bug 89707: Enable shape inside for non-rectangular shapes - ExclusionShapeValue* shapeValue = block->style()->shapeInside(); - BasicShape* shape = (shapeValue && shapeValue->type() == ExclusionShapeValue::SHAPE) ? shapeValue->shape() : 0; - return shape && (shape->type() == BasicShape::BASIC_SHAPE_RECTANGLE || shape->type() == BasicShape::BASIC_SHAPE_POLYGON); -} - -void ExclusionShapeInsideInfo::removeExclusionShapeInsideInfoForRenderBlock(const RenderBlock* block) -{ - if (!block->style() || !block->style()->shapeInside()) - return; - exclusionShapeInsideInfoMap().remove(block); -} - -void ExclusionShapeInsideInfo::computeShapeSize(LayoutUnit logicalWidth, LayoutUnit logicalHeight) -{ - if (!m_shapeSizeDirty && logicalWidth == m_logicalWidth && logicalHeight == m_logicalHeight) - return; - - m_shapeSizeDirty = false; - m_logicalWidth = logicalWidth; - m_logicalHeight = logicalHeight; - - // FIXME: Bug 89993: The wrap shape may come from the parent object - ExclusionShapeValue* shapeValue = m_block->style()->shapeInside(); - BasicShape* shape = (shapeValue && shapeValue->type() == ExclusionShapeValue::SHAPE) ? shapeValue->shape() : 0; - - ASSERT(shape); - - m_shape = ExclusionShape::createExclusionShape(shape, logicalWidth, logicalHeight, m_block->style()->writingMode()); - ASSERT(m_shape); -} - -bool ExclusionShapeInsideInfo::computeSegmentsForLine(LayoutUnit lineTop, LayoutUnit lineHeight) -{ - ASSERT(lineHeight >= 0); - m_lineTop = lineTop; - m_lineHeight = lineHeight; - m_segments.clear(); - - if (lineOverlapsShapeBounds()) { - ASSERT(m_shape); - m_shape->getIncludedIntervals(lineTop, std::min(lineHeight, shapeLogicalBottom() - lineTop), m_segments); - } - return m_segments.size(); -} - -} -#endif diff --git a/Source/WebCore/rendering/ExclusionShapeInsideInfo.h b/Source/WebCore/rendering/ExclusionShapeInsideInfo.h deleted file mode 100644 index 8be114d4b..000000000 --- a/Source/WebCore/rendering/ExclusionShapeInsideInfo.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) 2012 Adobe Systems Incorporated. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above - * copyright notice, this list of conditions and the following - * disclaimer. - * 2. Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, - * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF - * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef ExclusionShapeInsideInfo_h -#define ExclusionShapeInsideInfo_h - -#if ENABLE(CSS_EXCLUSIONS) - -#include "ExclusionShape.h" -#include "FloatRect.h" -#include "LayoutUnit.h" -#include <wtf/OwnPtr.h> -#include <wtf/PassOwnPtr.h> -#include <wtf/Vector.h> - -namespace WebCore { - -class RenderBlock; - -class ExclusionShapeInsideInfo { - WTF_MAKE_FAST_ALLOCATED; -public: - ~ExclusionShapeInsideInfo(); - - static PassOwnPtr<ExclusionShapeInsideInfo> create(RenderBlock* block) { return adoptPtr(new ExclusionShapeInsideInfo(block)); } - static ExclusionShapeInsideInfo* exclusionShapeInsideInfoForRenderBlock(const RenderBlock*); - static ExclusionShapeInsideInfo* ensureExclusionShapeInsideInfoForRenderBlock(RenderBlock*); - static void removeExclusionShapeInsideInfoForRenderBlock(const RenderBlock*); - static bool isExclusionShapeInsideInfoEnabledForRenderBlock(const RenderBlock*); - - LayoutUnit shapeLogicalTop() const { return shapeLogicalBoundsY(); } - LayoutUnit shapeLogicalBottom() const { return shapeLogicalBoundsMaxY(); } - bool lineOverlapsShapeBounds() const { return m_lineTop < shapeLogicalBottom() && m_lineTop + m_lineHeight >= shapeLogicalTop(); } - - bool hasSegments() const - { - return lineOverlapsShapeBounds() && m_segments.size(); - } - const SegmentList& segments() const - { - ASSERT(hasSegments()); - return m_segments; - } - bool computeSegmentsForLine(LayoutUnit lineTop, LayoutUnit lineHeight); - void computeShapeSize(LayoutUnit logicalWidth, LayoutUnit logicalHeight); - void dirtyShapeSize() { m_shapeSizeDirty = true; } - -private: - ExclusionShapeInsideInfo(RenderBlock*); - - inline LayoutUnit shapeLogicalBoundsY() const - { - ASSERT(m_shape); - // Use fromFloatCeil() to ensure that the returned LayoutUnit value is within the shape's bounds. - return LayoutUnit::fromFloatCeil(m_shape->shapeLogicalBoundingBox().y()); - } - - inline LayoutUnit shapeLogicalBoundsMaxY() const - { - ASSERT(m_shape); - // Use fromFloatFloor() to ensure that the returned LayoutUnit value is within the shape's bounds. - return LayoutUnit::fromFloatFloor(m_shape->shapeLogicalBoundingBox().maxY()); - } - - RenderBlock* m_block; - OwnPtr<ExclusionShape> m_shape; - - LayoutUnit m_lineTop; - LayoutUnit m_lineHeight; - LayoutUnit m_logicalWidth; - LayoutUnit m_logicalHeight; - - SegmentList m_segments; - bool m_shapeSizeDirty; -}; - -} -#endif -#endif diff --git a/Source/WebCore/rendering/FilterEffectRenderer.cpp b/Source/WebCore/rendering/FilterEffectRenderer.cpp index d47340ece..9f168c8bb 100644 --- a/Source/WebCore/rendering/FilterEffectRenderer.cpp +++ b/Source/WebCore/rendering/FilterEffectRenderer.cpp @@ -48,6 +48,7 @@ #include "CustomFilterProgram.h" #include "CustomFilterValidatedProgram.h" #include "FECustomFilter.h" +#include "GraphicsContext3D.h" #include "RenderView.h" #include "ValidatedCustomFilterOperation.h" #endif @@ -97,16 +98,12 @@ static PassRefPtr<FECustomFilter> createCustomFilterEffect(Filter* filter, Docum return 0; return FECustomFilter::create(filter, globalContext->context(), operation->validatedProgram(), operation->parameters(), - operation->meshRows(), operation->meshColumns(), operation->meshBoxType(), operation->meshType()); + operation->meshRows(), operation->meshColumns(), operation->meshType()); } #endif FilterEffectRenderer::FilterEffectRenderer() - : m_topOutset(0) - , m_rightOutset(0) - , m_bottomOutset(0) - , m_leftOutset(0) - , m_graphicsBufferAttached(false) + : m_graphicsBufferAttached(false) , m_hasFilterThatMovesPixels(false) #if ENABLE(CSS_SHADERS) , m_hasCustomShaderFilter(false) @@ -125,9 +122,15 @@ GraphicsContext* FilterEffectRenderer::inputContext() return sourceImage() ? sourceImage()->context() : 0; } -PassRefPtr<FilterEffect> FilterEffectRenderer::buildReferenceFilter(Document* document, PassRefPtr<FilterEffect> previousEffect, ReferenceFilterOperation* filterOperation) +PassRefPtr<FilterEffect> FilterEffectRenderer::buildReferenceFilter(RenderObject* renderer, PassRefPtr<FilterEffect> previousEffect, ReferenceFilterOperation* filterOperation) { #if ENABLE(SVG) + if (!renderer) + return 0; + + Document* document = renderer->document(); + ASSERT(document); + CachedSVGDocumentReference* cachedSVGDocumentReference = filterOperation->cachedSVGDocumentReference(); CachedSVGDocument* cachedSVGDocument = cachedSVGDocumentReference ? cachedSVGDocumentReference->document() : 0; @@ -140,8 +143,12 @@ PassRefPtr<FilterEffect> FilterEffectRenderer::buildReferenceFilter(Document* do return 0; Element* filter = document->getElementById(filterOperation->fragment()); - if (!filter) + if (!filter) { + // Although we did not find the referenced filter, it might exist later + // in the document + document->accessSVGExtensions()->addPendingResource(filterOperation->fragment(), toElement(renderer->node())); return 0; + } RefPtr<FilterEffect> effect; @@ -156,7 +163,7 @@ PassRefPtr<FilterEffect> FilterEffectRenderer::buildReferenceFilter(Document* do if (!node->isSVGElement()) continue; - SVGElement* element = static_cast<SVGElement*>(node); + SVGElement* element = toSVGElement(node); if (!element->isFilterEffect()) continue; @@ -172,25 +179,21 @@ PassRefPtr<FilterEffect> FilterEffectRenderer::buildReferenceFilter(Document* do } return effect; #else - UNUSED_PARAM(document); + UNUSED_PARAM(renderer); UNUSED_PARAM(previousEffect); UNUSED_PARAM(filterOperation); return 0; #endif } -bool FilterEffectRenderer::build(Document* document, const FilterOperations& operations) +bool FilterEffectRenderer::build(RenderObject* renderer, const FilterOperations& operations) { -#if !ENABLE(CSS_SHADERS) || !USE(3D_GRAPHICS) - UNUSED_PARAM(document); -#endif - #if ENABLE(CSS_SHADERS) m_hasCustomShaderFilter = false; #endif m_hasFilterThatMovesPixels = operations.hasFilterThatMovesPixels(); if (m_hasFilterThatMovesPixels) - operations.getOutsets(m_topOutset, m_rightOutset, m_bottomOutset, m_leftOutset); + m_outsets = operations.outsets(); // Keep the old effects on the stack until we've created the new effects. // New FECustomFilters can reuse cached resources from old FECustomFilters. @@ -204,7 +207,7 @@ bool FilterEffectRenderer::build(Document* document, const FilterOperations& ope switch (filterOperation->getOperationType()) { case FilterOperation::REFERENCE: { ReferenceFilterOperation* referenceOperation = static_cast<ReferenceFilterOperation*>(filterOperation); - effect = buildReferenceFilter(document, previousEffect, referenceOperation); + effect = buildReferenceFilter(renderer, previousEffect, referenceOperation); referenceOperation->setFilterEffect(effect); break; } @@ -308,8 +311,8 @@ bool FilterEffectRenderer::build(Document* document, const FilterOperations& ope BasicComponentTransferFilterOperation* componentTransferOperation = static_cast<BasicComponentTransferFilterOperation*>(filterOperation); ComponentTransferFunction transferFunction; transferFunction.type = FECOMPONENTTRANSFER_TYPE_LINEAR; - transferFunction.slope = 1; - transferFunction.intercept = narrowPrecisionToFloat(componentTransferOperation->amount()); + transferFunction.slope = narrowPrecisionToFloat(componentTransferOperation->amount()); + transferFunction.intercept = 0; ComponentTransferFunction nullFunction; effect = FEComponentTransfer::create(this, transferFunction, transferFunction, transferFunction, nullFunction); @@ -347,6 +350,7 @@ bool FilterEffectRenderer::build(Document* document, const FilterOperations& ope break; case FilterOperation::VALIDATED_CUSTOM: { ValidatedCustomFilterOperation* customFilterOperation = static_cast<ValidatedCustomFilterOperation*>(filterOperation); + Document* document = renderer ? renderer->document() : 0; effect = createCustomFilterEffect(this, document, customFilterOperation); if (effect) m_hasCustomShaderFilter = true; @@ -360,7 +364,7 @@ bool FilterEffectRenderer::build(Document* document, const FilterOperations& ope if (effect) { // Unlike SVG, filters applied here should not clip to their primitive subregions. effect->setClipsToBounds(false); - effect->setColorSpace(ColorSpaceDeviceRGB); + effect->setOperatingColorSpace(ColorSpaceDeviceRGB); if (filterOperation->getOperationType() != FilterOperation::REFERENCE) { effect->inputEffects().append(previousEffect); @@ -413,11 +417,9 @@ void FilterEffectRenderer::clearIntermediateResults() void FilterEffectRenderer::apply() { - lastEffect()->apply(); - -#if !USE(CG) - output()->transformColorSpace(lastEffect()->colorSpace(), ColorSpaceDeviceRGB); -#endif + RefPtr<FilterEffect> effect = lastEffect(); + effect->apply(); + effect->transformResultColorSpace(ColorSpaceDeviceRGB); } LayoutRect FilterEffectRenderer::computeSourceImageRectForDirtyRect(const LayoutRect& filterBoxRect, const LayoutRect& dirtyRect) @@ -434,8 +436,8 @@ LayoutRect FilterEffectRenderer::computeSourceImageRectForDirtyRect(const Layout if (hasFilterThatMovesPixels()) { // Note that the outsets are reversed here because we are going backwards -> we have the dirty rect and // need to find out what is the rectangle that might influence the result inside that dirty rect. - rectForRepaint.move(-m_rightOutset, -m_bottomOutset); - rectForRepaint.expand(m_leftOutset + m_rightOutset, m_topOutset + m_bottomOutset); + rectForRepaint.move(-m_outsets.right(), -m_outsets.bottom()); + rectForRepaint.expand(m_outsets.left() + m_outsets.right(), m_outsets.top() + m_outsets.bottom()); } rectForRepaint.intersect(filterBoxRect); return rectForRepaint; @@ -468,8 +470,17 @@ bool FilterEffectRendererHelper::prepareFilterEffect(RenderLayer* renderLayer, c } return true; } - -GraphicsContext* FilterEffectRendererHelper::beginFilterEffect(GraphicsContext* oldContext) + +GraphicsContext* FilterEffectRendererHelper::filterContext() const +{ + if (!m_haveFilterEffect) + return 0; + + FilterEffectRenderer* filter = m_renderLayer->filterRenderer(); + return filter->inputContext(); +} + +bool FilterEffectRendererHelper::beginFilterEffect() { ASSERT(m_renderLayer); @@ -480,21 +491,20 @@ GraphicsContext* FilterEffectRendererHelper::beginFilterEffect(GraphicsContext* if (!sourceGraphicsContext || !isFilterSizeValid(filter->filterRegion())) { // Disable the filters and continue. m_haveFilterEffect = false; - return oldContext; + return false; } - m_savedGraphicsContext = oldContext; - // Translate the context so that the contents of the layer is captuterd in the offscreen memory buffer. sourceGraphicsContext->save(); sourceGraphicsContext->translate(-m_paintOffset.x(), -m_paintOffset.y()); sourceGraphicsContext->clearRect(m_repaintRect); sourceGraphicsContext->clip(m_repaintRect); - - return sourceGraphicsContext; + + m_startedFilterEffect = true; + return true; } -GraphicsContext* FilterEffectRendererHelper::applyFilterEffect() +void FilterEffectRendererHelper::applyFilterEffect(GraphicsContext* destinationContext) { ASSERT(m_haveFilterEffect && m_renderLayer->filterRenderer()); FilterEffectRenderer* filter = m_renderLayer->filterRenderer(); @@ -506,11 +516,9 @@ GraphicsContext* FilterEffectRendererHelper::applyFilterEffect() LayoutRect destRect = filter->outputRect(); destRect.move(m_paintOffset.x(), m_paintOffset.y()); - m_savedGraphicsContext->drawImageBuffer(filter->output(), m_renderLayer->renderer()->style()->colorSpace(), pixelSnappedIntRect(destRect), CompositeSourceOver); + destinationContext->drawImageBuffer(filter->output(), m_renderLayer->renderer()->style()->colorSpace(), pixelSnappedIntRect(destRect), CompositeSourceOver); filter->clearIntermediateResults(); - - return m_savedGraphicsContext; } } // namespace WebCore diff --git a/Source/WebCore/rendering/FilterEffectRenderer.h b/Source/WebCore/rendering/FilterEffectRenderer.h index f73188f5c..0d3ac4017 100644 --- a/Source/WebCore/rendering/FilterEffectRenderer.h +++ b/Source/WebCore/rendering/FilterEffectRenderer.h @@ -34,6 +34,7 @@ #include "FloatRect.h" #include "GraphicsContext.h" #include "ImageBuffer.h" +#include "IntRectExtent.h" #include "LayoutRect.h" #include "SVGFilterBuilder.h" #include "SourceGraphic.h" @@ -50,30 +51,33 @@ class CustomFilterProgram; class Document; class GraphicsContext; class RenderLayer; +class RenderObject; class FilterEffectRendererHelper { public: FilterEffectRendererHelper(bool haveFilterEffect) - : m_savedGraphicsContext(0) - , m_renderLayer(0) + : m_renderLayer(0) , m_haveFilterEffect(haveFilterEffect) + , m_startedFilterEffect(false) { } bool haveFilterEffect() const { return m_haveFilterEffect; } - bool hasStartedFilterEffect() const { return m_savedGraphicsContext; } + bool hasStartedFilterEffect() const { return m_startedFilterEffect; } bool prepareFilterEffect(RenderLayer*, const LayoutRect& filterBoxRect, const LayoutRect& dirtyRect, const LayoutRect& layerRepaintRect); - GraphicsContext* beginFilterEffect(GraphicsContext* oldContext); - GraphicsContext* applyFilterEffect(); + bool beginFilterEffect(); + void applyFilterEffect(GraphicsContext* destinationContext); + + GraphicsContext* filterContext() const; const LayoutRect& repaintRect() const { return m_repaintRect; } private: - GraphicsContext* m_savedGraphicsContext; - RenderLayer* m_renderLayer; + RenderLayer* m_renderLayer; // FIXME: this is mainly used to get the FilterEffectRenderer. FilterEffectRendererHelper should be weaned off it. LayoutPoint m_paintOffset; LayoutRect m_repaintRect; bool m_haveFilterEffect; + bool m_startedFilterEffect; }; class FilterEffectRenderer : public Filter @@ -100,8 +104,8 @@ public: GraphicsContext* inputContext(); ImageBuffer* output() const { return lastEffect()->asImageBuffer(); } - bool build(Document*, const FilterOperations&); - PassRefPtr<FilterEffect> buildReferenceFilter(Document*, PassRefPtr<FilterEffect> previousEffect, ReferenceFilterOperation*); + bool build(RenderObject* renderer, const FilterOperations&); + PassRefPtr<FilterEffect> buildReferenceFilter(RenderObject* renderer, PassRefPtr<FilterEffect> previousEffect, ReferenceFilterOperation*); bool updateBackingStoreRect(const FloatRect& filterRect); void allocateBackingStoreIfNeeded(); void clearIntermediateResults(); @@ -139,11 +143,8 @@ private: FilterEffectList m_effects; RefPtr<SourceGraphic> m_sourceGraphic; - int m_topOutset; - int m_rightOutset; - int m_bottomOutset; - int m_leftOutset; - + IntRectExtent m_outsets; + bool m_graphicsBufferAttached; bool m_hasFilterThatMovesPixels; #if ENABLE(CSS_SHADERS) diff --git a/Source/WebCore/rendering/FixedTableLayout.cpp b/Source/WebCore/rendering/FixedTableLayout.cpp index e75764ac7..a6b65e934 100644 --- a/Source/WebCore/rendering/FixedTableLayout.cpp +++ b/Source/WebCore/rendering/FixedTableLayout.cpp @@ -77,8 +77,9 @@ FixedTableLayout::FixedTableLayout(RenderTable* table) { } -int FixedTableLayout::calcWidthArray(int) +int FixedTableLayout::calcWidthArray() { + // FIXME: We might want to wait until we have all of the first row before computing for the first time. int usedWidth = 0; // iterate over all <col> elements @@ -88,7 +89,10 @@ int FixedTableLayout::calcWidthArray(int) unsigned currentEffectiveColumn = 0; for (RenderTableCol* col = m_table->firstColumn(); col; col = col->nextColumn()) { - col->computePreferredLogicalWidths(); + // RenderTableCols don't have the concept of preferred logical width, but we need to clear their dirty bits + // so that if we call setPreferredWidthsDirty(true) on a col or one of its descendants, we'll mark it's + // ancestors as dirty. + col->clearPreferredLogicalWidthsDirtyBits(); // Width specified by column-groups that have column child does not affect column width in fixed layout tables if (col->isTableColumnGroupWithColumnChildren()) @@ -138,12 +142,12 @@ int FixedTableLayout::calcWidthArray(int) continue; RenderTableCell* cell = toRenderTableCell(child); - if (cell->preferredLogicalWidthsDirty()) - cell->computePreferredLogicalWidths(); Length logicalWidth = cell->styleOrColLogicalWidth(); unsigned span = cell->colSpan(); int fixedBorderBoxLogicalWidth = 0; + // FIXME: Support other length types. If the width is non-auto, it should probably just use + // RenderBox::computeLogicalWidthInRegionUsing to compute the width. if (logicalWidth.isFixed() && logicalWidth.isPositive()) { fixedBorderBoxLogicalWidth = cell->adjustBorderBoxLogicalWidthForBoxSizing(logicalWidth.value()); logicalWidth.setValue(fixedBorderBoxLogicalWidth); @@ -161,32 +165,28 @@ int FixedTableLayout::calcWidthArray(int) usedSpan += eSpan; ++currentColumn; } + + // FixedTableLayout doesn't use min/maxPreferredLogicalWidths, but we need to clear the + // dirty bit on the cell so that we'll correctly mark its ancestors dirty + // in case we later call setPreferredLogicalWidthsDirty(true) on it later. + if (cell->preferredLogicalWidthsDirty()) + cell->setPreferredLogicalWidthsDirty(false); } return usedWidth; } -void FixedTableLayout::computePreferredLogicalWidths(LayoutUnit& minWidth, LayoutUnit& maxWidth) +void FixedTableLayout::computeIntrinsicLogicalWidths(LayoutUnit& minWidth, LayoutUnit& maxWidth) { - // FIXME: This entire calculation is incorrect for both minwidth and maxwidth. - - // we might want to wait until we have all of the first row before - // layouting for the first time. - - // only need to calculate the minimum width as the sum of the - // cols/cells with a fixed width. - // - // The maximum width is max(minWidth, tableWidth). - int bordersPaddingAndSpacing = m_table->bordersPaddingAndSpacingInRowDirection(); - - int tableLogicalWidth = m_table->style()->logicalWidth().isFixed() ? m_table->style()->logicalWidth().value() - bordersPaddingAndSpacing : 0; - int mw = calcWidthArray(tableLogicalWidth) + bordersPaddingAndSpacing; + minWidth = maxWidth = calcWidthArray(); +} - minWidth = max(mw, tableLogicalWidth); - maxWidth = minWidth; +void FixedTableLayout::applyPreferredLogicalWidthQuirks(LayoutUnit& minWidth, LayoutUnit& maxWidth) const +{ + Length tableLogicalWidth = m_table->style()->logicalWidth(); + if (tableLogicalWidth.isFixed() && tableLogicalWidth.isPositive()) + minWidth = maxWidth = max<int>(minWidth, tableLogicalWidth.value() - m_table->bordersPaddingAndSpacingInRowDirection()); - // This quirk is very similar to one that exists in RenderBlock::calcBlockPrefWidths(). - // Here's the example for this one: /* <table style="width:100%; background-color:red"><tr><td> <table style="background-color:blue"><tr><td> @@ -211,7 +211,7 @@ void FixedTableLayout::layout() // FIXME: It is possible to be called without having properly updated our internal representation. // This means that our preferred logical widths were not recomputed as expected. if (nEffCols != m_width.size()) { - calcWidthArray(tableLogicalWidth); + calcWidthArray(); // FIXME: Table layout shouldn't modify our table structure (but does due to columns and column-groups). nEffCols = m_table->numEffCols(); } diff --git a/Source/WebCore/rendering/FixedTableLayout.h b/Source/WebCore/rendering/FixedTableLayout.h index 21ed65c51..68ba08468 100644 --- a/Source/WebCore/rendering/FixedTableLayout.h +++ b/Source/WebCore/rendering/FixedTableLayout.h @@ -34,11 +34,12 @@ class FixedTableLayout : public TableLayout { public: FixedTableLayout(RenderTable*); - virtual void computePreferredLogicalWidths(LayoutUnit& minWidth, LayoutUnit& maxWidth); + virtual void computeIntrinsicLogicalWidths(LayoutUnit& minWidth, LayoutUnit& maxWidth) OVERRIDE; + virtual void applyPreferredLogicalWidthQuirks(LayoutUnit& minWidth, LayoutUnit& maxWidth) const OVERRIDE; virtual void layout(); private: - int calcWidthArray(int tableWidth); + int calcWidthArray(); Vector<Length> m_width; }; diff --git a/Source/WebCore/rendering/FlowThreadController.cpp b/Source/WebCore/rendering/FlowThreadController.cpp index 3759a43e1..e0f1c6713 100644 --- a/Source/WebCore/rendering/FlowThreadController.cpp +++ b/Source/WebCore/rendering/FlowThreadController.cpp @@ -49,7 +49,7 @@ FlowThreadController::FlowThreadController(RenderView* view) : m_view(view) , m_currentRenderFlowThread(0) , m_isRenderNamedFlowThreadOrderDirty(false) - , m_autoLogicalHeightRegionsCount(0) + , m_flowThreadsWithAutoLogicalHeightRegions(0) { } @@ -74,7 +74,7 @@ RenderNamedFlowThread* FlowThreadController::ensureRenderFlowThreadWithName(cons // Sanity check for the absence of a named flow in the "CREATED" state with the same name. ASSERT(!namedFlows->flowByName(name)); - RenderNamedFlowThread* flowRenderer = new (m_view->renderArena()) RenderNamedFlowThread(m_view->document(), namedFlows->ensureFlowWithName(name)); + RenderNamedFlowThread* flowRenderer = RenderNamedFlowThread::createAnonymous(m_view->document(), namedFlows->ensureFlowWithName(name)); flowRenderer->setStyle(RenderFlowThread::createFlowThreadStyle(m_view->style())); m_renderNamedFlowThreadList->add(flowRenderer); @@ -97,9 +97,39 @@ void FlowThreadController::styleDidChange() void FlowThreadController::layoutRenderNamedFlowThreads() { - ASSERT(m_renderNamedFlowThreadList); + updateFlowThreadsChainIfNecessary(); + + for (RenderNamedFlowThreadList::iterator iter = m_renderNamedFlowThreadList->begin(); iter != m_renderNamedFlowThreadList->end(); ++iter) { + RenderNamedFlowThread* flowRenderer = *iter; + flowRenderer->layoutIfNeeded(); + } +} + +void FlowThreadController::registerNamedFlowContentNode(Node* contentNode, RenderNamedFlowThread* namedFlow) +{ + ASSERT(contentNode && contentNode->isElementNode()); + ASSERT(namedFlow); + ASSERT(!m_mapNamedFlowContentNodes.contains(contentNode)); + ASSERT(!namedFlow->hasContentNode(contentNode)); + m_mapNamedFlowContentNodes.add(contentNode, namedFlow); + namedFlow->registerNamedFlowContentNode(contentNode); +} + +void FlowThreadController::unregisterNamedFlowContentNode(Node* contentNode) +{ + ASSERT(contentNode && contentNode->isElementNode()); + HashMap<const Node*, RenderNamedFlowThread*>::iterator it = m_mapNamedFlowContentNodes.find(contentNode); + ASSERT(it != m_mapNamedFlowContentNodes.end()); + ASSERT(it->value); + ASSERT(it->value->hasContentNode(contentNode)); + it->value->unregisterNamedFlowContentNode(contentNode); + m_mapNamedFlowContentNodes.remove(contentNode); +} - ASSERT(isAutoLogicalHeightRegionsFlagConsistent()); +void FlowThreadController::updateFlowThreadsChainIfNecessary() +{ + ASSERT(m_renderNamedFlowThreadList); + ASSERT(isAutoLogicalHeightRegionsCountConsistent()); // Remove the left-over flow threads. RenderNamedFlowThreadList toRemoveList; @@ -131,63 +161,90 @@ void FlowThreadController::layoutRenderNamedFlowThreads() m_renderNamedFlowThreadList->swap(sortedList); setIsRenderNamedFlowThreadOrderDirty(false); } +} + +bool FlowThreadController::updateFlowThreadsNeedingLayout() +{ + bool needsTwoPassLayout = false; for (RenderNamedFlowThreadList::iterator iter = m_renderNamedFlowThreadList->begin(); iter != m_renderNamedFlowThreadList->end(); ++iter) { RenderNamedFlowThread* flowRenderer = *iter; - flowRenderer->layoutIfNeeded(); + ASSERT(!flowRenderer->needsTwoPhasesLayout()); + flowRenderer->setInConstrainedLayoutPhase(false); + if (flowRenderer->needsLayout() && flowRenderer->hasAutoLogicalHeightRegions()) + needsTwoPassLayout = true; } -} -void FlowThreadController::registerNamedFlowContentNode(Node* contentNode, RenderNamedFlowThread* namedFlow) -{ - ASSERT(contentNode && contentNode->isElementNode()); - ASSERT(namedFlow); - ASSERT(!m_mapNamedFlowContentNodes.contains(contentNode)); - ASSERT(!namedFlow->hasContentNode(contentNode)); - m_mapNamedFlowContentNodes.add(contentNode, namedFlow); - namedFlow->registerNamedFlowContentNode(contentNode); + if (needsTwoPassLayout) + resetFlowThreadsWithAutoHeightRegions(); + + return needsTwoPassLayout; } -void FlowThreadController::unregisterNamedFlowContentNode(Node* contentNode) +bool FlowThreadController::updateFlowThreadsNeedingTwoStepLayout() { - ASSERT(contentNode && contentNode->isElementNode()); - HashMap<Node*, RenderNamedFlowThread*>::iterator it = m_mapNamedFlowContentNodes.find(contentNode); - ASSERT(it != m_mapNamedFlowContentNodes.end()); - ASSERT(it->value); - ASSERT(it->value->hasContentNode(contentNode)); - it->value->unregisterNamedFlowContentNode(contentNode); - m_mapNamedFlowContentNodes.remove(contentNode); + bool needsTwoPassLayout = false; + + for (RenderNamedFlowThreadList::iterator iter = m_renderNamedFlowThreadList->begin(); iter != m_renderNamedFlowThreadList->end(); ++iter) { + RenderNamedFlowThread* flowRenderer = *iter; + if (flowRenderer->needsTwoPhasesLayout()) { + needsTwoPassLayout = true; + break; + } + } + + if (needsTwoPassLayout) + resetFlowThreadsWithAutoHeightRegions(); + + return needsTwoPassLayout; } -#ifndef NDEBUG -bool FlowThreadController::isAutoLogicalHeightRegionsFlagConsistent() const +void FlowThreadController::resetFlowThreadsWithAutoHeightRegions() { - if (!hasRenderNamedFlowThreads()) - return !hasAutoLogicalHeightRegions(); - - // Count the number of auto height regions - unsigned autoLogicalHeightRegions = 0; for (RenderNamedFlowThreadList::iterator iter = m_renderNamedFlowThreadList->begin(); iter != m_renderNamedFlowThreadList->end(); ++iter) { RenderNamedFlowThread* flowRenderer = *iter; - autoLogicalHeightRegions += flowRenderer->autoLogicalHeightRegionsCount(); + if (flowRenderer->hasAutoLogicalHeightRegions()) { + flowRenderer->markAutoLogicalHeightRegionsForLayout(); + flowRenderer->invalidateRegions(); + } } +} - return autoLogicalHeightRegions == m_autoLogicalHeightRegionsCount; +void FlowThreadController::updateFlowThreadsIntoConstrainedPhase() +{ + // Walk the flow chain in reverse order to update the auto-height regions and compute correct sizes for the containing regions. Only after this we can + // set the flow in the constrained layout phase. + for (RenderNamedFlowThreadList::reverse_iterator iter = m_renderNamedFlowThreadList->rbegin(); iter != m_renderNamedFlowThreadList->rend(); ++iter) { + RenderNamedFlowThread* flowRenderer = *iter; + ASSERT(!flowRenderer->hasRegions() || flowRenderer->hasValidRegionInfo()); + flowRenderer->layoutIfNeeded(); + if (flowRenderer->hasAutoLogicalHeightRegions()) { + ASSERT(flowRenderer->needsTwoPhasesLayout()); + flowRenderer->markAutoLogicalHeightRegionsForLayout(); + } + flowRenderer->setInConstrainedLayoutPhase(true); + flowRenderer->clearNeedsTwoPhasesLayout(); + } } -#endif -void FlowThreadController::resetRegionsOverrideLogicalContentHeight() +bool FlowThreadController::isContentNodeRegisteredWithAnyNamedFlow(const Node* contentNode) const { - ASSERT(m_view->normalLayoutPhase()); - for (RenderNamedFlowThreadList::iterator iter = m_renderNamedFlowThreadList->begin(); iter != m_renderNamedFlowThreadList->end(); ++iter) - (*iter)->resetRegionsOverrideLogicalContentHeight(); + return m_mapNamedFlowContentNodes.contains(contentNode); } -void FlowThreadController::markAutoLogicalHeightRegionsForLayout() +#ifndef NDEBUG +bool FlowThreadController::isAutoLogicalHeightRegionsCountConsistent() const { - ASSERT(m_view->constrainedFlowThreadsLayoutPhase()); - for (RenderNamedFlowThreadList::iterator iter = m_renderNamedFlowThreadList->begin(); iter != m_renderNamedFlowThreadList->end(); ++iter) - (*iter)->markAutoLogicalHeightRegionsForLayout(); + if (!hasRenderNamedFlowThreads()) + return !hasFlowThreadsWithAutoLogicalHeightRegions(); + + for (RenderNamedFlowThreadList::iterator iter = m_renderNamedFlowThreadList->begin(); iter != m_renderNamedFlowThreadList->end(); ++iter) { + if (!(*iter)->isAutoLogicalHeightRegionsCountConsistent()) + return false; + } + + return true; } +#endif } // namespace WebCore diff --git a/Source/WebCore/rendering/FlowThreadController.h b/Source/WebCore/rendering/FlowThreadController.h index 86780d4b1..ec8c74c68 100644 --- a/Source/WebCore/rendering/FlowThreadController.h +++ b/Source/WebCore/rendering/FlowThreadController.h @@ -66,29 +66,33 @@ public: void registerNamedFlowContentNode(Node*, RenderNamedFlowThread*); void unregisterNamedFlowContentNode(Node*); + bool isContentNodeRegisteredWithAnyNamedFlow(const Node*) const; - bool hasAutoLogicalHeightRegions() const { return m_autoLogicalHeightRegionsCount; } - void incrementAutoLogicalHeightRegions() { ++m_autoLogicalHeightRegionsCount; } - void decrementAutoLogicalHeightRegions() { ASSERT(m_autoLogicalHeightRegionsCount > 0); --m_autoLogicalHeightRegionsCount; } + bool hasFlowThreadsWithAutoLogicalHeightRegions() const { return m_flowThreadsWithAutoLogicalHeightRegions; } + void incrementFlowThreadsWithAutoLogicalHeightRegions() { ++m_flowThreadsWithAutoLogicalHeightRegions; } + void decrementFlowThreadsWithAutoLogicalHeightRegions() { ASSERT(m_flowThreadsWithAutoLogicalHeightRegions > 0); --m_flowThreadsWithAutoLogicalHeightRegions; } + + bool updateFlowThreadsNeedingLayout(); + bool updateFlowThreadsNeedingTwoStepLayout(); + void updateFlowThreadsIntoConstrainedPhase(); #ifndef NDEBUG - bool isAutoLogicalHeightRegionsFlagConsistent() const; + bool isAutoLogicalHeightRegionsCountConsistent() const; #endif - void resetRegionsOverrideLogicalContentHeight(); - void markAutoLogicalHeightRegionsForLayout(); - protected: FlowThreadController(RenderView*); + void updateFlowThreadsChainIfNecessary(); + void resetFlowThreadsWithAutoHeightRegions(); private: RenderView* m_view; RenderFlowThread* m_currentRenderFlowThread; bool m_isRenderNamedFlowThreadOrderDirty; - unsigned m_autoLogicalHeightRegionsCount; + unsigned m_flowThreadsWithAutoLogicalHeightRegions; OwnPtr<RenderNamedFlowThreadList> m_renderNamedFlowThreadList; // maps a content node to its render flow thread. - HashMap<Node*, RenderNamedFlowThread*> m_mapNamedFlowContentNodes; + HashMap<const Node*, RenderNamedFlowThread*> m_mapNamedFlowContentNodes; }; } diff --git a/Source/WebCore/rendering/HitTestLocation.cpp b/Source/WebCore/rendering/HitTestLocation.cpp new file mode 100644 index 000000000..118a38db2 --- /dev/null +++ b/Source/WebCore/rendering/HitTestLocation.cpp @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2006, 2008, 2011 Apple Inc. All rights reserved. + * Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * +*/ + +#include "config.h" +#include "HitTestLocation.h" + +#include "CachedImage.h" +#include "DocumentMarkerController.h" +#include "Frame.h" +#include "FrameSelection.h" +#include "FrameTree.h" +#include "HTMLAnchorElement.h" +#include "HTMLImageElement.h" +#include "HTMLInputElement.h" +#include "HTMLMediaElement.h" +#include "HTMLNames.h" +#include "HTMLParserIdioms.h" +#include "HTMLPlugInImageElement.h" +#include "HTMLVideoElement.h" +#include "RenderBlock.h" +#include "RenderImage.h" +#include "RenderInline.h" +#include "Scrollbar.h" + +#if ENABLE(SVG) +#include "SVGNames.h" +#include "XLinkNames.h" +#endif + +namespace WebCore { + +using namespace HTMLNames; + +HitTestLocation::HitTestLocation() + : m_region(0) + , m_isRectBased(false) + , m_isRectilinear(true) +{ +} + +HitTestLocation::HitTestLocation(const LayoutPoint& point) + : m_point(point) + , m_boundingBox(rectForPoint(point, 0, 0, 0, 0)) + , m_transformedPoint(point) + , m_transformedRect(m_boundingBox) + , m_region(0) + , m_isRectBased(false) + , m_isRectilinear(true) +{ +} + +HitTestLocation::HitTestLocation(const FloatPoint& point) + : m_point(flooredLayoutPoint(point)) + , m_boundingBox(rectForPoint(m_point, 0, 0, 0, 0)) + , m_transformedPoint(point) + , m_transformedRect(m_boundingBox) + , m_region(0) + , m_isRectBased(false) + , m_isRectilinear(true) +{ +} + +HitTestLocation::HitTestLocation(const FloatPoint& point, const FloatQuad& quad) + : m_transformedPoint(point) + , m_transformedRect(quad) + , m_region(0) + , m_isRectBased(true) +{ + m_point = flooredLayoutPoint(point); + m_boundingBox = enclosingIntRect(quad.boundingBox()); + m_isRectilinear = quad.isRectilinear(); +} + +HitTestLocation::HitTestLocation(const LayoutPoint& centerPoint, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding) + : m_point(centerPoint) + , m_boundingBox(rectForPoint(centerPoint, topPadding, rightPadding, bottomPadding, leftPadding)) + , m_transformedPoint(centerPoint) + , m_region(0) + , m_isRectBased(topPadding || rightPadding || bottomPadding || leftPadding) + , m_isRectilinear(true) +{ + m_transformedRect = FloatQuad(m_boundingBox); +} + +HitTestLocation::HitTestLocation(const HitTestLocation& other, const LayoutSize& offset, RenderRegion* region) + : m_point(other.m_point) + , m_boundingBox(other.m_boundingBox) + , m_transformedPoint(other.m_transformedPoint) + , m_transformedRect(other.m_transformedRect) + , m_region(region ? region : other.m_region) + , m_isRectBased(other.m_isRectBased) + , m_isRectilinear(other.m_isRectilinear) +{ + move(offset); +} + +HitTestLocation::HitTestLocation(const HitTestLocation& other) + : m_point(other.m_point) + , m_boundingBox(other.m_boundingBox) + , m_transformedPoint(other.m_transformedPoint) + , m_transformedRect(other.m_transformedRect) + , m_region(other.m_region) + , m_isRectBased(other.m_isRectBased) + , m_isRectilinear(other.m_isRectilinear) +{ +} + +HitTestLocation::~HitTestLocation() +{ +} + +HitTestLocation& HitTestLocation::operator=(const HitTestLocation& other) +{ + m_point = other.m_point; + m_boundingBox = other.m_boundingBox; + m_transformedPoint = other.m_transformedPoint; + m_transformedRect = other.m_transformedRect; + m_region = other.m_region; + m_isRectBased = other.m_isRectBased; + m_isRectilinear = other.m_isRectilinear; + + return *this; +} + +void HitTestLocation::move(const LayoutSize& offset) +{ + m_point.move(offset); + m_transformedPoint.move(offset); + m_transformedRect.move(offset); + m_boundingBox = enclosingIntRect(m_transformedRect.boundingBox()); +} + +template<typename RectType> +bool HitTestLocation::intersectsRect(const RectType& rect) const +{ + // FIXME: When the hit test is not rect based we should use rect.contains(m_point). + // That does change some corner case tests though. + + // First check if rect even intersects our bounding box. + if (!rect.intersects(m_boundingBox)) + return false; + + // If the transformed rect is rectilinear the bounding box intersection was accurate. + if (m_isRectilinear) + return true; + + // If rect fully contains our bounding box, we are also sure of an intersection. + if (rect.contains(m_boundingBox)) + return true; + + // Otherwise we need to do a slower quad based intersection test. + return m_transformedRect.intersectsRect(rect); +} + +bool HitTestLocation::intersects(const LayoutRect& rect) const +{ + return intersectsRect(rect); +} + +bool HitTestLocation::intersects(const FloatRect& rect) const +{ + return intersectsRect(rect); +} + +bool HitTestLocation::intersects(const RoundedRect& rect) const +{ + return rect.intersectsQuad(m_transformedRect); +} + +IntRect HitTestLocation::rectForPoint(const LayoutPoint& point, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding) +{ + IntPoint actualPoint(flooredIntPoint(point)); + actualPoint -= IntSize(leftPadding, topPadding); + + IntSize actualPadding(leftPadding + rightPadding, topPadding + bottomPadding); + // As IntRect is left inclusive and right exclusive (seeing IntRect::contains(x, y)), adding "1". + // FIXME: Remove this once non-rect based hit-detection stops using IntRect:intersects. + actualPadding += IntSize(1, 1); + + return IntRect(actualPoint, actualPadding); +} + +} // namespace WebCore diff --git a/Source/WebCore/rendering/HitTestLocation.h b/Source/WebCore/rendering/HitTestLocation.h new file mode 100644 index 000000000..5efe5f77d --- /dev/null +++ b/Source/WebCore/rendering/HitTestLocation.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. + * Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * +*/ + +#ifndef HitTestLocation_h +#define HitTestLocation_h + +#include "FloatQuad.h" +#include "FloatRect.h" +#include "HitTestRequest.h" +#include "LayoutRect.h" +#include "RoundedRect.h" +#include "TextDirection.h" +#include <wtf/Forward.h> +#include <wtf/ListHashSet.h> +#include <wtf/OwnPtr.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + +class Element; +class Frame; +#if ENABLE(VIDEO) +class HTMLMediaElement; +#endif +class Image; +class KURL; +class Node; +class RenderRegion; +class Scrollbar; + +class HitTestLocation { +public: + + HitTestLocation(); + HitTestLocation(const LayoutPoint&); + HitTestLocation(const FloatPoint&); + HitTestLocation(const FloatPoint&, const FloatQuad&); + // Pass non-zero padding values to perform a rect-based hit test. + HitTestLocation(const LayoutPoint& centerPoint, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding); + // Make a copy the HitTestLocation in a new region by applying given offset to internal point and area. + HitTestLocation(const HitTestLocation&, const LayoutSize& offset, RenderRegion* = 0); + HitTestLocation(const HitTestLocation&); + ~HitTestLocation(); + HitTestLocation& operator=(const HitTestLocation&); + + const LayoutPoint& point() const { return m_point; } + IntPoint roundedPoint() const { return roundedIntPoint(m_point); } + + RenderRegion* region() const { return m_region; } + + // Rect-based hit test related methods. + bool isRectBasedTest() const { return m_isRectBased; } + bool isRectilinear() const { return m_isRectilinear; } + IntRect boundingBox() const { return m_boundingBox; } + + static IntRect rectForPoint(const LayoutPoint&, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding); + int topPadding() const { return roundedPoint().y() - m_boundingBox.y(); } + int rightPadding() const { return m_boundingBox.maxX() - roundedPoint().x() - 1; } + int bottomPadding() const { return m_boundingBox.maxY() - roundedPoint().y() - 1; } + int leftPadding() const { return roundedPoint().x() - m_boundingBox.x(); } + + bool intersects(const LayoutRect&) const; + bool intersects(const FloatRect&) const; + bool intersects(const RoundedRect&) const; + + const FloatPoint& transformedPoint() const { return m_transformedPoint; } + const FloatQuad& transformedRect() const { return m_transformedRect; } + +private: + template<typename RectType> + bool intersectsRect(const RectType&) const; + void move(const LayoutSize& offset); + + // This is cached forms of the more accurate point and area below. + LayoutPoint m_point; + IntRect m_boundingBox; + + FloatPoint m_transformedPoint; + FloatQuad m_transformedRect; + + RenderRegion* m_region; // The region we're inside. + + bool m_isRectBased; + bool m_isRectilinear; +}; + +} // namespace WebCore + +#endif // HitTestLocation_h diff --git a/Source/WebCore/rendering/HitTestRequest.h b/Source/WebCore/rendering/HitTestRequest.h index d937c2504..40d8d5fe5 100644 --- a/Source/WebCore/rendering/HitTestRequest.h +++ b/Source/WebCore/rendering/HitTestRequest.h @@ -35,7 +35,11 @@ public: IgnoreClipping = 1 << 5, SVGClipContent = 1 << 6, TouchEvent = 1 << 7, - AllowShadowContent = 1 << 8 + DisallowShadowContent = 1 << 8, + AllowFrameScrollbars = 1 << 9, + AllowChildFrameContent = 1 << 10, + ChildFrameHitTest = 1 << 11, + AccessibilityHitTest = 1 << 12 }; typedef unsigned HitTestRequestType; @@ -53,7 +57,10 @@ public: bool svgClipContent() const { return m_requestType & SVGClipContent; } bool touchEvent() const { return m_requestType & TouchEvent; } bool mouseEvent() const { return !touchEvent(); } - bool allowsShadowContent() const { return m_requestType & AllowShadowContent; } + bool disallowsShadowContent() const { return m_requestType & DisallowShadowContent; } + bool allowsFrameScrollbars() const { return m_requestType & AllowFrameScrollbars; } + bool allowsChildFrameContent() const { return m_requestType & AllowChildFrameContent; } + bool isChildFrameHitTest() const { return m_requestType & ChildFrameHitTest; } // Convenience functions bool touchMove() const { return move() && touchEvent(); } diff --git a/Source/WebCore/rendering/HitTestResult.cpp b/Source/WebCore/rendering/HitTestResult.cpp index 38f1f679f..e0ecdbba0 100644 --- a/Source/WebCore/rendering/HitTestResult.cpp +++ b/Source/WebCore/rendering/HitTestResult.cpp @@ -22,24 +22,32 @@ #include "config.h" #include "HitTestResult.h" +#include "CachedImage.h" #include "DocumentMarkerController.h" +#include "Editor.h" #include "Frame.h" #include "FrameSelection.h" #include "FrameTree.h" #include "HTMLAnchorElement.h" -#include "HTMLVideoElement.h" +#include "HTMLAreaElement.h" +#include "HTMLAudioElement.h" #include "HTMLImageElement.h" #include "HTMLInputElement.h" #include "HTMLMediaElement.h" #include "HTMLNames.h" #include "HTMLParserIdioms.h" #include "HTMLPlugInImageElement.h" +#include "HTMLTextAreaElement.h" +#include "HTMLVideoElement.h" +#include "HitTestLocation.h" #include "RenderBlock.h" #include "RenderImage.h" #include "RenderInline.h" #include "Scrollbar.h" +#include "UserGestureIndicator.h" #if ENABLE(SVG) +#include "SVGImageElement.h" #include "SVGNames.h" #include "XLinkNames.h" #endif @@ -48,150 +56,6 @@ namespace WebCore { using namespace HTMLNames; -HitTestLocation::HitTestLocation() - : m_region(0) - , m_isRectBased(false) - , m_isRectilinear(true) -{ -} - -HitTestLocation::HitTestLocation(const LayoutPoint& point) - : m_point(point) - , m_boundingBox(rectForPoint(point, 0, 0, 0, 0)) - , m_transformedPoint(point) - , m_transformedRect(m_boundingBox) - , m_region(0) - , m_isRectBased(false) - , m_isRectilinear(true) -{ -} - -HitTestLocation::HitTestLocation(const FloatPoint& point) - : m_point(flooredLayoutPoint(point)) - , m_boundingBox(rectForPoint(m_point, 0, 0, 0, 0)) - , m_transformedPoint(point) - , m_transformedRect(m_boundingBox) - , m_region(0) - , m_isRectBased(false) - , m_isRectilinear(true) -{ -} - -HitTestLocation::HitTestLocation(const FloatPoint& point, const FloatQuad& quad) - : m_transformedPoint(point) - , m_transformedRect(quad) - , m_region(0) - , m_isRectBased(true) -{ - m_point = flooredLayoutPoint(point); - m_boundingBox = enclosingIntRect(quad.boundingBox()); - m_isRectilinear = quad.isRectilinear(); -} - -HitTestLocation::HitTestLocation(const LayoutPoint& centerPoint, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding) - : m_point(centerPoint) - , m_boundingBox(rectForPoint(centerPoint, topPadding, rightPadding, bottomPadding, leftPadding)) - , m_transformedPoint(centerPoint) - , m_region(0) - , m_isRectBased(topPadding || rightPadding || bottomPadding || leftPadding) - , m_isRectilinear(true) -{ - m_transformedRect = FloatQuad(m_boundingBox); -} - -HitTestLocation::HitTestLocation(const HitTestLocation& other, const LayoutSize& offset, RenderRegion* region) - : m_point(other.m_point) - , m_boundingBox(other.m_boundingBox) - , m_transformedPoint(other.m_transformedPoint) - , m_transformedRect(other.m_transformedRect) - , m_region(region ? region : other.m_region) - , m_isRectBased(other.m_isRectBased) - , m_isRectilinear(other.m_isRectilinear) -{ - move(offset); -} - -HitTestLocation::HitTestLocation(const HitTestLocation& other) - : m_point(other.m_point) - , m_boundingBox(other.m_boundingBox) - , m_transformedPoint(other.m_transformedPoint) - , m_transformedRect(other.m_transformedRect) - , m_region(other.m_region) - , m_isRectBased(other.m_isRectBased) - , m_isRectilinear(other.m_isRectilinear) -{ -} - -HitTestLocation::~HitTestLocation() -{ -} - -HitTestLocation& HitTestLocation::operator=(const HitTestLocation& other) -{ - m_point = other.m_point; - m_boundingBox = other.m_boundingBox; - m_transformedPoint = other.m_transformedPoint; - m_transformedRect = other.m_transformedRect; - m_region = other.m_region; - m_isRectBased = other.m_isRectBased; - m_isRectilinear = other.m_isRectilinear; - - return *this; -} - -void HitTestLocation::move(const LayoutSize& offset) -{ - m_point.move(offset); - m_transformedPoint.move(offset); - m_transformedRect.move(offset); - m_boundingBox = enclosingIntRect(m_transformedRect.boundingBox()); -} - -template<typename RectType> -bool HitTestLocation::intersectsRect(const RectType& rect) const -{ - // FIXME: When the hit test is not rect based we should use rect.contains(m_point). - // That does change some corner case tests though. - - // First check if rect even intersects our bounding box. - if (!rect.intersects(m_boundingBox)) - return false; - - // If the transformed rect is rectilinear the bounding box intersection was accurate. - if (m_isRectilinear) - return true; - - // If rect fully contains our bounding box, we are also sure of an intersection. - if (rect.contains(m_boundingBox)) - return true; - - // Otherwise we need to do a slower quad based intersection test. - return m_transformedRect.intersectsRect(rect); -} - -bool HitTestLocation::intersects(const LayoutRect& rect) const -{ - return intersectsRect(rect); -} - -bool HitTestLocation::intersects(const FloatRect& rect) const -{ - return intersectsRect(rect); -} - -IntRect HitTestLocation::rectForPoint(const LayoutPoint& point, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding) -{ - IntPoint actualPoint(flooredIntPoint(point)); - actualPoint -= IntSize(leftPadding, topPadding); - - IntSize actualPadding(leftPadding + rightPadding, topPadding + bottomPadding); - // As IntRect is left inclusive and right exclusive (seeing IntRect::contains(x, y)), adding "1". - // FIXME: Remove this once non-rect based hit-detection stops using IntRect:intersects. - actualPadding += IntSize(1, 1); - - return IntRect(actualPoint, actualPadding); -} - HitTestResult::HitTestResult() : m_isOverWidget(false) { @@ -199,21 +63,21 @@ HitTestResult::HitTestResult() HitTestResult::HitTestResult(const LayoutPoint& point) : m_hitTestLocation(point) - , m_pointInMainFrame(point) + , m_pointInInnerNodeFrame(point) , m_isOverWidget(false) { } HitTestResult::HitTestResult(const LayoutPoint& centerPoint, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding) : m_hitTestLocation(centerPoint, topPadding, rightPadding, bottomPadding, leftPadding) - , m_pointInMainFrame(centerPoint) + , m_pointInInnerNodeFrame(centerPoint) , m_isOverWidget(false) { } HitTestResult::HitTestResult(const HitTestLocation& other) : m_hitTestLocation(other) - , m_pointInMainFrame(m_hitTestLocation.point()) + , m_pointInInnerNodeFrame(m_hitTestLocation.point()) , m_isOverWidget(false) { } @@ -222,7 +86,7 @@ HitTestResult::HitTestResult(const HitTestResult& other) : m_hitTestLocation(other.m_hitTestLocation) , m_innerNode(other.innerNode()) , m_innerNonSharedNode(other.innerNonSharedNode()) - , m_pointInMainFrame(other.m_pointInMainFrame) + , m_pointInInnerNodeFrame(other.m_pointInInnerNodeFrame) , m_localPoint(other.localPoint()) , m_innerURLElement(other.URLElement()) , m_scrollbar(other.scrollbar()) @@ -241,7 +105,7 @@ HitTestResult& HitTestResult::operator=(const HitTestResult& other) m_hitTestLocation = other.m_hitTestLocation; m_innerNode = other.innerNode(); m_innerNonSharedNode = other.innerNonSharedNode(); - m_pointInMainFrame = other.m_pointInMainFrame; + m_pointInInnerNodeFrame = other.m_pointInInnerNodeFrame; m_localPoint = other.localPoint(); m_innerURLElement = other.URLElement(); m_scrollbar = other.scrollbar(); @@ -257,21 +121,25 @@ void HitTestResult::setToNonShadowAncestor() { Node* node = innerNode(); if (node) - node = node->shadowAncestorNode(); + node = node->document()->ancestorInThisScope(node); setInnerNode(node); node = innerNonSharedNode(); if (node) - node = node->shadowAncestorNode(); + node = node->document()->ancestorInThisScope(node); setInnerNonSharedNode(node); } void HitTestResult::setInnerNode(Node* n) { + if (n && n->isPseudoElement()) + n = n->parentOrShadowHostNode(); m_innerNode = n; } void HitTestResult::setInnerNonSharedNode(Node* n) { + if (n && n->isPseudoElement()) + n = n->parentOrShadowHostNode(); m_innerNonSharedNode = n; } @@ -356,7 +224,7 @@ String HitTestResult::title(TextDirection& dir) const // For <area> tags in image maps, walk the tree for the <area>, not the <img> using it. for (Node* titleNode = m_innerNode.get(); titleNode; titleNode = titleNode->parentNode()) { if (titleNode->isElementNode()) { - String title = static_cast<Element*>(titleNode)->title(); + String title = toElement(titleNode)->title(); if (!title.isEmpty()) { if (RenderObject* renderer = titleNode->renderer()) dir = renderer->style()->direction(); @@ -405,13 +273,13 @@ String HitTestResult::altDisplayString() const if (!m_innerNonSharedNode) return String(); - if (m_innerNonSharedNode->hasTagName(imgTag)) { - HTMLImageElement* image = static_cast<HTMLImageElement*>(m_innerNonSharedNode.get()); + if (isHTMLImageElement(m_innerNonSharedNode.get())) { + HTMLImageElement* image = toHTMLImageElement(m_innerNonSharedNode.get()); return displayString(image->getAttribute(altAttr), m_innerNonSharedNode.get()); } - if (m_innerNonSharedNode->hasTagName(inputTag)) { - HTMLInputElement* input = static_cast<HTMLInputElement*>(m_innerNonSharedNode.get()); + if (isHTMLInputElement(m_innerNonSharedNode.get())) { + HTMLInputElement* input = toHTMLInputElement(m_innerNonSharedNode.get()); return displayString(input->alt(), m_innerNonSharedNode.get()); } @@ -450,15 +318,15 @@ KURL HitTestResult::absoluteImageURL() const AtomicString urlString; if (m_innerNonSharedNode->hasTagName(embedTag) - || m_innerNonSharedNode->hasTagName(imgTag) - || m_innerNonSharedNode->hasTagName(inputTag) - || m_innerNonSharedNode->hasTagName(objectTag) + || isHTMLImageElement(m_innerNonSharedNode.get()) + || isHTMLInputElement(m_innerNonSharedNode.get()) + || m_innerNonSharedNode->hasTagName(objectTag) #if ENABLE(SVG) - || m_innerNonSharedNode->hasTagName(SVGNames::imageTag) + || isSVGImageElement(m_innerNonSharedNode.get()) #endif ) { - Element* element = static_cast<Element*>(m_innerNonSharedNode.get()); - urlString = element->getAttribute(element->imageSourceAttributeName()); + Element* element = toElement(m_innerNonSharedNode.get()); + urlString = element->imageSourceURL(); } else return KURL(); @@ -473,7 +341,7 @@ KURL HitTestResult::absolutePDFURL() const if (!m_innerNonSharedNode->hasTagName(embedTag) && !m_innerNonSharedNode->hasTagName(objectTag)) return KURL(); - HTMLPlugInImageElement* element = static_cast<HTMLPlugInImageElement*>(m_innerNonSharedNode.get()); + HTMLPlugInImageElement* element = toHTMLPlugInImageElement(m_innerNonSharedNode.get()); KURL url = m_innerNonSharedNode->document()->completeURL(stripLeadingAndTrailingHTMLSpaces(element->url())); if (!url.isValid()) return KURL(); @@ -513,8 +381,8 @@ HTMLMediaElement* HitTestResult::mediaElement() const if (!(m_innerNonSharedNode->renderer() && m_innerNonSharedNode->renderer()->isMedia())) return 0; - if (m_innerNonSharedNode->hasTagName(HTMLNames::videoTag) || m_innerNonSharedNode->hasTagName(HTMLNames::audioTag)) - return static_cast<HTMLMediaElement*>(m_innerNonSharedNode.get()); + if (m_innerNonSharedNode->hasTagName(HTMLNames::videoTag) || isHTMLAudioElement(m_innerNonSharedNode.get())) + return toHTMLMediaElement(m_innerNonSharedNode.get()); return 0; } #endif @@ -535,14 +403,37 @@ void HitTestResult::toggleMediaLoopPlayback() const #endif } +bool HitTestResult::mediaIsInFullscreen() const +{ +#if ENABLE(VIDEO) + if (HTMLMediaElement* mediaElement = this->mediaElement()) + return mediaElement->isVideo() && mediaElement->isFullscreen(); +#endif + return false; +} + +void HitTestResult::toggleMediaFullscreenState() const +{ +#if ENABLE(VIDEO) + if (HTMLMediaElement* mediaElement = this->mediaElement()) { + if (mediaElement->isVideo() && mediaElement->supportsFullscreen()) { + UserGestureIndicator indicator(DefinitelyProcessingUserGesture); + mediaElement->toggleFullscreenState(); + } + } +#endif +} + void HitTestResult::enterFullscreenForVideo() const { #if ENABLE(VIDEO) HTMLMediaElement* mediaElt(mediaElement()); if (mediaElt && mediaElt->hasTagName(HTMLNames::videoTag)) { - HTMLVideoElement* videoElt = static_cast<HTMLVideoElement*>(mediaElt); - if (!videoElt->isFullscreen() && mediaElt->supportsFullscreen()) + HTMLVideoElement* videoElt = toHTMLVideoElement(mediaElt); + if (!videoElt->isFullscreen() && mediaElt->supportsFullscreen()) { + UserGestureIndicator indicator(DefinitelyProcessingUserGesture); videoElt->enterFullscreen(); + } } #endif } @@ -623,7 +514,7 @@ KURL HitTestResult::absoluteLinkURL() const return KURL(); AtomicString urlString; - if (m_innerURLElement->hasTagName(aTag) || m_innerURLElement->hasTagName(areaTag) || m_innerURLElement->hasTagName(linkTag)) + if (isHTMLAnchorElement(m_innerURLElement.get()) || isHTMLAreaElement(m_innerURLElement.get()) || m_innerURLElement->hasTagName(linkTag)) urlString = m_innerURLElement->getAttribute(hrefAttr); #if ENABLE(SVG) else if (m_innerURLElement->hasTagName(SVGNames::aTag)) @@ -640,8 +531,8 @@ bool HitTestResult::isLiveLink() const if (!(m_innerURLElement && m_innerURLElement->document())) return false; - if (m_innerURLElement->hasTagName(aTag)) - return static_cast<HTMLAnchorElement*>(m_innerURLElement.get())->isLiveLink(); + if (isHTMLAnchorElement(m_innerURLElement.get())) + return toHTMLAnchorElement(m_innerURLElement.get())->isLiveLink(); #if ENABLE(SVG) if (m_innerURLElement->hasTagName(SVGNames::aTag)) return m_innerURLElement->isLink(); @@ -650,6 +541,11 @@ bool HitTestResult::isLiveLink() const return false; } +bool HitTestResult::isOverLink() const +{ + return m_innerURLElement && m_innerURLElement->isLink(); +} + String HitTestResult::titleDisplayString() const { if (!m_innerURLElement) @@ -674,11 +570,11 @@ bool HitTestResult::isContentEditable() const if (!m_innerNonSharedNode) return false; - if (m_innerNonSharedNode->hasTagName(textareaTag)) + if (isHTMLTextAreaElement(m_innerNonSharedNode.get())) return true; - if (m_innerNonSharedNode->hasTagName(inputTag)) - return static_cast<HTMLInputElement*>(m_innerNonSharedNode.get())->isTextField(); + if (isHTMLInputElement(m_innerNonSharedNode.get())) + return toHTMLInputElement(m_innerNonSharedNode.get())->isTextField(); return m_innerNonSharedNode->rendererIsEditable(); } @@ -694,8 +590,8 @@ bool HitTestResult::addNodeToRectBasedTestResult(Node* node, const HitTestReques if (!node) return true; - if (!request.allowsShadowContent()) - node = node->shadowAncestorNode(); + if (request.disallowsShadowContent()) + node = node->document()->ancestorInThisScope(node); mutableRectBasedTestResult().add(node); @@ -714,8 +610,8 @@ bool HitTestResult::addNodeToRectBasedTestResult(Node* node, const HitTestReques if (!node) return true; - if (!request.allowsShadowContent()) - node = node->shadowAncestorNode(); + if (request.disallowsShadowContent()) + node = node->document()->ancestorInThisScope(node); mutableRectBasedTestResult().add(node); @@ -731,7 +627,7 @@ void HitTestResult::append(const HitTestResult& other) m_innerNode = other.innerNode(); m_innerNonSharedNode = other.innerNonSharedNode(); m_localPoint = other.localPoint(); - m_pointInMainFrame = other.m_pointInMainFrame; + m_pointInInnerNodeFrame = other.m_pointInInnerNodeFrame; m_innerURLElement = other.URLElement(); m_scrollbar = other.scrollbar(); m_isOverWidget = other.isOverWidget(); @@ -764,7 +660,7 @@ Vector<String> HitTestResult::dictationAlternatives() const if (!m_innerNonSharedNode) return Vector<String>(); - DocumentMarker* marker = m_innerNonSharedNode->document()->markers()->markerContainingPoint(hitTestLocation().point(), DocumentMarker::DictationAlternatives); + DocumentMarker* marker = m_innerNonSharedNode->document()->markers()->markerContainingPoint(pointInInnerNodeFrame(), DocumentMarker::DictationAlternatives); if (!marker) return Vector<String>(); @@ -772,7 +668,7 @@ Vector<String> HitTestResult::dictationAlternatives() const if (!frame) return Vector<String>(); - return frame->editor()->dictationAlternativesForMarker(marker); + return frame->editor().dictationAlternativesForMarker(marker); } Node* HitTestResult::targetNode() const @@ -790,4 +686,14 @@ Node* HitTestResult::targetNode() const return node; } +Element* HitTestResult::innerElement() const +{ + for (Node* node = m_innerNode.get(); node; node = node->parentNode()) { + if (node->isElementNode()) + return toElement(node); + } + + return 0; +} + } // namespace WebCore diff --git a/Source/WebCore/rendering/HitTestResult.h b/Source/WebCore/rendering/HitTestResult.h index 57a41dab5..b6f770345 100644 --- a/Source/WebCore/rendering/HitTestResult.h +++ b/Source/WebCore/rendering/HitTestResult.h @@ -24,6 +24,7 @@ #include "FloatQuad.h" #include "FloatRect.h" +#include "HitTestLocation.h" #include "HitTestRequest.h" #include "LayoutRect.h" #include "TextDirection.h" @@ -45,62 +46,6 @@ class Node; class RenderRegion; class Scrollbar; -// FIXME: HitTestLocation should be moved to a separate file. -class HitTestLocation { -public: - - HitTestLocation(); - HitTestLocation(const LayoutPoint&); - HitTestLocation(const FloatPoint&); - HitTestLocation(const FloatPoint&, const FloatQuad&); - // Pass non-zero padding values to perform a rect-based hit test. - HitTestLocation(const LayoutPoint& centerPoint, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding); - // Make a copy the HitTestLocation in a new region by applying given offset to internal point and area. - HitTestLocation(const HitTestLocation&, const LayoutSize& offset, RenderRegion* = 0); - HitTestLocation(const HitTestLocation&); - ~HitTestLocation(); - HitTestLocation& operator=(const HitTestLocation&); - - const LayoutPoint& point() const { return m_point; } - IntPoint roundedPoint() const { return roundedIntPoint(m_point); } - - RenderRegion* region() const { return m_region; } - - // Rect-based hit test related methods. - bool isRectBasedTest() const { return m_isRectBased; } - bool isRectilinear() const { return m_isRectilinear; } - IntRect boundingBox() const { return m_boundingBox; } - - static IntRect rectForPoint(const LayoutPoint&, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding); - int topPadding() const { return roundedPoint().y() - m_boundingBox.y(); } - int rightPadding() const { return m_boundingBox.maxX() - roundedPoint().x() - 1; } - int bottomPadding() const { return m_boundingBox.maxY() - roundedPoint().y() - 1; } - int leftPadding() const { return roundedPoint().x() - m_boundingBox.x(); } - - bool intersects(const LayoutRect&) const; - bool intersects(const FloatRect&) const; - - const FloatPoint& transformedPoint() const { return m_transformedPoint; } - const FloatQuad& transformedRect() const { return m_transformedRect; } - -private: - template<typename RectType> - bool intersectsRect(const RectType&) const; - void move(const LayoutSize& offset); - - // This is cached forms of the more accurate point and area below. - LayoutPoint m_point; - IntRect m_boundingBox; - - FloatPoint m_transformedPoint; - FloatQuad m_transformedRect; - - RenderRegion* m_region; // The region we're inside. - - bool m_isRectBased; - bool m_isRectilinear; -}; - class HitTestResult { public: typedef ListHashSet<RefPtr<Node> > NodeSet; @@ -115,6 +60,7 @@ public: HitTestResult& operator=(const HitTestResult&); Node* innerNode() const { return m_innerNode.get(); } + Element* innerElement() const; Node* innerNonSharedNode() const { return m_innerNonSharedNode.get(); } Element* URLElement() const { return m_innerURLElement.get(); } Scrollbar* scrollbar() const { return m_scrollbar.get(); } @@ -124,12 +70,11 @@ public: bool isRectBasedTest() const { return m_hitTestLocation.isRectBasedTest(); } // The hit-tested point in the coordinates of the main frame. - const LayoutPoint& pointInMainFrame() const { return m_pointInMainFrame; } + const LayoutPoint& pointInMainFrame() const { return m_hitTestLocation.point(); } IntPoint roundedPointInMainFrame() const { return roundedIntPoint(pointInMainFrame()); } - void setPointInMainFrame(const LayoutPoint& p) { m_pointInMainFrame = p; } // The hit-tested point in the coordinates of the innerNode frame, the frame containing innerNode. - const LayoutPoint& pointInInnerNodeFrame() const { return m_hitTestLocation.point(); } + const LayoutPoint& pointInInnerNodeFrame() const { return m_pointInInnerNodeFrame; } IntPoint roundedPointInInnerNodeFrame() const { return roundedIntPoint(pointInInnerNodeFrame()); } Frame* innerNodeFrame() const; @@ -163,9 +108,12 @@ public: KURL absoluteLinkURL() const; String textContent() const; bool isLiveLink() const; + bool isOverLink() const; bool isContentEditable() const; void toggleMediaControlsDisplay() const; void toggleMediaLoopPlayback() const; + bool mediaIsInFullscreen() const; + void toggleMediaFullscreenState() const; void enterFullscreenForVideo() const; bool mediaControlsEnabled() const; bool mediaLoopEnabled() const; @@ -202,7 +150,7 @@ private: RefPtr<Node> m_innerNode; RefPtr<Node> m_innerNonSharedNode; - LayoutPoint m_pointInMainFrame; // The hit-tested point in main-frame coordinates. + LayoutPoint m_pointInInnerNodeFrame; // The hit-tested point in innerNode frame coordinates. LayoutPoint m_localPoint; // A point in the local coordinate space of m_innerNonSharedNode's renderer. Allows us to efficiently // determine where inside the renderer we hit on subsequent operations. RefPtr<Element> m_innerURLElement; diff --git a/Source/WebCore/rendering/InlineBox.cpp b/Source/WebCore/rendering/InlineBox.cpp index 18079ff0b..883490b37 100644 --- a/Source/WebCore/rendering/InlineBox.cpp +++ b/Source/WebCore/rendering/InlineBox.cpp @@ -44,25 +44,23 @@ struct SameSizeAsInlineBox { FloatPoint b; float c; uint32_t d : 32; -#ifndef NDEBUG +#if !ASSERT_DISABLED bool f; #endif }; COMPILE_ASSERT(sizeof(InlineBox) == sizeof(SameSizeAsInlineBox), InlineBox_size_guard); -#ifndef NDEBUG +#if !ASSERT_DISABLED static bool inInlineBoxDetach; #endif -#ifndef NDEBUG - +#if !ASSERT_DISABLED InlineBox::~InlineBox() { if (!m_hasBadParent && m_parent) m_parent->setHasBadChildList(); } - #endif void InlineBox::remove() @@ -73,11 +71,11 @@ void InlineBox::remove() void InlineBox::destroy(RenderArena* renderArena) { -#ifndef NDEBUG +#if !ASSERT_DISABLED inInlineBoxDetach = true; #endif delete this; -#ifndef NDEBUG +#if !ASSERT_DISABLED inInlineBoxDetach = false; #endif @@ -93,7 +91,6 @@ void* InlineBox::operator new(size_t sz, RenderArena* renderArena) void InlineBox::operator delete(void* ptr, size_t sz) { ASSERT(inInlineBoxDetach); - // Stash size where destroy can find it. *(size_t *)ptr = sz; } @@ -248,7 +245,11 @@ bool InlineBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result // Hit test all phases of replaced elements atomically, as though the replaced element established its // own stacking context. (See Appendix E.2, section 6.4 on inline block/table elements in the CSS2.1 // specification.) - return renderer()->hitTest(request, result, locationInContainer, accumulatedOffset); + LayoutPoint childPoint = accumulatedOffset; + if (parent()->renderer()->style()->isFlippedBlocksWritingMode()) // Faster than calling containingBlock(). + childPoint = renderer()->containingBlock()->flipForWritingModeForChild(toRenderBox(renderer()), childPoint); + + return renderer()->hitTest(request, result, locationInContainer, childPoint); } const RootInlineBox* InlineBox::root() const diff --git a/Source/WebCore/rendering/InlineBox.h b/Source/WebCore/rendering/InlineBox.h index e2cf85fdc..2f480c8a9 100644 --- a/Source/WebCore/rendering/InlineBox.h +++ b/Source/WebCore/rendering/InlineBox.h @@ -40,7 +40,7 @@ public: , m_parent(0) , m_renderer(obj) , m_logicalWidth(0) -#ifndef NDEBUG +#if !ASSERT_DISABLED , m_hasBadParent(false) #endif { @@ -55,7 +55,7 @@ public: , m_topLeft(topLeft) , m_logicalWidth(logicalWidth) , m_bitfields(firstLine, constructed, dirty, extracted, isHorizontal) -#ifndef NDEBUG +#if !ASSERT_DISABLED , m_hasBadParent(false) #endif { @@ -272,7 +272,7 @@ public: // visibleLeftEdge, visibleRightEdge are in the parent's coordinate system. virtual float placeEllipsisBox(bool ltr, float visibleLeftEdge, float visibleRightEdge, float ellipsisWidth, float &truncatedWidth, bool&); -#ifndef NDEBUG +#if !ASSERT_DISABLED void setHasBadParent(); #endif @@ -415,19 +415,19 @@ protected: // For InlineFlowBox and InlineTextBox bool extracted() const { return m_bitfields.extracted(); } -#ifndef NDEBUG +#if !ASSERT_DISABLED private: bool m_hasBadParent; #endif }; -#ifdef NDEBUG +#if ASSERT_DISABLED inline InlineBox::~InlineBox() { } #endif -#ifndef NDEBUG +#if !ASSERT_DISABLED inline void InlineBox::setHasBadParent() { m_hasBadParent = true; diff --git a/Source/WebCore/rendering/InlineFlowBox.cpp b/Source/WebCore/rendering/InlineFlowBox.cpp index a0ef3c6c3..654f111b3 100644 --- a/Source/WebCore/rendering/InlineFlowBox.cpp +++ b/Source/WebCore/rendering/InlineFlowBox.cpp @@ -20,7 +20,6 @@ #include "config.h" #include "InlineFlowBox.h" -#include "CachedImage.h" #include "CSSPropertyNames.h" #include "Document.h" #include "EllipsisBox.h" @@ -369,15 +368,24 @@ void InlineFlowBox::determineSpacingForFlowBoxes(bool lastLine, bool isLogically float InlineFlowBox::placeBoxesInInlineDirection(float logicalLeft, bool& needsWordSpacing, GlyphOverflowAndFallbackFontsMap& textBoxDataMap) { // Set our x position. - setLogicalLeft(logicalLeft); - + beginPlacingBoxRangesInInlineDirection(logicalLeft); + float startLogicalLeft = logicalLeft; logicalLeft += borderLogicalLeft() + paddingLogicalLeft(); float minLogicalLeft = startLogicalLeft; float maxLogicalRight = logicalLeft; - for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { + placeBoxRangeInInlineDirection(firstChild(), 0, logicalLeft, minLogicalLeft, maxLogicalRight, needsWordSpacing, textBoxDataMap); + + logicalLeft += borderLogicalRight() + paddingLogicalRight(); + endPlacingBoxRangesInInlineDirection(startLogicalLeft, logicalLeft, minLogicalLeft, maxLogicalRight); + return logicalLeft; +} + +float InlineFlowBox::placeBoxRangeInInlineDirection(InlineBox* firstChild, InlineBox* lastChild, float& logicalLeft, float& minLogicalLeft, float& maxLogicalRight, bool& needsWordSpacing, GlyphOverflowAndFallbackFontsMap& textBoxDataMap) +{ + for (InlineBox* curr = firstChild; curr && curr != lastChild; curr = curr->nextOnLine()) { if (curr->renderer()->isText()) { InlineTextBox* text = toInlineTextBox(curr); RenderText* rt = toRenderText(text->renderer()); @@ -426,14 +434,11 @@ float InlineFlowBox::placeBoxesInInlineDirection(float logicalLeft, bool& needsW if (knownToHaveNoOverflow()) maxLogicalRight = max(logicalLeft, maxLogicalRight); logicalLeft += logicalRightMargin; + // If we encounter any space after this inline block then ensure it is treated as the space between two words. + needsWordSpacing = true; } } } - - logicalLeft += borderLogicalRight() + paddingLogicalRight(); - setLogicalWidth(logicalLeft - startLogicalLeft); - if (knownToHaveNoOverflow() && (minLogicalLeft < startLogicalLeft || maxLogicalRight > logicalLeft)) - clearKnownToHaveNoOverflow(); return logicalLeft; } @@ -442,7 +447,7 @@ bool InlineFlowBox::requiresIdeographicBaseline(const GlyphOverflowAndFallbackFo if (isHorizontal()) return false; - if (renderer()->style(isFirstLineStyle())->fontDescription().textOrientation() == TextOrientationUpright + if (renderer()->style(isFirstLineStyle())->fontDescription().nonCJKGlyphOrientation() == NonCJKGlyphOrientationUpright || renderer()->style(isFirstLineStyle())->font().primaryFont()->hasVerticalGlyphs()) return true; @@ -475,6 +480,13 @@ bool InlineFlowBox::requiresIdeographicBaseline(const GlyphOverflowAndFallbackFo return false; } +static bool verticalAlignApplies(RenderObject* curr) +{ + // http://www.w3.org/TR/CSS2/visudet.html#propdef-vertical-align - vertical-align + // only applies to inline level and table-cell elements + return !curr->isText() || curr->parent()->isInline() || curr->parent()->isTableCell(); +} + void InlineFlowBox::adjustMaxAscentAndDescent(int& maxAscent, int& maxDescent, int maxPositionTop, int maxPositionBottom) { for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { @@ -482,7 +494,8 @@ void InlineFlowBox::adjustMaxAscentAndDescent(int& maxAscent, int& maxDescent, i // positioned elements if (curr->renderer()->isOutOfFlowPositioned()) continue; // Positioned placeholders don't affect calculations. - if (curr->verticalAlign() == TOP || curr->verticalAlign() == BOTTOM) { + + if ((curr->verticalAlign() == TOP || curr->verticalAlign() == BOTTOM) && verticalAlignApplies(curr->renderer())) { int lineHeight = curr->lineHeight(); if (curr->verticalAlign() == TOP) { if (maxAscent + maxDescent < lineHeight) @@ -565,10 +578,10 @@ void InlineFlowBox::computeLogicalBoxHeights(RootInlineBox* rootBox, LayoutUnit& rootBox->ascentAndDescentForBox(curr, textBoxDataMap, ascent, descent, affectsAscent, affectsDescent); LayoutUnit boxHeight = ascent + descent; - if (curr->verticalAlign() == TOP) { + if (curr->verticalAlign() == TOP && verticalAlignApplies(curr->renderer())) { if (maxPositionTop < boxHeight) maxPositionTop = boxHeight; - } else if (curr->verticalAlign() == BOTTOM) { + } else if (curr->verticalAlign() == BOTTOM && verticalAlignApplies(curr->renderer())) { if (maxPositionBottom < boxHeight) maxPositionBottom = boxHeight; } else if (!inlineFlowBox || strictMode || inlineFlowBox->hasTextChildren() || (inlineFlowBox->descendantsHaveSameLineHeightAndBaseline() && inlineFlowBox->hasTextDescendants()) @@ -614,7 +627,7 @@ void InlineFlowBox::placeBoxesInBlockDirection(LayoutUnit top, LayoutUnit maxHei if (descendantsHaveSameLineHeightAndBaseline()) { adjustmentForChildrenWithSameLineHeightAndBaseline = logicalTop(); if (parent()) - adjustmentForChildrenWithSameLineHeightAndBaseline += (boxModelObject()->borderBefore() + boxModelObject()->paddingBefore()); + adjustmentForChildrenWithSameLineHeightAndBaseline += (boxModelObject()->borderAndPaddingBefore()); } for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { @@ -628,9 +641,10 @@ void InlineFlowBox::placeBoxesInBlockDirection(LayoutUnit top, LayoutUnit maxHei InlineFlowBox* inlineFlowBox = curr->isInlineFlowBox() ? toInlineFlowBox(curr) : 0; bool childAffectsTopBottomPos = true; - if (curr->verticalAlign() == TOP) + + if (curr->verticalAlign() == TOP && verticalAlignApplies(curr->renderer())) curr->setLogicalTop(top); - else if (curr->verticalAlign() == BOTTOM) + else if (curr->verticalAlign() == BOTTOM && verticalAlignApplies(curr->renderer())) curr->setLogicalTop(top + maxHeight - curr->lineHeight()); else { if (!strictMode && inlineFlowBox && !inlineFlowBox->hasTextChildren() && !curr->boxModelObject()->hasInlineDirectionBordersOrPadding() @@ -732,6 +746,25 @@ void InlineFlowBox::placeBoxesInBlockDirection(LayoutUnit top, LayoutUnit maxHei } } +#if ENABLE(CSS3_TEXT) +void InlineFlowBox::computeMaxLogicalTop(float& maxLogicalTop) const +{ + for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { + if (curr->renderer()->isOutOfFlowPositioned()) + continue; // Positioned placeholders don't affect calculations. + + if (descendantsHaveSameLineHeightAndBaseline()) + continue; + + maxLogicalTop = max<float>(maxLogicalTop, curr->y()); + float localMaxLogicalTop = 0; + if (curr->isInlineFlowBox()) + toInlineFlowBox(curr)->computeMaxLogicalTop(localMaxLogicalTop); + maxLogicalTop = max<float>(maxLogicalTop, localMaxLogicalTop); + } +} +#endif // CSS3_TEXT + void InlineFlowBox::flipLinesInBlockDirection(LayoutUnit lineTop, LayoutUnit lineBottom) { // Flip the box on the line such that the top is now relative to the lineBottom instead of the lineTop. @@ -1018,6 +1051,26 @@ bool InlineFlowBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& re } // Now check ourselves. Pixel snap hit testing. + if (!visibleToHitTesting()) + return false; + + // Do not hittest content beyond the ellipsis box. + if (isRootInlineBox() && hasEllipsisBox()) { + const EllipsisBox* ellipsisBox = root()->ellipsisBox(); + LayoutRect boundsRect(roundedFrameRect()); + + if (isHorizontal()) + renderer()->style()->isLeftToRightDirection() ? boundsRect.shiftXEdgeTo(ellipsisBox->right()) : boundsRect.setWidth(ellipsisBox->left() - left()); + else + boundsRect.shiftYEdgeTo(ellipsisBox->right()); + + flipForWritingMode(boundsRect); + boundsRect.moveBy(accumulatedOffset); + // We are beyond the ellipsis box. + if (locationInContainer.intersects(boundsRect)) + return false; + } + LayoutRect frameRect = roundedFrameRect(); LayoutUnit minX = frameRect.x(); LayoutUnit minY = frameRect.y(); @@ -1040,7 +1093,7 @@ bool InlineFlowBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& re flipForWritingMode(rect); rect.moveBy(accumulatedOffset); - if (visibleToHitTesting() && locationInContainer.intersects(rect)) { + if (locationInContainer.intersects(rect)) { renderer()->updateHitTestResult(result, flipForWritingMode(locationInContainer.point() - toLayoutSize(accumulatedOffset))); // Don't add in m_x or m_y here, we want coords in the containing block's space. if (!result.addNodeToRectBasedTestResult(renderer()->node(), request, locationInContainer, rect)) return true; @@ -1108,7 +1161,7 @@ void InlineFlowBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, PaintPhase paintPhase = paintInfo.phase == PaintPhaseChildOutlines ? PaintPhaseOutline : paintInfo.phase; PaintInfo childInfo(paintInfo); childInfo.phase = paintPhase; - childInfo.updatePaintingRootForChildren(renderer()); + childInfo.updateSubtreePaintRootForChildren(renderer()); // Paint our children. if (paintPhase != PaintPhaseSelfOutline) { diff --git a/Source/WebCore/rendering/InlineFlowBox.h b/Source/WebCore/rendering/InlineFlowBox.h index 36d48dc87..d3fdd5ec8 100644 --- a/Source/WebCore/rendering/InlineFlowBox.h +++ b/Source/WebCore/rendering/InlineFlowBox.h @@ -80,7 +80,7 @@ public: InlineBox* firstChild() const { checkConsistency(); return m_firstChild; } InlineBox* lastChild() const { checkConsistency(); return m_lastChild; } - virtual bool isLeaf() const { return false; } + virtual bool isLeaf() const FINAL { return false; } InlineBox* firstLeafChild() const; InlineBox* lastLeafChild() const; @@ -88,7 +88,7 @@ public: typedef void (*CustomInlineBoxRangeReverse)(void* userData, Vector<InlineBox*>::iterator first, Vector<InlineBox*>::iterator last); void collectLeafBoxesInLogicalOrder(Vector<InlineBox*>&, CustomInlineBoxRangeReverse customReverseImplementation = 0, void* userData = 0) const; - virtual void setConstructed() + virtual void setConstructed() FINAL { InlineBox::setConstructed(); for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) @@ -96,9 +96,9 @@ public: } void addToLine(InlineBox* child); - virtual void deleteLine(RenderArena*); - virtual void extractLine(); - virtual void attachLine(); + virtual void deleteLine(RenderArena*) FINAL; + virtual void extractLine() FINAL; + virtual void attachLine() FINAL; virtual void adjustPosition(float dx, float dy); virtual void extractLineBoxFromRenderObject(); @@ -109,8 +109,8 @@ public: IntRect roundedFrameRect() const; - virtual void paintBoxDecorations(PaintInfo&, const LayoutPoint&); - virtual void paintMask(PaintInfo&, const LayoutPoint&); + virtual void paintBoxDecorations(PaintInfo&, const LayoutPoint&) FINAL; + virtual void paintMask(PaintInfo&, const LayoutPoint&) FINAL; void paintFillLayers(const PaintInfo&, const Color&, const FillLayer*, const LayoutRect&, CompositeOperator = CompositeSourceOver); void paintFillLayer(const PaintInfo&, const Color&, const FillLayer*, const LayoutRect&, CompositeOperator = CompositeSourceOver); void paintBoxShadow(const PaintInfo&, RenderStyle*, ShadowStyle, const LayoutRect&); @@ -173,6 +173,15 @@ public: void determineSpacingForFlowBoxes(bool lastLine, bool isLogicallyLastRunWrapped, RenderObject* logicallyLastRunRenderer); LayoutUnit getFlowSpacingLogicalWidth(); float placeBoxesInInlineDirection(float logicalLeft, bool& needsWordSpacing, GlyphOverflowAndFallbackFontsMap&); + float placeBoxRangeInInlineDirection(InlineBox* firstChild, InlineBox* lastChild, float& logicalLeft, float& minLogicalLeft, float& maxLogicalRight, bool& needsWordSpacing, GlyphOverflowAndFallbackFontsMap&); + void beginPlacingBoxRangesInInlineDirection(float logicalLeft) { setLogicalLeft(logicalLeft); } + void endPlacingBoxRangesInInlineDirection(float logicalLeft, float logicalRight, float minLogicalLeft, float maxLogicalRight) + { + setLogicalWidth(logicalRight - logicalLeft); + if (knownToHaveNoOverflow() && (minLogicalLeft < logicalLeft || maxLogicalRight > logicalRight)) + clearKnownToHaveNoOverflow(); + } + void computeLogicalBoxHeights(RootInlineBox*, LayoutUnit& maxPositionTop, LayoutUnit& maxPositionBottom, int& maxAscent, int& maxDescent, bool& setMaxAscent, bool& setMaxDescent, bool strictMode, GlyphOverflowAndFallbackFontsMap&, FontBaseline, VerticalPositionCache&); @@ -192,7 +201,7 @@ public: virtual RenderObject::SelectionState selectionState(); - virtual bool canAccommodateEllipsis(bool ltr, int blockEdge, int ellipsisWidth) const OVERRIDE; + virtual bool canAccommodateEllipsis(bool ltr, int blockEdge, int ellipsisWidth) const OVERRIDE FINAL; virtual float placeEllipsisBox(bool ltr, float blockLeftEdge, float blockRightEdge, float ellipsisWidth, float &truncatedWidth, bool&) OVERRIDE; bool hasTextChildren() const { return m_hasTextChildren; } @@ -210,16 +219,6 @@ public: { return m_overflow ? m_overflow->layoutOverflowRect() : enclosingLayoutRect(frameRectIncludingLineHeight(lineTop, lineBottom)); } - LayoutUnit logicalLeftLayoutOverflow() const - { - return m_overflow ? (isHorizontal() ? m_overflow->layoutOverflowRect().x() : m_overflow->layoutOverflowRect().y()) : - static_cast<LayoutUnit>(logicalLeft()); - } - LayoutUnit logicalRightLayoutOverflow() const - { - return m_overflow ? (isHorizontal() ? m_overflow->layoutOverflowRect().maxX() : m_overflow->layoutOverflowRect().maxY()) : - static_cast<LayoutUnit>(ceilf(logicalRight())); - } LayoutUnit logicalTopLayoutOverflow(LayoutUnit lineTop) const { if (m_overflow) @@ -300,7 +299,7 @@ private: protected: OwnPtr<RenderOverflow> m_overflow; - virtual bool isInlineFlowBox() const { return true; } + virtual bool isInlineFlowBox() const FINAL { return true; } InlineBox* m_firstChild; InlineBox* m_lastChild; @@ -308,6 +307,11 @@ protected: InlineFlowBox* m_prevLineBox; // The previous box that also uses our RenderObject InlineFlowBox* m_nextLineBox; // The next box that also uses our RenderObject +#if ENABLE(CSS3_TEXT) + // Maximum logicalTop among all children of an InlineFlowBox. Used to + // calculate the offset for TextUnderlinePositionUnder. + void computeMaxLogicalTop(float& maxLogicalTop) const; +#endif // CSS3_TEXT private: unsigned m_includeLogicalLeftEdge : 1; unsigned m_includeLogicalRightEdge : 1; @@ -339,13 +343,13 @@ private: inline InlineFlowBox* toInlineFlowBox(InlineBox* object) { - ASSERT(!object || object->isInlineFlowBox()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isInlineFlowBox()); return static_cast<InlineFlowBox*>(object); } inline const InlineFlowBox* toInlineFlowBox(const InlineBox* object) { - ASSERT(!object || object->isInlineFlowBox()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isInlineFlowBox()); return static_cast<const InlineFlowBox*>(object); } diff --git a/Source/WebCore/rendering/InlineIterator.h b/Source/WebCore/rendering/InlineIterator.h index bef9a9815..db5843708 100644 --- a/Source/WebCore/rendering/InlineIterator.h +++ b/Source/WebCore/rendering/InlineIterator.h @@ -26,7 +26,6 @@ #include "BidiRun.h" #include "RenderBlock.h" #include "RenderText.h" -#include <wtf/AlwaysInline.h> #include <wtf/StdLibExtras.h> namespace WebCore { @@ -85,6 +84,7 @@ public: return (m_obj && m_obj->isBR()) || atTextParagraphSeparator(); } + UChar characterAt(unsigned) const; UChar current() const; UChar previousInSameNode() const; ALWAYS_INLINE WTF::Unicode::Direction direction() const; @@ -175,6 +175,23 @@ enum EmptyInlineBehavior { IncludeEmptyInlines, }; +static bool isEmptyInline(RenderObject* object) +{ + if (!object->isRenderInline()) + return false; + + for (RenderObject* curr = object->firstChild(); curr; curr = curr->nextSibling()) { + if (curr->isFloatingOrOutOfFlowPositioned()) + continue; + if (curr->isText() && toRenderText(curr)->isAllCollapsibleWhitespace()) + continue; + + if (!isEmptyInline(curr)) + return false; + } + return true; +} + // FIXME: This function is misleadingly named. It has little to do with bidi. // This function will iterate over inlines within a block, optionally notifying // a bidi resolver as it enters/exits inlines (so it can push/pop embedding levels). @@ -224,7 +241,7 @@ static inline RenderObject* bidiNextShared(RenderObject* root, RenderObject* cur break; if (isIteratorTarget(next) - || ((emptyInlineBehavior == IncludeEmptyInlines || !next->firstChild()) // Always return EMPTY inlines. + || ((emptyInlineBehavior == IncludeEmptyInlines || isEmptyInline(next)) // Always return EMPTY inlines. && next->isRenderInline())) break; current = next; @@ -264,7 +281,7 @@ static inline RenderObject* bidiFirstSkippingEmptyInlines(RenderObject* root, In if (o->isRenderInline()) { notifyObserverEnteredObject(resolver, o); - if (o->firstChild()) + if (!isEmptyInline(o)) o = bidiNextSkippingEmptyInlines(root, o, resolver); else { // Never skip empty inlines. @@ -352,25 +369,29 @@ inline bool InlineIterator::atEnd() const return !m_obj; } -inline UChar InlineIterator::current() const +inline UChar InlineIterator::characterAt(unsigned index) const { if (!m_obj || !m_obj->isText()) return 0; RenderText* text = toRenderText(m_obj); - if (m_pos >= text->textLength()) + if (index >= text->textLength()) return 0; - return text->characterAt(m_pos); + return text->characterAt(index); +} + +inline UChar InlineIterator::current() const +{ + return characterAt(m_pos); } inline UChar InlineIterator::previousInSameNode() const { - if (!m_obj || !m_obj->isText() || !m_pos) + if (!m_pos) return 0; - RenderText* text = toRenderText(m_obj); - return text->characterAt(m_pos - 1); + return characterAt(m_pos - 1); } ALWAYS_INLINE WTF::Unicode::Direction InlineIterator::direction() const @@ -467,6 +488,14 @@ public: // We don't need to mark the end of the run because this is implicit: it is either endOfLine or the end of the // isolate, when we call createBidiRunsForLine it will stop at whichever comes first. addPlaceholderRunForIsolatedInline(resolver, obj, pos); + // FIXME: Inline isolates don't work properly with collapsing whitespace, see webkit.org/b/109624 + // For now, if we enter an isolate between midpoints, we increment our current midpoint or else + // we'll leave the isolate and ignore the content that follows. + MidpointState<InlineIterator>& midpointState = resolver.midpointState(); + if (midpointState.betweenMidpoints && midpointState.midpoints[midpointState.currentMidpoint].object() == obj) { + midpointState.betweenMidpoints = false; + ++midpointState.currentMidpoint; + } } private: diff --git a/Source/WebCore/rendering/InlineTextBox.cpp b/Source/WebCore/rendering/InlineTextBox.cpp index 56ab76cc8..49e2e1dd0 100644 --- a/Source/WebCore/rendering/InlineTextBox.cpp +++ b/Source/WebCore/rendering/InlineTextBox.cpp @@ -47,13 +47,20 @@ #include "SVGTextRunRenderingContext.h" #include "Text.h" #include "break_lines.h" -#include <wtf/AlwaysInline.h> #include <wtf/text/CString.h> using namespace std; namespace WebCore { +struct SameSizeAsInlineTextBox : public InlineBox { + unsigned variables[1]; + unsigned short variables2[2]; + void* pointers[2]; +}; + +COMPILE_ASSERT(sizeof(InlineTextBox) == sizeof(SameSizeAsInlineTextBox), InlineTextBox_should_stay_small); + typedef WTF::HashMap<const InlineTextBox*, LayoutRect> InlineTextBoxOverflowMap; static InlineTextBoxOverflowMap* gTextBoxesWithOverflow; @@ -205,7 +212,12 @@ LayoutRect InlineTextBox::localSelectionRect(int startPos, int endPos) if (respectHyphen) endPos = textRun.length(); - LayoutRect r = enclosingIntRect(font.selectionRectForText(textRun, FloatPoint(logicalLeft(), selTop), selHeight, sPos, ePos)); + FloatPoint startingPoint = FloatPoint(logicalLeft(), selTop); + LayoutRect r; + if (sPos || ePos != static_cast<int>(m_len)) + r = enclosingIntRect(font.selectionRectForText(textRun, startingPoint, selHeight, sPos, ePos)); + else // Avoid computing the font width when the entire line box is selected as an optimization. + r = enclosingIntRect(FloatRect(startingPoint, FloatSize(m_logicalWidth, selHeight))); LayoutUnit logicalWidth = r.width(); if (r.x() > logicalRight()) @@ -285,7 +297,7 @@ float InlineTextBox::placeEllipsisBox(bool flowIsLTR, float visibleLeftEdge, flo // and the ellipsis edge. m_truncation = cFullTruncation; truncatedWidth += ellipsisWidth; - return min(ellipsisX, x()); + return flowIsLTR ? min(ellipsisX, x()) : max(ellipsisX, right() - ellipsisWidth); } // Set the truncation index on the text run. @@ -360,13 +372,29 @@ bool InlineTextBox::isLineBreak() const bool InlineTextBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, LayoutUnit /* lineTop */, LayoutUnit /*lineBottom*/) { + if (!visibleToHitTesting()) + return false; + if (isLineBreak()) return false; - FloatPoint boxOrigin = locationIncludingFlipping(); - boxOrigin.moveBy(accumulatedOffset); - FloatRect rect(boxOrigin, size()); - if (m_truncation != cFullTruncation && visibleToHitTesting() && locationInContainer.intersects(rect)) { + if (m_truncation == cFullTruncation) + return false; + + FloatRect rect(locationIncludingFlipping(), size()); + // Make sure truncated text is ignored while hittesting. + if (m_truncation != cNoTruncation) { + LayoutUnit widthOfVisibleText = toRenderText(renderer())->width(m_start, m_truncation, textPos(), isFirstLineStyle()); + + if (isHorizontal()) + renderer()->style()->isLeftToRightDirection() ? rect.setWidth(widthOfVisibleText) : rect.shiftXEdgeTo(right() - widthOfVisibleText); + else + rect.setHeight(widthOfVisibleText); + } + + rect.moveBy(accumulatedOffset); + + if (locationInContainer.intersects(rect)) { renderer()->updateHitTestResult(result, flipForWritingMode(locationInContainer.point() - toLayoutSize(accumulatedOffset))); if (!result.addNodeToRectBasedTestResult(renderer()->node(), request, locationInContainer, rect)) return true; @@ -383,21 +411,21 @@ FloatSize InlineTextBox::applyShadowToGraphicsContext(GraphicsContext* context, int shadowX = horizontal ? shadow->x() : shadow->y(); int shadowY = horizontal ? shadow->y() : -shadow->x(); FloatSize shadowOffset(shadowX, shadowY); - int shadowBlur = shadow->blur(); + int shadowRadius = shadow->radius(); const Color& shadowColor = shadow->color(); if (shadow->next() || stroked || !opaque) { FloatRect shadowRect(textRect); - shadowRect.inflate(shadowBlur); + shadowRect.inflate(shadow->paintingExtent()); shadowRect.move(shadowOffset); context->save(); context->clip(shadowRect); - extraOffset = FloatSize(0, 2 * textRect.height() + max(0.0f, shadowOffset.height()) + shadowBlur); + extraOffset = FloatSize(0, 2 * textRect.height() + max(0.0f, shadowOffset.height()) + shadowRadius); shadowOffset -= extraOffset; } - context->setShadow(shadowOffset, shadowBlur, shadowColor, context->fillColorSpace()); + context->setShadow(shadowOffset, shadowRadius, shadowColor, context->fillColorSpace()); return extraOffset; } @@ -428,7 +456,8 @@ static void paintTextWithShadows(GraphicsContext* context, const Font& font, con context->drawText(font, textRun, textOrigin + extraOffset, 0, endOffset); else context->drawEmphasisMarks(font, textRun, emphasisMark, textOrigin + extraOffset + IntSize(0, emphasisMarkOffset), 0, endOffset); - } if (startOffset < truncationPoint) { + } + if (startOffset < truncationPoint) { if (emphasisMark.isEmpty()) context->drawText(font, textRun, textOrigin + extraOffset, startOffset, truncationPoint); else @@ -520,7 +549,6 @@ void InlineTextBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, // truncated string i.e. |Hello|CBA| -> |...lo|CBA| LayoutUnit widthOfVisibleText = toRenderText(renderer())->width(m_start, m_truncation, textPos(), isFirstLineStyle()); LayoutUnit widthOfHiddenText = m_logicalWidth - widthOfVisibleText; - // FIXME: The hit testing logic also needs to take this translation into account. LayoutSize truncationOffset(isLeftToRightDirection() ? widthOfHiddenText : -widthOfHiddenText, 0); adjustedPaintOffset.move(isHorizontal() ? truncationOffset : truncationOffset.transposedSize()); } @@ -543,17 +571,17 @@ void InlineTextBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, context->concatCTM(rotation(boxRect, Clockwise)); // Determine whether or not we have composition underlines to draw. - bool containsComposition = renderer()->node() && renderer()->frame()->editor()->compositionNode() == renderer()->node(); - bool useCustomUnderlines = containsComposition && renderer()->frame()->editor()->compositionUsesCustomUnderlines(); + bool containsComposition = renderer()->node() && renderer()->frame()->editor().compositionNode() == renderer()->node(); + bool useCustomUnderlines = containsComposition && renderer()->frame()->editor().compositionUsesCustomUnderlines(); // Determine the text colors and selection colors. Color textFillColor; Color textStrokeColor; Color emphasisMarkColor; float textStrokeWidth = styleToUse->textStrokeWidth(); - const ShadowData* textShadow = paintInfo.forceBlackText ? 0 : styleToUse->textShadow(); + const ShadowData* textShadow = paintInfo.forceBlackText() ? 0 : styleToUse->textShadow(); - if (paintInfo.forceBlackText) { + if (paintInfo.forceBlackText()) { textFillColor = Color::black; textStrokeColor = Color::black; emphasisMarkColor = Color::black; @@ -595,14 +623,14 @@ void InlineTextBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, const ShadowData* selectionShadow = textShadow; if (haveSelection) { // Check foreground color first. - Color foreground = paintInfo.forceBlackText ? Color::black : renderer()->selectionForegroundColor(); + Color foreground = paintInfo.forceBlackText() ? Color::black : renderer()->selectionForegroundColor(); if (foreground.isValid() && foreground != selectionFillColor) { if (!paintSelectedTextOnly) paintSelectedTextSeparately = true; selectionFillColor = foreground; } - Color emphasisMarkForeground = paintInfo.forceBlackText ? Color::black : renderer()->selectionEmphasisMarkColor(); + Color emphasisMarkForeground = paintInfo.forceBlackText() ? Color::black : renderer()->selectionEmphasisMarkColor(); if (emphasisMarkForeground.isValid() && emphasisMarkForeground != selectionEmphasisMarkColor) { if (!paintSelectedTextOnly) paintSelectedTextSeparately = true; @@ -610,7 +638,7 @@ void InlineTextBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, } if (RenderStyle* pseudoStyle = renderer()->getCachedPseudoStyle(SELECTION)) { - const ShadowData* shadow = paintInfo.forceBlackText ? 0 : pseudoStyle->textShadow(); + const ShadowData* shadow = paintInfo.forceBlackText() ? 0 : pseudoStyle->textShadow(); if (shadow != selectionShadow) { if (!paintSelectedTextOnly) paintSelectedTextSeparately = true; @@ -624,7 +652,7 @@ void InlineTextBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, selectionStrokeWidth = strokeWidth; } - Color stroke = paintInfo.forceBlackText ? Color::black : pseudoStyle->visitedDependentColor(CSSPropertyWebkitTextStrokeColor); + Color stroke = paintInfo.forceBlackText() ? Color::black : pseudoStyle->visitedDependentColor(CSSPropertyWebkitTextStrokeColor); if (stroke != selectionStrokeColor) { if (!paintSelectedTextOnly) paintSelectedTextSeparately = true; @@ -652,8 +680,8 @@ void InlineTextBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, if (containsComposition && !useCustomUnderlines) paintCompositionBackground(context, boxOrigin, styleToUse, font, - renderer()->frame()->editor()->compositionStart(), - renderer()->frame()->editor()->compositionEnd()); + renderer()->frame()->editor().compositionStart(), + renderer()->frame()->editor().compositionEnd()); paintDocumentMarkers(context, boxOrigin, styleToUse, font, true); @@ -679,7 +707,7 @@ void InlineTextBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, if (!combinedText) { string = textRenderer()->text(); if (static_cast<unsigned>(length) != string.length() || m_start) { - ASSERT(static_cast<unsigned>(m_start + length) <= string.length()); + ASSERT_WITH_SECURITY_IMPLICATION(static_cast<unsigned>(m_start + length) <= string.length()); string = string.substringSharingImpl(m_start, length); } maximumLength = textRenderer()->textLength() - m_start; @@ -766,17 +794,21 @@ void InlineTextBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, } // Paint decorations - ETextDecoration textDecorations = styleToUse->textDecorationsInEffect(); - if (textDecorations != TDNONE && paintInfo.phase != PaintPhaseSelection) { + TextDecoration textDecorations = styleToUse->textDecorationsInEffect(); + if (textDecorations != TextDecorationNone && paintInfo.phase != PaintPhaseSelection) { updateGraphicsContext(context, textFillColor, textStrokeColor, textStrokeWidth, styleToUse->colorSpace()); + if (combinedText) + context->concatCTM(rotation(boxRect, Clockwise)); paintDecoration(context, boxOrigin, textDecorations, styleToUse->textDecorationStyle(), textShadow); + if (combinedText) + context->concatCTM(rotation(boxRect, Counterclockwise)); } if (paintInfo.phase == PaintPhaseForeground) { paintDocumentMarkers(context, boxOrigin, styleToUse, font, false); if (useCustomUnderlines) { - const Vector<CompositionUnderline>& underlines = renderer()->frame()->editor()->customCompositionUnderlines(); + const Vector<CompositionUnderline>& underlines = renderer()->frame()->editor().customCompositionUnderlines(); size_t numUnderlines = underlines.size(); for (size_t index = 0; index < numUnderlines; ++index) { @@ -859,7 +891,7 @@ void InlineTextBox::paintSelection(GraphicsContext* context, const FloatPoint& b String string = textRenderer()->text(); if (string.length() != static_cast<unsigned>(length) || m_start) { - ASSERT(static_cast<unsigned>(m_start + length) <= string.length()); + ASSERT_WITH_SECURITY_IMPLICATION(static_cast<unsigned>(m_start + length) <= string.length()); string = string.substringSharingImpl(m_start, length); } @@ -920,7 +952,7 @@ void InlineTextBox::paintCustomHighlight(const LayoutPoint& paintOffset, const A FloatRect rootRect(paintOffset.x() + r->x(), paintOffset.y() + selectionTop(), r->logicalWidth(), selectionHeight()); FloatRect textRect(paintOffset.x() + x(), rootRect.y(), logicalWidth(), rootRect.height()); - page->chrome()->client()->paintCustomHighlight(renderer()->node(), type, textRect, rootRect, true, false); + page->chrome().client()->paintCustomHighlight(renderer()->node(), type, textRect, rootRect, true, false); } #endif @@ -943,7 +975,6 @@ static StrokeStyle textDecorationStyleToStrokeStyle(TextDecorationStyle decorati strokeStyle = DashedStroke; break; case TextDecorationStyleWavy: - // FIXME: https://bugs.webkit.org/show_bug.cgi?id=92868 - Needs platform support. strokeStyle = WavyStroke; break; #endif // CSS3_TEXT @@ -952,7 +983,158 @@ static StrokeStyle textDecorationStyleToStrokeStyle(TextDecorationStyle decorati return strokeStyle; } -void InlineTextBox::paintDecoration(GraphicsContext* context, const FloatPoint& boxOrigin, ETextDecoration deco, TextDecorationStyle decorationStyle, const ShadowData* shadow) +#if ENABLE(CSS3_TEXT) +static int computeUnderlineOffset(const TextUnderlinePosition underlinePosition, const FontMetrics& fontMetrics, const InlineTextBox* inlineTextBox, const int textDecorationThickness) +{ + // Compute the gap between the font and the underline. Use at least one + // pixel gap, if underline is thick then use a bigger gap. + const int gap = max<int>(1, ceilf(textDecorationThickness / 2.0)); + + // According to the specification TextUnderlinePositionAuto should default to 'alphabetic' for horizontal text + // and to 'under Left' for vertical text (e.g. japanese). We support only horizontal text for now. + switch (underlinePosition) { + case TextUnderlinePositionAlphabetic: + case TextUnderlinePositionAuto: + return fontMetrics.ascent() + gap; // Position underline near the alphabetic baseline. + case TextUnderlinePositionUnder: { + // Position underline relative to the under edge of the lowest element's content box. + const float offset = inlineTextBox->root()->maxLogicalTop() - inlineTextBox->logicalTop(); + if (offset > 0) + return inlineTextBox->logicalHeight() + gap + offset; + return inlineTextBox->logicalHeight() + gap; + } + } + + ASSERT_NOT_REACHED(); + return fontMetrics.ascent() + gap; +} +#endif // CSS3_TEXT + +#if ENABLE(CSS3_TEXT) +static void adjustStepToDecorationLength(float& step, float& controlPointDistance, float length) +{ + ASSERT(step > 0); + + if (length <= 0) + return; + + unsigned stepCount = static_cast<unsigned>(length / step); + + // Each Bezier curve starts at the same pixel that the previous one + // ended. We need to subtract (stepCount - 1) pixels when calculating the + // length covered to account for that. + float uncoveredLength = length - (stepCount * step - (stepCount - 1)); + float adjustment = uncoveredLength / stepCount; + step += adjustment; + controlPointDistance += adjustment; +} + +/* + * Draw one cubic Bezier curve and repeat the same pattern long the the decoration's axis. + * The start point (p1), controlPoint1, controlPoint2 and end point (p2) of the Bezier curve + * form a diamond shape: + * + * step + * |-----------| + * + * controlPoint1 + * + + * + * + * . . + * . . + * . . + * (x1, y1) p1 + . + p2 (x2, y2) - <--- Decoration's axis + * . . | + * . . | + * . . | controlPointDistance + * | + * | + * + - + * controlPoint2 + * + * |-----------| + * step + */ +static void strokeWavyTextDecoration(GraphicsContext* context, FloatPoint& p1, FloatPoint& p2, float strokeThickness) +{ + context->adjustLineToPixelBoundaries(p1, p2, strokeThickness, context->strokeStyle()); + + Path path; + path.moveTo(p1); + + // Distance between decoration's axis and Bezier curve's control points. + // The height of the curve is based on this distance. Use a minimum of 6 pixels distance since + // the actual curve passes approximately at half of that distance, that is 3 pixels. + // The minimum height of the curve is also approximately 3 pixels. Increases the curve's height + // as strockThickness increases to make the curve looks better. + float controlPointDistance = 3 * max<float>(2, strokeThickness); + + // Increment used to form the diamond shape between start point (p1), control + // points and end point (p2) along the axis of the decoration. Makes the + // curve wider as strockThickness increases to make the curve looks better. + float step = 2 * max<float>(2, strokeThickness); + + bool isVerticalLine = (p1.x() == p2.x()); + + if (isVerticalLine) { + ASSERT(p1.x() == p2.x()); + + float xAxis = p1.x(); + float y1; + float y2; + + if (p1.y() < p2.y()) { + y1 = p1.y(); + y2 = p2.y(); + } else { + y1 = p2.y(); + y2 = p1.y(); + } + + adjustStepToDecorationLength(step, controlPointDistance, y2 - y1); + FloatPoint controlPoint1(xAxis + controlPointDistance, 0); + FloatPoint controlPoint2(xAxis - controlPointDistance, 0); + + for (float y = y1; y + 2 * step <= y2;) { + controlPoint1.setY(y + step); + controlPoint2.setY(y + step); + y += 2 * step; + path.addBezierCurveTo(controlPoint1, controlPoint2, FloatPoint(xAxis, y)); + } + } else { + ASSERT(p1.y() == p2.y()); + + float yAxis = p1.y(); + float x1; + float x2; + + if (p1.x() < p2.x()) { + x1 = p1.x(); + x2 = p2.x(); + } else { + x1 = p2.x(); + x2 = p1.x(); + } + + adjustStepToDecorationLength(step, controlPointDistance, x2 - x1); + FloatPoint controlPoint1(0, yAxis + controlPointDistance); + FloatPoint controlPoint2(0, yAxis - controlPointDistance); + + for (float x = x1; x + 2 * step <= x2;) { + controlPoint1.setX(x + step); + controlPoint2.setX(x + step); + x += 2 * step; + path.addBezierCurveTo(controlPoint1, controlPoint2, FloatPoint(x, yAxis)); + } + } + + context->setShouldAntialias(true); + context->strokePath(path); +} +#endif // CSS3_TEXT + +void InlineTextBox::paintDecoration(GraphicsContext* context, const FloatPoint& boxOrigin, TextDecoration deco, TextDecorationStyle decorationStyle, const ShadowData* shadow) { // FIXME: We should improve this rule and not always just assume 1. const float textDecorationThickness = 1.f; @@ -979,7 +1161,7 @@ void InlineTextBox::paintDecoration(GraphicsContext* context, const FloatPoint& bool isPrinting = textRenderer()->document()->printing(); context->setStrokeThickness(textDecorationThickness); - bool linesAreOpaque = !isPrinting && (!(deco & UNDERLINE) || underline.alpha() == 255) && (!(deco & OVERLINE) || overline.alpha() == 255) && (!(deco & LINE_THROUGH) || linethrough.alpha() == 255); + bool linesAreOpaque = !isPrinting && (!(deco & TextDecorationUnderline) || underline.alpha() == 255) && (!(deco & TextDecorationOverline) || overline.alpha() == 255) && (!(deco & TextDecorationLineThrough) || linethrough.alpha() == 255); RenderStyle* styleToUse = renderer()->style(isFirstLineStyle()); int baseline = styleToUse->fontMetrics().ascent(); @@ -989,13 +1171,14 @@ void InlineTextBox::paintDecoration(GraphicsContext* context, const FloatPoint& if (!linesAreOpaque && shadow && shadow->next()) { FloatRect clipRect(localOrigin, FloatSize(width, baseline + 2)); for (const ShadowData* s = shadow; s; s = s->next()) { + int shadowExtent = s->paintingExtent(); FloatRect shadowRect(localOrigin, FloatSize(width, baseline + 2)); - shadowRect.inflate(s->blur()); + shadowRect.inflate(shadowExtent); int shadowX = isHorizontal() ? s->x() : s->y(); int shadowY = isHorizontal() ? s->y() : -s->x(); shadowRect.move(shadowX, shadowY); clipRect.unite(shadowRect); - extraOffset = max(extraOffset, max(0, shadowY) + s->blur()); + extraOffset = max(extraOffset, max(0, shadowY) + shadowExtent); } context->save(); context->clip(clipRect); @@ -1016,7 +1199,7 @@ void InlineTextBox::paintDecoration(GraphicsContext* context, const FloatPoint& } int shadowX = isHorizontal() ? shadow->x() : shadow->y(); int shadowY = isHorizontal() ? shadow->y() : -shadow->x(); - context->setShadow(FloatSize(shadowX, shadowY - extraOffset), shadow->blur(), shadow->color(), colorSpace); + context->setShadow(FloatSize(shadowX, shadowY - extraOffset), shadow->radius(), shadow->color(), colorSpace); setShadow = true; shadow = shadow->next(); } @@ -1026,29 +1209,66 @@ void InlineTextBox::paintDecoration(GraphicsContext* context, const FloatPoint& float doubleOffset = textDecorationThickness + 1.f; #endif // CSS3_TEXT context->setStrokeStyle(textDecorationStyleToStrokeStyle(decorationStyle)); - if (deco & UNDERLINE) { + if (deco & TextDecorationUnderline) { context->setStrokeColor(underline, colorSpace); +#if ENABLE(CSS3_TEXT) + TextUnderlinePosition underlinePosition = styleToUse->textUnderlinePosition(); + const int underlineOffset = computeUnderlineOffset(underlinePosition, styleToUse->fontMetrics(), this, textDecorationThickness); + + switch (decorationStyle) { + case TextDecorationStyleWavy: { + FloatPoint start(localOrigin.x(), localOrigin.y() + underlineOffset + doubleOffset); + FloatPoint end(localOrigin.x() + width, localOrigin.y() + underlineOffset + doubleOffset); + strokeWavyTextDecoration(context, start, end, textDecorationThickness); + break; + } + default: + context->drawLineForText(FloatPoint(localOrigin.x(), localOrigin.y() + underlineOffset), width, isPrinting); + + if (decorationStyle == TextDecorationStyleDouble) + context->drawLineForText(FloatPoint(localOrigin.x(), localOrigin.y() + underlineOffset + doubleOffset), width, isPrinting); + } +#else // Leave one pixel of white between the baseline and the underline. context->drawLineForText(FloatPoint(localOrigin.x(), localOrigin.y() + baseline + 1), width, isPrinting); -#if ENABLE(CSS3_TEXT) - if (decorationStyle == TextDecorationStyleDouble) - context->drawLineForText(FloatPoint(localOrigin.x(), localOrigin.y() + baseline + 1 + doubleOffset), width, isPrinting); #endif // CSS3_TEXT } - if (deco & OVERLINE) { + if (deco & TextDecorationOverline) { context->setStrokeColor(overline, colorSpace); - context->drawLineForText(localOrigin, width, isPrinting); #if ENABLE(CSS3_TEXT) - if (decorationStyle == TextDecorationStyleDouble) - context->drawLineForText(FloatPoint(localOrigin.x(), localOrigin.y() - doubleOffset), width, isPrinting); + switch (decorationStyle) { + case TextDecorationStyleWavy: { + FloatPoint start(localOrigin.x(), localOrigin.y() - doubleOffset); + FloatPoint end(localOrigin.x() + width, localOrigin.y() - doubleOffset); + strokeWavyTextDecoration(context, start, end, textDecorationThickness); + break; + } + default: +#endif // CSS3_TEXT + context->drawLineForText(localOrigin, width, isPrinting); +#if ENABLE(CSS3_TEXT) + if (decorationStyle == TextDecorationStyleDouble) + context->drawLineForText(FloatPoint(localOrigin.x(), localOrigin.y() - doubleOffset), width, isPrinting); + } #endif // CSS3_TEXT } - if (deco & LINE_THROUGH) { + if (deco & TextDecorationLineThrough) { context->setStrokeColor(linethrough, colorSpace); - context->drawLineForText(FloatPoint(localOrigin.x(), localOrigin.y() + 2 * baseline / 3), width, isPrinting); #if ENABLE(CSS3_TEXT) - if (decorationStyle == TextDecorationStyleDouble) - context->drawLineForText(FloatPoint(localOrigin.x(), localOrigin.y() + doubleOffset + 2 * baseline / 3), width, isPrinting); + switch (decorationStyle) { + case TextDecorationStyleWavy: { + FloatPoint start(localOrigin.x(), localOrigin.y() + 2 * baseline / 3); + FloatPoint end(localOrigin.x() + width, localOrigin.y() + 2 * baseline / 3); + strokeWavyTextDecoration(context, start, end, textDecorationThickness); + break; + } + default: +#endif // CSS3_TEXT + context->drawLineForText(FloatPoint(localOrigin.x(), localOrigin.y() + 2 * baseline / 3), width, isPrinting); +#if ENABLE(CSS3_TEXT) + if (decorationStyle == TextDecorationStyleDouble) + context->drawLineForText(FloatPoint(localOrigin.x(), localOrigin.y() + doubleOffset + 2 * baseline / 3), width, isPrinting); + } #endif // CSS3_TEXT } } while (shadow); @@ -1120,7 +1340,7 @@ void InlineTextBox::paintDocumentMarker(GraphicsContext* pt, const FloatPoint& b // display a toolTip. We don't do this for misspelling markers. if (grammar || isDictationMarker) { markerRect.move(-boxOrigin.x(), -boxOrigin.y()); - markerRect = renderer()->localToAbsoluteQuad(FloatRect(markerRect), SnapOffsetForTransforms).enclosingBoundingBox(); + markerRect = renderer()->localToAbsoluteQuad(FloatRect(markerRect)).enclosingBoundingBox(); toRenderedDocumentMarker(marker)->setRenderedRect(markerRect); } } @@ -1158,11 +1378,11 @@ void InlineTextBox::paintTextMatchMarker(GraphicsContext* pt, const FloatPoint& // Always compute and store the rect associated with this marker. The computed rect is in absolute coordinates. IntRect markerRect = enclosingIntRect(font.selectionRectForText(run, IntPoint(x(), selectionTop()), selHeight, sPos, ePos)); - markerRect = renderer()->localToAbsoluteQuad(FloatRect(markerRect), SnapOffsetForTransforms).enclosingBoundingBox(); + markerRect = renderer()->localToAbsoluteQuad(FloatRect(markerRect)).enclosingBoundingBox(); toRenderedDocumentMarker(marker)->setRenderedRect(markerRect); // Optionally highlight the text - if (renderer()->frame()->editor()->markedTextMatchesAreHighlighted()) { + if (renderer()->frame()->editor().markedTextMatchesAreHighlighted()) { Color color = marker->activeMatch() ? renderer()->theme()->platformActiveTextSearchHighlightColor() : renderer()->theme()->platformInactiveTextSearchHighlightColor(); @@ -1186,7 +1406,7 @@ void InlineTextBox::computeRectForReplacementMarker(DocumentMarker* marker, Rend // Compute and store the rect associated with this marker. IntRect markerRect = enclosingIntRect(font.selectionRectForText(run, startPoint, h, sPos, ePos)); - markerRect = renderer()->localToAbsoluteQuad(FloatRect(markerRect), SnapOffsetForTransforms).enclosingBoundingBox(); + markerRect = renderer()->localToAbsoluteQuad(FloatRect(markerRect)).enclosingBoundingBox(); toRenderedDocumentMarker(marker)->setRenderedRect(markerRect); } @@ -1200,7 +1420,7 @@ void InlineTextBox::paintDocumentMarkers(GraphicsContext* pt, const FloatPoint& // Give any document markers that touch this run a chance to draw before the text has been drawn. // Note end() points at the last char, not one past it like endOffset and ranges do. - for ( ; markerIt != markers.end(); markerIt++) { + for ( ; markerIt != markers.end(); ++markerIt) { DocumentMarker* marker = *markerIt; // Paint either the background markers or the foreground markers, but not both diff --git a/Source/WebCore/rendering/InlineTextBox.h b/Source/WebCore/rendering/InlineTextBox.h index 1f2a63acf..a1c9d930b 100644 --- a/Source/WebCore/rendering/InlineTextBox.h +++ b/Source/WebCore/rendering/InlineTextBox.h @@ -57,7 +57,7 @@ public: { } - virtual void destroy(RenderArena*); + virtual void destroy(RenderArena*) FINAL; InlineTextBox* prevTextBox() const { return m_prevTextBox; } InlineTextBox* nextTextBox() const { return m_nextTextBox; } @@ -76,7 +76,7 @@ public: unsigned short truncation() { return m_truncation; } - virtual void markDirty(bool dirty = true) OVERRIDE; + virtual void markDirty(bool dirty = true) OVERRIDE FINAL; using InlineBox::hasHyphen; using InlineBox::setHasHyphen; @@ -85,8 +85,8 @@ public: static inline bool compareByStart(const InlineTextBox* first, const InlineTextBox* second) { return first->start() < second->start(); } - virtual int baselinePosition(FontBaseline) const; - virtual LayoutUnit lineHeight() const; + virtual int baselinePosition(FontBaseline) const FINAL; + virtual LayoutUnit lineHeight() const FINAL; bool getEmphasisMarkPosition(RenderStyle*, TextEmphasisPosition&) const; @@ -101,6 +101,7 @@ public: virtual void showBox(int = 0) const; virtual const char* boxName() const; #endif + private: LayoutUnit selectionTop(); LayoutUnit selectionBottom(); @@ -124,19 +125,19 @@ public: RenderText* textRenderer() const; private: - virtual void deleteLine(RenderArena*); - virtual void extractLine(); - virtual void attachLine(); + virtual void deleteLine(RenderArena*) FINAL; + virtual void extractLine() FINAL; + virtual void attachLine() FINAL; public: - virtual RenderObject::SelectionState selectionState(); + virtual RenderObject::SelectionState selectionState() FINAL; private: - virtual void clearTruncation() { m_truncation = cNoTruncation; } - virtual float placeEllipsisBox(bool flowIsLTR, float visibleLeftEdge, float visibleRightEdge, float ellipsisWidth, float &truncatedWidth, bool& foundBox) OVERRIDE; + virtual void clearTruncation() FINAL { m_truncation = cNoTruncation; } + virtual float placeEllipsisBox(bool flowIsLTR, float visibleLeftEdge, float visibleRightEdge, float ellipsisWidth, float &truncatedWidth, bool& foundBox) OVERRIDE FINAL; public: - virtual bool isLineBreak() const; + virtual bool isLineBreak() const FINAL; void setExpansion(int newExpansion) { @@ -146,11 +147,11 @@ public: } private: - virtual bool isInlineTextBox() const { return true; } + virtual bool isInlineTextBox() const FINAL { return true; } public: - virtual int caretMinOffset() const; - virtual int caretMaxOffset() const; + virtual int caretMinOffset() const FINAL; + virtual int caretMaxOffset() const FINAL; private: float textPos() const; // returns the x position relative to the left start of the text line. @@ -183,7 +184,7 @@ protected: #endif private: - void paintDecoration(GraphicsContext*, const FloatPoint& boxOrigin, ETextDecoration, TextDecorationStyle, const ShadowData*); + void paintDecoration(GraphicsContext*, const FloatPoint& boxOrigin, TextDecoration, TextDecorationStyle, const ShadowData*); void paintSelection(GraphicsContext*, const FloatPoint& boxOrigin, RenderStyle*, const Font&, Color textColor); void paintDocumentMarker(GraphicsContext*, const FloatPoint& boxOrigin, DocumentMarker*, RenderStyle*, const Font&, bool grammar); void paintTextMatchMarker(GraphicsContext*, const FloatPoint& boxOrigin, DocumentMarker*, RenderStyle*, const Font&); @@ -198,13 +199,13 @@ private: inline InlineTextBox* toInlineTextBox(InlineBox* inlineBox) { - ASSERT(!inlineBox || inlineBox->isInlineTextBox()); + ASSERT_WITH_SECURITY_IMPLICATION(!inlineBox || inlineBox->isInlineTextBox()); return static_cast<InlineTextBox*>(inlineBox); } inline const InlineTextBox* toInlineTextBox(const InlineBox* inlineBox) { - ASSERT(!inlineBox || inlineBox->isInlineTextBox()); + ASSERT_WITH_SECURITY_IMPLICATION(!inlineBox || inlineBox->isInlineTextBox()); return static_cast<const InlineTextBox*>(inlineBox); } diff --git a/Source/WebCore/rendering/LayoutState.cpp b/Source/WebCore/rendering/LayoutState.cpp index 485d91775..28a8ffbea 100644 --- a/Source/WebCore/rendering/LayoutState.cpp +++ b/Source/WebCore/rendering/LayoutState.cpp @@ -39,8 +39,8 @@ LayoutState::LayoutState(LayoutState* prev, RenderBox* renderer, const LayoutSiz : m_columnInfo(columnInfo) , m_lineGrid(0) , m_next(prev) -#if ENABLE(CSS_EXCLUSIONS) - , m_exclusionShapeInsideInfo(0) +#if ENABLE(CSS_SHAPES) + , m_shapeInsideInfo(0) #endif #ifndef NDEBUG , m_renderer(renderer) @@ -110,11 +110,12 @@ LayoutState::LayoutState(LayoutState* prev, RenderBox* renderer, const LayoutSiz if (!m_columnInfo) m_columnInfo = m_next->m_columnInfo; -#if ENABLE(CSS_EXCLUSIONS) +#if ENABLE(CSS_SHAPES) if (renderer->isRenderBlock()) { - m_exclusionShapeInsideInfo = toRenderBlock(renderer)->exclusionShapeInsideInfo(); - if (!m_exclusionShapeInsideInfo) - m_exclusionShapeInsideInfo = m_next->m_exclusionShapeInsideInfo; + const RenderBlock* renderBlock = toRenderBlock(renderer); + m_shapeInsideInfo = renderBlock->shapeInsideInfo(); + if (!m_shapeInsideInfo && m_next->m_shapeInsideInfo && renderBlock->allowsShapeInsideInfoSharing()) + m_shapeInsideInfo = m_next->m_shapeInsideInfo; } #endif @@ -147,8 +148,8 @@ LayoutState::LayoutState(RenderObject* root) , m_columnInfo(0) , m_lineGrid(0) , m_next(0) -#if ENABLE(CSS_EXCLUSIONS) - , m_exclusionShapeInsideInfo(0) +#if ENABLE(CSS_SHAPES) + , m_shapeInsideInfo(0) #endif , m_pageLogicalHeight(0) #ifndef NDEBUG @@ -156,7 +157,7 @@ LayoutState::LayoutState(RenderObject* root) #endif { RenderObject* container = root->container(); - FloatPoint absContentPoint = container->localToAbsolute(FloatPoint(), UseTransforms | SnapOffsetForTransforms); + FloatPoint absContentPoint = container->localToAbsolute(FloatPoint(), UseTransforms); m_paintOffset = LayoutSize(absContentPoint.x(), absContentPoint.y()); if (container->hasOverflowClip()) { diff --git a/Source/WebCore/rendering/LayoutState.h b/Source/WebCore/rendering/LayoutState.h index 38571f91d..d7749c52c 100644 --- a/Source/WebCore/rendering/LayoutState.h +++ b/Source/WebCore/rendering/LayoutState.h @@ -38,8 +38,8 @@ class RenderBlock; class RenderBox; class RenderObject; class RenderFlowThread; -#if ENABLE(CSS_EXCLUSIONS) -class ExclusionShapeInsideInfo; +#if ENABLE(CSS_SHAPES) +class ShapeInsideInfo; #endif class LayoutState { @@ -56,8 +56,8 @@ public: , m_columnInfo(0) , m_lineGrid(0) , m_next(0) -#if ENABLE(CSS_EXCLUSIONS) - , m_exclusionShapeInsideInfo(0) +#if ENABLE(CSS_SHAPES) + , m_shapeInsideInfo(0) #endif , m_pageLogicalHeight(0) #ifndef NDEBUG @@ -98,8 +98,8 @@ public: bool needsBlockDirectionLocationSetBeforeLayout() const { return m_lineGrid || (m_isPaginated && m_pageLogicalHeight); } -#if ENABLE(CSS_EXCLUSIONS) - ExclusionShapeInsideInfo* exclusionShapeInsideInfo() const { return m_exclusionShapeInsideInfo; } +#if ENABLE(CSS_SHAPES) + ShapeInsideInfo* shapeInsideInfo() const { return m_shapeInsideInfo; } #endif private: // The normal operator new is disallowed. @@ -125,8 +125,8 @@ public: // The current line grid that we're snapping to and the offset of the start of the grid. RenderBlock* m_lineGrid; LayoutState* m_next; -#if ENABLE(CSS_EXCLUSIONS) - ExclusionShapeInsideInfo* m_exclusionShapeInsideInfo; +#if ENABLE(CSS_SHAPES) + ShapeInsideInfo* m_shapeInsideInfo; #endif // FIXME: Distinguish between the layout clip rect and the paint clip rect which may be larger, diff --git a/Source/WebCore/rendering/LogicalSelectionOffsetCaches.h b/Source/WebCore/rendering/LogicalSelectionOffsetCaches.h new file mode 100644 index 000000000..3c59fb676 --- /dev/null +++ b/Source/WebCore/rendering/LogicalSelectionOffsetCaches.h @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2013 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef LogicalSelectionOffsetCaches_h +#define LogicalSelectionOffsetCaches_h + +#include "RenderBlock.h" + +namespace WebCore { + +static inline bool isContainingBlockCandidateForAbsolutelyPositionedObject(RenderObject* object) +{ + return object->style()->position() != StaticPosition + || (object->hasTransform() && object->isRenderBlock()) +#if ENABLE(SVG) + || object->isSVGForeignObject() +#endif + || object->isRenderView(); +} + +static inline bool isNonRenderBlockInline(RenderObject* object) +{ + return (object->isInline() && !object->isReplaced()) || !object->isRenderBlock(); +} + +static inline RenderObject* containingBlockForFixedPosition(RenderObject* parent) +{ + RenderObject* object = parent; + while (object && !object->canContainFixedPositionObjects()) + object = object->parent(); + ASSERT(!object || !object->isAnonymousBlock()); + return object; +} + +static inline RenderObject* containingBlockForAbsolutePosition(RenderObject* parent) +{ + RenderObject* object = parent; + while (object && !isContainingBlockCandidateForAbsolutelyPositionedObject(object)) + object = object->parent(); + + // For a relatively positioned inline, return its nearest non-anonymous containing block, + // not the inline itself, to avoid having a positioned objects list in all RenderInlines + // and use RenderBlock* as RenderObject::containingBlock's return type. + // Use RenderBlock::container() to obtain the inline. + if (object && object->isRenderInline()) + object = object->containingBlock(); + + while (object && object->isAnonymousBlock()) + object = object->containingBlock(); + + return object; +} + +static inline RenderObject* containingBlockForObjectInFlow(RenderObject* parent) +{ + RenderObject* object = parent; + while (object && isNonRenderBlockInline(object)) + object = object->parent(); + return object; +} + +class LogicalSelectionOffsetCaches { +public: + class ContainingBlockInfo { + public: + ContainingBlockInfo() + : m_block(0) + , m_cache(0) + , m_hasFloatsOrFlowThreads(false) + , m_cachedLogicalLeftSelectionOffset(false) + , m_cachedLogicalRightSelectionOffset(false) + { } + + void setBlock(RenderBlock* block, const LogicalSelectionOffsetCaches* cache) + { + m_block = block; + m_hasFloatsOrFlowThreads = m_hasFloatsOrFlowThreads || m_block->containsFloats() || m_block->flowThreadContainingBlock(); + m_cache = cache; + m_cachedLogicalLeftSelectionOffset = false; + m_cachedLogicalRightSelectionOffset = false; + } + + RenderBlock* block() const { return m_block; } + const LogicalSelectionOffsetCaches* cache() const { return m_cache; } + + LayoutUnit logicalLeftSelectionOffset(RenderBlock* rootBlock, LayoutUnit position) const + { + ASSERT(m_cache); + if (m_hasFloatsOrFlowThreads || !m_cachedLogicalLeftSelectionOffset) { + m_cachedLogicalLeftSelectionOffset = true; + m_logicalLeftSelectionOffset = m_block->logicalLeftSelectionOffset(rootBlock, position, *m_cache); + } else + ASSERT(m_logicalLeftSelectionOffset == m_block->logicalLeftSelectionOffset(rootBlock, position, *m_cache)); + return m_logicalLeftSelectionOffset; + } + + LayoutUnit logicalRightSelectionOffset(RenderBlock* rootBlock, LayoutUnit position) const + { + ASSERT(m_cache); + if (m_hasFloatsOrFlowThreads || !m_cachedLogicalRightSelectionOffset) { + m_cachedLogicalRightSelectionOffset = true; + m_logicalRightSelectionOffset = m_block->logicalRightSelectionOffset(rootBlock, position, *m_cache); + } else + ASSERT(m_logicalRightSelectionOffset == m_block->logicalRightSelectionOffset(rootBlock, position, *m_cache)); + return m_logicalRightSelectionOffset; + } + + private: + RenderBlock* m_block; + const LogicalSelectionOffsetCaches* m_cache; + bool m_hasFloatsOrFlowThreads : 1; + mutable bool m_cachedLogicalLeftSelectionOffset : 1; + mutable bool m_cachedLogicalRightSelectionOffset : 1; + mutable LayoutUnit m_logicalLeftSelectionOffset; + mutable LayoutUnit m_logicalRightSelectionOffset; + + }; + + LogicalSelectionOffsetCaches(RenderBlock* rootBlock) + { + ASSERT(rootBlock->isSelectionRoot()); + RenderObject* parent = rootBlock->parent(); + + // LogicalSelectionOffsetCaches should not be used on an orphaned tree. + m_containingBlockForFixedPosition.setBlock(toRenderBlock(containingBlockForFixedPosition(parent)), 0); + m_containingBlockForAbsolutePosition.setBlock(toRenderBlock(containingBlockForAbsolutePosition(parent)), 0); + m_containingBlockForInflowPosition.setBlock(toRenderBlock(containingBlockForObjectInFlow(parent)), 0); + } + + LogicalSelectionOffsetCaches(RenderBlock* block, const LogicalSelectionOffsetCaches& cache) + : m_containingBlockForFixedPosition(cache.m_containingBlockForFixedPosition) + , m_containingBlockForAbsolutePosition(cache.m_containingBlockForAbsolutePosition) + { + if (block->canContainFixedPositionObjects()) + m_containingBlockForFixedPosition.setBlock(block, &cache); + + if (isContainingBlockCandidateForAbsolutelyPositionedObject(block) && !block->isRenderInline() && !block->isAnonymousBlock()) + m_containingBlockForFixedPosition.setBlock(block, &cache); + + m_containingBlockForInflowPosition.setBlock(block, &cache); + } + + const ContainingBlockInfo& containingBlockInfo(RenderBlock* block) const + { + EPosition position = block->style()->position(); + if (position == FixedPosition) { + ASSERT(block->containingBlock() == m_containingBlockForFixedPosition.block()); + return m_containingBlockForFixedPosition; + } + if (position == AbsolutePosition) { + ASSERT(block->containingBlock() == m_containingBlockForAbsolutePosition.block()); + return m_containingBlockForAbsolutePosition; + } + ASSERT(block->containingBlock() == m_containingBlockForInflowPosition.block()); + return m_containingBlockForInflowPosition; + } + +private: + ContainingBlockInfo m_containingBlockForFixedPosition; + ContainingBlockInfo m_containingBlockForAbsolutePosition; + ContainingBlockInfo m_containingBlockForInflowPosition; +}; + +} // namespace WebCore + +#endif // LogicalSelectionOffsetCaches_h diff --git a/Source/WebCore/rendering/PaintInfo.h b/Source/WebCore/rendering/PaintInfo.h index 2781496e6..417859dd5 100644 --- a/Source/WebCore/rendering/PaintInfo.h +++ b/Source/WebCore/rendering/PaintInfo.h @@ -42,6 +42,7 @@ namespace WebCore { class OverlapTestRequestClient; class RenderInline; +class RenderLayerModelObject; class RenderObject; class RenderRegion; @@ -52,37 +53,43 @@ typedef HashMap<OverlapTestRequestClient*, IntRect> OverlapTestRequestMap; * (tx|ty) is the calculated position of the parent */ struct PaintInfo { - PaintInfo(GraphicsContext* newContext, const IntRect& newRect, PaintPhase newPhase, bool newForceBlackText, - RenderObject* newPaintingRoot, RenderRegion* region, ListHashSet<RenderInline*>* newOutlineObjects, - OverlapTestRequestMap* overlapTestRequests = 0) + PaintInfo(GraphicsContext* newContext, const IntRect& newRect, PaintPhase newPhase, PaintBehavior newPaintBehavior, + RenderObject* newSubtreePaintRoot = 0, RenderRegion* region = 0, ListHashSet<RenderInline*>* newOutlineObjects = 0, + OverlapTestRequestMap* overlapTestRequests = 0, const RenderLayerModelObject* newPaintContainer = 0) : context(newContext) , rect(newRect) , phase(newPhase) - , forceBlackText(newForceBlackText) - , paintingRoot(newPaintingRoot) + , paintBehavior(newPaintBehavior) + , subtreePaintRoot(newSubtreePaintRoot) , renderRegion(region) , outlineObjects(newOutlineObjects) , overlapTestRequests(overlapTestRequests) + , paintContainer(newPaintContainer) { } - void updatePaintingRootForChildren(const RenderObject* renderer) + void updateSubtreePaintRootForChildren(const RenderObject* renderer) { - if (!paintingRoot) + if (!subtreePaintRoot) return; // If we're the painting root, kids draw normally, and see root of 0. - if (paintingRoot == renderer) { - paintingRoot = 0; + if (subtreePaintRoot == renderer) { + subtreePaintRoot = 0; return; } } bool shouldPaintWithinRoot(const RenderObject* renderer) const { - return !paintingRoot || paintingRoot == renderer; + return !subtreePaintRoot || subtreePaintRoot == renderer; } + bool forceBlackText() const { return paintBehavior & PaintBehaviorForceBlackText; } + + bool skipRootBackground() const { return paintBehavior & PaintBehaviorSkipRootBackground; } + bool paintRootBackgroundOnly() const { return paintBehavior & PaintBehaviorRootBackgroundOnly; } + #if ENABLE(SVG) void applyTransform(const AffineTransform& localToAncestorTransform) { @@ -104,11 +111,12 @@ struct PaintInfo { GraphicsContext* context; IntRect rect; PaintPhase phase; - bool forceBlackText; - RenderObject* paintingRoot; // used to draw just one element and its visual kids + PaintBehavior paintBehavior; + RenderObject* subtreePaintRoot; // used to draw just one element and its visual children RenderRegion* renderRegion; ListHashSet<RenderInline*>* outlineObjects; // used to list outlines that should be painted by a block with inline children OverlapTestRequestMap* overlapTestRequests; + const RenderLayerModelObject* paintContainer; // the layer object that originates the current painting }; } // namespace WebCore diff --git a/Source/WebCore/rendering/PaintPhase.h b/Source/WebCore/rendering/PaintPhase.h index be4c61be4..af0bd59b9 100644 --- a/Source/WebCore/rendering/PaintPhase.h +++ b/Source/WebCore/rendering/PaintPhase.h @@ -57,7 +57,9 @@ enum PaintBehaviorFlags { PaintBehaviorSelectionOnly = 1 << 0, PaintBehaviorForceBlackText = 1 << 1, PaintBehaviorFlattenCompositingLayers = 1 << 2, - PaintBehaviorRenderingSVGMask = 1 << 3 + PaintBehaviorRenderingSVGMask = 1 << 3, + PaintBehaviorSkipRootBackground = 1 << 4, + PaintBehaviorRootBackgroundOnly = 1 << 5 }; typedef unsigned PaintBehavior; diff --git a/Source/WebCore/rendering/RegionOversetState.h b/Source/WebCore/rendering/RegionOversetState.h new file mode 100644 index 000000000..e29e370a2 --- /dev/null +++ b/Source/WebCore/rendering/RegionOversetState.h @@ -0,0 +1,33 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef RegionOversetState_h +#define RegionOversetState_h + +namespace WebCore { + +enum RegionOversetState { + RegionUndefined, + RegionEmpty, + RegionFit, + RegionOverset +}; + +} // namespace WebCore + +#endif // RegionOversetState_h diff --git a/Source/WebCore/rendering/RenderApplet.h b/Source/WebCore/rendering/RenderApplet.h index 151660066..881e21c0b 100644 --- a/Source/WebCore/rendering/RenderApplet.h +++ b/Source/WebCore/rendering/RenderApplet.h @@ -30,7 +30,7 @@ class HTMLAppletElement; class RenderApplet : public RenderEmbeddedObject { public: - RenderApplet(HTMLAppletElement*); + explicit RenderApplet(HTMLAppletElement*); virtual ~RenderApplet(); private: diff --git a/Source/WebCore/rendering/RenderArena.cpp b/Source/WebCore/rendering/RenderArena.cpp index cc2362059..c1adee2b1 100644 --- a/Source/WebCore/rendering/RenderArena.cpp +++ b/Source/WebCore/rendering/RenderArena.cpp @@ -36,9 +36,11 @@ #include "config.h" #include "RenderArena.h" +#include <limits> #include <stdlib.h> #include <string.h> #include <wtf/Assertions.h> +#include <wtf/CryptographicallyRandomNumber.h> #define ROUNDUP(x, y) ((((x)+((y)-1))/(y))*(y)) @@ -89,10 +91,8 @@ RenderArena::RenderArena(unsigned arenaSize) // should immediately crash on the first invalid vtable access for a stale // RenderObject pointer. // See http://download.crowdstrike.com/papers/hes-exploiting-a-coalmine.pdf. - - // The bottom bits are predictable because the binary is loaded on a - // boundary. This just shifts most of those predictable bits out. - m_mask = ~(reinterpret_cast<uintptr_t>(WTF::fastMalloc) >> 13); + WTF::cryptographicallyRandomValues(&m_mask, sizeof(m_mask)); + m_mask |= (static_cast<uintptr_t>(3) << (std::numeric_limits<uintptr_t>::digits - 2)) | 1; } RenderArena::~RenderArena() diff --git a/Source/WebCore/rendering/RenderArena.h b/Source/WebCore/rendering/RenderArena.h index 5538effd6..0f003db5c 100644 --- a/Source/WebCore/rendering/RenderArena.h +++ b/Source/WebCore/rendering/RenderArena.h @@ -38,15 +38,16 @@ #include "Arena.h" #include <wtf/FastAllocBase.h> #include <wtf/Noncopyable.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> namespace WebCore { static const size_t gMaxRecycledSize = 1024; -class RenderArena { - WTF_MAKE_NONCOPYABLE(RenderArena); WTF_MAKE_FAST_ALLOCATED; +class RenderArena : public RefCounted<RenderArena> { public: - RenderArena(unsigned arenaSize = 8192); + static PassRefPtr<RenderArena> create() { return adoptRef(new RenderArena); } ~RenderArena(); // Memory management functions @@ -57,6 +58,8 @@ public: size_t totalRenderArenaAllocatedBytes() const { return m_totalAllocated; } private: + RenderArena(unsigned arenaSize = 8192); + // Underlying arena pool ArenaPool m_pool; diff --git a/Source/WebCore/rendering/RenderBR.h b/Source/WebCore/rendering/RenderBR.h index 25e90da8d..91486f29c 100644 --- a/Source/WebCore/rendering/RenderBR.h +++ b/Source/WebCore/rendering/RenderBR.h @@ -33,7 +33,7 @@ class Position; class RenderBR : public RenderText { public: - RenderBR(Node*); + explicit RenderBR(Node*); virtual ~RenderBR(); virtual const char* renderName() const { return "RenderBR"; } @@ -63,13 +63,13 @@ private: inline RenderBR* toRenderBR(RenderObject* object) { - ASSERT(!object || object->isBR()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isBR()); return static_cast<RenderBR*>(object); } inline const RenderBR* toRenderBR(const RenderObject* object) { - ASSERT(!object || object->isBR()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isBR()); return static_cast<const RenderBR*>(object); } diff --git a/Source/WebCore/rendering/RenderBlock.cpp b/Source/WebCore/rendering/RenderBlock.cpp index 058e90431..c1b3fe154 100644 --- a/Source/WebCore/rendering/RenderBlock.cpp +++ b/Source/WebCore/rendering/RenderBlock.cpp @@ -27,43 +27,47 @@ #include "AXObjectCache.h" #include "ColumnInfo.h" #include "Document.h" +#include "Editor.h" #include "Element.h" #include "FloatQuad.h" #include "Frame.h" #include "FrameSelection.h" #include "FrameView.h" #include "GraphicsContext.h" -#include "HTMLFormElement.h" +#include "HTMLInputElement.h" #include "HTMLNames.h" +#include "HitTestLocation.h" #include "HitTestResult.h" #include "InlineIterator.h" #include "InlineTextBox.h" #include "LayoutRepainter.h" +#include "LogicalSelectionOffsetCaches.h" #include "OverflowEvent.h" -#include "PODFreeListArena.h" #include "Page.h" #include "PaintInfo.h" #include "RenderBoxRegionInfo.h" #include "RenderCombineText.h" #include "RenderDeprecatedFlexibleBox.h" -#include "RenderImage.h" +#include "RenderFlexibleBox.h" #include "RenderInline.h" #include "RenderLayer.h" #include "RenderMarquee.h" #include "RenderNamedFlowThread.h" #include "RenderRegion.h" -#include "RenderReplica.h" #include "RenderTableCell.h" #include "RenderTextFragment.h" #include "RenderTheme.h" #include "RenderView.h" -#include "Settings.h" #include "SVGTextRunRenderingContext.h" +#include "Settings.h" #include "ShadowRoot.h" #include "TransformState.h" -#include <wtf/StdLibExtras.h> -#if ENABLE(CSS_EXCLUSIONS) -#include "ExclusionShapeInsideInfo.h" +#include <wtf/StackStats.h> +#include <wtf/TemporaryChange.h> + +#if ENABLE(CSS_SHAPES) +#include "ShapeInsideInfo.h" +#include "ShapeOutsideInfo.h" #endif using namespace std; @@ -97,7 +101,7 @@ struct SameSizeAsMarginInfo { LayoutUnit margins[2]; }; -typedef WTF::HashMap<const RenderBox*, ColumnInfo*> ColumnInfoMap; +typedef WTF::HashMap<const RenderBox*, OwnPtr<ColumnInfo> > ColumnInfoMap; static ColumnInfoMap* gColumnInfoMap = 0; static TrackedDescendantsMap* gPositionedDescendantsMap = 0; @@ -106,12 +110,14 @@ static TrackedDescendantsMap* gPercentHeightDescendantsMap = 0; static TrackedContainerMap* gPositionedContainerMap = 0; static TrackedContainerMap* gPercentHeightContainerMap = 0; -typedef WTF::HashMap<RenderBlock*, ListHashSet<RenderInline*>*> ContinuationOutlineTableMap; +typedef WTF::HashMap<RenderBlock*, OwnPtr<ListHashSet<RenderInline*> > > ContinuationOutlineTableMap; typedef WTF::HashSet<RenderBlock*> DelayedUpdateScrollInfoSet; static int gDelayUpdateScrollInfo = 0; static DelayedUpdateScrollInfoSet* gDelayedUpdateScrollInfoSet = 0; +static bool gColumnFlowSplitEnabled = true; + bool RenderBlock::s_canPropagateFloatIntoSibling = false; // This class helps dispatching the 'overflow' event on layout change. overflow can be set on RenderBoxes, yet the existing code @@ -158,16 +164,17 @@ private: RenderBlock::MarginInfo::MarginInfo(RenderBlock* block, LayoutUnit beforeBorderPadding, LayoutUnit afterBorderPadding) : m_atBeforeSideOfBlock(true) , m_atAfterSideOfBlock(false) - , m_marginBeforeQuirk(false) - , m_marginAfterQuirk(false) + , m_hasMarginBeforeQuirk(false) + , m_hasMarginAfterQuirk(false) , m_determinedMarginBeforeQuirk(false) + , m_discardMargin(false) { RenderStyle* blockStyle = block->style(); ASSERT(block->isRenderView() || block->parent()); m_canCollapseWithChildren = !block->isRenderView() && !block->isRoot() && !block->isOutOfFlowPositioned() && !block->isFloating() && !block->isTableCell() && !block->hasOverflowClip() && !block->isInlineBlockOrInlineTable() - && !block->isWritingModeRoot() && !block->parent()->isFlexibleBox() && blockStyle->hasAutoColumnCount() && blockStyle->hasAutoColumnWidth() - && !blockStyle->columnSpan(); + && !block->isRenderFlowThread() && !block->isWritingModeRoot() && !block->parent()->isFlexibleBox() + && blockStyle->hasAutoColumnCount() && blockStyle->hasAutoColumnWidth() && !blockStyle->columnSpan(); m_canCollapseMarginBeforeWithChildren = m_canCollapseWithChildren && !beforeBorderPadding && blockStyle->marginBeforeCollapse() != MSEPARATE; @@ -178,20 +185,24 @@ RenderBlock::MarginInfo::MarginInfo(RenderBlock* block, LayoutUnit beforeBorderP m_canCollapseMarginAfterWithChildren = m_canCollapseWithChildren && (afterBorderPadding == 0) && (blockStyle->logicalHeight().isAuto() && !blockStyle->logicalHeight().value()) && blockStyle->marginAfterCollapse() != MSEPARATE; - m_quirkContainer = block->isTableCell() || block->isBody() || blockStyle->marginBeforeCollapse() == MDISCARD - || blockStyle->marginAfterCollapse() == MDISCARD; + m_quirkContainer = block->isTableCell() || block->isBody(); + + m_discardMargin = m_canCollapseMarginBeforeWithChildren && block->mustDiscardMarginBefore(); - m_positiveMargin = m_canCollapseMarginBeforeWithChildren ? block->maxPositiveMarginBefore() : LayoutUnit(); - m_negativeMargin = m_canCollapseMarginBeforeWithChildren ? block->maxNegativeMarginBefore() : LayoutUnit(); + m_positiveMargin = (m_canCollapseMarginBeforeWithChildren && !block->mustDiscardMarginBefore()) ? block->maxPositiveMarginBefore() : LayoutUnit(); + m_negativeMargin = (m_canCollapseMarginBeforeWithChildren && !block->mustDiscardMarginBefore()) ? block->maxNegativeMarginBefore() : LayoutUnit(); } // ------------------------------------------------------------------------------------------------------- -RenderBlock::RenderBlock(Node* node) - : RenderBox(node) - , m_lineHeight(-1) - , m_beingDestroyed(false) - , m_hasMarkupTruncation(false) +RenderBlock::RenderBlock(ContainerNode* node) + : RenderBox(node) + , m_lineHeight(-1) + , m_hasMarginBeforeQuirk(false) + , m_hasMarginAfterQuirk(false) + , m_beingDestroyed(false) + , m_hasMarkupTruncation(false) + , m_hasBorderOrPaddingLogicalWidthChanged(false) { setChildrenInline(true); COMPILE_ASSERT(sizeof(RenderBlock::FloatingObject) == sizeof(SameSizeAsFloatingObject), FloatingObject_should_stay_small); @@ -200,21 +211,19 @@ RenderBlock::RenderBlock(Node* node) static void removeBlockFromDescendantAndContainerMaps(RenderBlock* block, TrackedDescendantsMap*& descendantMap, TrackedContainerMap*& containerMap) { - if (TrackedRendererListHashSet* descendantSet = descendantMap->take(block)) { + if (OwnPtr<TrackedRendererListHashSet> descendantSet = descendantMap->take(block)) { TrackedRendererListHashSet::iterator end = descendantSet->end(); for (TrackedRendererListHashSet::iterator descendant = descendantSet->begin(); descendant != end; ++descendant) { - HashSet<RenderBlock*>* containerSet = containerMap->get(*descendant); - ASSERT(containerSet); - if (!containerSet) + TrackedContainerMap::iterator it = containerMap->find(*descendant); + ASSERT(it != containerMap->end()); + if (it == containerMap->end()) continue; + HashSet<RenderBlock*>* containerSet = it->value.get(); ASSERT(containerSet->contains(block)); containerSet->remove(block); - if (containerSet->isEmpty()) { - containerMap->remove(*descendant); - delete containerSet; - } + if (containerSet->isEmpty()) + containerMap->remove(it); } - delete descendantSet; } } @@ -224,7 +233,7 @@ RenderBlock::~RenderBlock() deleteAllValues(m_floatingObjects->set()); if (hasColumns()) - delete gColumnInfoMap->take(this); + gColumnInfoMap->take(this); if (gPercentHeightDescendantsMap) removeBlockFromDescendantAndContainerMaps(this, gPercentHeightDescendantsMap, gPercentHeightContainerMap); @@ -232,11 +241,23 @@ RenderBlock::~RenderBlock() removeBlockFromDescendantAndContainerMaps(this, gPositionedDescendantsMap, gPositionedContainerMap); } +RenderBlock* RenderBlock::createAnonymous(Document* document) +{ + RenderBlock* renderer = new (document->renderArena()) RenderBlock(0); + renderer->setDocumentForAnonymous(document); + return renderer; +} + void RenderBlock::willBeDestroyed() { // Mark as being destroyed to avoid trouble with merges in removeChild(). m_beingDestroyed = true; + if (!documentBeingDestroyed()) { + if (firstChild() && firstChild()->isRunIn()) + moveRunInToOriginalPosition(firstChild()); + } + // Make sure to destroy anonymous children first while they are still connected to the rest of the tree, so that they will // properly dirty line boxes that they are removed from. Effects that do :before/:after only on hover could crash otherwise. children()->destroyLeftoverChildren(); @@ -277,10 +298,6 @@ void RenderBlock::willBeDestroyed() if (lineGridBox()) lineGridBox()->destroy(renderArena()); -#if ENABLE(CSS_EXCLUSIONS) - ExclusionShapeInsideInfo::removeExclusionShapeInsideInfoForRenderBlock(this); -#endif - if (UNLIKELY(gDelayedUpdateScrollInfoSet != 0)) gDelayedUpdateScrollInfoSet->remove(this); @@ -322,14 +339,28 @@ void RenderBlock::styleWillChange(StyleDifference diff, const RenderStyle* newSt RenderBox::styleWillChange(diff, newStyle); } +static bool borderOrPaddingLogicalWidthChanged(const RenderStyle* oldStyle, const RenderStyle* newStyle) +{ + if (newStyle->isHorizontalWritingMode()) + return oldStyle->borderLeftWidth() != newStyle->borderLeftWidth() + || oldStyle->borderRightWidth() != newStyle->borderRightWidth() + || oldStyle->paddingLeft() != newStyle->paddingLeft() + || oldStyle->paddingRight() != newStyle->paddingRight(); + + return oldStyle->borderTopWidth() != newStyle->borderTopWidth() + || oldStyle->borderBottomWidth() != newStyle->borderBottomWidth() + || oldStyle->paddingTop() != newStyle->paddingTop() + || oldStyle->paddingBottom() != newStyle->paddingBottom(); +} + void RenderBlock::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) { RenderBox::styleDidChange(diff, oldStyle); - -#if ENABLE(CSS_EXCLUSIONS) - // FIXME: Bug 89993: Style changes should affect the ExclusionShapeInsideInfos for other render blocks that - // share the same ExclusionShapeInsideInfo - updateExclusionShapeInsideInfoAfterStyleChange(style()->shapeInside(), oldStyle ? oldStyle->shapeInside() : 0); + + RenderStyle* newStyle = style(); + +#if ENABLE(CSS_SHAPES) + updateShapeInsideInfoAfterStyleChange(newStyle->resolvedShapeInside(), oldStyle ? oldStyle->resolvedShapeInside() : 0); #endif if (!isAnonymousBlock()) { @@ -337,7 +368,7 @@ void RenderBlock::styleDidChange(StyleDifference diff, const RenderStyle* oldSty for (RenderBlock* currCont = blockElementContinuation(); currCont; currCont = currCont->blockElementContinuation()) { RenderBoxModelObject* nextCont = currCont->continuation(); currCont->setContinuation(0); - currCont->setStyle(style()); + currCont->setStyle(newStyle); currCont->setContinuation(nextCont); } } @@ -345,12 +376,6 @@ void RenderBlock::styleDidChange(StyleDifference diff, const RenderStyle* oldSty propagateStyleToAnonymousChildren(true); m_lineHeight = -1; - // Update pseudos for :before and :after now. - if (!isAnonymous() && document()->styleSheetCollection()->usesBeforeAfterRules() && canHaveGeneratedChildren()) { - updateBeforeAfterContent(BEFORE); - updateBeforeAfterContent(AFTER); - } - // After our style changed, if we lose our ability to propagate floats into next sibling // blocks, then we need to find the top most parent containing that overhanging float and // then mark its descendants with floats for layout and clear all floats from its next @@ -380,14 +405,10 @@ void RenderBlock::styleDidChange(StyleDifference diff, const RenderStyle* oldSty parentBlock->markAllDescendantsWithFloatsForLayout(); parentBlock->markSiblingsWithFloatsForLayout(); } -} - -void RenderBlock::updateBeforeAfterContent(PseudoId pseudoId) -{ - // If this is an anonymous wrapper, then the parent applies its own pseudo-element style to it. - if (parent() && parent()->createsAnonymousWrapper()) - return; - children()->updateBeforeAfterContent(this, pseudoId); + + // It's possible for our border/padding to change, but for the overall logical width of the block to + // end up being the same. We keep track of this change so in layoutBlock, we can know to set relayoutChildren=true. + m_hasBorderOrPaddingLogicalWidthChanged = oldStyle && diff == StyleDifferenceLayout && needsLayout() && borderOrPaddingLogicalWidthChanged(oldStyle, newStyle); } RenderBlock* RenderBlock::continuationBefore(RenderObject* beforeChild) @@ -566,7 +587,7 @@ RenderBlock* RenderBlock::clone() const // generated content added yet. cloneBlock->setChildrenInline(cloneBlock->firstChild() ? cloneBlock->firstChild()->isInline() : childrenInline()); } - cloneBlock->setInRenderFlowThread(inRenderFlowThread()); + cloneBlock->setFlowThreadState(flowThreadState()); return cloneBlock; } @@ -601,15 +622,9 @@ void RenderBlock::splitBlocks(RenderBlock* fromBlock, RenderBlock* toBlock, RenderBoxModelObject* curr = toRenderBoxModelObject(parent()); RenderBoxModelObject* currChild = this; RenderObject* currChildNextSibling = currChild->nextSibling(); - bool documentUsesBeforeAfterRules = document()->styleSheetCollection()->usesBeforeAfterRules(); - // Note: |this| can be destroyed inside this loop if it is an empty anonymous - // block and we try to call updateBeforeAfterContent inside which removes the - // generated content and additionally cleans up |this| empty anonymous block. - // See RenderBlock::removeChild(). DO NOT reference any local variables to |this| - // after this point. while (curr && curr != fromBlock) { - ASSERT(curr->isRenderBlock()); + ASSERT_WITH_SECURITY_IMPLICATION(curr->isRenderBlock()); RenderBlock* blockCurr = toRenderBlock(curr); @@ -630,16 +645,6 @@ void RenderBlock::splitBlocks(RenderBlock* fromBlock, RenderBlock* toBlock, cloneBlock->setContinuation(oldCont); } - // Someone may have indirectly caused a <q> to split. When this happens, the :after content - // has to move into the inline continuation. Call updateBeforeAfterContent to ensure that the inline's :after - // content gets properly destroyed. - bool isLastChild = (currChildNextSibling == blockCurr->lastChild()); - if (documentUsesBeforeAfterRules) - blockCurr->children()->updateBeforeAfterContent(blockCurr, AFTER); - if (isLastChild && currChildNextSibling != blockCurr->lastChild()) - currChildNextSibling = 0; // We destroyed the last child, so now we need to update - // the value of currChildNextSibling. - // Now we need to take all of the children starting from the first child // *after* currChild and append them all to the clone. blockCurr->moveChildrenTo(cloneBlock, currChildNextSibling, 0, true); @@ -798,10 +803,6 @@ RenderBlock* RenderBlock::columnsBlockForSpanningElement(RenderObject* newChild) void RenderBlock::addChildIgnoringAnonymousColumnBlocks(RenderObject* newChild, RenderObject* beforeChild) { - // Make sure we don't append things after :after-generated content if we have it. - if (!beforeChild) - beforeChild = afterPseudoElementRenderer(); - if (beforeChild && beforeChild->parent() != this) { RenderObject* beforeChildContainer = beforeChild->parent(); while (beforeChildContainer->parent() != this) @@ -857,47 +858,33 @@ void RenderBlock::addChildIgnoringAnonymousColumnBlocks(RenderObject* newChild, beforeChild = beforeChild->nextSibling(); // Check for a spanning element in columns. - RenderBlock* columnsBlockAncestor = columnsBlockForSpanningElement(newChild); - if (columnsBlockAncestor) { - // We are placing a column-span element inside a block. - RenderBlock* newBox = createAnonymousColumnSpanBlock(); + if (gColumnFlowSplitEnabled) { + RenderBlock* columnsBlockAncestor = columnsBlockForSpanningElement(newChild); + if (columnsBlockAncestor) { + TemporaryChange<bool> columnFlowSplitEnabled(gColumnFlowSplitEnabled, false); + // We are placing a column-span element inside a block. + RenderBlock* newBox = createAnonymousColumnSpanBlock(); - if (columnsBlockAncestor != this) { - // We are nested inside a multi-column element and are being split by the span. We have to break up - // our block into continuations. - RenderBoxModelObject* oldContinuation = continuation(); - - // When we split an anonymous block, there's no need to do any continuation hookup, - // since we haven't actually split a real element. - if (!isAnonymousBlock()) - setContinuation(newBox); - - // Someone may have put a <p> inside a <q>, causing a split. When this happens, the :after content - // has to move into the inline continuation. Call updateBeforeAfterContent to ensure that our :after - // content gets properly destroyed. - bool isFirstChild = (beforeChild == firstChild()); - bool isLastChild = (beforeChild == lastChild()); - if (document()->styleSheetCollection()->usesBeforeAfterRules()) - children()->updateBeforeAfterContent(this, AFTER); - if (isLastChild && beforeChild != lastChild()) { - // We destroyed the last child, so now we need to update our insertion - // point to be 0. It's just a straight append now. - beforeChild = 0; - } else if (isFirstChild && beforeChild != firstChild()) { - // If beforeChild was the last anonymous block that collapsed, - // then we need to update its value. - beforeChild = firstChild(); + if (columnsBlockAncestor != this && !isRenderFlowThread()) { + // We are nested inside a multi-column element and are being split by the span. We have to break up + // our block into continuations. + RenderBoxModelObject* oldContinuation = continuation(); + + // When we split an anonymous block, there's no need to do any continuation hookup, + // since we haven't actually split a real element. + if (!isAnonymousBlock()) + setContinuation(newBox); + + splitFlow(beforeChild, newBox, newChild, oldContinuation); + return; } - splitFlow(beforeChild, newBox, newChild, oldContinuation); + // We have to perform a split of this block's children. This involves creating an anonymous block box to hold + // the column-spanning |newChild|. We take all of the children from before |newChild| and put them into + // one anonymous columns block, and all of the children after |newChild| go into another anonymous block. + makeChildrenAnonymousColumnBlocks(beforeChild, newBox, newChild); return; } - - // We have to perform a split of this block's children. This involves creating an anonymous block box to hold - // the column-spanning |newChild|. We take all of the children from before |newChild| and put them into - // one anonymous columns block, and all of the children after |newChild| go into another anonymous block. - makeChildrenAnonymousColumnBlocks(beforeChild, newBox, newChild); - return; } bool madeBoxesNonInline = false; @@ -938,7 +925,7 @@ void RenderBlock::addChildIgnoringAnonymousColumnBlocks(RenderObject* newChild, RenderBox::addChild(newChild, beforeChild); // Handle placement of run-ins. - placeRunInIfNeeded(newChild, DoNotPlaceGeneratedRunIn); + placeRunInIfNeeded(newChild); if (madeBoxesNonInline && parent() && isAnonymousBlock() && parent()->isRenderBlock()) toRenderBlock(parent())->removeLeftoverAnonymousBlock(this); @@ -1013,11 +1000,12 @@ void RenderBlock::deleteLineBoxTree() } } m_lineBoxes.deleteLineBoxTree(renderArena()); - if (UNLIKELY(AXObjectCache::accessibilityEnabled())) - document()->axObjectCache()->recomputeIsIgnored(this); + + if (AXObjectCache* cache = document()->existingAXObjectCache()) + cache->recomputeIsIgnored(this); } -RootInlineBox* RenderBlock::createRootInlineBox() +RootInlineBox* RenderBlock::createRootInlineBox() { return new (renderArena()) RootInlineBox(this); } @@ -1027,8 +1015,10 @@ RootInlineBox* RenderBlock::createAndAppendRootInlineBox() RootInlineBox* rootBox = createRootInlineBox(); m_lineBoxes.appendLineBox(rootBox); - if (UNLIKELY(AXObjectCache::accessibilityEnabled()) && m_lineBoxes.firstLineBox() == rootBox) - document()->axObjectCache()->recomputeIsIgnored(this); + if (UNLIKELY(AXObjectCache::accessibilityEnabled()) && m_lineBoxes.firstLineBox() == rootBox) { + if (AXObjectCache* cache = document()->existingAXObjectCache()) + cache->recomputeIsIgnored(this); + } return rootBox; } @@ -1120,12 +1110,16 @@ void RenderBlock::removeLeftoverAnonymousBlock(RenderBlock* child) if (child->nextSibling()) child->nextSibling()->setPreviousSibling(child->previousSibling()); } + + child->children()->setFirstChild(0); + child->m_next = 0; + + // Remove all the information in the flow thread associated with the leftover anonymous block. + child->removeFromRenderFlowThread(); + child->setParent(0); child->setPreviousSibling(0); child->setNextSibling(0); - - child->children()->setFirstChild(0); - child->m_next = 0; child->destroy(); } @@ -1161,7 +1155,7 @@ void RenderBlock::collapseAnonymousBoxChild(RenderBlock* parent, RenderObject* c parent->setChildrenInline(child->childrenInline()); RenderObject* nextSibling = child->nextSibling(); - RenderFlowThread* childFlowThread = child->enclosingRenderFlowThread(); + RenderFlowThread* childFlowThread = child->flowThreadContainingBlock(); CurrentRenderFlowThreadMaintainer flowThreadMaintainer(childFlowThread); RenderBlock* anonBlock = toRenderBlock(parent->children()->removeChildNode(parent, child, child->hasLayer())); @@ -1173,6 +1167,46 @@ void RenderBlock::collapseAnonymousBoxChild(RenderBlock* parent, RenderObject* c anonBlock->destroy(); } +void RenderBlock::moveAllChildrenIncludingFloatsTo(RenderBlock* toBlock, bool fullRemoveInsert) +{ + moveAllChildrenTo(toBlock, fullRemoveInsert); + + // When a portion of the render tree is being detached, anonymous blocks + // will be combined as their children are deleted. In this process, the + // anonymous block later in the tree is merged into the one preceeding it. + // It can happen that the later block (this) contains floats that the + // previous block (toBlock) did not contain, and thus are not in the + // floating objects list for toBlock. This can result in toBlock containing + // floats that are not in it's floating objects list, but are in the + // floating objects lists of siblings and parents. This can cause problems + // when the float itself is deleted, since the deletion code assumes that + // if a float is not in it's containing block's floating objects list, it + // isn't in any floating objects list. In order to preserve this condition + // (removing it has serious performance implications), we need to copy the + // floating objects from the old block (this) to the new block (toBlock). + // The float's metrics will likely all be wrong, but since toBlock is + // already marked for layout, this will get fixed before anything gets + // displayed. + // See bug https://bugs.webkit.org/show_bug.cgi?id=115566 + if (m_floatingObjects) { + if (!toBlock->m_floatingObjects) + toBlock->createFloatingObjects(); + + const FloatingObjectSet& fromFloatingObjectSet = m_floatingObjects->set(); + FloatingObjectSetIterator end = fromFloatingObjectSet.end(); + + for (FloatingObjectSetIterator it = fromFloatingObjectSet.begin(); it != end; ++it) { + FloatingObject* floatingObject = *it; + + // Don't insert the object again if it's already in the list + if (toBlock->containsFloat(floatingObject->renderer())) + continue; + + toBlock->m_floatingObjects->add(floatingObject->clone()); + } + } +} + void RenderBlock::removeChild(RenderObject* oldChild) { // No need to waste time in merging or removing empty anonymous blocks. @@ -1182,6 +1216,9 @@ void RenderBlock::removeChild(RenderObject* oldChild) return; } + // This protects against column split flows when anonymous blocks are getting merged. + TemporaryChange<bool> columnFlowSplitEnabled(gColumnFlowSplitEnabled, false); + // If this child is a block, and if our previous and next siblings are // both anonymous blocks with inline content, then we can go ahead and // fold the inline content back together. @@ -1222,7 +1259,7 @@ void RenderBlock::removeChild(RenderObject* oldChild) } else { // Take all the children out of the |next| block and put them in // the |prev| block. - nextBlock->moveAllChildrenTo(prevBlock, nextBlock->hasLayer() || prevBlock->hasLayer()); + nextBlock->moveAllChildrenIncludingFloatsTo(prevBlock, nextBlock->hasLayer() || prevBlock->hasLayer()); // Delete the now-empty block's lines and nuke it. nextBlock->deleteLineBoxTree(); @@ -1353,6 +1390,7 @@ void RenderBlock::finishDelayUpdateScrollInfo() RenderBlock* block = *it; if (block->hasOverflowClip()) { block->layer()->updateScrollInfoAfterLayout(); + block->clearLayoutOverflow(); } } } @@ -1391,69 +1429,118 @@ void RenderBlock::layout() // It's safe to check for control clip here, since controls can never be table cells. // If we have a lightweight clip, there can never be any overflow from children. - if (hasControlClip() && m_overflow) + if (hasControlClip() && m_overflow && !gDelayUpdateScrollInfo) clearLayoutOverflow(); + + invalidateBackgroundObscurationStatus(); } -#if ENABLE(CSS_EXCLUSIONS) -void RenderBlock::updateExclusionShapeInsideInfoAfterStyleChange(const ExclusionShapeValue* shapeInside, const ExclusionShapeValue* oldShapeInside) +#if ENABLE(CSS_SHAPES) +void RenderBlock::updateShapeInsideInfoAfterStyleChange(const ShapeValue* shapeInside, const ShapeValue* oldShapeInside) { // FIXME: A future optimization would do a deep comparison for equality. if (shapeInside == oldShapeInside) return; if (shapeInside) { - ExclusionShapeInsideInfo* exclusionShapeInsideInfo = ExclusionShapeInsideInfo::ensureExclusionShapeInsideInfoForRenderBlock(this); - exclusionShapeInsideInfo->dirtyShapeSize(); - } else - ExclusionShapeInsideInfo::removeExclusionShapeInsideInfoForRenderBlock(this); + ShapeInsideInfo* shapeInsideInfo = ensureShapeInsideInfo(); + shapeInsideInfo->dirtyShapeSize(); + } else { + setShapeInsideInfo(nullptr); + markShapeInsideDescendantsForLayout(); + } +} + +void RenderBlock::markShapeInsideDescendantsForLayout() +{ + if (!everHadLayout()) + return; + if (childrenInline()) { + setNeedsLayout(true); + return; + } + for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { + if (!child->isRenderBlock()) + continue; + RenderBlock* childBlock = toRenderBlock(child); + childBlock->markShapeInsideDescendantsForLayout(); + } } #endif -void RenderBlock::updateRegionsAndExclusionsLogicalSize() +static inline bool shapeInfoRequiresRelayout(const RenderBlock* block) { -#if ENABLE(CSS_EXCLUSIONS) - if (!inRenderFlowThread() && !exclusionShapeInsideInfo()) +#if !ENABLE(CSS_SHAPES) + return false; #else - if (!inRenderFlowThread()) + ShapeInsideInfo* info = block->shapeInsideInfo(); + if (info) + info->setNeedsLayout(info->shapeSizeDirty()); + else + info = block->layoutShapeInsideInfo(); + return info && info->needsLayout(); #endif - return; +} + +bool RenderBlock::updateRegionsAndShapesBeforeChildLayout(RenderFlowThread* flowThread) +{ +#if ENABLE(CSS_SHAPES) + if (!flowThread && !shapeInsideInfo()) +#else + if (!flowThread) +#endif + return shapeInfoRequiresRelayout(this); LayoutUnit oldHeight = logicalHeight(); LayoutUnit oldTop = logicalTop(); // Compute the maximum logical height content may cause this block to expand to // FIXME: These should eventually use the const computeLogicalHeight rather than updateLogicalHeight - setLogicalHeight(LayoutUnit::max() / 2); + setLogicalHeight(RenderFlowThread::maxLogicalHeight()); updateLogicalHeight(); -#if ENABLE(CSS_EXCLUSIONS) - computeExclusionShapeSize(); +#if ENABLE(CSS_SHAPES) + computeShapeSize(); #endif // Set our start and end regions. No regions above or below us will be considered by our children. They are // effectively clamped to our region range. - computeRegionRangeForBlock(); + computeRegionRangeForBlock(flowThread); setLogicalHeight(oldHeight); setLogicalTop(oldTop); + + return shapeInfoRequiresRelayout(this); } -#if ENABLE(CSS_EXCLUSIONS) -void RenderBlock::computeExclusionShapeSize() +#if ENABLE(CSS_SHAPES) +void RenderBlock::computeShapeSize() { - ExclusionShapeInsideInfo* exclusionShapeInsideInfo = this->exclusionShapeInsideInfo(); - if (exclusionShapeInsideInfo) { + ShapeInsideInfo* shapeInsideInfo = this->shapeInsideInfo(); + if (shapeInsideInfo) { bool percentageLogicalHeightResolvable = percentageLogicalHeightIsResolvableFromBlock(this, false); - exclusionShapeInsideInfo->computeShapeSize(logicalWidth(), percentageLogicalHeightResolvable ? logicalHeight() : LayoutUnit()); + shapeInsideInfo->setShapeSize(logicalWidth(), percentageLogicalHeightResolvable ? logicalHeight() : LayoutUnit()); } } #endif -void RenderBlock::computeRegionRangeForBlock() +void RenderBlock::updateRegionsAndShapesAfterChildLayout(RenderFlowThread* flowThread, bool heightChanged) +{ +#if ENABLE(CSS_SHAPES) + // A previous sibling has changed dimension, so we need to relayout the shape with the content + ShapeInsideInfo* shapeInsideInfo = layoutShapeInsideInfo(); + if (heightChanged && shapeInsideInfo) + shapeInsideInfo->dirtyShapeSize(); +#else + UNUSED_PARAM(heightChanged); +#endif + computeRegionRangeForBlock(flowThread); +} + +void RenderBlock::computeRegionRangeForBlock(RenderFlowThread* flowThread) { - if (inRenderFlowThread()) - enclosingRenderFlowThread()->setRegionRangeForBox(this, offsetFromLogicalTopOfFirstPage()); + if (flowThread) + flowThread->setRegionRangeForBox(this, offsetFromLogicalTopOfFirstPage()); } bool RenderBlock::updateLogicalWidthAndColumnWidth() @@ -1464,7 +1551,10 @@ bool RenderBlock::updateLogicalWidthAndColumnWidth() updateLogicalWidth(); calcColumnWidth(); - return oldWidth != logicalWidth() || oldColumnWidth != desiredColumnWidth(); + bool hasBorderOrPaddingLogicalWidthChanged = m_hasBorderOrPaddingLogicalWidthChanged; + m_hasBorderOrPaddingLogicalWidthChanged = false; + + return oldWidth != logicalWidth() || oldColumnWidth != desiredColumnWidth() || hasBorderOrPaddingLogicalWidthChanged; } void RenderBlock::checkForPaginationLogicalHeightChange(LayoutUnit& pageLogicalHeight, bool& pageLogicalHeightChanged, bool& hasSpecifiedPageLogicalHeight) @@ -1475,17 +1565,18 @@ void RenderBlock::checkForPaginationLogicalHeightChange(LayoutUnit& pageLogicalH // We need to go ahead and set our explicit page height if one exists, so that we can // avoid doing two layout passes. updateLogicalHeight(); - LayoutUnit columnHeight = contentLogicalHeight(); + LayoutUnit columnHeight = isRenderView() ? view()->pageOrViewLogicalHeight() : contentLogicalHeight(); if (columnHeight > 0) { pageLogicalHeight = columnHeight; hasSpecifiedPageLogicalHeight = true; } setLogicalHeight(0); } - if (colInfo->columnHeight() != pageLogicalHeight && everHadLayout()) { - colInfo->setColumnHeight(pageLogicalHeight); + + if (colInfo->columnHeight() != pageLogicalHeight && everHadLayout()) pageLogicalHeightChanged = true; - } + + colInfo->setColumnHeight(pageLogicalHeight); if (!hasSpecifiedPageLogicalHeight && !pageLogicalHeight) colInfo->clearForcedBreaks(); @@ -1493,7 +1584,7 @@ void RenderBlock::checkForPaginationLogicalHeightChange(LayoutUnit& pageLogicalH colInfo->setPaginationUnit(paginationUnit()); } else if (isRenderFlowThread()) { pageLogicalHeight = 1; // This is just a hack to always make sure we have a page logical height. - pageLogicalHeightChanged = toRenderFlowThread(this)->pageLogicalHeightChanged(); + pageLogicalHeightChanged = toRenderFlowThread(this)->pageLogicalSizeChanged(); } } @@ -1512,11 +1603,11 @@ void RenderBlock::layoutBlock(bool relayoutChildren, LayoutUnit pageLogicalHeigh if (updateLogicalWidthAndColumnWidth()) relayoutChildren = true; - m_overflow.clear(); - clearFloats(); LayoutUnit previousHeight = logicalHeight(); + // FIXME: should this start out as borderAndPaddingLogicalHeight() + scrollbarLogicalHeight(), + // for consistency with other render classes? setLogicalHeight(0); bool pageLogicalHeightChanged = false; @@ -1527,12 +1618,12 @@ void RenderBlock::layoutBlock(bool relayoutChildren, LayoutUnit pageLogicalHeigh RenderStyle* styleToUse = style(); LayoutStateMaintainer statePusher(renderView, this, locationOffset(), hasColumns() || hasTransform() || hasReflection() || styleToUse->isFlippedBlocksWritingMode(), pageLogicalHeight, pageLogicalHeightChanged, columnInfo()); - if (inRenderFlowThread()) { - // Regions changing widths can force us to relayout our children. - if (logicalWidthChangedInRegions()) - relayoutChildren = true; - } - updateRegionsAndExclusionsLogicalSize(); + // Regions changing widths can force us to relayout our children. + RenderFlowThread* flowThread = flowThreadContainingBlock(); + if (logicalWidthChangedInRegions(flowThread)) + relayoutChildren = true; + if (updateRegionsAndShapesBeforeChildLayout(flowThread)) + relayoutChildren = true; // We use four values, maxTopPos, maxTopNeg, maxBottomPos, and maxBottomNeg, to track // our current maximal positive and negative margins. These values are used when we @@ -1548,8 +1639,8 @@ void RenderBlock::layoutBlock(bool relayoutChildren, LayoutUnit pageLogicalHeigh if (!isCell) { initMaxMarginValues(); - setMarginBeforeQuirk(styleToUse->marginBefore().quirk()); - setMarginAfterQuirk(styleToUse->marginAfter().quirk()); + setHasMarginBeforeQuirk(styleToUse->hasMarginBeforeQuirk()); + setHasMarginAfterQuirk(styleToUse->hasMarginAfterQuirk()); setPaginationStrut(0); } @@ -1564,16 +1655,22 @@ void RenderBlock::layoutBlock(bool relayoutChildren, LayoutUnit pageLogicalHeigh layoutBlockChildren(relayoutChildren, maxFloatLogicalBottom); // Expand our intrinsic height to encompass floats. - LayoutUnit toAdd = borderAfter() + paddingAfter() + scrollbarLogicalHeight(); + LayoutUnit toAdd = borderAndPaddingAfter() + scrollbarLogicalHeight(); if (lowestFloatLogicalBottom() > (logicalHeight() - toAdd) && expandsToEncloseOverhangingFloats()) setLogicalHeight(lowestFloatLogicalBottom() + toAdd); if (relayoutForPagination(hasSpecifiedPageLogicalHeight, pageLogicalHeight, statePusher)) return; - + // Calculate our new height. LayoutUnit oldHeight = logicalHeight(); LayoutUnit oldClientAfterEdge = clientLogicalBottom(); + + // Before updating the final size of the flow thread make sure a forced break is applied after the content. + // This ensures the size information is correctly computed for the last auto-height region receiving content. + if (isRenderFlowThread()) + toRenderFlowThread(this)->applyBreakAfterContent(oldClientAfterEdge); + updateLogicalHeight(); LayoutUnit newHeight = logicalHeight(); if (oldHeight != newHeight) { @@ -1589,18 +1686,21 @@ void RenderBlock::layoutBlock(bool relayoutChildren, LayoutUnit pageLogicalHeigh } } - if (previousHeight != newHeight) + bool heightChanged = (previousHeight != newHeight); + if (heightChanged) relayoutChildren = true; layoutPositionedObjects(relayoutChildren || isRoot()); - computeRegionRangeForBlock(); + updateRegionsAndShapesAfterChildLayout(flowThread, heightChanged); // Add overflow from children (unless we're multi-column, since in that case all our child overflow is clipped anyway). computeOverflow(oldClientAfterEdge); statePusher.pop(); + fitBorderToLinesIfNeeded(); + if (renderView->layoutState()->m_pageLogicalHeight) setPageLogicalOffset(renderView->layoutState()->pageLogicalOffset(this, logicalTop())); @@ -1652,7 +1752,7 @@ void RenderBlock::layoutBlock(bool relayoutChildren, LayoutUnit pageLogicalHeigh repaintRectangle(reflectedRect(repaintRect)); } } - + setNeedsLayout(false); } @@ -1676,6 +1776,8 @@ void RenderBlock::addOverflowFromChildren() void RenderBlock::computeOverflow(LayoutUnit oldClientAfterEdge, bool recomputeFloats) { + m_overflow.clear(); + // Add overflow from children. addOverflowFromChildren(); @@ -1696,16 +1798,39 @@ void RenderBlock::computeOverflow(LayoutUnit oldClientAfterEdge, bool recomputeF else rectToApply = LayoutRect(clientRect.x(), clientRect.y(), max<LayoutUnit>(0, oldClientAfterEdge - clientRect.x()), 1); addLayoutOverflow(rectToApply); + if (hasRenderOverflow()) + m_overflow->setLayoutClientAfterEdge(oldClientAfterEdge); } + // Allow our overflow to catch cases where the caret in an empty editable element with negative text indent needs to get painted. + LayoutUnit textIndent = textIndentOffset(); + if (textIndent < 0) { + LayoutRect clientRect(clientBoxRect()); + LayoutRect rectToApply = LayoutRect(clientRect.x() + min<LayoutUnit>(0, textIndent), clientRect.y(), clientRect.width() - min<LayoutUnit>(0, textIndent), clientRect.height()); + addVisualOverflow(rectToApply); + } + // Add visual overflow from box-shadow and border-image-outset. addVisualEffectOverflow(); // Add visual overflow from theme. addVisualOverflowFromTheme(); - if (isRenderFlowThread()) - enclosingRenderFlowThread()->computeOverflowStateForRegions(oldClientAfterEdge); + if (isRenderNamedFlowThread()) + toRenderNamedFlowThread(this)->computeOversetStateForRegions(oldClientAfterEdge); +} + +void RenderBlock::clearLayoutOverflow() +{ + if (!m_overflow) + return; + + if (visualOverflowRect() == borderBoxRect()) { + m_overflow.clear(); + return; + } + + m_overflow->setLayoutOverflow(borderBoxRect()); } void RenderBlock::addOverflowFromBlockChildren() @@ -1728,7 +1853,6 @@ void RenderBlock::addOverflowFromFloats() if (r->isDescendant()) addOverflowFromChild(r->m_renderer, IntSize(xPositionForFloatIncludingMargin(r), yPositionForFloatIncludingMargin(r))); } - return; } void RenderBlock::addOverflowFromPositionedObjects() @@ -1764,7 +1888,7 @@ void RenderBlock::addVisualOverflowFromTheme() bool RenderBlock::expandsToEncloseOverhangingFloats() const { - return isInlineBlockOrInlineTable() || isFloatingOrOutOfFlowPositioned() || hasOverflowClip() || (parent() && parent()->isDeprecatedFlexibleBox()) + return isInlineBlockOrInlineTable() || isFloatingOrOutOfFlowPositioned() || hasOverflowClip() || (parent() && parent()->isFlexibleBoxIncludingDeprecated()) || hasColumns() || isTableCell() || isTableCaption() || isFieldset() || isWritingModeRoot() || isRoot(); } @@ -1774,7 +1898,7 @@ void RenderBlock::adjustPositionedBlock(RenderBox* child, const MarginInfo& marg bool hasStaticBlockPosition = child->style()->hasStaticBlockPosition(isHorizontal); LayoutUnit logicalTop = logicalHeight(); - setStaticInlinePositionForChild(child, logicalTop, startOffsetForContent(logicalTop)); + updateStaticInlinePositionForChild(child, logicalTop); if (!marginInfo.canCollapseWithMarginBefore()) { // Positioned blocks don't collapse margins, so add the margin provided by @@ -1813,34 +1937,6 @@ void RenderBlock::adjustFloatingBlock(const MarginInfo& marginInfo) setLogicalHeight(logicalHeight() - marginOffset); } -bool RenderBlock::handleSpecialChild(RenderBox* child, const MarginInfo& marginInfo) -{ - // Handle in the given order - return handlePositionedChild(child, marginInfo) - || handleFloatingChild(child, marginInfo); -} - - -bool RenderBlock::handlePositionedChild(RenderBox* child, const MarginInfo& marginInfo) -{ - if (child->isOutOfFlowPositioned()) { - child->containingBlock()->insertPositionedObject(child); - adjustPositionedBlock(child, marginInfo); - return true; - } - return false; -} - -bool RenderBlock::handleFloatingChild(RenderBox* child, const MarginInfo& marginInfo) -{ - if (child->isFloating()) { - insertFloatingObject(child); - adjustFloatingBlock(marginInfo); - return true; - } - return false; -} - static void destroyRunIn(RenderBoxModelObject* runIn) { ASSERT(runIn->isRunIn()); @@ -1858,12 +1954,12 @@ static void destroyRunIn(RenderBoxModelObject* runIn) runIn->destroy(); } -void RenderBlock::placeRunInIfNeeded(RenderObject* newChild, PlaceGeneratedRunInFlag flag) +void RenderBlock::placeRunInIfNeeded(RenderObject* newChild) { - if (newChild->isRunIn() && (flag == PlaceGeneratedRunIn || !newChild->isBeforeOrAfterContent())) + if (newChild->isRunIn()) moveRunInUnderSiblingBlockIfNeeded(newChild); else if (RenderObject* prevSibling = newChild->previousSibling()) { - if (prevSibling->isRunIn() && (flag == PlaceGeneratedRunIn || !newChild->isBeforeOrAfterContent())) + if (prevSibling->isRunIn()) moveRunInUnderSiblingBlockIfNeeded(prevSibling); } } @@ -1871,31 +1967,18 @@ void RenderBlock::placeRunInIfNeeded(RenderObject* newChild, PlaceGeneratedRunIn RenderBoxModelObject* RenderBlock::createReplacementRunIn(RenderBoxModelObject* runIn) { ASSERT(runIn->isRunIn()); + ASSERT(runIn->node()); - // First we destroy any :before/:after content. It will be regenerated by the new run-in. - // Exception is if the run-in itself is generated. - if (runIn->style()->styleType() != BEFORE && runIn->style()->styleType() != AFTER) { - RenderObject* generatedContent; - if (runIn->getCachedPseudoStyle(BEFORE) && (generatedContent = runIn->beforePseudoElementRenderer())) - generatedContent->destroy(); - if (runIn->getCachedPseudoStyle(AFTER) && (generatedContent = runIn->afterPseudoElementRenderer())) - generatedContent->destroy(); - } - - bool newRunInShouldBeBlock = !runIn->isRenderBlock(); - Node* runInNode = runIn->node(); RenderBoxModelObject* newRunIn = 0; - if (newRunInShouldBeBlock) - newRunIn = new (renderArena()) RenderBlock(runInNode ? runInNode : document()); + if (!runIn->isRenderBlock()) + newRunIn = new (renderArena()) RenderBlock(runIn->node()); else - newRunIn = new (renderArena()) RenderInline(runInNode ? runInNode : document()); + newRunIn = new (renderArena()) RenderInline(toElement(runIn->node())); + + runIn->node()->setRenderer(newRunIn); newRunIn->setStyle(runIn->style()); - - runIn->moveAllChildrenTo(newRunIn, true); - // If the run-in had an element, we need to set the new renderer. - if (runInNode) - runInNode->setRenderer(newRunIn); + runIn->moveAllChildrenTo(newRunIn, true); return newRunIn; } @@ -1927,6 +2010,9 @@ void RenderBlock::moveRunInUnderSiblingBlockIfNeeded(RenderObject* runIn) if (!curr || !curr->isRenderBlock() || !curr->childrenInline()) return; + if (toRenderBlock(curr)->beingDestroyed()) + return; + // Per CSS3, "A run-in cannot run in to a block that already starts with a // run-in or that itself is a run-in". if (curr->isRunIn() || (curr->firstChild() && curr->firstChild()->isRunIn())) @@ -1991,6 +2077,13 @@ void RenderBlock::moveRunInToOriginalPosition(RenderObject* runIn) LayoutUnit RenderBlock::collapseMargins(RenderBox* child, MarginInfo& marginInfo) { + bool childDiscardMarginBefore = mustDiscardMarginBeforeForChild(child); + bool childDiscardMarginAfter = mustDiscardMarginAfterForChild(child); + bool childIsSelfCollapsing = child->isSelfCollapsingBlock(); + + // The child discards the before margin when the the after margin has discard in the case of a self collapsing block. + childDiscardMarginBefore = childDiscardMarginBefore || (childDiscardMarginAfter && childIsSelfCollapsing); + // Get the four margin values for the child and cache them. const MarginValues childMargins = marginValuesForChild(child); @@ -2000,84 +2093,106 @@ LayoutUnit RenderBlock::collapseMargins(RenderBox* child, MarginInfo& marginInfo // For self-collapsing blocks, collapse our bottom margins into our // top to get new posTop and negTop values. - if (child->isSelfCollapsingBlock()) { + if (childIsSelfCollapsing) { posTop = max(posTop, childMargins.positiveMarginAfter()); negTop = max(negTop, childMargins.negativeMarginAfter()); } // See if the top margin is quirky. We only care if this child has // margins that will collapse with us. - bool topQuirk = child->isMarginBeforeQuirk() || style()->marginBeforeCollapse() == MDISCARD; + bool topQuirk = hasMarginBeforeQuirk(child); if (marginInfo.canCollapseWithMarginBefore()) { - // This child is collapsing with the top of the - // block. If it has larger margin values, then we need to update - // our own maximal values. - if (!document()->inQuirksMode() || !marginInfo.quirkContainer() || !topQuirk) - setMaxMarginBeforeValues(max(posTop, maxPositiveMarginBefore()), max(negTop, maxNegativeMarginBefore())); - - // The minute any of the margins involved isn't a quirk, don't - // collapse it away, even if the margin is smaller (www.webreference.com - // has an example of this, a <dt> with 0.8em author-specified inside - // a <dl> inside a <td>. - if (!marginInfo.determinedMarginBeforeQuirk() && !topQuirk && (posTop - negTop)) { - setMarginBeforeQuirk(false); - marginInfo.setDeterminedMarginBeforeQuirk(true); - } - - if (!marginInfo.determinedMarginBeforeQuirk() && topQuirk && !marginBefore()) - // We have no top margin and our top child has a quirky margin. - // We will pick up this quirky margin and pass it through. - // This deals with the <td><div><p> case. - // Don't do this for a block that split two inlines though. You do - // still apply margins in this case. - setMarginBeforeQuirk(true); + if (!childDiscardMarginBefore && !marginInfo.discardMargin()) { + // This child is collapsing with the top of the + // block. If it has larger margin values, then we need to update + // our own maximal values. + if (!document()->inQuirksMode() || !marginInfo.quirkContainer() || !topQuirk) + setMaxMarginBeforeValues(max(posTop, maxPositiveMarginBefore()), max(negTop, maxNegativeMarginBefore())); + + // The minute any of the margins involved isn't a quirk, don't + // collapse it away, even if the margin is smaller (www.webreference.com + // has an example of this, a <dt> with 0.8em author-specified inside + // a <dl> inside a <td>. + if (!marginInfo.determinedMarginBeforeQuirk() && !topQuirk && (posTop - negTop)) { + setHasMarginBeforeQuirk(false); + marginInfo.setDeterminedMarginBeforeQuirk(true); + } + + if (!marginInfo.determinedMarginBeforeQuirk() && topQuirk && !marginBefore()) + // We have no top margin and our top child has a quirky margin. + // We will pick up this quirky margin and pass it through. + // This deals with the <td><div><p> case. + // Don't do this for a block that split two inlines though. You do + // still apply margins in this case. + setHasMarginBeforeQuirk(true); + } else + // The before margin of the container will also discard all the margins it is collapsing with. + setMustDiscardMarginBefore(); + } + + // Once we find a child with discardMarginBefore all the margins collapsing with us must also discard. + if (childDiscardMarginBefore) { + marginInfo.setDiscardMargin(true); + marginInfo.clearMargin(); } if (marginInfo.quirkContainer() && marginInfo.atBeforeSideOfBlock() && (posTop - negTop)) - marginInfo.setMarginBeforeQuirk(topQuirk); + marginInfo.setHasMarginBeforeQuirk(topQuirk); LayoutUnit beforeCollapseLogicalTop = logicalHeight(); LayoutUnit logicalTop = beforeCollapseLogicalTop; - if (child->isSelfCollapsingBlock()) { - // This child has no height. We need to compute our - // position before we collapse the child's margins together, - // so that we can get an accurate position for the zero-height block. - LayoutUnit collapsedBeforePos = max(marginInfo.positiveMargin(), childMargins.positiveMarginBefore()); - LayoutUnit collapsedBeforeNeg = max(marginInfo.negativeMargin(), childMargins.negativeMarginBefore()); - marginInfo.setMargin(collapsedBeforePos, collapsedBeforeNeg); - - // Now collapse the child's margins together, which means examining our - // bottom margin values as well. - marginInfo.setPositiveMarginIfLarger(childMargins.positiveMarginAfter()); - marginInfo.setNegativeMarginIfLarger(childMargins.negativeMarginAfter()); - - if (!marginInfo.canCollapseWithMarginBefore()) - // We need to make sure that the position of the self-collapsing block - // is correct, since it could have overflowing content - // that needs to be positioned correctly (e.g., a block that - // had a specified height of 0 but that actually had subcontent). - logicalTop = logicalHeight() + collapsedBeforePos - collapsedBeforeNeg; - } - else { - if (child->style()->marginBeforeCollapse() == MSEPARATE) { - setLogicalHeight(logicalHeight() + marginInfo.margin() + marginBeforeForChild(child)); - logicalTop = logicalHeight(); + if (childIsSelfCollapsing) { + // For a self collapsing block both the before and after margins get discarded. The block doesn't contribute anything to the height of the block. + // Also, the child's top position equals the logical height of the container. + if (!childDiscardMarginBefore && !marginInfo.discardMargin()) { + // This child has no height. We need to compute our + // position before we collapse the child's margins together, + // so that we can get an accurate position for the zero-height block. + LayoutUnit collapsedBeforePos = max(marginInfo.positiveMargin(), childMargins.positiveMarginBefore()); + LayoutUnit collapsedBeforeNeg = max(marginInfo.negativeMargin(), childMargins.negativeMarginBefore()); + marginInfo.setMargin(collapsedBeforePos, collapsedBeforeNeg); + + // Now collapse the child's margins together, which means examining our + // bottom margin values as well. + marginInfo.setPositiveMarginIfLarger(childMargins.positiveMarginAfter()); + marginInfo.setNegativeMarginIfLarger(childMargins.negativeMarginAfter()); + + if (!marginInfo.canCollapseWithMarginBefore()) + // We need to make sure that the position of the self-collapsing block + // is correct, since it could have overflowing content + // that needs to be positioned correctly (e.g., a block that + // had a specified height of 0 but that actually had subcontent). + logicalTop = logicalHeight() + collapsedBeforePos - collapsedBeforeNeg; } - else if (!marginInfo.atBeforeSideOfBlock() || - (!marginInfo.canCollapseMarginBeforeWithChildren() - && (!document()->inQuirksMode() || !marginInfo.quirkContainer() || !marginInfo.marginBeforeQuirk()))) { + } else { + if (mustSeparateMarginBeforeForChild(child)) { + ASSERT(!marginInfo.discardMargin() || (marginInfo.discardMargin() && !marginInfo.margin())); + // If we are at the before side of the block and we collapse, ignore the computed margin + // and just add the child margin to the container height. This will correctly position + // the child inside the container. + LayoutUnit separateMargin = !marginInfo.canCollapseWithMarginBefore() ? marginInfo.margin() : LayoutUnit(0); + setLogicalHeight(logicalHeight() + separateMargin + marginBeforeForChild(child)); + logicalTop = logicalHeight(); + } else if (!marginInfo.discardMargin() && (!marginInfo.atBeforeSideOfBlock() + || (!marginInfo.canCollapseMarginBeforeWithChildren() + && (!document()->inQuirksMode() || !marginInfo.quirkContainer() || !marginInfo.hasMarginBeforeQuirk())))) { // We're collapsing with a previous sibling's margins and not // with the top of the block. setLogicalHeight(logicalHeight() + max(marginInfo.positiveMargin(), posTop) - max(marginInfo.negativeMargin(), negTop)); logicalTop = logicalHeight(); } - marginInfo.setPositiveMargin(childMargins.positiveMarginAfter()); - marginInfo.setNegativeMargin(childMargins.negativeMarginAfter()); + marginInfo.setDiscardMargin(childDiscardMarginAfter); + + if (!marginInfo.discardMargin()) { + marginInfo.setPositiveMargin(childMargins.positiveMarginAfter()); + marginInfo.setNegativeMargin(childMargins.negativeMarginAfter()); + } else + marginInfo.clearMargin(); if (marginInfo.margin()) - marginInfo.setMarginAfterQuirk(child->isMarginAfterQuirk() || style()->marginAfterCollapse() == MDISCARD); + marginInfo.setHasMarginAfterQuirk(hasMarginAfterQuirk(child)); } // If margins would pull us past the top of the next page, then we need to pull back and pretend like the margins @@ -2093,12 +2208,15 @@ LayoutUnit RenderBlock::collapseMargins(RenderBox* child, MarginInfo& marginInfo // If we have collapsed into a previous sibling and so reduced the height of the parent, ensure any floats that now // overhang from the previous sibling are added to our parent. If the child's previous sibling itself is a float the child will avoid // or clear it anyway, so don't worry about any floating children it may contain. + LayoutUnit oldLogicalHeight = logicalHeight(); + setLogicalHeight(logicalTop); RenderObject* prev = child->previousSibling(); if (prev && prev->isBlockFlow() && !prev->isFloatingOrOutOfFlowPositioned()) { RenderBlock* block = toRenderBlock(prev); if (block->containsFloats() && !block->avoidsFloats() && (block->logicalTop() + block->lowestFloatLogicalBottom()) > logicalTop) addOverhangingFloats(block, false); } + setLogicalHeight(oldLogicalHeight); return logicalTop; } @@ -2110,12 +2228,19 @@ LayoutUnit RenderBlock::clearFloatsIfNeeded(RenderBox* child, MarginInfo& margin return yPos; if (child->isSelfCollapsingBlock()) { + bool childDiscardMargin = mustDiscardMarginBeforeForChild(child) || mustDiscardMarginAfterForChild(child); + // For self-collapsing blocks that clear, they can still collapse their // margins with following siblings. Reset the current margins to represent // the self-collapsing block's margins only. - MarginValues childMargins = marginValuesForChild(child); - marginInfo.setPositiveMargin(max(childMargins.positiveMarginBefore(), childMargins.positiveMarginAfter())); - marginInfo.setNegativeMargin(max(childMargins.negativeMarginBefore(), childMargins.negativeMarginAfter())); + // If DISCARD is specified for -webkit-margin-collapse, reset the margin values. + if (!childDiscardMargin) { + MarginValues childMargins = marginValuesForChild(child); + marginInfo.setPositiveMargin(max(childMargins.positiveMarginBefore(), childMargins.positiveMarginAfter())); + marginInfo.setNegativeMargin(max(childMargins.negativeMarginBefore(), childMargins.negativeMarginAfter())); + } else + marginInfo.clearMargin(); + marginInfo.setDiscardMargin(childDiscardMargin); // CSS2.1 states: // "If the top and bottom margins of an element with clearance are adjoining, its margins collapse with @@ -2134,7 +2259,8 @@ LayoutUnit RenderBlock::clearFloatsIfNeeded(RenderBox* child, MarginInfo& margin // Move the top of the child box to the bottom of the float ignoring the child's top margin. LayoutUnit collapsedMargin = collapsedMarginBeforeForChild(child); setLogicalHeight(child->logicalTop() - collapsedMargin); - heightIncrease -= collapsedMargin; + // A negative collapsed margin-top value cancels itself out as it has already been factored into |yPos| above. + heightIncrease -= max(LayoutUnit(), collapsedMargin); } else // Increase our height by the amount we had to clear. setLogicalHeight(logicalHeight() + heightIncrease); @@ -2147,6 +2273,9 @@ LayoutUnit RenderBlock::clearFloatsIfNeeded(RenderBox* child, MarginInfo& margin // margins involved. setMaxMarginBeforeValues(oldTopPosMargin, oldTopNegMargin); marginInfo.setAtBeforeSideOfBlock(false); + + // In case the child discarded the before margin of the block we need to reset the mustDiscardMarginBefore flag to the initial value. + setMustDiscardMarginBefore(style()->marginBeforeCollapse() == MDISCARD); } LayoutUnit logicalTop = yPos + heightIncrease; @@ -2158,12 +2287,22 @@ LayoutUnit RenderBlock::clearFloatsIfNeeded(RenderBox* child, MarginInfo& margin return logicalTop; } -void RenderBlock::marginBeforeEstimateForChild(RenderBox* child, LayoutUnit& positiveMarginBefore, LayoutUnit& negativeMarginBefore) const +void RenderBlock::marginBeforeEstimateForChild(RenderBox* child, LayoutUnit& positiveMarginBefore, LayoutUnit& negativeMarginBefore, bool& discardMarginBefore) const { - // FIXME: We should deal with the margin-collapse-* style extensions that prevent collapsing and that discard margins. // Give up if in quirks mode and we're a body/table cell and the top margin of the child box is quirky. - if (document()->inQuirksMode() && child->isMarginBeforeQuirk() && (isTableCell() || isBody())) + // Give up if the child specified -webkit-margin-collapse: separate that prevents collapsing. + // FIXME: Use writing mode independent accessor for marginBeforeCollapse. + if ((document()->inQuirksMode() && hasMarginAfterQuirk(child) && (isTableCell() || isBody())) || child->style()->marginBeforeCollapse() == MSEPARATE) + return; + + // The margins are discarded by a child that specified -webkit-margin-collapse: discard. + // FIXME: Use writing mode independent accessor for marginBeforeCollapse. + if (child->style()->marginBeforeCollapse() == MDISCARD) { + positiveMarginBefore = 0; + negativeMarginBefore = 0; + discardMarginBefore = true; return; + } LayoutUnit beforeChildMargin = marginBeforeForChild(child); positiveMarginBefore = max(positiveMarginBefore, beforeChildMargin); @@ -2176,7 +2315,7 @@ void RenderBlock::marginBeforeEstimateForChild(RenderBox* child, LayoutUnit& pos if (childBlock->childrenInline() || childBlock->isWritingModeRoot()) return; - MarginInfo childMarginInfo(childBlock, childBlock->borderBefore() + childBlock->paddingBefore(), childBlock->borderAfter() + childBlock->paddingAfter()); + MarginInfo childMarginInfo(childBlock, childBlock->borderAndPaddingBefore(), childBlock->borderAndPaddingAfter()); if (!childMarginInfo.canCollapseMarginBeforeWithChildren()) return; @@ -2193,12 +2332,15 @@ void RenderBlock::marginBeforeEstimateForChild(RenderBox* child, LayoutUnit& pos // Make sure to update the block margins now for the grandchild box so that we're looking at current values. if (grandchildBox->needsLayout()) { grandchildBox->computeAndSetBlockDirectionMargins(this); - grandchildBox->setMarginBeforeQuirk(grandchildBox->style()->marginBefore().quirk()); - grandchildBox->setMarginAfterQuirk(grandchildBox->style()->marginAfter().quirk()); + if (grandchildBox->isRenderBlock()) { + RenderBlock* grandchildBlock = toRenderBlock(grandchildBox); + grandchildBlock->setHasMarginBeforeQuirk(grandchildBox->style()->hasMarginBeforeQuirk()); + grandchildBlock->setHasMarginAfterQuirk(grandchildBox->style()->hasMarginAfterQuirk()); + } } // Collapse the margin of the grandchild box with our own to produce an estimate. - childBlock->marginBeforeEstimateForChild(grandchildBox, positiveMarginBefore, negativeMarginBefore); + childBlock->marginBeforeEstimateForChild(grandchildBox, positiveMarginBefore, negativeMarginBefore, discardMarginBefore); } LayoutUnit RenderBlock::estimateLogicalTopPosition(RenderBox* child, const MarginInfo& marginInfo, LayoutUnit& estimateWithoutPagination) @@ -2209,19 +2351,22 @@ LayoutUnit RenderBlock::estimateLogicalTopPosition(RenderBox* child, const Margi if (!marginInfo.canCollapseWithMarginBefore()) { LayoutUnit positiveMarginBefore = 0; LayoutUnit negativeMarginBefore = 0; + bool discardMarginBefore = false; if (child->selfNeedsLayout()) { // Try to do a basic estimation of how the collapse is going to go. - marginBeforeEstimateForChild(child, positiveMarginBefore, negativeMarginBefore); + marginBeforeEstimateForChild(child, positiveMarginBefore, negativeMarginBefore, discardMarginBefore); } else { // Use the cached collapsed margin values from a previous layout. Most of the time they // will be right. MarginValues marginValues = marginValuesForChild(child); positiveMarginBefore = max(positiveMarginBefore, marginValues.positiveMarginBefore()); negativeMarginBefore = max(negativeMarginBefore, marginValues.negativeMarginBefore()); + discardMarginBefore = mustDiscardMarginBeforeForChild(child); } // Collapse the result with our current margins. - logicalTopEstimate += max(marginInfo.positiveMargin(), positiveMarginBefore) - max(marginInfo.negativeMargin(), negativeMarginBefore); + if (!discardMarginBefore) + logicalTopEstimate += max(marginInfo.positiveMargin(), positiveMarginBefore) - max(marginInfo.negativeMargin(), negativeMarginBefore); } // Adjust logicalTopEstimate down to the next page if the margins are so large that we don't fit on the current @@ -2249,10 +2394,9 @@ LayoutUnit RenderBlock::estimateLogicalTopPosition(RenderBox* child, const Margi return logicalTopEstimate; } -LayoutUnit RenderBlock::computeStartPositionDeltaForChildAvoidingFloats(const RenderBox* child, LayoutUnit childMarginStart, - RenderRegion* region, LayoutUnit offsetFromLogicalTopOfFirstPage) +LayoutUnit RenderBlock::computeStartPositionDeltaForChildAvoidingFloats(const RenderBox* child, LayoutUnit childMarginStart, RenderRegion* region) { - LayoutUnit startPosition = startOffsetForContent(region, offsetFromLogicalTopOfFirstPage); + LayoutUnit startPosition = startOffsetForContent(region); // Add in our start margin. LayoutUnit oldPosition = startPosition + childMarginStart; @@ -2260,9 +2404,9 @@ LayoutUnit RenderBlock::computeStartPositionDeltaForChildAvoidingFloats(const Re LayoutUnit blockOffset = logicalTopForChild(child); if (region) - blockOffset = max(blockOffset, blockOffset + (region->logicalTopForFlowThreadContent() - offsetFromLogicalTopOfFirstPage)); + blockOffset = max(blockOffset, blockOffset + (region->logicalTopForFlowThreadContent() - offsetFromLogicalTopOfFirstPage())); - LayoutUnit startOff = startOffsetForLine(blockOffset, false, region, offsetFromLogicalTopOfFirstPage, logicalHeightForChild(child)); + LayoutUnit startOff = startOffsetForLine(blockOffset, false, region, logicalHeightForChild(child)); if (style()->textAlign() != WEBKIT_CENTER && !child->style()->marginStartUsing(style()).isAuto()) { if (childMarginStart < 0) @@ -2274,7 +2418,7 @@ LayoutUnit RenderBlock::computeStartPositionDeltaForChildAvoidingFloats(const Re return newPosition - oldPosition; } -void RenderBlock::determineLogicalLeftPositionForChild(RenderBox* child) +void RenderBlock::determineLogicalLeftPositionForChild(RenderBox* child, ApplyLayoutDeltaMode applyDelta) { LayoutUnit startPosition = borderStart() + paddingStart(); if (style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft()) @@ -2287,27 +2431,34 @@ void RenderBlock::determineLogicalLeftPositionForChild(RenderBox* child) // Some objects (e.g., tables, horizontal rules, overflow:auto blocks) avoid floats. They need // to shift over as necessary to dodge any floats that might get in the way. - if (child->avoidsFloats() && containsFloats() && !inRenderFlowThread()) + if (child->avoidsFloats() && containsFloats() && !flowThreadContainingBlock()) newPosition += computeStartPositionDeltaForChildAvoidingFloats(child, marginStartForChild(child)); - setLogicalLeftForChild(child, style()->isLeftToRightDirection() ? newPosition : totalAvailableLogicalWidth - newPosition - logicalWidthForChild(child), ApplyLayoutDelta); + setLogicalLeftForChild(child, style()->isLeftToRightDirection() ? newPosition : totalAvailableLogicalWidth - newPosition - logicalWidthForChild(child), applyDelta); } void RenderBlock::setCollapsedBottomMargin(const MarginInfo& marginInfo) { if (marginInfo.canCollapseWithMarginAfter() && !marginInfo.canCollapseWithMarginBefore()) { + // Update the after side margin of the container to discard if the after margin of the last child also discards and we collapse with it. + // Don't update the max margin values because we won't need them anyway. + if (marginInfo.discardMargin()) { + setMustDiscardMarginAfter(); + return; + } + // Update our max pos/neg bottom margins, since we collapsed our bottom margins // with our children. setMaxMarginAfterValues(max(maxPositiveMarginAfter(), marginInfo.positiveMargin()), max(maxNegativeMarginAfter(), marginInfo.negativeMargin())); - if (!marginInfo.marginAfterQuirk()) - setMarginAfterQuirk(false); + if (!marginInfo.hasMarginAfterQuirk()) + setHasMarginAfterQuirk(false); - if (marginInfo.marginAfterQuirk() && marginAfter() == 0) + if (marginInfo.hasMarginAfterQuirk() && !marginAfter()) // We have no bottom margin and our last child has a quirky margin. // We will pick up this quirky margin and pass it through. // This deals with the <td><div><p> case. - setMarginAfterQuirk(true); + setHasMarginAfterQuirk(true); } } @@ -2316,11 +2467,8 @@ void RenderBlock::handleAfterSideOfBlock(LayoutUnit beforeSide, LayoutUnit after marginInfo.setAtAfterSideOfBlock(true); // If we can't collapse with children then go ahead and add in the bottom margin. - // Don't do this for ordinary anonymous blocks as only the enclosing box should add in - // its margin. - if (!marginInfo.canCollapseWithMarginAfter() && !marginInfo.canCollapseWithMarginBefore() - && (!isAnonymousBlock() || isAnonymousColumnsBlock() || isAnonymousColumnSpanBlock()) - && (!document()->inQuirksMode() || !marginInfo.quirkContainer() || !marginInfo.marginAfterQuirk())) + if (!marginInfo.discardMargin() && (!marginInfo.canCollapseWithMarginAfter() && !marginInfo.canCollapseWithMarginBefore() + && (!document()->inQuirksMode() || !marginInfo.quirkContainer() || !marginInfo.hasMarginAfterQuirk()))) setLogicalHeight(logicalHeight() + marginInfo.margin()); // Now add in our bottom border/padding. @@ -2360,28 +2508,55 @@ void RenderBlock::setLogicalTopForChild(RenderBox* child, LayoutUnit logicalTop, } } -void RenderBlock::layoutBlockChildren(bool relayoutChildren, LayoutUnit& maxFloatLogicalBottom) +void RenderBlock::updateBlockChildDirtyBitsBeforeLayout(bool relayoutChildren, RenderBox* child) { - if (gPercentHeightDescendantsMap) { - if (TrackedRendererListHashSet* descendants = gPercentHeightDescendantsMap->get(this)) { - TrackedRendererListHashSet::iterator end = descendants->end(); - for (TrackedRendererListHashSet::iterator it = descendants->begin(); it != end; ++it) { - RenderBox* box = *it; - while (box != this) { - if (box->normalChildNeedsLayout()) - break; - box->setChildNeedsLayout(true, MarkOnlyThis); - box = box->containingBlock(); - ASSERT(box); - if (!box) - break; - } - } + // FIXME: Technically percentage height objects only need a relayout if their percentage isn't going to be turned into + // an auto value. Add a method to determine this, so that we can avoid the relayout. + if (relayoutChildren || (child->hasRelativeLogicalHeight() && !isRenderView()) || child->hasViewportPercentageLogicalHeight()) + child->setChildNeedsLayout(true, MarkOnlyThis); + + // If relayoutChildren is set and the child has percentage padding or an embedded content box, we also need to invalidate the childs pref widths. + if (relayoutChildren && child->needsPreferredWidthsRecalculation()) + child->setPreferredLogicalWidthsDirty(true, MarkOnlyThis); +} + +void RenderBlock::dirtyForLayoutFromPercentageHeightDescendants() +{ + if (!gPercentHeightDescendantsMap) + return; + + TrackedRendererListHashSet* descendants = gPercentHeightDescendantsMap->get(this); + if (!descendants) + return; + + TrackedRendererListHashSet::iterator end = descendants->end(); + for (TrackedRendererListHashSet::iterator it = descendants->begin(); it != end; ++it) { + RenderBox* box = *it; + while (box != this) { + if (box->normalChildNeedsLayout()) + break; + box->setChildNeedsLayout(true, MarkOnlyThis); + + // If the width of an image is affected by the height of a child (e.g., an image with an aspect ratio), + // then we have to dirty preferred widths, since even enclosing blocks can become dirty as a result. + // (A horizontal flexbox that contains an inline image wrapped in an anonymous block for example.) + if (box->hasAspectRatio()) + box->setPreferredLogicalWidthsDirty(true); + + box = box->containingBlock(); + ASSERT(box); + if (!box) + break; } } +} + +void RenderBlock::layoutBlockChildren(bool relayoutChildren, LayoutUnit& maxFloatLogicalBottom) +{ + dirtyForLayoutFromPercentageHeightDescendants(); - LayoutUnit beforeEdge = borderBefore() + paddingBefore(); - LayoutUnit afterEdge = borderAfter() + paddingAfter() + scrollbarLogicalHeight(); + LayoutUnit beforeEdge = borderAndPaddingBefore(); + LayoutUnit afterEdge = borderAndPaddingAfter() + scrollbarLogicalHeight(); setLogicalHeight(beforeEdge); @@ -2409,20 +2584,18 @@ void RenderBlock::layoutBlockChildren(bool relayoutChildren, LayoutUnit& maxFloa if (childToExclude == child) continue; // Skip this child, since it will be positioned by the specialized subclass (fieldsets and ruby runs). - // Make sure we layout children if they need it. - // FIXME: Technically percentage height objects only need a relayout if their percentage isn't going to be turned into - // an auto value. Add a method to determine this, so that we can avoid the relayout. - if (relayoutChildren || (child->hasRelativeLogicalHeight() && !isRenderView())) - child->setChildNeedsLayout(true, MarkOnlyThis); - - // If relayoutChildren is set and the child has percentage padding or an embedded content box, we also need to invalidate the childs pref widths. - if (relayoutChildren && child->needsPreferredWidthsRecalculation()) - child->setPreferredLogicalWidthsDirty(true, MarkOnlyThis); + updateBlockChildDirtyBitsBeforeLayout(relayoutChildren, child); - // Handle the four types of special elements first. These include positioned content, floating content, compacts and - // run-ins. When we encounter these four types of objects, we don't actually lay them out as normal flow blocks. - if (handleSpecialChild(child, marginInfo)) + if (child->isOutOfFlowPositioned()) { + child->containingBlock()->insertPositionedObject(child); + adjustPositionedBlock(child, marginInfo); continue; + } + if (child->isFloating()) { + insertFloatingObject(child); + adjustFloatingBlock(marginInfo); + continue; + } // Lay out the child. layoutBlockChild(child, marginInfo, previousFloatLogicalBottom, maxFloatLogicalBottom); @@ -2441,13 +2614,6 @@ void RenderBlock::layoutBlockChild(RenderBox* child, MarginInfo& marginInfo, Lay // The child is a normal flow object. Compute the margins we will use for collapsing now. child->computeAndSetBlockDirectionMargins(this); - // Do not allow a collapse if the margin-before-collapse style is set to SEPARATE. - RenderStyle* childStyle = child->style(); - if (childStyle->marginBeforeCollapse() == MSEPARATE) { - marginInfo.setAtBeforeSideOfBlock(false); - marginInfo.clearMargin(); - } - // Try to guess our correct logical top position. In most cases this guess will // be correct. Only if we're wrong (when we compute the real logical top position) // will we have to potentially relayout. @@ -2468,6 +2634,14 @@ void RenderBlock::layoutBlockChild(RenderBox* child, MarginInfo& marginInfo, Lay bool markDescendantsWithFloats = false; if (logicalTopEstimate != oldLogicalTop && !child->avoidsFloats() && childRenderBlock && childRenderBlock->containsFloats()) markDescendantsWithFloats = true; +#if ENABLE(SUBPIXEL_LAYOUT) + else if (UNLIKELY(logicalTopEstimate.mightBeSaturated())) + // logicalTopEstimate, returned by estimateLogicalTopPosition, might be saturated for + // very large elements. If it does the comparison with oldLogicalTop might yield a + // false negative as adding and removing margins, borders etc from a saturated number + // might yield incorrect results. If this is the case always mark for layout. + markDescendantsWithFloats = true; +#endif else if (!child->avoidsFloats() || child->shrinkToAvoidFloats()) { // If an element might be affected by the presence of floats, then always mark it for // layout. @@ -2511,7 +2685,7 @@ void RenderBlock::layoutBlockChild(RenderBox* child, MarginInfo& marginInfo, Lay // Now we have a final top position. See if it really does end up being different from our estimate. // clearFloatsIfNeeded can also mark the child as needing a layout even though we didn't move. This happens // when collapseMargins dynamically adds overhanging floats because of a child with negative margins. - if (logicalTopAfterClear != logicalTopEstimate || child->needsLayout()) { + if (logicalTopAfterClear != logicalTopEstimate || child->needsLayout() || (paginated && childRenderBlock && childRenderBlock->shouldBreakAtLineToAvoidWidow())) { if (child->shrinkToAvoidFloats()) { // The child's width depends on the line width. // When the child shifts to clear an item, its width can @@ -2537,11 +2711,11 @@ void RenderBlock::layoutBlockChild(RenderBox* child, MarginInfo& marginInfo, Lay marginInfo.setAtBeforeSideOfBlock(false); // Now place the child in the correct left position - determineLogicalLeftPositionForChild(child); + determineLogicalLeftPositionForChild(child, ApplyLayoutDelta); // Update our height now that the child has been placed in the correct position. setLogicalHeight(logicalHeight() + logicalHeightForChild(child)); - if (childStyle->marginAfterCollapse() == MSEPARATE) { + if (mustSeparateMarginAfterForChild(child)) { setLogicalHeight(logicalHeight() + marginAfterForChild(child)); marginInfo.clearMargin(); } @@ -2621,16 +2795,23 @@ bool RenderBlock::simplifiedLayout() simplifiedNormalFlowLayout(); // Lay out our positioned objects if our positioned child bit is set. - if (posChildNeedsLayout()) - layoutPositionedObjects(false); + // Also, if an absolute position element inside a relative positioned container moves, and the absolute element has a fixed position + // child, neither the fixed element nor its container learn of the movement since posChildNeedsLayout() is only marked as far as the + // relative positioned container. So if we can have fixed pos objects in our positioned objects list check if any of them + // are statically positioned and thus need to move with their absolute ancestors. + bool canContainFixedPosObjects = canContainFixedPositionObjects(); + if (posChildNeedsLayout() || canContainFixedPosObjects) + layoutPositionedObjects(false, !posChildNeedsLayout() && canContainFixedPosObjects); // Recompute our overflow information. // FIXME: We could do better here by computing a temporary overflow object from layoutPositionedObjects and only // updating our overflow if we either used to have overflow or if the new temporary object has overflow. // For now just always recompute overflow. This is no worse performance-wise than the old code that called rightmostPosition and // lowestPosition on every relayout so it's not a regression. - m_overflow.clear(); - computeOverflow(clientLogicalBottom(), true); + // computeOverflow expects the bottom edge before we clamp our height. Since this information isn't available during + // simplifiedLayout, we cache the value in m_overflow. + LayoutUnit oldClientAfterEdge = hasRenderOverflow() ? m_overflow->layoutClientAfterEdge() : clientLogicalBottom(); + computeOverflow(oldClientAfterEdge, true); statePusher.pop(); @@ -2642,7 +2823,38 @@ bool RenderBlock::simplifiedLayout() return true; } -void RenderBlock::layoutPositionedObjects(bool relayoutChildren) +void RenderBlock::markFixedPositionObjectForLayoutIfNeeded(RenderObject* child) +{ + if (child->style()->position() != FixedPosition) + return; + + bool hasStaticBlockPosition = child->style()->hasStaticBlockPosition(isHorizontalWritingMode()); + bool hasStaticInlinePosition = child->style()->hasStaticInlinePosition(isHorizontalWritingMode()); + if (!hasStaticBlockPosition && !hasStaticInlinePosition) + return; + + RenderObject* o = child->parent(); + while (o && !o->isRenderView() && o->style()->position() != AbsolutePosition) + o = o->parent(); + if (o->style()->position() != AbsolutePosition) + return; + + RenderBox* box = toRenderBox(child); + if (hasStaticInlinePosition) { + LogicalExtentComputedValues computedValues; + box->computeLogicalWidthInRegion(computedValues); + LayoutUnit newLeft = computedValues.m_position; + if (newLeft != box->logicalLeft()) + child->setChildNeedsLayout(true, MarkOnlyThis); + } else if (hasStaticBlockPosition) { + LayoutUnit oldTop = box->logicalTop(); + box->updateLogicalHeight(); + if (box->logicalTop() != oldTop) + child->setChildNeedsLayout(true, MarkOnlyThis); + } +} + +void RenderBlock::layoutPositionedObjects(bool relayoutChildren, bool fixedPositionObjectsOnly) { TrackedRendererListHashSet* positionedDescendants = positionedObjects(); if (!positionedDescendants) @@ -2655,6 +2867,16 @@ void RenderBlock::layoutPositionedObjects(bool relayoutChildren) TrackedRendererListHashSet::iterator end = positionedDescendants->end(); for (TrackedRendererListHashSet::iterator it = positionedDescendants->begin(); it != end; ++it) { r = *it; + + // A fixed position element with an absolute positioned ancestor has no way of knowing if the latter has changed position. So + // if this is a fixed position element, mark it for layout if it has an abspos ancestor and needs to move with that ancestor, i.e. + // it has static position. + markFixedPositionObjectForLayoutIfNeeded(r); + if (fixedPositionObjectsOnly) { + r->layoutIfNeeded(); + continue; + } + // When a non-positioned block element moves, it may have positioned children that are implicitly positioned relative to the // non-positioned block. Rather than trying to detect all of these movement cases, we just always lay out positioned // objects that are positioned implicitly like this. Such objects are rare, and so in typical DHTML menu usage (where everything is @@ -2718,7 +2940,7 @@ void RenderBlock::markForPaginationRelayoutIfNeeded() if (needsLayout()) return; - if (view()->layoutState()->pageLogicalHeightChanged() || (view()->layoutState()->pageLogicalHeight() && view()->layoutState()->pageLogicalOffset(this, logicalTop()) != pageLogicalOffset())) + if (view()->layoutState()->pageLogicalHeightChanged() || (view()->layoutState()->pageLogicalHeight() && view()->layoutState()->pageLogicalOffset(this, logicalTop()) != pageLogicalOffset()) || shouldBreakAtLineToAvoidWidow()) setChildNeedsLayout(true, MarkOnlyThis); } @@ -2741,7 +2963,7 @@ void RenderBlock::repaintOverhangingFloats(bool paintAllDescendants) // condition is replaced with being a descendant of us. if (logicalBottomForFloat(r) > logicalHeight() && ((paintAllDescendants && r->m_renderer->isDescendantOf(this)) || r->shouldPaint()) && !r->m_renderer->hasSelfPaintingLayer()) { r->m_renderer->repaint(); - r->m_renderer->repaintOverhangingFloats(); + r->m_renderer->repaintOverhangingFloats(false); } } } @@ -2772,7 +2994,7 @@ void RenderBlock::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) // 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. - if (hasOverflowClip() && style()->visibility() == VISIBLE && (phase == PaintPhaseBlockBackground || phase == PaintPhaseChildBlockBackground) && paintInfo.shouldPaintWithinRoot(this)) + if (hasOverflowClip() && style()->visibility() == VISIBLE && (phase == PaintPhaseBlockBackground || phase == PaintPhaseChildBlockBackground) && paintInfo.shouldPaintWithinRoot(this) && !paintInfo.paintRootBackgroundOnly()) layer()->paintOverflowControls(paintInfo.context, roundedIntPoint(adjustedPaintOffset), paintInfo.rect); } @@ -2786,7 +3008,7 @@ void RenderBlock::paintColumnRules(PaintInfo& paintInfo, const LayoutPoint& pain EBorderStyle ruleStyle = style()->columnRuleStyle(); LayoutUnit ruleThickness = style()->columnRuleWidth(); LayoutUnit colGap = columnGap(); - bool renderRule = ruleStyle > BHIDDEN && !ruleTransparent && ruleThickness <= colGap; + bool renderRule = ruleStyle > BHIDDEN && !ruleTransparent; if (!renderRule) return; @@ -2831,10 +3053,10 @@ void RenderBlock::paintColumnRules(PaintInfo& paintInfo, const LayoutPoint& pain bool topToBottom = !style()->isFlippedBlocksWritingMode() ^ colInfo->progressionIsReversed(); LayoutUnit ruleLeft = isHorizontalWritingMode() ? borderLeft() + paddingLeft() - : colGap / 2 - colGap - ruleThickness / 2 + (!colInfo->progressionIsReversed() ? borderBefore() + paddingBefore() : borderAfter() + paddingAfter()); + : colGap / 2 - colGap - ruleThickness / 2 + (!colInfo->progressionIsReversed() ? borderAndPaddingBefore() : borderAndPaddingAfter()); LayoutUnit ruleWidth = isHorizontalWritingMode() ? contentWidth() : ruleThickness; LayoutUnit ruleTop = isHorizontalWritingMode() - ? colGap / 2 - colGap - ruleThickness / 2 + (!colInfo->progressionIsReversed() ? borderBefore() + paddingBefore() : borderAfter() + paddingAfter()) + ? colGap / 2 - colGap - ruleThickness / 2 + (!colInfo->progressionIsReversed() ? borderAndPaddingBefore() : borderAndPaddingAfter()) : borderStart() + paddingStart(); LayoutUnit ruleHeight = isHorizontalWritingMode() ? ruleThickness : contentHeight(); LayoutRect ruleRect(ruleLeft, ruleTop, ruleWidth, ruleHeight); @@ -2864,6 +3086,36 @@ void RenderBlock::paintColumnRules(PaintInfo& paintInfo, const LayoutPoint& pain } } +LayoutUnit RenderBlock::initialBlockOffsetForPainting() const +{ + ColumnInfo* colInfo = columnInfo(); + LayoutUnit result = 0; + if (colInfo->progressionAxis() == ColumnInfo::BlockAxis && colInfo->progressionIsReversed()) { + LayoutRect colRect = columnRectAt(colInfo, 0); + result = isHorizontalWritingMode() ? colRect.y() : colRect.x(); + result -= borderAndPaddingBefore(); + if (style()->isFlippedBlocksWritingMode()) + result = -result; + } + return result; +} + +LayoutUnit RenderBlock::blockDeltaForPaintingNextColumn() const +{ + ColumnInfo* colInfo = columnInfo(); + LayoutUnit blockDelta = -colInfo->columnHeight(); + LayoutUnit colGap = columnGap(); + if (colInfo->progressionAxis() == ColumnInfo::BlockAxis) { + if (!colInfo->progressionIsReversed()) + blockDelta = colGap; + else + blockDelta -= (colInfo->columnHeight() + colGap); + } + if (style()->isFlippedBlocksWritingMode()) + blockDelta = -blockDelta; + return blockDelta; +} + void RenderBlock::paintColumnContents(PaintInfo& paintInfo, const LayoutPoint& paintOffset, bool paintingFloats) { // We need to do multiple passes, breaking up our child painting into strips. @@ -2872,20 +3124,16 @@ void RenderBlock::paintColumnContents(PaintInfo& paintInfo, const LayoutPoint& p unsigned colCount = columnCount(colInfo); if (!colCount) return; - LayoutUnit currLogicalTopOffset = 0; LayoutUnit colGap = columnGap(); + LayoutUnit currLogicalTopOffset = initialBlockOffsetForPainting(); + LayoutUnit blockDelta = blockDeltaForPaintingNextColumn(); for (unsigned i = 0; i < colCount; i++) { // For each rect, we clip to the rect, and then we adjust our coords. LayoutRect colRect = columnRectAt(colInfo, i); flipForWritingMode(colRect); + LayoutUnit logicalLeftOffset = (isHorizontalWritingMode() ? colRect.x() : colRect.y()) - logicalLeftOffsetForContent(); LayoutSize offset = isHorizontalWritingMode() ? LayoutSize(logicalLeftOffset, currLogicalTopOffset) : LayoutSize(currLogicalTopOffset, logicalLeftOffset); - if (colInfo->progressionAxis() == ColumnInfo::BlockAxis) { - if (isHorizontalWritingMode()) - offset.expand(0, colRect.y() - borderTop() - paddingTop()); - else - offset.expand(colRect.x() - borderLeft() - paddingLeft(), 0); - } colRect.moveBy(paintOffset); PaintInfo info(paintInfo); info.rect.intersect(pixelSnappedIntRect(colRect)); @@ -2913,12 +3161,7 @@ void RenderBlock::paintColumnContents(PaintInfo& paintInfo, const LayoutPoint& p else paintContents(info, adjustedPaintOffset); } - - LayoutUnit blockDelta = (isHorizontalWritingMode() ? colRect.height() : colRect.width()); - if (style()->isFlippedBlocksWritingMode()) - currLogicalTopOffset += blockDelta; - else - currLogicalTopOffset -= blockDelta; + currLogicalTopOffset += blockDelta; } } @@ -2939,7 +3182,7 @@ void RenderBlock::paintContents(PaintInfo& paintInfo, const LayoutPoint& paintOf // We don't paint our own background, but we do let the kids paint their backgrounds. PaintInfo paintInfoForChild(paintInfo); paintInfoForChild.phase = newPhase; - paintInfoForChild.updatePaintingRootForChildren(this); + paintInfoForChild.updateSubtreePaintRootForChildren(this); // FIXME: Paint-time pagination is obsolete and is now only used by embedded WebViews inside AppKit // NSViews. Do not add any more code for this. @@ -3026,7 +3269,7 @@ void RenderBlock::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffs if ((paintPhase == PaintPhaseBlockBackground || paintPhase == PaintPhaseChildBlockBackground) && style()->visibility() == VISIBLE) { if (hasBoxDecorations()) paintBoxDecorations(paintInfo, paintOffset); - if (hasColumns()) + if (hasColumns() && !paintInfo.paintRootBackgroundOnly()) paintColumnRules(paintInfo, paintOffset); } @@ -3036,7 +3279,7 @@ void RenderBlock::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffs } // We're done. We don't bother painting any children. - if (paintPhase == PaintPhaseBlockBackground) + if (paintPhase == PaintPhaseBlockBackground || paintInfo.paintRootBackgroundOnly()) return; // Adjust our painting position if we're inside a scrolled layer (e.g., an overflow:auto div). @@ -3068,15 +3311,12 @@ void RenderBlock::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffs // 5. paint outline. if ((paintPhase == PaintPhaseOutline || paintPhase == PaintPhaseSelfOutline) && hasOutline() && style()->visibility() == VISIBLE) - paintOutline(paintInfo.context, LayoutRect(paintOffset, size())); + paintOutline(paintInfo, LayoutRect(paintOffset, size())); // 6. paint continuation outlines. if ((paintPhase == PaintPhaseOutline || paintPhase == PaintPhaseChildOutlines)) { RenderInline* inlineCont = inlineElementContinuation(); - // FIXME: For now, do not add continuations for outline painting by our containing block if we are a relative positioned - // anonymous block (i.e. have our own layer). This is because a block depends on renderers in its continuation table being - // in the same layer. - if (inlineCont && inlineCont->hasOutline() && inlineCont->style()->visibility() == VISIBLE && !hasLayer()) { + if (inlineCont && inlineCont->hasOutline() && inlineCont->style()->visibility() == VISIBLE) { RenderInline* inlineRenderer = toRenderInline(inlineCont->node()->renderer()); RenderBlock* cb = containingBlock(); @@ -3088,10 +3328,13 @@ void RenderBlock::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffs } } - if (!inlineEnclosedInSelfPaintingLayer) + // Do not add continuations for outline painting by our containing block if we are a relative positioned + // anonymous block (i.e. have our own layer), paint them straightaway instead. This is because a block depends on renderers in its continuation table being + // in the same layer. + if (!inlineEnclosedInSelfPaintingLayer && !hasLayer()) cb->addContinuationWithOutline(inlineRenderer); - else if (!inlineRenderer->firstLineBox()) - inlineRenderer->paintOutline(paintInfo.context, paintOffset - locationOffset() + inlineRenderer->containingBlock()->location()); + else if (!inlineRenderer->firstLineBox() || (!inlineEnclosedInSelfPaintingLayer && hasLayer())) + inlineRenderer->paintOutline(paintInfo, paintOffset - locationOffset() + inlineRenderer->containingBlock()->location()); } paintContinuationOutlines(paintInfo, paintOffset); } @@ -3115,7 +3358,7 @@ LayoutPoint RenderBlock::flipFloatForWritingModeForChild(const FloatingObject* c // case. if (isHorizontalWritingMode()) return LayoutPoint(point.x(), point.y() + height() - child->renderer()->height() - 2 * yPositionForFloatIncludingMargin(child)); - return LayoutPoint(point.x() + width() - child->width() - 2 * xPositionForFloatIncludingMargin(child), point.y()); + return LayoutPoint(point.x() + width() - child->renderer()->width() - 2 * xPositionForFloatIncludingMargin(child), point.y()); } void RenderBlock::paintFloats(PaintInfo& paintInfo, const LayoutPoint& paintOffset, bool preservePhase) @@ -3180,7 +3423,7 @@ void RenderBlock::addContinuationWithOutline(RenderInline* flow) ListHashSet<RenderInline*>* continuations = table->get(this); if (!continuations) { continuations = new ListHashSet<RenderInline*>; - table->set(this, continuations); + table->set(this, adoptPtr(continuations)); } continuations->add(flow); @@ -3205,7 +3448,7 @@ void RenderBlock::paintContinuationOutlines(PaintInfo& info, const LayoutPoint& if (table->isEmpty()) return; - ListHashSet<RenderInline*>* continuations = table->get(this); + OwnPtr<ListHashSet<RenderInline*> > continuations = table->take(this); if (!continuations) return; @@ -3219,12 +3462,8 @@ void RenderBlock::paintContinuationOutlines(PaintInfo& info, const LayoutPoint& for ( ; block && block != this; block = block->containingBlock()) accumulatedPaintOffset.moveBy(block->location()); ASSERT(block); - flow->paintOutline(info.context, accumulatedPaintOffset); + flow->paintOutline(info, accumulatedPaintOffset); } - - // Delete - delete continuations; - table->remove(this); } bool RenderBlock::shouldPaintSelectionGaps() const @@ -3234,8 +3473,9 @@ bool RenderBlock::shouldPaintSelectionGaps() const bool RenderBlock::isSelectionRoot() const { - if (!node()) + if (isPseudoElement()) return false; + ASSERT(node() || isAnonymous()); // FIXME: Eventually tables should have to learn how to fill gaps between cells, at least in simple non-spanning cases. if (isTable()) @@ -3264,30 +3504,31 @@ GapRects RenderBlock::selectionGapRectsForRepaint(const RenderLayerModelObject* if (!shouldPaintSelectionGaps()) return GapRects(); - // FIXME: this is broken with transforms TransformState transformState(TransformState::ApplyTransformDirection, FloatPoint()); - mapLocalToContainer(repaintContainer, transformState); + mapLocalToContainer(repaintContainer, transformState, ApplyContainerFlip | UseTransforms); LayoutPoint offsetFromRepaintContainer = roundedLayoutPoint(transformState.mappedPoint()); if (hasOverflowClip()) offsetFromRepaintContainer -= scrolledContentOffset(); + LogicalSelectionOffsetCaches cache(this); LayoutUnit lastTop = 0; - LayoutUnit lastLeft = logicalLeftSelectionOffset(this, lastTop); - LayoutUnit lastRight = logicalRightSelectionOffset(this, lastTop); + LayoutUnit lastLeft = logicalLeftSelectionOffset(this, lastTop, cache); + LayoutUnit lastRight = logicalRightSelectionOffset(this, lastTop, cache); - return selectionGaps(this, offsetFromRepaintContainer, IntSize(), lastTop, lastLeft, lastRight); + return selectionGaps(this, offsetFromRepaintContainer, IntSize(), lastTop, lastLeft, lastRight, cache); } void RenderBlock::paintSelection(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { if (shouldPaintSelectionGaps() && paintInfo.phase == PaintPhaseForeground) { + LogicalSelectionOffsetCaches cache(this); LayoutUnit lastTop = 0; - LayoutUnit lastLeft = logicalLeftSelectionOffset(this, lastTop); - LayoutUnit lastRight = logicalRightSelectionOffset(this, lastTop); + LayoutUnit lastLeft = logicalLeftSelectionOffset(this, lastTop, cache); + LayoutUnit lastRight = logicalRightSelectionOffset(this, lastTop, cache); GraphicsContextStateSaver stateSaver(*paintInfo.context); - LayoutRect gapRectsBounds = selectionGaps(this, paintOffset, LayoutSize(), lastTop, lastLeft, lastRight, &paintInfo); + LayoutRect gapRectsBounds = selectionGaps(this, paintOffset, LayoutSize(), lastTop, lastLeft, lastRight, cache, &paintInfo); if (!gapRectsBounds.isEmpty()) { if (RenderLayer* layer = enclosingLayer()) { gapRectsBounds.moveBy(-paintOffset); @@ -3339,7 +3580,7 @@ LayoutRect RenderBlock::logicalRectToPhysicalRect(const LayoutPoint& rootBlockPh } GapRects RenderBlock::selectionGaps(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, - LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const PaintInfo* paintInfo) + LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const LogicalSelectionOffsetCaches& cache, const PaintInfo* paintInfo) { // IMPORTANT: Callers of this method that intend for painting to happen need to do a save/restore. // Clip out floating and positioned objects when painting selection gaps. @@ -3376,25 +3617,27 @@ GapRects RenderBlock::selectionGaps(RenderBlock* rootBlock, const LayoutPoint& r if (hasColumns() || hasTransform() || style()->columnSpan()) { // FIXME: We should learn how to gap fill multiple columns and transforms eventually. lastLogicalTop = blockDirectionOffset(rootBlock, offsetFromRootBlock) + logicalHeight(); - lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, logicalHeight()); - lastLogicalRight = logicalRightSelectionOffset(rootBlock, logicalHeight()); + lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, logicalHeight(), cache); + lastLogicalRight = logicalRightSelectionOffset(rootBlock, logicalHeight(), cache); return result; } if (childrenInline()) - result = inlineSelectionGaps(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight, paintInfo); + result = inlineSelectionGaps(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight, cache, paintInfo); else - result = blockSelectionGaps(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight, paintInfo); + result = blockSelectionGaps(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight, cache, paintInfo); // Go ahead and fill the vertical gap all the way to the bottom of our block if the selection extends past our block. - if (rootBlock == this && (selectionState() != SelectionBoth && selectionState() != SelectionEnd)) - result.uniteCenter(blockSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight, - logicalHeight(), paintInfo)); + if (rootBlock == this && (selectionState() != SelectionBoth && selectionState() != SelectionEnd)) { + result.uniteCenter(blockSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, + lastLogicalTop, lastLogicalLeft, lastLogicalRight, logicalHeight(), cache, paintInfo)); + } + return result; } GapRects RenderBlock::inlineSelectionGaps(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, - LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const PaintInfo* paintInfo) + LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const LogicalSelectionOffsetCaches& cache, const PaintInfo* paintInfo) { GapRects result; @@ -3405,8 +3648,8 @@ GapRects RenderBlock::inlineSelectionGaps(RenderBlock* rootBlock, const LayoutPo // Go ahead and update our lastLogicalTop to be the bottom of the block. <hr>s or empty blocks with height can trip this // case. lastLogicalTop = blockDirectionOffset(rootBlock, offsetFromRootBlock) + logicalHeight(); - lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, logicalHeight()); - lastLogicalRight = logicalRightSelectionOffset(rootBlock, logicalHeight()); + lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, logicalHeight(), cache); + lastLogicalRight = logicalRightSelectionOffset(rootBlock, logicalHeight(), cache); } return result; } @@ -3422,15 +3665,14 @@ GapRects RenderBlock::inlineSelectionGaps(RenderBlock* rootBlock, const LayoutPo if (!containsStart && !lastSelectedLine && selectionState() != SelectionStart && selectionState() != SelectionBoth) - result.uniteCenter(blockSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight, - selTop, paintInfo)); + result.uniteCenter(blockSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight, selTop, cache, paintInfo)); LayoutRect logicalRect(curr->logicalLeft(), selTop, curr->logicalWidth(), selTop + selHeight); logicalRect.move(isHorizontalWritingMode() ? offsetFromRootBlock : offsetFromRootBlock.transposedSize()); LayoutRect physicalRect = rootBlock->logicalRectToPhysicalRect(rootBlockPhysicalPosition, logicalRect); if (!paintInfo || (isHorizontalWritingMode() && physicalRect.y() < paintInfo->rect.maxY() && physicalRect.maxY() > paintInfo->rect.y()) || (!isHorizontalWritingMode() && physicalRect.x() < paintInfo->rect.maxX() && physicalRect.maxX() > paintInfo->rect.x())) - result.unite(curr->lineSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, selTop, selHeight, paintInfo)); + result.unite(curr->lineSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, selTop, selHeight, cache, paintInfo)); lastSelectedLine = curr; } @@ -3442,20 +3684,25 @@ GapRects RenderBlock::inlineSelectionGaps(RenderBlock* rootBlock, const LayoutPo if (lastSelectedLine && selectionState() != SelectionEnd && selectionState() != SelectionBoth) { // Go ahead and update our lastY to be the bottom of the last selected line. lastLogicalTop = blockDirectionOffset(rootBlock, offsetFromRootBlock) + lastSelectedLine->selectionBottom(); - lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, lastSelectedLine->selectionBottom()); - lastLogicalRight = logicalRightSelectionOffset(rootBlock, lastSelectedLine->selectionBottom()); + lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, lastSelectedLine->selectionBottom(), cache); + lastLogicalRight = logicalRightSelectionOffset(rootBlock, lastSelectedLine->selectionBottom(), cache); } return result; } GapRects RenderBlock::blockSelectionGaps(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, - LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const PaintInfo* paintInfo) + LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const LogicalSelectionOffsetCaches& cache, const PaintInfo* paintInfo) { GapRects result; // Go ahead and jump right to the first block child that contains some selected objects. RenderBox* curr; for (curr = firstChildBox(); curr && curr->selectionState() == SelectionNone; curr = curr->nextSiblingBox()) { } + + if (!curr) + return result; + + LogicalSelectionOffsetCaches childCache(this, cache); for (bool sawSelectionEnd = false; curr && !sawSelectionEnd; curr = curr->nextSiblingBox()) { SelectionState childState = curr->selectionState(); @@ -3477,10 +3724,11 @@ GapRects RenderBlock::blockSelectionGaps(RenderBlock* rootBlock, const LayoutPoi bool fillBlockGaps = paintsOwnSelection || (curr->canBeSelectionLeaf() && childState != SelectionNone); if (fillBlockGaps) { // We need to fill the vertical gap above this object. - if (childState == SelectionEnd || childState == SelectionInside) + if (childState == SelectionEnd || childState == SelectionInside) { // Fill the gap above the object. - result.uniteCenter(blockSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight, - curr->logicalTop(), paintInfo)); + result.uniteCenter(blockSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, + lastLogicalTop, lastLogicalLeft, lastLogicalRight, curr->logicalTop(), cache, paintInfo)); + } // Only fill side gaps for objects that paint their own selection if we know for sure the selection is going to extend all the way *past* // our object. We know this if the selection did not end inside our object. @@ -3492,26 +3740,27 @@ GapRects RenderBlock::blockSelectionGaps(RenderBlock* rootBlock, const LayoutPoi getSelectionGapInfo(childState, leftGap, rightGap); if (leftGap) - result.uniteLeft(logicalLeftSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, this, curr->logicalLeft(), curr->logicalTop(), curr->logicalHeight(), paintInfo)); + result.uniteLeft(logicalLeftSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, this, curr->logicalLeft(), curr->logicalTop(), curr->logicalHeight(), cache, paintInfo)); if (rightGap) - result.uniteRight(logicalRightSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, this, curr->logicalRight(), curr->logicalTop(), curr->logicalHeight(), paintInfo)); + result.uniteRight(logicalRightSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, this, curr->logicalRight(), curr->logicalTop(), curr->logicalHeight(), cache, paintInfo)); // Update lastLogicalTop to be just underneath the object. lastLogicalLeft and lastLogicalRight extend as far as // they can without bumping into floating or positioned objects. Ideally they will go right up // to the border of the root selection block. lastLogicalTop = blockDirectionOffset(rootBlock, offsetFromRootBlock) + curr->logicalBottom(); - lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, curr->logicalBottom()); - lastLogicalRight = logicalRightSelectionOffset(rootBlock, curr->logicalBottom()); - } else if (childState != SelectionNone) + lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, curr->logicalBottom(), cache); + lastLogicalRight = logicalRightSelectionOffset(rootBlock, curr->logicalBottom(), cache); + } else if (childState != SelectionNone) { // We must be a block that has some selected object inside it. Go ahead and recur. result.unite(toRenderBlock(curr)->selectionGaps(rootBlock, rootBlockPhysicalPosition, LayoutSize(offsetFromRootBlock.width() + curr->x(), offsetFromRootBlock.height() + curr->y()), - lastLogicalTop, lastLogicalLeft, lastLogicalRight, paintInfo)); + lastLogicalTop, lastLogicalLeft, lastLogicalRight, childCache, paintInfo)); + } } return result; } LayoutRect RenderBlock::blockSelectionGap(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, - LayoutUnit lastLogicalTop, LayoutUnit lastLogicalLeft, LayoutUnit lastLogicalRight, LayoutUnit logicalBottom, const PaintInfo* paintInfo) + LayoutUnit lastLogicalTop, LayoutUnit lastLogicalLeft, LayoutUnit lastLogicalRight, LayoutUnit logicalBottom, const LogicalSelectionOffsetCaches& cache, const PaintInfo* paintInfo) { LayoutUnit logicalTop = lastLogicalTop; LayoutUnit logicalHeight = blockDirectionOffset(rootBlock, offsetFromRootBlock) + logicalBottom - logicalTop; @@ -3519,8 +3768,8 @@ LayoutRect RenderBlock::blockSelectionGap(RenderBlock* rootBlock, const LayoutPo return LayoutRect(); // Get the selection offsets for the bottom of the gap - LayoutUnit logicalLeft = max(lastLogicalLeft, logicalLeftSelectionOffset(rootBlock, logicalBottom)); - LayoutUnit logicalRight = min(lastLogicalRight, logicalRightSelectionOffset(rootBlock, logicalBottom)); + LayoutUnit logicalLeft = max(lastLogicalLeft, logicalLeftSelectionOffset(rootBlock, logicalBottom, cache)); + LayoutUnit logicalRight = min(lastLogicalRight, logicalRightSelectionOffset(rootBlock, logicalBottom, cache)); LayoutUnit logicalWidth = logicalRight - logicalLeft; if (logicalWidth <= 0) return LayoutRect(); @@ -3532,11 +3781,12 @@ LayoutRect RenderBlock::blockSelectionGap(RenderBlock* rootBlock, const LayoutPo } LayoutRect RenderBlock::logicalLeftSelectionGap(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, - RenderObject* selObj, LayoutUnit logicalLeft, LayoutUnit logicalTop, LayoutUnit logicalHeight, const PaintInfo* paintInfo) + RenderObject* selObj, LayoutUnit logicalLeft, LayoutUnit logicalTop, LayoutUnit logicalHeight, const LogicalSelectionOffsetCaches& cache, const PaintInfo* paintInfo) { LayoutUnit rootBlockLogicalTop = blockDirectionOffset(rootBlock, offsetFromRootBlock) + logicalTop; - LayoutUnit rootBlockLogicalLeft = max(logicalLeftSelectionOffset(rootBlock, logicalTop), logicalLeftSelectionOffset(rootBlock, logicalTop + logicalHeight)); - LayoutUnit rootBlockLogicalRight = min(inlineDirectionOffset(rootBlock, offsetFromRootBlock) + floorToInt(logicalLeft), min(logicalRightSelectionOffset(rootBlock, logicalTop), logicalRightSelectionOffset(rootBlock, logicalTop + logicalHeight))); + LayoutUnit rootBlockLogicalLeft = max(logicalLeftSelectionOffset(rootBlock, logicalTop, cache), logicalLeftSelectionOffset(rootBlock, logicalTop + logicalHeight, cache)); + LayoutUnit rootBlockLogicalRight = min(inlineDirectionOffset(rootBlock, offsetFromRootBlock) + floorToInt(logicalLeft), + min(logicalRightSelectionOffset(rootBlock, logicalTop, cache), logicalRightSelectionOffset(rootBlock, logicalTop + logicalHeight, cache))); LayoutUnit rootBlockLogicalWidth = rootBlockLogicalRight - rootBlockLogicalLeft; if (rootBlockLogicalWidth <= 0) return LayoutRect(); @@ -3548,11 +3798,12 @@ LayoutRect RenderBlock::logicalLeftSelectionGap(RenderBlock* rootBlock, const La } LayoutRect RenderBlock::logicalRightSelectionGap(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, - RenderObject* selObj, LayoutUnit logicalRight, LayoutUnit logicalTop, LayoutUnit logicalHeight, const PaintInfo* paintInfo) + RenderObject* selObj, LayoutUnit logicalRight, LayoutUnit logicalTop, LayoutUnit logicalHeight, const LogicalSelectionOffsetCaches& cache, const PaintInfo* paintInfo) { LayoutUnit rootBlockLogicalTop = blockDirectionOffset(rootBlock, offsetFromRootBlock) + logicalTop; - LayoutUnit rootBlockLogicalLeft = max(inlineDirectionOffset(rootBlock, offsetFromRootBlock) + floorToInt(logicalRight), max(logicalLeftSelectionOffset(rootBlock, logicalTop), logicalLeftSelectionOffset(rootBlock, logicalTop + logicalHeight))); - LayoutUnit rootBlockLogicalRight = min(logicalRightSelectionOffset(rootBlock, logicalTop), logicalRightSelectionOffset(rootBlock, logicalTop + logicalHeight)); + LayoutUnit rootBlockLogicalLeft = max(inlineDirectionOffset(rootBlock, offsetFromRootBlock) + floorToInt(logicalRight), + max(logicalLeftSelectionOffset(rootBlock, logicalTop, cache), logicalLeftSelectionOffset(rootBlock, logicalTop + logicalHeight, cache))); + LayoutUnit rootBlockLogicalRight = min(logicalRightSelectionOffset(rootBlock, logicalTop, cache), logicalRightSelectionOffset(rootBlock, logicalTop + logicalHeight, cache)); LayoutUnit rootBlockLogicalWidth = rootBlockLogicalRight - rootBlockLogicalLeft; if (rootBlockLogicalWidth <= 0) return LayoutRect(); @@ -3574,37 +3825,45 @@ void RenderBlock::getSelectionGapInfo(SelectionState state, bool& leftGap, bool& (state == RenderObject::SelectionEnd && !ltr); } -LayoutUnit RenderBlock::logicalLeftSelectionOffset(RenderBlock* rootBlock, LayoutUnit position) +LayoutUnit RenderBlock::logicalLeftSelectionOffset(RenderBlock* rootBlock, LayoutUnit position, const LogicalSelectionOffsetCaches& cache) { LayoutUnit logicalLeft = logicalLeftOffsetForLine(position, false); if (logicalLeft == logicalLeftOffsetForContent()) { - if (rootBlock != this) - // The border can potentially be further extended by our containingBlock(). - return containingBlock()->logicalLeftSelectionOffset(rootBlock, position + logicalTop()); + if (rootBlock != this) // The border can potentially be further extended by our containingBlock(). + return cache.containingBlockInfo(this).logicalLeftSelectionOffset(rootBlock, position + logicalTop()); return logicalLeft; } else { RenderBlock* cb = this; + const LogicalSelectionOffsetCaches* currentCache = &cache; while (cb != rootBlock) { logicalLeft += cb->logicalLeft(); - cb = cb->containingBlock(); + + ASSERT(currentCache); + const LogicalSelectionOffsetCaches::ContainingBlockInfo& info = currentCache->containingBlockInfo(cb); + cb = info.block(); + currentCache = info.cache(); } } return logicalLeft; } -LayoutUnit RenderBlock::logicalRightSelectionOffset(RenderBlock* rootBlock, LayoutUnit position) +LayoutUnit RenderBlock::logicalRightSelectionOffset(RenderBlock* rootBlock, LayoutUnit position, const LogicalSelectionOffsetCaches& cache) { LayoutUnit logicalRight = logicalRightOffsetForLine(position, false); if (logicalRight == logicalRightOffsetForContent()) { - if (rootBlock != this) - // The border can potentially be further extended by our containingBlock(). - return containingBlock()->logicalRightSelectionOffset(rootBlock, position + logicalTop()); + if (rootBlock != this) // The border can potentially be further extended by our containingBlock(). + return cache.containingBlockInfo(this).logicalRightSelectionOffset(rootBlock, position + logicalTop()); return logicalRight; } else { RenderBlock* cb = this; + const LogicalSelectionOffsetCaches* currentCache = &cache; while (cb != rootBlock) { logicalRight += cb->logicalLeft(); - cb = cb->containingBlock(); + + ASSERT(currentCache); + const LogicalSelectionOffsetCaches::ContainingBlockInfo& info = currentCache->containingBlockInfo(cb); + cb = info.block(); + currentCache = info.cache(); } } return logicalRight; @@ -3652,7 +3911,7 @@ void RenderBlock::insertIntoTrackedRendererMaps(RenderBox* descendant, TrackedDe TrackedRendererListHashSet* descendantSet = descendantsMap->get(this); if (!descendantSet) { descendantSet = new TrackedRendererListHashSet; - descendantsMap->set(this, descendantSet); + descendantsMap->set(this, adoptPtr(descendantSet)); } bool added = descendantSet->add(descendant).isNewEntry; if (!added) { @@ -3664,7 +3923,7 @@ void RenderBlock::insertIntoTrackedRendererMaps(RenderBox* descendant, TrackedDe HashSet<RenderBlock*>* containerSet = containerMap->get(descendant); if (!containerSet) { containerSet = new HashSet<RenderBlock*>; - containerMap->set(descendant, containerSet); + containerMap->set(descendant, adoptPtr(containerSet)); } ASSERT(!containerSet->contains(this)); containerSet->add(this); @@ -3675,7 +3934,7 @@ void RenderBlock::removeFromTrackedRendererMaps(RenderBox* descendant, TrackedDe if (!descendantsMap) return; - HashSet<RenderBlock*>* containerSet = containerMap->take(descendant); + OwnPtr<HashSet<RenderBlock*> > containerSet = containerMap->take(descendant); if (!containerSet) return; @@ -3688,19 +3947,16 @@ void RenderBlock::removeFromTrackedRendererMaps(RenderBox* descendant, TrackedDe // their ancestor chain before being moved. See webkit bug 93766. // ASSERT(descendant->isDescendantOf(container)); - TrackedRendererListHashSet* descendantSet = descendantsMap->get(container); - ASSERT(descendantSet); - if (!descendantSet) + TrackedDescendantsMap::iterator descendantsMapIterator = descendantsMap->find(container); + ASSERT(descendantsMapIterator != descendantsMap->end()); + if (descendantsMapIterator == descendantsMap->end()) continue; + TrackedRendererListHashSet* descendantSet = descendantsMapIterator->value.get(); ASSERT(descendantSet->contains(descendant)); descendantSet->remove(descendant); - if (descendantSet->isEmpty()) { - descendantsMap->remove(container); - delete descendantSet; - } + if (descendantSet->isEmpty()) + descendantsMap->remove(descendantsMapIterator); } - - delete containerSet; } TrackedRendererListHashSet* RenderBlock::positionedObjects() const @@ -3774,7 +4030,7 @@ RenderBlock::FloatingObject* RenderBlock::insertFloatingObject(RenderBox* o) // Create the list of special objects if we don't aleady have one if (!m_floatingObjects) - m_floatingObjects = adoptPtr(new FloatingObjects(this, isHorizontalWritingMode())); + createFloatingObjects(); else { // Don't insert the object again if it's already in the list const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set(); @@ -3800,8 +4056,14 @@ RenderBlock::FloatingObject* RenderBlock::insertFloatingObject(RenderBox* o) o->updateLogicalWidth(); o->computeAndSetBlockDirectionMargins(this); } + setLogicalWidthForFloat(newObj, logicalWidthForChild(o) + marginStartForChild(o) + marginEndForChild(o)); +#if ENABLE(CSS_SHAPES) + if (ShapeOutsideInfo* shapeOutside = o->shapeOutsideInfo()) + shapeOutside->setShapeSize(logicalWidthForChild(o), logicalHeightForChild(o)); +#endif + newObj->setShouldPaint(!o->hasSelfPaintingLayer()); // If a layer exists, the float will paint itself. Otherwise someone else will. newObj->setIsDescendant(true); newObj->m_renderer = o; @@ -3851,7 +4113,7 @@ void RenderBlock::removeFloatingObject(RenderBox* o) void RenderBlock::removeFloatingObjectsBelow(FloatingObject* lastFloat, int logicalOffset) { - if (!m_floatingObjects) + if (!containsFloats()) return; const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set(); @@ -3869,21 +4131,37 @@ void RenderBlock::removeFloatingObjectsBelow(FloatingObject* lastFloat, int logi LayoutPoint RenderBlock::computeLogicalLocationForFloat(const FloatingObject* floatingObject, LayoutUnit logicalTopOffset) const { RenderBox* childBox = floatingObject->renderer(); - LayoutUnit logicalRightOffset = logicalRightOffsetForContent(logicalTopOffset); // Constant part of right offset. LayoutUnit logicalLeftOffset = logicalLeftOffsetForContent(logicalTopOffset); // Constant part of left offset. + LayoutUnit logicalRightOffset; // Constant part of right offset. +#if ENABLE(CSS_SHAPES) + // FIXME Bug 102948: This only works for shape outside directly set on this block. + ShapeInsideInfo* shapeInsideInfo = this->shapeInsideInfo(); + // FIXME Bug 102846: Take into account the height of the content. The offset should be + // equal to the maximum segment length. + if (shapeInsideInfo && shapeInsideInfo->hasSegments() && shapeInsideInfo->segments().size() == 1) { + // FIXME Bug 102949: Add support for shapes with multipe segments. + + // The segment offsets are relative to the content box. + logicalRightOffset = logicalLeftOffset + shapeInsideInfo->segments()[0].logicalRight; + logicalLeftOffset += shapeInsideInfo->segments()[0].logicalLeft; + } else +#endif + logicalRightOffset = logicalRightOffsetForContent(logicalTopOffset); + LayoutUnit floatLogicalWidth = min(logicalWidthForFloat(floatingObject), logicalRightOffset - logicalLeftOffset); // The width we look for. LayoutUnit floatLogicalLeft; + bool insideFlowThread = flowThreadContainingBlock(); + if (childBox->style()->floating() == LeftFloat) { LayoutUnit heightRemainingLeft = 1; LayoutUnit heightRemainingRight = 1; - floatLogicalLeft = logicalLeftOffsetForLine(logicalTopOffset, logicalLeftOffset, false, &heightRemainingLeft); - // FIXME: LayoutUnit::epsilon is probably only necessary here due to lost precision elsewhere https://bugs.webkit.org/show_bug.cgi?id=94000 - while (logicalRightOffsetForLine(logicalTopOffset, logicalRightOffset, false, &heightRemainingRight) - floatLogicalLeft + LayoutUnit::epsilon() < floatLogicalWidth) { + floatLogicalLeft = logicalLeftOffsetForLineIgnoringShapeOutside(logicalTopOffset, logicalLeftOffset, false, &heightRemainingLeft); + while (logicalRightOffsetForLineIgnoringShapeOutside(logicalTopOffset, logicalRightOffset, false, &heightRemainingRight) - floatLogicalLeft < floatLogicalWidth) { logicalTopOffset += min(heightRemainingLeft, heightRemainingRight); - floatLogicalLeft = logicalLeftOffsetForLine(logicalTopOffset, logicalLeftOffset, false, &heightRemainingLeft); - if (inRenderFlowThread()) { + floatLogicalLeft = logicalLeftOffsetForLineIgnoringShapeOutside(logicalTopOffset, logicalLeftOffset, false, &heightRemainingLeft); + if (insideFlowThread) { // Have to re-evaluate all of our offsets, since they may have changed. logicalRightOffset = logicalRightOffsetForContent(logicalTopOffset); // Constant part of right offset. logicalLeftOffset = logicalLeftOffsetForContent(logicalTopOffset); // Constant part of left offset. @@ -3894,12 +4172,11 @@ LayoutPoint RenderBlock::computeLogicalLocationForFloat(const FloatingObject* fl } else { LayoutUnit heightRemainingLeft = 1; LayoutUnit heightRemainingRight = 1; - floatLogicalLeft = logicalRightOffsetForLine(logicalTopOffset, logicalRightOffset, false, &heightRemainingRight); - // FIXME: LayoutUnit::epsilon is probably only necessary here due to lost precision elsewhere https://bugs.webkit.org/show_bug.cgi?id=94000 - while (floatLogicalLeft - logicalLeftOffsetForLine(logicalTopOffset, logicalLeftOffset, false, &heightRemainingLeft) + LayoutUnit::epsilon() < floatLogicalWidth) { + floatLogicalLeft = logicalRightOffsetForLineIgnoringShapeOutside(logicalTopOffset, logicalRightOffset, false, &heightRemainingRight); + while (floatLogicalLeft - logicalLeftOffsetForLineIgnoringShapeOutside(logicalTopOffset, logicalLeftOffset, false, &heightRemainingLeft) < floatLogicalWidth) { logicalTopOffset += min(heightRemainingLeft, heightRemainingRight); - floatLogicalLeft = logicalRightOffsetForLine(logicalTopOffset, logicalRightOffset, false, &heightRemainingRight); - if (inRenderFlowThread()) { + floatLogicalLeft = logicalRightOffsetForLineIgnoringShapeOutside(logicalTopOffset, logicalRightOffset, false, &heightRemainingRight); + if (insideFlowThread) { // Have to re-evaluate all of our offsets, since they may have changed. logicalRightOffset = logicalRightOffsetForContent(logicalTopOffset); // Constant part of right offset. logicalLeftOffset = logicalLeftOffsetForContent(logicalTopOffset); // Constant part of left offset. @@ -3971,6 +4248,7 @@ bool RenderBlock::positionNewFloats() LayoutPoint floatLogicalLocation = computeLogicalLocationForFloat(floatingObject, logicalTop); setLogicalLeftForFloat(floatingObject, floatLogicalLocation.x()); + setLogicalLeftForChild(childBox, floatLogicalLocation.x() + childLogicalLeftMargin); setLogicalTopForChild(childBox, floatLogicalLocation.y() + marginBeforeForChild(childBox)); @@ -4000,6 +4278,7 @@ bool RenderBlock::positionNewFloats() floatLogicalLocation = computeLogicalLocationForFloat(floatingObject, newLogicalTop); setLogicalLeftForFloat(floatingObject, floatLogicalLocation.x()); + setLogicalLeftForChild(childBox, floatLogicalLocation.x() + childLogicalLeftMargin); setLogicalTopForChild(childBox, floatLogicalLocation.y() + marginBeforeForChild(childBox)); @@ -4010,6 +4289,7 @@ bool RenderBlock::positionNewFloats() } setLogicalTopForFloat(floatingObject, floatLogicalLocation.y()); + setLogicalHeightForFloat(floatingObject, logicalHeightForChild(childBox) + marginBeforeForChild(childBox) + marginAfterForChild(childBox)); m_floatingObjects->addPlacedObject(floatingObject); @@ -4129,7 +4409,7 @@ inline void RenderBlock::FloatIntervalSearchAdapter<FloatTypeValue>::collectIfNe return; // All the objects returned from the tree should be already placed. - ASSERT(r->isPlaced() && rangesIntersect(m_renderer->pixelSnappedLogicalTopForFloat(r), m_renderer->pixelSnappedLogicalBottomForFloat(r), m_lowValue, m_highValue)); + ASSERT(r->isPlaced() && rangesIntersect(m_renderer->logicalTopForFloat(r), m_renderer->logicalBottomForFloat(r), m_lowValue, m_highValue)); if (FloatTypeValue == FloatingObject::FloatLeft && m_renderer->logicalRightForFloat(r) > m_offset) { @@ -4144,6 +4424,10 @@ inline void RenderBlock::FloatIntervalSearchAdapter<FloatTypeValue>::collectIfNe if (m_heightRemaining) *m_heightRemaining = m_renderer->logicalBottomForFloat(r) - m_lowValue; } + +#if ENABLE(CSS_SHAPES) + m_last = r; +#endif } LayoutUnit RenderBlock::textIndentOffset() const @@ -4157,27 +4441,30 @@ LayoutUnit RenderBlock::textIndentOffset() const return minimumValueForLength(style()->textIndent(), cw, renderView); } -LayoutUnit RenderBlock::logicalLeftOffsetForContent(RenderRegion* region, LayoutUnit offsetFromLogicalTopOfFirstPage) const +LayoutUnit RenderBlock::logicalLeftOffsetForContent(RenderRegion* region) const { LayoutUnit logicalLeftOffset = style()->isHorizontalWritingMode() ? borderLeft() + paddingLeft() : borderTop() + paddingTop(); - if (!inRenderFlowThread()) + if (!region) return logicalLeftOffset; - LayoutRect boxRect = borderBoxRectInRegion(region, offsetFromLogicalTopOfFirstPage); + LayoutRect boxRect = borderBoxRectInRegion(region); return logicalLeftOffset + (isHorizontalWritingMode() ? boxRect.x() : boxRect.y()); } -LayoutUnit RenderBlock::logicalRightOffsetForContent(RenderRegion* region, LayoutUnit offsetFromLogicalTopOfFirstPage) const +LayoutUnit RenderBlock::logicalRightOffsetForContent(RenderRegion* region) const { LayoutUnit logicalRightOffset = style()->isHorizontalWritingMode() ? borderLeft() + paddingLeft() : borderTop() + paddingTop(); logicalRightOffset += availableLogicalWidth(); - if (!inRenderFlowThread()) + if (!region) return logicalRightOffset; - LayoutRect boxRect = borderBoxRectInRegion(region, offsetFromLogicalTopOfFirstPage); + LayoutRect boxRect = borderBoxRectInRegion(region); return logicalRightOffset - (logicalWidth() - (isHorizontalWritingMode() ? boxRect.maxX() : boxRect.maxY())); } -LayoutUnit RenderBlock::logicalLeftOffsetForLine(LayoutUnit logicalTop, LayoutUnit fixedOffset, bool applyTextIndent, LayoutUnit* heightRemaining, LayoutUnit logicalHeight) const +LayoutUnit RenderBlock::logicalLeftFloatOffsetForLine(LayoutUnit logicalTop, LayoutUnit fixedOffset, LayoutUnit* heightRemaining, LayoutUnit logicalHeight, ShapeOutsideFloatOffsetMode offsetMode) const { +#if !ENABLE(CSS_SHAPES) + UNUSED_PARAM(offsetMode); +#endif LayoutUnit left = fixedOffset; if (m_floatingObjects && m_floatingObjects->hasLeftObjects()) { if (heightRemaining) @@ -4185,8 +4472,25 @@ LayoutUnit RenderBlock::logicalLeftOffsetForLine(LayoutUnit logicalTop, LayoutUn FloatIntervalSearchAdapter<FloatingObject::FloatLeft> adapter(this, roundToInt(logicalTop), roundToInt(logicalTop + logicalHeight), left, heightRemaining); m_floatingObjects->placedFloatsTree().allOverlapsWithAdapter(adapter); + +#if ENABLE(CSS_SHAPES) + const FloatingObject* lastFloat = adapter.lastFloat(); + if (offsetMode == ShapeOutsideFloatShapeOffset && lastFloat) { + if (ShapeOutsideInfo* shapeOutside = lastFloat->renderer()->shapeOutsideInfo()) { + shapeOutside->computeSegmentsForContainingBlockLine(logicalTop, logicalTopForFloat(lastFloat), logicalHeight); + left += shapeOutside->rightSegmentMarginBoxDelta(); + } + } +#endif } + return left; +} + +LayoutUnit RenderBlock::adjustLogicalLeftOffsetForLine(LayoutUnit offsetFromFloats, bool applyTextIndent) const +{ + LayoutUnit left = offsetFromFloats; + if (applyTextIndent && style()->isLeftToRightDirection()) left += textIndentOffset(); @@ -4223,8 +4527,11 @@ LayoutUnit RenderBlock::logicalLeftOffsetForLine(LayoutUnit logicalTop, LayoutUn return left; } -LayoutUnit RenderBlock::logicalRightOffsetForLine(LayoutUnit logicalTop, LayoutUnit fixedOffset, bool applyTextIndent, LayoutUnit* heightRemaining, LayoutUnit logicalHeight) const +LayoutUnit RenderBlock::logicalRightFloatOffsetForLine(LayoutUnit logicalTop, LayoutUnit fixedOffset, LayoutUnit* heightRemaining, LayoutUnit logicalHeight, ShapeOutsideFloatOffsetMode offsetMode) const { +#if !ENABLE(CSS_SHAPES) + UNUSED_PARAM(offsetMode); +#endif LayoutUnit right = fixedOffset; if (m_floatingObjects && m_floatingObjects->hasRightObjects()) { if (heightRemaining) @@ -4233,8 +4540,26 @@ LayoutUnit RenderBlock::logicalRightOffsetForLine(LayoutUnit logicalTop, LayoutU LayoutUnit rightFloatOffset = fixedOffset; FloatIntervalSearchAdapter<FloatingObject::FloatRight> adapter(this, roundToInt(logicalTop), roundToInt(logicalTop + logicalHeight), rightFloatOffset, heightRemaining); m_floatingObjects->placedFloatsTree().allOverlapsWithAdapter(adapter); + +#if ENABLE(CSS_SHAPES) + const FloatingObject* lastFloat = adapter.lastFloat(); + if (offsetMode == ShapeOutsideFloatShapeOffset && lastFloat) { + if (ShapeOutsideInfo* shapeOutside = lastFloat->renderer()->shapeOutsideInfo()) { + shapeOutside->computeSegmentsForContainingBlockLine(logicalTop, logicalTopForFloat(lastFloat), logicalHeight); + rightFloatOffset += shapeOutside->leftSegmentMarginBoxDelta(); + } + } +#endif + right = min(right, rightFloatOffset); } + + return right; +} + +LayoutUnit RenderBlock::adjustLogicalRightOffsetForLine(LayoutUnit offsetFromFloats, bool applyTextIndent) const +{ + LayoutUnit right = offsetFromFloats; if (applyTextIndent && !style()->isLeftToRightDirection()) right -= textIndentOffset(); @@ -4509,8 +4834,7 @@ LayoutUnit RenderBlock::addOverhangingFloats(RenderBlock* child, bool makeChildP // We create the floating object list lazily. if (!m_floatingObjects) - m_floatingObjects = adoptPtr(new FloatingObjects(this, isHorizontalWritingMode())); - + createFloatingObjects(); m_floatingObjects->add(floatingObj); } } else { @@ -4582,7 +4906,7 @@ void RenderBlock::addIntrudingFloats(RenderBlock* prev, LayoutUnit logicalLeftOf // We create the floating object list lazily. if (!m_floatingObjects) - m_floatingObjects = adoptPtr(new FloatingObjects(this, isHorizontalWritingMode())); + createFloatingObjects(); m_floatingObjects->add(floatingObj); } } @@ -4602,7 +4926,7 @@ bool RenderBlock::containsFloat(RenderBox* renderer) const void RenderBlock::markAllDescendantsWithFloatsForLayout(RenderBox* floatToRemove, bool inLayout) { - if (!everHadLayout()) + if (!everHadLayout() && !containsFloats()) return; MarkingBehavior markParents = inLayout ? MarkOnlyThis : MarkContainingBlockChain; @@ -4679,7 +5003,7 @@ LayoutUnit RenderBlock::getClearDelta(RenderBox* child, LayoutUnit logicalTop) return newLogicalTop - logicalTop; RenderRegion* region = regionAtBlockOffset(logicalTopForChild(child)); - LayoutRect borderBox = child->borderBoxRectInRegion(region, offsetFromLogicalTopOfFirstPage() + logicalTopForChild(child), DoNotCacheRenderBoxRegionInfo); + LayoutRect borderBox = child->borderBoxRectInRegion(region, DoNotCacheRenderBoxRegionInfo); LayoutUnit childLogicalWidthAtOldLogicalTopOffset = isHorizontalWritingMode() ? borderBox.width() : borderBox.height(); // FIXME: None of this is right for perpendicular writing-mode children. @@ -4691,7 +5015,7 @@ LayoutUnit RenderBlock::getClearDelta(RenderBox* child, LayoutUnit logicalTop) child->setLogicalTop(newLogicalTop); child->updateLogicalWidth(); region = regionAtBlockOffset(logicalTopForChild(child)); - borderBox = child->borderBoxRectInRegion(region, offsetFromLogicalTopOfFirstPage() + logicalTopForChild(child), DoNotCacheRenderBoxRegionInfo); + borderBox = child->borderBoxRectInRegion(region, DoNotCacheRenderBoxRegionInfo); LayoutUnit childLogicalWidthAtNewLogicalTopOffset = isHorizontalWritingMode() ? borderBox.width() : borderBox.height(); child->setLogicalTop(childOldLogicalTop); @@ -4768,7 +5092,7 @@ bool RenderBlock::nodeAtPoint(const HitTestRequest& request, HitTestResult& resu // Hit test contents if we don't have columns. if (!hasColumns()) { if (hitTestContents(request, result, locationInContainer, toLayoutPoint(scrolledOffset), hitTestAction)) { - updateHitTestResult(result, locationInContainer.point() - localOffset); + updateHitTestResult(result, flipForWritingMode(locationInContainer.point() - localOffset)); return true; } if (hitTestAction == HitTestFloat && hitTestFloats(request, result, locationInContainer, toLayoutPoint(scrolledOffset))) @@ -4779,6 +5103,15 @@ bool RenderBlock::nodeAtPoint(const HitTestRequest& request, HitTestResult& resu } } + // Check if the point is outside radii. + if (!isRenderView() && style()->hasBorderRadius()) { + LayoutRect borderRect = borderBoxRect(); + borderRect.moveBy(adjustedLocation); + RoundedRect border = style()->getRoundedBorderFor(borderRect, view()); + if (!locationInContainer.intersects(border)) + return false; + } + // Now hit test our background if (hitTestAction == HitTestBlockBackground || hitTestAction == HitTestChildBlockBackground) { LayoutRect boundsRect(adjustedLocation, size()); @@ -4833,7 +5166,10 @@ public: { int colCount = m_colInfo->columnCount(); m_colIndex = colCount - 1; - m_currLogicalTopOffset = colCount * m_colInfo->columnHeight() * m_direction; + + m_currLogicalTopOffset = m_block.initialBlockOffsetForPainting(); + m_currLogicalTopOffset = colCount * m_block.blockDeltaForPaintingNextColumn(); + update(); } @@ -4851,12 +5187,6 @@ public: { LayoutUnit currLogicalLeftOffset = (m_isHorizontal ? m_colRect.x() : m_colRect.y()) - m_logicalLeft; offset += m_isHorizontal ? LayoutSize(currLogicalLeftOffset, m_currLogicalTopOffset) : LayoutSize(m_currLogicalTopOffset, currLogicalLeftOffset); - if (m_colInfo->progressionAxis() == ColumnInfo::BlockAxis) { - if (m_isHorizontal) - offset.expand(0, m_colRect.y() - m_block.borderTop() - m_block.paddingTop()); - else - offset.expand(m_colRect.x() - m_block.borderLeft() - m_block.paddingLeft(), 0); - } } private: @@ -4864,10 +5194,9 @@ private: { if (m_colIndex < 0) return; - m_colRect = m_block.columnRectAt(const_cast<ColumnInfo*>(m_colInfo), m_colIndex); m_block.flipForWritingMode(m_colRect); - m_currLogicalTopOffset -= (m_isHorizontal ? m_colRect.height() : m_colRect.width()) * m_direction; + m_currLogicalTopOffset -= m_block.blockDeltaForPaintingNextColumn(); } const RenderBlock& m_block; @@ -4943,22 +5272,22 @@ Position RenderBlock::positionForBox(InlineBox *box, bool start) const if (!box) return Position(); - if (!box->renderer()->node()) - return createLegacyEditingPosition(node(), start ? caretMinOffset() : caretMaxOffset()); + if (!box->renderer()->nonPseudoNode()) + return createLegacyEditingPosition(nonPseudoNode(), start ? caretMinOffset() : caretMaxOffset()); if (!box->isInlineTextBox()) - return createLegacyEditingPosition(box->renderer()->node(), start ? box->renderer()->caretMinOffset() : box->renderer()->caretMaxOffset()); + return createLegacyEditingPosition(box->renderer()->nonPseudoNode(), start ? box->renderer()->caretMinOffset() : box->renderer()->caretMaxOffset()); InlineTextBox* textBox = toInlineTextBox(box); - return createLegacyEditingPosition(box->renderer()->node(), start ? textBox->start() : textBox->start() + textBox->len()); + return createLegacyEditingPosition(box->renderer()->nonPseudoNode(), start ? textBox->start() : textBox->start() + textBox->len()); } static inline bool isEditingBoundary(RenderObject* ancestor, RenderObject* child) { - ASSERT(!ancestor || ancestor->node()); - ASSERT(child && child->node()); + ASSERT(!ancestor || ancestor->nonPseudoNode()); + ASSERT(child && child->nonPseudoNode()); return !ancestor || !ancestor->parent() || (ancestor->hasLayer() && ancestor->parent()->isRenderView()) - || ancestor->node()->rendererIsEditable() == child->node()->rendererIsEditable(); + || ancestor->nonPseudoNode()->rendererIsEditable() == child->nonPseudoNode()->rendererIsEditable(); } // FIXME: This function should go on RenderObject as an instance method. Then @@ -4974,14 +5303,14 @@ static VisiblePosition positionForPointRespectingEditingBoundaries(RenderBlock* LayoutPoint pointInChildCoordinates(toLayoutPoint(pointInParentCoordinates - childLocation)); // If this is an anonymous renderer, we just recur normally - Node* childNode = child->node(); + Node* childNode = child->nonPseudoNode(); if (!childNode) return child->positionForPoint(pointInChildCoordinates); // Otherwise, first make sure that the editability of the parent and child agree. // If they don't agree, then we return a visible position just before or after the child RenderObject* ancestor = parent; - while (ancestor && !ancestor->node()) + while (ancestor && !ancestor->nonPseudoNode()) ancestor = ancestor->parent(); // If we can't find an ancestor to check editability on, or editability is unchanged, we recur like normal @@ -5039,7 +5368,7 @@ VisiblePosition RenderBlock::positionForPointWithInlineChildren(const LayoutPoin } } - bool moveCaretToBoundary = document()->frame()->editor()->behavior().shouldMoveCaretToHorizontalBoundaryWhenPastTopOrBottom(); + bool moveCaretToBoundary = document()->frame()->editor().behavior().shouldMoveCaretToHorizontalBoundaryWhenPastTopOrBottom(); if (!moveCaretToBoundary && !closestBox && lastRootBoxWithChildren) { // y coordinate is below last root line box, pretend we hit it @@ -5218,7 +5547,7 @@ void RenderBlock::setDesiredColumnCountAndWidth(int count, LayoutUnit width) bool destroyColumns = !requiresColumns(count); if (destroyColumns) { if (hasColumns()) { - delete gColumnInfoMap->take(this); + gColumnInfoMap->take(this); setHasColumns(false); } } else { @@ -5229,7 +5558,7 @@ void RenderBlock::setDesiredColumnCountAndWidth(int count, LayoutUnit width) if (!gColumnInfoMap) gColumnInfoMap = new ColumnInfoMap; info = new ColumnInfo; - gColumnInfoMap->add(this, info); + gColumnInfoMap->add(this, adoptPtr(info)); setHasColumns(true); } info->setDesiredColumnCount(count); @@ -5300,9 +5629,9 @@ LayoutRect RenderBlock::columnRectAt(ColumnInfo* colInfo, unsigned index) const // Compute the appropriate rect based off our information. LayoutUnit colLogicalWidth = colInfo->desiredColumnWidth(); LayoutUnit colLogicalHeight = colInfo->columnHeight(); - LayoutUnit colLogicalTop = borderBefore() + paddingBefore(); + LayoutUnit colLogicalTop = borderAndPaddingBefore(); LayoutUnit colLogicalLeft = logicalLeftOffsetForContent(); - int colGap = columnGap(); + LayoutUnit colGap = columnGap(); if (colInfo->progressionAxis() == ColumnInfo::InlineAxis) { if (style()->isLeftToRightDirection() ^ colInfo->progressionIsReversed()) colLogicalLeft += index * (colLogicalWidth + colGap); @@ -5330,7 +5659,7 @@ bool RenderBlock::relayoutForPagination(bool hasSpecifiedPageLogicalHeight, Layo addOverflowFromInlineChildren(); else addOverflowFromBlockChildren(); - LayoutUnit layoutOverflowLogicalBottom = (isHorizontalWritingMode() ? layoutOverflowRect().maxY() : layoutOverflowRect().maxX()) - borderBefore() - paddingBefore(); + LayoutUnit layoutOverflowLogicalBottom = (isHorizontalWritingMode() ? layoutOverflowRect().maxY() : layoutOverflowRect().maxX()) - borderAndPaddingBefore(); // FIXME: We don't balance properly at all in the presence of forced page breaks. We need to understand what // the distance between forced page breaks is so that we can avoid making the minimum column height too tall. @@ -5344,7 +5673,7 @@ bool RenderBlock::relayoutForPagination(bool hasSpecifiedPageLogicalHeight, Layo // maximum page break distance. if (!pageLogicalHeight) { LayoutUnit distanceBetweenBreaks = max<LayoutUnit>(colInfo->maximumDistanceBetweenForcedBreaks(), - view()->layoutState()->pageLogicalOffset(this, borderBefore() + paddingBefore() + layoutOverflowLogicalBottom) - colInfo->forcedBreakOffset()); + view()->layoutState()->pageLogicalOffset(this, borderAndPaddingBefore() + layoutOverflowLogicalBottom) - colInfo->forcedBreakOffset()); columnHeight = max(colInfo->minimumColumnHeight(), distanceBetweenBreaks); } } else if (layoutOverflowLogicalBottom > boundedMultiply(pageLogicalHeight, desiredColumnCount)) { @@ -5364,7 +5693,7 @@ bool RenderBlock::relayoutForPagination(bool hasSpecifiedPageLogicalHeight, Layo colInfo->setColumnCountAndHeight(ceilf((float)layoutOverflowLogicalBottom / pageLogicalHeight), pageLogicalHeight); if (columnCount(colInfo)) { - setLogicalHeight(borderBefore() + paddingBefore() + colInfo->columnHeight() + borderAfter() + paddingAfter() + scrollbarLogicalHeight()); + setLogicalHeight(borderAndPaddingBefore() + colInfo->columnHeight() + borderAndPaddingAfter() + scrollbarLogicalHeight()); m_overflow.clear(); } else m_overflow = savedOverflow.release(); @@ -5476,13 +5805,13 @@ void RenderBlock::adjustRectForColumns(LayoutRect& r) const LayoutRect result; bool isHorizontal = isHorizontalWritingMode(); - LayoutUnit beforeBorderPadding = borderBefore() + paddingBefore(); + LayoutUnit beforeBorderPadding = borderAndPaddingBefore(); LayoutUnit colHeight = colInfo->columnHeight(); if (!colHeight) return; LayoutUnit startOffset = max(isHorizontal ? r.y() : r.x(), beforeBorderPadding); - LayoutUnit endOffset = min<LayoutUnit>(isHorizontal ? r.maxY() : r.maxX(), beforeBorderPadding + colCount * colHeight); + LayoutUnit endOffset = max(min<LayoutUnit>(isHorizontal ? r.maxY() : r.maxX(), beforeBorderPadding + colCount * colHeight), beforeBorderPadding); // FIXME: Can overflow on fast/block/float/float-not-removed-from-next-sibling4.html, see https://bugs.webkit.org/show_bug.cgi?id=68744 unsigned startColumn = (startOffset - beforeBorderPadding) / colHeight; @@ -5525,7 +5854,7 @@ LayoutPoint RenderBlock::flipForWritingModeIncludingColumns(const LayoutPoint& p return point; ColumnInfo* colInfo = columnInfo(); LayoutUnit columnLogicalHeight = colInfo->columnHeight(); - LayoutUnit expandedLogicalHeight = borderBefore() + paddingBefore() + columnCount(colInfo) * columnLogicalHeight + borderAfter() + paddingAfter() + scrollbarLogicalHeight(); + LayoutUnit expandedLogicalHeight = borderAndPaddingBefore() + columnCount(colInfo) * columnLogicalHeight + borderAndPaddingAfter() + scrollbarLogicalHeight(); if (isHorizontalWritingMode()) return LayoutPoint(point.x(), expandedLogicalHeight - point.y()); return LayoutPoint(expandedLogicalHeight - point.x(), point.y()); @@ -5539,7 +5868,7 @@ void RenderBlock::adjustStartEdgeForWritingModeIncludingColumns(LayoutRect& rect ColumnInfo* colInfo = columnInfo(); LayoutUnit columnLogicalHeight = colInfo->columnHeight(); - LayoutUnit expandedLogicalHeight = borderBefore() + paddingBefore() + columnCount(colInfo) * columnLogicalHeight + borderAfter() + paddingAfter() + scrollbarLogicalHeight(); + LayoutUnit expandedLogicalHeight = borderAndPaddingBefore() + columnCount(colInfo) * columnLogicalHeight + borderAndPaddingAfter() + scrollbarLogicalHeight(); if (isHorizontalWritingMode()) rect.setY(expandedLogicalHeight - rect.maxY()); @@ -5561,7 +5890,7 @@ void RenderBlock::adjustForColumns(LayoutSize& offset, const LayoutPoint& point) for (unsigned i = 0; i < colCount; ++i) { // Compute the edges for a given column in the block progression direction. - LayoutRect sliceRect = LayoutRect(logicalLeft, borderBefore() + paddingBefore() + i * colLogicalHeight, colLogicalWidth, colLogicalHeight); + LayoutRect sliceRect = LayoutRect(logicalLeft, borderAndPaddingBefore() + i * colLogicalHeight, colLogicalWidth, colLogicalHeight); if (!isHorizontalWritingMode()) sliceRect = sliceRect.transposedRect(); @@ -5573,7 +5902,7 @@ void RenderBlock::adjustForColumns(LayoutSize& offset, const LayoutPoint& point) if (colInfo->progressionAxis() == ColumnInfo::InlineAxis) offset.expand(columnRectAt(colInfo, i).x() - logicalLeft, -logicalOffset); else - offset.expand(0, columnRectAt(colInfo, i).y() - logicalOffset - borderBefore() - paddingBefore()); + offset.expand(0, columnRectAt(colInfo, i).y() - logicalOffset - borderAndPaddingBefore()); return; } } else { @@ -5581,61 +5910,55 @@ void RenderBlock::adjustForColumns(LayoutSize& offset, const LayoutPoint& point) if (colInfo->progressionAxis() == ColumnInfo::InlineAxis) offset.expand(-logicalOffset, columnRectAt(colInfo, i).y() - logicalLeft); else - offset.expand(columnRectAt(colInfo, i).x() - logicalOffset - borderBefore() - paddingBefore(), 0); + offset.expand(columnRectAt(colInfo, i).x() - logicalOffset - borderAndPaddingBefore(), 0); return; } } } } +void RenderBlock::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const +{ + if (childrenInline()) { + // FIXME: Remove this const_cast. + const_cast<RenderBlock*>(this)->computeInlinePreferredLogicalWidths(minLogicalWidth, maxLogicalWidth); + } else + computeBlockPreferredLogicalWidths(minLogicalWidth, maxLogicalWidth); + + maxLogicalWidth = max(minLogicalWidth, maxLogicalWidth); + + if (!style()->autoWrap() && childrenInline()) { + // A horizontal marquee with inline children has no minimum width. + if (layer() && layer()->marquee() && layer()->marquee()->isHorizontal()) + minLogicalWidth = 0; + } + + if (isTableCell()) { + Length tableCellWidth = toRenderTableCell(this)->styleOrColLogicalWidth(); + if (tableCellWidth.isFixed() && tableCellWidth.value() > 0) + maxLogicalWidth = max(minLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(tableCellWidth.value())); + } + + int scrollbarWidth = instrinsicScrollbarLogicalWidth(); + maxLogicalWidth += scrollbarWidth; + minLogicalWidth += scrollbarWidth; +} + void RenderBlock::computePreferredLogicalWidths() { ASSERT(preferredLogicalWidthsDirty()); updateFirstLetter(); + m_minPreferredLogicalWidth = 0; + m_maxPreferredLogicalWidth = 0; + RenderStyle* styleToUse = style(); if (!isTableCell() && styleToUse->logicalWidth().isFixed() && styleToUse->logicalWidth().value() >= 0 - && style()->marqueeBehavior() != MALTERNATE && !(isDeprecatedFlexItem() && !styleToUse->logicalWidth().intValue())) + && !(isDeprecatedFlexItem() && !styleToUse->logicalWidth().intValue())) m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalWidth().value()); - else { - m_minPreferredLogicalWidth = 0; - m_maxPreferredLogicalWidth = 0; - - if (childrenInline()) - computeInlinePreferredLogicalWidths(); - else - computeBlockPreferredLogicalWidths(); - - m_maxPreferredLogicalWidth = max(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth); - - if (!styleToUse->autoWrap() && childrenInline()) { - m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth; - - // A horizontal marquee with inline children has no minimum width. - if (layer() && layer()->marquee() && layer()->marquee()->isHorizontal()) - m_minPreferredLogicalWidth = 0; - } - - int scrollbarWidth = 0; - // FIXME: This should only be done for horizontal writing mode. - // For vertical writing mode, this should check overflowX and use the horizontalScrollbarHeight. - if (hasOverflowClip() && styleToUse->overflowY() == OSCROLL) { - layer()->setHasVerticalScrollbar(true); - scrollbarWidth = verticalScrollbarWidth(); - m_maxPreferredLogicalWidth += scrollbarWidth; - } - - if (isTableCell()) { - Length w = toRenderTableCell(this)->styleOrColLogicalWidth(); - if (w.isFixed() && w.value() > 0) { - m_maxPreferredLogicalWidth = max(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(w.value())); - scrollbarWidth = 0; - } - } - - m_minPreferredLogicalWidth += scrollbarWidth; - } + else + computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth); if (styleToUse->logicalMinWidth().isFixed() && styleToUse->logicalMinWidth().value() > 0) { m_maxPreferredLogicalWidth = max(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMinWidth().value())); @@ -5783,7 +6106,7 @@ static inline LayoutUnit adjustFloatForSubPixelLayout(float value) } -void RenderBlock::computeInlinePreferredLogicalWidths() +void RenderBlock::computeInlinePreferredLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) { float inlineMax = 0; float inlineMin = 0; @@ -5806,7 +6129,12 @@ void RenderBlock::computeInlinePreferredLogicalWidths() autoWrap = oldAutoWrap = styleToUse->autoWrap(); InlineMinMaxIterator childIterator(this); - bool addedTextIndent = false; // Only gets added in once. + + // Only gets added to the max preffered width once. + bool addedTextIndent = false; + // Signals the text indent was more negative than the min preferred width + bool hasRemainingNegativeTextIndent = false; + LayoutUnit textIndent = minimumValueForLength(styleToUse->textIndent(), cw, view()); RenderObject* prevFloat = 0; bool isPrevChildInlineFlow = false; @@ -5900,22 +6228,21 @@ void RenderBlock::computeInlinePreferredLogicalWidths() bool canBreakReplacedElement = !child->isImage() || allowImagesToBreak; if ((canBreakReplacedElement && (autoWrap || oldAutoWrap) && (!isPrevChildInlineFlow || shouldBreakLineAfterText)) || clearPreviousFloat) { - updatePreferredWidth(m_minPreferredLogicalWidth, inlineMin); + updatePreferredWidth(minLogicalWidth, inlineMin); inlineMin = 0; } // If we're supposed to clear the previous float, then terminate maxwidth as well. if (clearPreviousFloat) { - updatePreferredWidth(m_maxPreferredLogicalWidth, inlineMax); + updatePreferredWidth(maxLogicalWidth, inlineMax); inlineMax = 0; } // Add in text-indent. This is added in only once. - LayoutUnit ti = 0; - if (!addedTextIndent) { - ti = textIndent; - childMin += ti.ceilToFloat(); - childMax += ti.ceilToFloat(); + if (!addedTextIndent && !child->isFloating()) { + LayoutUnit ceiledIndent = textIndent.ceilToFloat(); + childMin += ceiledIndent; + childMax += ceiledIndent; if (childMin < 0) textIndent = adjustFloatForSubPixelLayout(childMin); @@ -5928,19 +6255,19 @@ void RenderBlock::computeInlinePreferredLogicalWidths() if (!autoWrap || !canBreakReplacedElement || (isPrevChildInlineFlow && !shouldBreakLineAfterText)) { if (child->isFloating()) - updatePreferredWidth(m_minPreferredLogicalWidth, childMin); + updatePreferredWidth(minLogicalWidth, childMin); else inlineMin += childMin; } else { // Now check our line. - updatePreferredWidth(m_minPreferredLogicalWidth, childMin); + updatePreferredWidth(minLogicalWidth, childMin); // Now start a new line. inlineMin = 0; } if (autoWrap && canBreakReplacedElement && isPrevChildInlineFlow) { - updatePreferredWidth(m_minPreferredLogicalWidth, inlineMin); + updatePreferredWidth(minLogicalWidth, inlineMin); inlineMin = 0; } @@ -5955,7 +6282,7 @@ void RenderBlock::computeInlinePreferredLogicalWidths() RenderText* t = toRenderText(child); if (t->isWordBreak()) { - updatePreferredWidth(m_minPreferredLogicalWidth, inlineMin); + updatePreferredWidth(minLogicalWidth, inlineMin); inlineMin = 0; continue; } @@ -5979,7 +6306,7 @@ void RenderBlock::computeInlinePreferredLogicalWidths() // This text object will not be rendered, but it may still provide a breaking opportunity. if (!hasBreak && childMax == 0) { if (autoWrap && (beginWS || endWS)) { - updatePreferredWidth(m_minPreferredLogicalWidth, inlineMin); + updatePreferredWidth(minLogicalWidth, inlineMin); inlineMin = 0; } continue; @@ -5992,18 +6319,24 @@ void RenderBlock::computeInlinePreferredLogicalWidths() // Add in text-indent. This is added in only once. float ti = 0; - if (!addedTextIndent) { + if (!addedTextIndent || hasRemainingNegativeTextIndent) { ti = textIndent.ceilToFloat(); - childMin += ti; - childMax += ti; beginMin += ti; - beginMax += ti; - if (childMin < 0) - textIndent = childMin; - else + // It the text indent negative and larger than the child minimum, we re-use the remainder + // in future minimum calculations, but using the negative value again on the maximum + // will lead to under-counting the max pref width. + if (!addedTextIndent) { + childMax += ti; + beginMax += ti; addedTextIndent = true; + } + + if (childMin < 0) { + textIndent = childMin; + hasRemainingNegativeTextIndent = true; + } } // If we have no breakable characters at all, @@ -6016,10 +6349,10 @@ void RenderBlock::computeInlinePreferredLogicalWidths() // we start and end with whitespace. if (beginWS) // Go ahead and end the current line. - updatePreferredWidth(m_minPreferredLogicalWidth, inlineMin); + updatePreferredWidth(minLogicalWidth, inlineMin); else { inlineMin += beginMin; - updatePreferredWidth(m_minPreferredLogicalWidth, inlineMin); + updatePreferredWidth(minLogicalWidth, inlineMin); childMin -= ti; } @@ -6028,11 +6361,11 @@ void RenderBlock::computeInlinePreferredLogicalWidths() if (endWS) { // We end in whitespace, which means we can go ahead // and end our current line. - updatePreferredWidth(m_minPreferredLogicalWidth, inlineMin); + updatePreferredWidth(minLogicalWidth, inlineMin); inlineMin = 0; shouldBreakLineAfterText = false; } else { - updatePreferredWidth(m_minPreferredLogicalWidth, inlineMin); + updatePreferredWidth(minLogicalWidth, inlineMin); inlineMin = endMin; shouldBreakLineAfterText = true; } @@ -6040,8 +6373,8 @@ void RenderBlock::computeInlinePreferredLogicalWidths() if (hasBreak) { inlineMax += beginMax; - updatePreferredWidth(m_maxPreferredLogicalWidth, inlineMax); - updatePreferredWidth(m_maxPreferredLogicalWidth, childMax); + updatePreferredWidth(maxLogicalWidth, inlineMax); + updatePreferredWidth(maxLogicalWidth, childMax); inlineMax = endMax; addedTextIndent = true; } else @@ -6052,8 +6385,8 @@ void RenderBlock::computeInlinePreferredLogicalWidths() if (child->isListMarker()) stripFrontSpaces = true; } else { - updatePreferredWidth(m_minPreferredLogicalWidth, inlineMin); - updatePreferredWidth(m_maxPreferredLogicalWidth, inlineMax); + updatePreferredWidth(minLogicalWidth, inlineMin); + updatePreferredWidth(maxLogicalWidth, inlineMax); inlineMin = inlineMax = 0; stripFrontSpaces = true; trailingSpaceChild = 0; @@ -6071,11 +6404,11 @@ void RenderBlock::computeInlinePreferredLogicalWidths() if (styleToUse->collapseWhiteSpace()) stripTrailingSpace(inlineMax, inlineMin, trailingSpaceChild); - updatePreferredWidth(m_minPreferredLogicalWidth, inlineMin); - updatePreferredWidth(m_maxPreferredLogicalWidth, inlineMax); + updatePreferredWidth(minLogicalWidth, inlineMin); + updatePreferredWidth(maxLogicalWidth, inlineMax); } -void RenderBlock::computeBlockPreferredLogicalWidths() +void RenderBlock::computeBlockPreferredLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const { RenderStyle* styleToUse = style(); bool nowrap = styleToUse->whiteSpace() == NOWRAP; @@ -6094,11 +6427,11 @@ void RenderBlock::computeBlockPreferredLogicalWidths() if (child->isFloating() || (child->isBox() && toRenderBox(child)->avoidsFloats())) { LayoutUnit floatTotalWidth = floatLeftWidth + floatRightWidth; if (childStyle->clear() & CLEFT) { - m_maxPreferredLogicalWidth = max(floatTotalWidth, m_maxPreferredLogicalWidth); + maxLogicalWidth = max(floatTotalWidth, maxLogicalWidth); floatLeftWidth = 0; } if (childStyle->clear() & CRIGHT) { - m_maxPreferredLogicalWidth = max(floatTotalWidth, m_maxPreferredLogicalWidth); + maxLogicalWidth = max(floatTotalWidth, maxLogicalWidth); floatRightWidth = 0; } } @@ -6129,11 +6462,11 @@ void RenderBlock::computeBlockPreferredLogicalWidths() } LayoutUnit w = childMinPreferredLogicalWidth + margin; - m_minPreferredLogicalWidth = max(w, m_minPreferredLogicalWidth); + minLogicalWidth = max(w, minLogicalWidth); // IE ignores tables for calculation of nowrap. Makes some sense. if (nowrap && !child->isTable()) - m_maxPreferredLogicalWidth = max(w, m_maxPreferredLogicalWidth); + maxLogicalWidth = max(w, maxLogicalWidth); w = childMaxPreferredLogicalWidth + margin; @@ -6151,26 +6484,26 @@ void RenderBlock::computeBlockPreferredLogicalWidths() w = max(w, floatLeftWidth + floatRightWidth); } else - m_maxPreferredLogicalWidth = max(floatLeftWidth + floatRightWidth, m_maxPreferredLogicalWidth); + maxLogicalWidth = max(floatLeftWidth + floatRightWidth, maxLogicalWidth); floatLeftWidth = floatRightWidth = 0; } if (child->isFloating()) { - if (styleToUse->floating() == LeftFloat) + if (childStyle->floating() == LeftFloat) floatLeftWidth += w; else floatRightWidth += w; } else - m_maxPreferredLogicalWidth = max(w, m_maxPreferredLogicalWidth); + maxLogicalWidth = max(w, maxLogicalWidth); child = child->nextSibling(); } // Always make sure these values are non-negative. - m_minPreferredLogicalWidth = max<LayoutUnit>(0, m_minPreferredLogicalWidth); - m_maxPreferredLogicalWidth = max<LayoutUnit>(0, m_maxPreferredLogicalWidth); + minLogicalWidth = max<LayoutUnit>(0, minLogicalWidth); + maxLogicalWidth = max<LayoutUnit>(0, maxLogicalWidth); - m_maxPreferredLogicalWidth = max(floatLeftWidth + floatRightWidth, m_maxPreferredLogicalWidth); + maxLogicalWidth = max(floatLeftWidth + floatRightWidth, maxLogicalWidth); } bool RenderBlock::hasLineIfEmpty() const @@ -6181,7 +6514,7 @@ bool RenderBlock::hasLineIfEmpty() const if (node()->isRootEditableElement()) return true; - if (node()->isShadowRoot() && toShadowRoot(node())->host()->hasTagName(inputTag)) + if (node()->isShadowRoot() && isHTMLInputElement(toShadowRoot(node())->host())) return true; return false; @@ -6329,10 +6662,17 @@ RenderBlock* RenderBlock::firstLineBlock() const if (hasPseudo) break; RenderObject* parentBlock = firstLineBlock->parent(); - if (firstLineBlock->isReplaced() || firstLineBlock->isFloating() || - !parentBlock || parentBlock->firstChild() != firstLineBlock || !parentBlock->isBlockFlow()) + // We include isRenderButton in this check because buttons are + // implemented using flex box but should still support first-line. The + // flex box spec requires that flex box does not support first-line, + // though. + // FIXME: Remove when buttons are implemented with align-items instead + // of flexbox. + if (firstLineBlock->isReplaced() || firstLineBlock->isFloating() + || !parentBlock || parentBlock->firstChild() != firstLineBlock || !parentBlock->isBlockFlow() + || (parentBlock->isFlexibleBox() && !parentBlock->isRenderButton())) break; - ASSERT(parentBlock->isRenderBlock()); + ASSERT_WITH_SECURITY_IMPLICATION(parentBlock->isRenderBlock()); firstLineBlock = toRenderBlock(parentBlock); } @@ -6374,14 +6714,21 @@ static inline RenderObject* findFirstLetterBlock(RenderBlock* start) { RenderObject* firstLetterBlock = start; while (true) { + // We include isRenderButton in these two checks because buttons are + // implemented using flex box but should still support first-letter. + // The flex box spec requires that flex box does not support + // first-letter, though. + // FIXME: Remove when buttons are implemented with align-items instead + // of flexbox. bool canHaveFirstLetterRenderer = firstLetterBlock->style()->hasPseudoStyle(FIRST_LETTER) - && firstLetterBlock->canHaveGeneratedChildren(); + && firstLetterBlock->canHaveGeneratedChildren() + && (!firstLetterBlock->isFlexibleBox() || firstLetterBlock->isRenderButton()); if (canHaveFirstLetterRenderer) return firstLetterBlock; RenderObject* parentBlock = firstLetterBlock->parent(); if (firstLetterBlock->isReplaced() || !parentBlock || parentBlock->firstChild() != firstLetterBlock || - !parentBlock->isBlockFlow()) + !parentBlock->isBlockFlow() || (parentBlock->isFlexibleBox() && !parentBlock->isRenderButton())) return 0; firstLetterBlock = parentBlock; } @@ -6400,9 +6747,9 @@ void RenderBlock::updateFirstLetterStyle(RenderObject* firstLetterBlock, RenderO // The first-letter renderer needs to be replaced. Create a new renderer of the right type. RenderObject* newFirstLetter; if (pseudoStyle->display() == INLINE) - newFirstLetter = new (renderArena()) RenderInline(document()); + newFirstLetter = RenderInline::createAnonymous(document()); else - newFirstLetter = new (renderArena()) RenderBlock(document()); + newFirstLetter = RenderBlock::createAnonymous(document()); newFirstLetter->setStyle(pseudoStyle); // Move the first letter into the new renderer. @@ -6446,9 +6793,9 @@ void RenderBlock::createFirstLetterRenderer(RenderObject* firstLetterBlock, Rend RenderStyle* pseudoStyle = styleForFirstLetter(firstLetterBlock, firstLetterContainer); RenderObject* firstLetter = 0; if (pseudoStyle->display() == INLINE) - firstLetter = new (renderArena()) RenderInline(document()); + firstLetter = RenderInline::createAnonymous(document()); else - firstLetter = new (renderArena()) RenderBlock(document()); + firstLetter = RenderBlock::createAnonymous(document()); firstLetter->setStyle(pseudoStyle); firstLetterContainer->addChild(firstLetter, currentChild); @@ -6571,28 +6918,6 @@ static bool shouldCheckLines(RenderObject* obj) && (!obj->isDeprecatedFlexibleBox() || obj->style()->boxOrient() == VERTICAL); } -static RootInlineBox* getLineAtIndex(RenderBlock* block, int i, int& count) -{ - if (block->style()->visibility() == VISIBLE) { - if (block->childrenInline()) { - for (RootInlineBox* box = block->firstRootBox(); box; box = box->nextRootBox()) { - if (count++ == i) - return box; - } - } - else { - for (RenderObject* obj = block->firstChild(); obj; obj = obj->nextSibling()) { - if (shouldCheckLines(obj)) { - RootInlineBox *box = getLineAtIndex(toRenderBlock(obj), i, count); - if (box) - return box; - } - } - } - } - return 0; -} - static int getHeightForLineCount(RenderBlock* block, int l, bool includeBottom, int& count) { if (block->style()->visibility() == VISIBLE) { @@ -6620,23 +6945,54 @@ static int getHeightForLineCount(RenderBlock* block, int l, bool includeBottom, return -1; } -RootInlineBox* RenderBlock::lineAtIndex(int i) +RootInlineBox* RenderBlock::lineAtIndex(int i) const { - int count = 0; - return getLineAtIndex(this, i, count); + ASSERT(i >= 0); + + if (style()->visibility() != VISIBLE) + return 0; + + if (childrenInline()) { + for (RootInlineBox* box = firstRootBox(); box; box = box->nextRootBox()) + if (!i--) + return box; + } else { + for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { + if (!shouldCheckLines(child)) + continue; + if (RootInlineBox* box = toRenderBlock(child)->lineAtIndex(i)) + return box; + } + } + + return 0; } -int RenderBlock::lineCount() +int RenderBlock::lineCount(const RootInlineBox* stopRootInlineBox, bool* found) const { int count = 0; + if (style()->visibility() == VISIBLE) { if (childrenInline()) - for (RootInlineBox* box = firstRootBox(); box; box = box->nextRootBox()) + for (RootInlineBox* box = firstRootBox(); box; box = box->nextRootBox()) { count++; + if (box == stopRootInlineBox) { + if (found) + *found = true; + break; + } + } else for (RenderObject* obj = firstChild(); obj; obj = obj->nextSibling()) - if (shouldCheckLines(obj)) - count += toRenderBlock(obj)->lineCount(); + if (shouldCheckLines(obj)) { + bool recursiveFound = false; + count += toRenderBlock(obj)->lineCount(stopRootInlineBox, &recursiveFound); + if (recursiveFound) { + if (found) + *found = true; + break; + } + } } return count; } @@ -6691,32 +7047,30 @@ void RenderBlock::adjustForBorderFit(LayoutUnit x, LayoutUnit& left, LayoutUnit& } } -void RenderBlock::borderFitAdjust(LayoutRect& rect) const +void RenderBlock::fitBorderToLinesIfNeeded() { - if (style()->borderFit() == BorderFitBorder) + if (style()->borderFit() == BorderFitBorder || hasOverrideWidth()) return; // Walk any normal flow lines to snugly fit. LayoutUnit left = LayoutUnit::max(); LayoutUnit right = LayoutUnit::min(); - LayoutUnit oldWidth = rect.width(); + LayoutUnit oldWidth = contentWidth(); adjustForBorderFit(0, left, right); - if (left != LayoutUnit::max()) { - left = min(left, oldWidth - (borderRight() + paddingRight())); - - left -= (borderLeft() + paddingLeft()); - if (left > 0) { - rect.move(left, 0); - rect.expand(-left, 0); - } - } - if (right != LayoutUnit::min()) { - right = max(right, borderLeft() + paddingLeft()); - - right += (borderRight() + paddingRight()); - if (right < oldWidth) - rect.expand(-(oldWidth - right), 0); - } + + // Clamp to our existing edges. We can never grow. We only shrink. + LayoutUnit leftEdge = borderLeft() + paddingLeft(); + LayoutUnit rightEdge = leftEdge + oldWidth; + left = min(rightEdge, max(leftEdge, left)); + right = max(leftEdge, min(rightEdge, right)); + + LayoutUnit newContentWidth = right - left; + if (newContentWidth == oldWidth) + return; + + setOverrideLogicalContentWidth(newContentWidth); + layoutBlock(false); + clearOverrideLogicalContentWidth(); } void RenderBlock::clearTruncation() @@ -6757,6 +7111,99 @@ void RenderBlock::setMaxMarginAfterValues(LayoutUnit pos, LayoutUnit neg) m_rareData->m_margins.setNegativeMarginAfter(neg); } +void RenderBlock::setMustDiscardMarginBefore(bool value) +{ + if (style()->marginBeforeCollapse() == MDISCARD) { + ASSERT(value); + return; + } + + if (!m_rareData && !value) + return; + + if (!m_rareData) + m_rareData = adoptPtr(new RenderBlockRareData(this)); + + m_rareData->m_discardMarginBefore = value; +} + +void RenderBlock::setMustDiscardMarginAfter(bool value) +{ + if (style()->marginAfterCollapse() == MDISCARD) { + ASSERT(value); + return; + } + + if (!m_rareData && !value) + return; + + if (!m_rareData) + m_rareData = adoptPtr(new RenderBlockRareData(this)); + + m_rareData->m_discardMarginAfter = value; +} + +bool RenderBlock::mustDiscardMarginBefore() const +{ + return style()->marginBeforeCollapse() == MDISCARD || (m_rareData && m_rareData->m_discardMarginBefore); +} + +bool RenderBlock::mustDiscardMarginAfter() const +{ + return style()->marginAfterCollapse() == MDISCARD || (m_rareData && m_rareData->m_discardMarginAfter); +} + +bool RenderBlock::mustDiscardMarginBeforeForChild(const RenderBox* child) const +{ + ASSERT(!child->selfNeedsLayout()); + if (!child->isWritingModeRoot()) + return child->isRenderBlock() ? toRenderBlock(child)->mustDiscardMarginBefore() : (child->style()->marginBeforeCollapse() == MDISCARD); + if (child->isHorizontalWritingMode() == isHorizontalWritingMode()) + return child->isRenderBlock() ? toRenderBlock(child)->mustDiscardMarginAfter() : (child->style()->marginAfterCollapse() == MDISCARD); + + // FIXME: We return false here because the implementation is not geometrically complete. We have values only for before/after, not start/end. + // In case the boxes are perpendicular we assume the property is not specified. + return false; +} + +bool RenderBlock::mustDiscardMarginAfterForChild(const RenderBox* child) const +{ + ASSERT(!child->selfNeedsLayout()); + if (!child->isWritingModeRoot()) + return child->isRenderBlock() ? toRenderBlock(child)->mustDiscardMarginAfter() : (child->style()->marginAfterCollapse() == MDISCARD); + if (child->isHorizontalWritingMode() == isHorizontalWritingMode()) + return child->isRenderBlock() ? toRenderBlock(child)->mustDiscardMarginBefore() : (child->style()->marginBeforeCollapse() == MDISCARD); + + // FIXME: See |mustDiscardMarginBeforeForChild| above. + return false; +} + +bool RenderBlock::mustSeparateMarginBeforeForChild(const RenderBox* child) const +{ + ASSERT(!child->selfNeedsLayout()); + const RenderStyle* childStyle = child->style(); + if (!child->isWritingModeRoot()) + return childStyle->marginBeforeCollapse() == MSEPARATE; + if (child->isHorizontalWritingMode() == isHorizontalWritingMode()) + return childStyle->marginAfterCollapse() == MSEPARATE; + + // FIXME: See |mustDiscardMarginBeforeForChild| above. + return false; +} + +bool RenderBlock::mustSeparateMarginAfterForChild(const RenderBox* child) const +{ + ASSERT(!child->selfNeedsLayout()); + const RenderStyle* childStyle = child->style(); + if (!child->isWritingModeRoot()) + return childStyle->marginAfterCollapse() == MSEPARATE; + if (child->isHorizontalWritingMode() == isHorizontalWritingMode()) + return childStyle->marginBeforeCollapse() == MSEPARATE; + + // FIXME: See |mustDiscardMarginBeforeForChild| above. + return false; +} + void RenderBlock::setPaginationStrut(LayoutUnit strut) { if (!m_rareData) { @@ -6777,6 +7224,23 @@ void RenderBlock::setPageLogicalOffset(LayoutUnit logicalOffset) m_rareData->m_pageLogicalOffset = logicalOffset; } +void RenderBlock::setBreakAtLineToAvoidWidow(RootInlineBox* lineToBreak) +{ + ASSERT(lineToBreak); + if (!m_rareData) + m_rareData = adoptPtr(new RenderBlockRareData(this)); + m_rareData->m_shouldBreakAtLineToAvoidWidow = true; + m_rareData->m_lineBreakToAvoidWidow = lineToBreak; +} + +void RenderBlock::clearShouldBreakAtLineToAvoidWidow() const +{ + if (!m_rareData) + return; + m_rareData->m_shouldBreakAtLineToAvoidWidow = false; + m_rareData->m_lineBreakToAvoidWidow = 0; +} + void RenderBlock::absoluteRects(Vector<IntRect>& rects, const LayoutPoint& accumulatedOffset) const { // For blocks inside inlines, we go ahead and include margins so that we run right up to the @@ -6863,29 +7327,14 @@ LayoutRect RenderBlock::localCaretRect(InlineBox* inlineBox, int caretOffset, La LayoutRect caretRect = localCaretRectForEmptyElement(width(), textIndentOffset()); - if (extraWidthToEndOfLine) { - if (isRenderBlock()) { - *extraWidthToEndOfLine = width() - caretRect.maxX(); - } else { - // FIXME: This code looks wrong. - // myRight and containerRight are set up, but then clobbered. - // So *extraWidthToEndOfLine will always be 0 here. - - LayoutUnit myRight = caretRect.maxX(); - // FIXME: why call localToAbsoluteForContent() twice here, too? - FloatPoint absRightPoint = localToAbsolute(FloatPoint(myRight, 0)); - - LayoutUnit containerRight = containingBlock()->x() + containingBlockLogicalWidthForContent(); - FloatPoint absContainerPoint = localToAbsolute(FloatPoint(containerRight, 0)); - - *extraWidthToEndOfLine = absContainerPoint.x() - absRightPoint.x(); - } - } + // FIXME: Does this need to adjust for vertical orientation? + if (extraWidthToEndOfLine) + *extraWidthToEndOfLine = width() - caretRect.maxX(); return caretRect; } -void RenderBlock::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint& additionalOffset) +void RenderBlock::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint& additionalOffset, const RenderLayerModelObject* paintContainer) { // For blocks inside inlines, we go ahead and include margins so that we run right up to the // inline boxes above and below us (thus getting merged with them to form a single irregular @@ -6920,16 +7369,16 @@ void RenderBlock::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint& a FloatPoint pos; // FIXME: This doesn't work correctly with transforms. if (box->layer()) - pos = curr->localToAbsolute(); + pos = curr->localToContainerPoint(FloatPoint(), paintContainer); else pos = FloatPoint(additionalOffset.x() + box->x(), additionalOffset.y() + box->y()); - box->addFocusRingRects(rects, flooredLayoutPoint(pos)); + box->addFocusRingRects(rects, flooredLayoutPoint(pos), paintContainer); } } } if (inlineElementContinuation()) - inlineElementContinuation()->addFocusRingRects(rects, flooredLayoutPoint(additionalOffset + inlineElementContinuation()->containingBlock()->location() - location())); + inlineElementContinuation()->addFocusRingRects(rects, flooredLayoutPoint(additionalOffset + inlineElementContinuation()->containingBlock()->location() - location()), paintContainer); } RenderBox* RenderBlock::createAnonymousBoxWithSameTypeAs(const RenderObject* parent) const @@ -6945,16 +7394,17 @@ bool RenderBlock::hasNextPage(LayoutUnit logicalOffset, PageBoundaryRule pageBou { ASSERT(view()->layoutState() && view()->layoutState()->isPaginated()); - if (!inRenderFlowThread()) + RenderFlowThread* flowThread = flowThreadContainingBlock(); + if (!flowThread) return true; // Printing and multi-column both make new pages to accommodate content. // See if we're in the last region. LayoutUnit pageOffset = offsetFromLogicalTopOfFirstPage() + logicalOffset; - RenderRegion* region = enclosingRenderFlowThread()->regionAtBlockOffset(pageOffset, this); + RenderRegion* region = flowThread->regionAtBlockOffset(pageOffset, this); if (!region) return false; if (region->isLastRegion()) - return region->isRenderRegionSet() || region->style()->regionOverflow() == BreakRegionOverflow + return region->isRenderRegionSet() || region->style()->regionFragment() == BreakRegionFragment || (pageBoundaryRule == IncludePageBoundary && pageOffset == region->logicalTopForFlowThreadContent()); return true; } @@ -6996,7 +7446,8 @@ LayoutUnit RenderBlock::applyBeforeBreak(RenderBox* child, LayoutUnit logicalOff // FIXME: Add page break checking here when we support printing. bool checkColumnBreaks = view()->layoutState()->isPaginatingColumns(); bool checkPageBreaks = !checkColumnBreaks && view()->layoutState()->m_pageLogicalHeight; // FIXME: Once columns can print we have to check this. - bool checkRegionBreaks = inRenderFlowThread(); + RenderFlowThread* flowThread = flowThreadContainingBlock(); + bool checkRegionBreaks = flowThread && flowThread->isRenderNamedFlowThread(); bool checkBeforeAlways = (checkColumnBreaks && child->style()->columnBreakBefore() == PBALWAYS) || (checkPageBreaks && child->style()->pageBreakBefore() == PBALWAYS) || (checkRegionBreaks && child->style()->regionBreakBefore() == PBALWAYS); if (checkBeforeAlways && inNormalFlow(child) && hasNextPage(logicalOffset, IncludePageBoundary)) { @@ -7004,7 +7455,7 @@ LayoutUnit RenderBlock::applyBeforeBreak(RenderBox* child, LayoutUnit logicalOff view()->layoutState()->addForcedColumnBreak(child, logicalOffset); if (checkRegionBreaks) { LayoutUnit offsetBreakAdjustment = 0; - if (enclosingRenderFlowThread()->addForcedRegionBreak(offsetFromLogicalTopOfFirstPage() + logicalOffset, child, true, &offsetBreakAdjustment)) + if (flowThread->addForcedRegionBreak(offsetFromLogicalTopOfFirstPage() + logicalOffset, child, true, &offsetBreakAdjustment)) return logicalOffset + offsetBreakAdjustment; } return nextPageLogicalTop(logicalOffset, IncludePageBoundary); @@ -7017,18 +7468,22 @@ LayoutUnit RenderBlock::applyAfterBreak(RenderBox* child, LayoutUnit logicalOffs // FIXME: Add page break checking here when we support printing. bool checkColumnBreaks = view()->layoutState()->isPaginatingColumns(); bool checkPageBreaks = !checkColumnBreaks && view()->layoutState()->m_pageLogicalHeight; // FIXME: Once columns can print we have to check this. - bool checkRegionBreaks = inRenderFlowThread(); + RenderFlowThread* flowThread = flowThreadContainingBlock(); + bool checkRegionBreaks = flowThread && flowThread->isRenderNamedFlowThread(); bool checkAfterAlways = (checkColumnBreaks && child->style()->columnBreakAfter() == PBALWAYS) || (checkPageBreaks && child->style()->pageBreakAfter() == PBALWAYS) || (checkRegionBreaks && child->style()->regionBreakAfter() == PBALWAYS); if (checkAfterAlways && inNormalFlow(child) && hasNextPage(logicalOffset, IncludePageBoundary)) { - marginInfo.setMarginAfterQuirk(true); // Cause margins to be discarded for any following content. + LayoutUnit marginOffset = marginInfo.canCollapseWithMarginBefore() ? LayoutUnit() : marginInfo.margin(); + + // So our margin doesn't participate in the next collapsing steps. + marginInfo.clearMargin(); + if (checkColumnBreaks) view()->layoutState()->addForcedColumnBreak(child, logicalOffset); if (checkRegionBreaks) { - LayoutUnit marginOffset = marginInfo.canCollapseWithMarginBefore() ? LayoutUnit() : marginInfo.margin(); LayoutUnit offsetBreakAdjustment = 0; - if (enclosingRenderFlowThread()->addForcedRegionBreak(offsetFromLogicalTopOfFirstPage() + logicalOffset + marginOffset, child, false, &offsetBreakAdjustment)) - return logicalOffset + offsetBreakAdjustment; + if (flowThread->addForcedRegionBreak(offsetFromLogicalTopOfFirstPage() + logicalOffset + marginOffset, child, false, &offsetBreakAdjustment)) + return logicalOffset + marginOffset + offsetBreakAdjustment; } return nextPageLogicalTop(logicalOffset, IncludePageBoundary); } @@ -7042,21 +7497,23 @@ LayoutUnit RenderBlock::pageLogicalTopForOffset(LayoutUnit offset) const LayoutUnit blockLogicalTop = isHorizontalWritingMode() ? renderView->layoutState()->m_layoutOffset.height() : renderView->layoutState()->m_layoutOffset.width(); LayoutUnit cumulativeOffset = offset + blockLogicalTop; - if (!inRenderFlowThread()) { + RenderFlowThread* flowThread = flowThreadContainingBlock(); + if (!flowThread) { LayoutUnit pageLogicalHeight = renderView->layoutState()->pageLogicalHeight(); if (!pageLogicalHeight) return 0; return cumulativeOffset - roundToInt(cumulativeOffset - firstPageLogicalTop) % roundToInt(pageLogicalHeight); } - return enclosingRenderFlowThread()->pageLogicalTopForOffset(cumulativeOffset); + return flowThread->pageLogicalTopForOffset(cumulativeOffset); } LayoutUnit RenderBlock::pageLogicalHeightForOffset(LayoutUnit offset) const { RenderView* renderView = view(); - if (!inRenderFlowThread()) + RenderFlowThread* flowThread = flowThreadContainingBlock(); + if (!flowThread) return renderView->layoutState()->m_pageLogicalHeight; - return enclosingRenderFlowThread()->pageLogicalHeightForOffset(offset + offsetFromLogicalTopOfFirstPage()); + return flowThread->pageLogicalHeightForOffset(offset + offsetFromLogicalTopOfFirstPage()); } LayoutUnit RenderBlock::pageRemainingLogicalHeightForOffset(LayoutUnit offset, PageBoundaryRule pageBoundaryRule) const @@ -7064,7 +7521,8 @@ LayoutUnit RenderBlock::pageRemainingLogicalHeightForOffset(LayoutUnit offset, P RenderView* renderView = view(); offset += offsetFromLogicalTopOfFirstPage(); - if (!inRenderFlowThread()) { + RenderFlowThread* flowThread = flowThreadContainingBlock(); + if (!flowThread) { LayoutUnit pageLogicalHeight = renderView->layoutState()->m_pageLogicalHeight; LayoutUnit remainingHeight = pageLogicalHeight - intMod(offset, pageLogicalHeight); if (pageBoundaryRule == IncludePageBoundary) { @@ -7075,25 +7533,24 @@ LayoutUnit RenderBlock::pageRemainingLogicalHeightForOffset(LayoutUnit offset, P return remainingHeight; } - return enclosingRenderFlowThread()->pageRemainingLogicalHeightForOffset(offset, pageBoundaryRule); + return flowThread->pageRemainingLogicalHeightForOffset(offset, pageBoundaryRule); } LayoutUnit RenderBlock::adjustForUnsplittableChild(RenderBox* child, LayoutUnit logicalOffset, bool includeMargins) { bool checkColumnBreaks = view()->layoutState()->isPaginatingColumns(); bool checkPageBreaks = !checkColumnBreaks && view()->layoutState()->m_pageLogicalHeight; - bool checkRegionBreaks = inRenderFlowThread(); + RenderFlowThread* flowThread = flowThreadContainingBlock(); + bool checkRegionBreaks = flowThread && flowThread->isRenderNamedFlowThread(); bool isUnsplittable = child->isUnsplittableForPagination() || (checkColumnBreaks && child->style()->columnBreakInside() == PBAVOID) || (checkPageBreaks && child->style()->pageBreakInside() == PBAVOID) || (checkRegionBreaks && child->style()->regionBreakInside() == PBAVOID); if (!isUnsplittable) return logicalOffset; LayoutUnit childLogicalHeight = logicalHeightForChild(child) + (includeMargins ? marginBeforeForChild(child) + marginAfterForChild(child) : LayoutUnit()); - LayoutState* layoutState = view()->layoutState(); - if (layoutState->m_columnInfo) - layoutState->m_columnInfo->updateMinimumColumnHeight(childLogicalHeight); LayoutUnit pageLogicalHeight = pageLogicalHeightForOffset(logicalOffset); - bool hasUniformPageLogicalHeight = !inRenderFlowThread() || enclosingRenderFlowThread()->regionsHaveUniformLogicalHeight(); + bool hasUniformPageLogicalHeight = !flowThread || flowThread->regionsHaveUniformLogicalHeight(); + updateMinimumPageHeight(logicalOffset, childLogicalHeight); if (!pageLogicalHeight || (hasUniformPageLogicalHeight && childLogicalHeight > pageLogicalHeight) || !hasNextPage(logicalOffset)) return logicalOffset; @@ -7121,7 +7578,39 @@ bool RenderBlock::pushToNextPageWithMinimumLogicalHeight(LayoutUnit& adjustment, return !checkRegion; } -void RenderBlock::adjustLinePositionForPagination(RootInlineBox* lineBox, LayoutUnit& delta) +void RenderBlock::setPageBreak(LayoutUnit offset, LayoutUnit spaceShortage) +{ + if (RenderFlowThread* flowThread = flowThreadContainingBlock()) + flowThread->setPageBreak(offsetFromLogicalTopOfFirstPage() + offset, spaceShortage); +} + +void RenderBlock::updateMinimumPageHeight(LayoutUnit offset, LayoutUnit minHeight) +{ + if (RenderFlowThread* flowThread = flowThreadContainingBlock()) + flowThread->updateMinimumPageHeight(offsetFromLogicalTopOfFirstPage() + offset, minHeight); + else if (ColumnInfo* colInfo = view()->layoutState()->m_columnInfo) + colInfo->updateMinimumColumnHeight(minHeight); +} + +static inline LayoutUnit calculateMinimumPageHeight(RenderStyle* renderStyle, RootInlineBox* lastLine, LayoutUnit lineTop, LayoutUnit lineBottom) +{ + // We may require a certain minimum number of lines per page in order to satisfy + // orphans and widows, and that may affect the minimum page height. + unsigned lineCount = max<unsigned>(renderStyle->hasAutoOrphans() ? 1 : renderStyle->orphans(), renderStyle->hasAutoWidows() ? 1 : renderStyle->widows()); + if (lineCount > 1) { + RootInlineBox* line = lastLine; + for (unsigned i = 1; i < lineCount && line->prevRootBox(); i++) + line = line->prevRootBox(); + + // FIXME: Paginating using line overflow isn't all fine. See FIXME in + // adjustLinePositionForPagination() for more details. + LayoutRect overflow = line->logicalVisualOverflowRect(line->lineTop(), line->lineBottom()); + lineTop = min(line->lineTopWithLeading(), overflow.y()); + } + return lineBottom - lineTop; +} + +void RenderBlock::adjustLinePositionForPagination(RootInlineBox* lineBox, LayoutUnit& delta, RenderFlowThread* flowThread) { // FIXME: For now we paginate using line overflow. This ensures that lines don't overlap at all when we // put a strut between them for pagination purposes. However, this really isn't the desired rendering, since @@ -7144,23 +7633,24 @@ void RenderBlock::adjustLinePositionForPagination(RootInlineBox* lineBox, Layout // line and all following lines. LayoutRect logicalVisualOverflow = lineBox->logicalVisualOverflowRect(lineBox->lineTop(), lineBox->lineBottom()); LayoutUnit logicalOffset = min(lineBox->lineTopWithLeading(), logicalVisualOverflow.y()); - LayoutUnit lineHeight = max(lineBox->lineBottomWithLeading(), logicalVisualOverflow.maxY()) - logicalOffset; - RenderView* renderView = view(); - LayoutState* layoutState = renderView->layoutState(); - if (layoutState->m_columnInfo) - layoutState->m_columnInfo->updateMinimumColumnHeight(lineHeight); + LayoutUnit logicalBottom = max(lineBox->lineBottomWithLeading(), logicalVisualOverflow.maxY()); + LayoutUnit lineHeight = logicalBottom - logicalOffset; + updateMinimumPageHeight(logicalOffset, calculateMinimumPageHeight(style(), lineBox, logicalOffset, logicalBottom)); logicalOffset += delta; lineBox->setPaginationStrut(0); lineBox->setIsFirstAfterPageBreak(false); LayoutUnit pageLogicalHeight = pageLogicalHeightForOffset(logicalOffset); - bool hasUniformPageLogicalHeight = !inRenderFlowThread() || enclosingRenderFlowThread()->regionsHaveUniformLogicalHeight(); + bool hasUniformPageLogicalHeight = !flowThread || flowThread->regionsHaveUniformLogicalHeight(); // If lineHeight is greater than pageLogicalHeight, but logicalVisualOverflow.height() still fits, we are // still going to add a strut, so that the visible overflow fits on a single page. if (!pageLogicalHeight || (hasUniformPageLogicalHeight && logicalVisualOverflow.height() > pageLogicalHeight) || !hasNextPage(logicalOffset)) return; LayoutUnit remainingLogicalHeight = pageRemainingLogicalHeightForOffset(logicalOffset, ExcludePageBoundary); - if (remainingLogicalHeight < lineHeight) { + + if (remainingLogicalHeight < lineHeight || (shouldBreakAtLineToAvoidWidow() && lineBreakToAvoidWidow() == lineBox)) { + if (shouldBreakAtLineToAvoidWidow() && lineBreakToAvoidWidow() == lineBox) + clearShouldBreakAtLineToAvoidWidow(); // If we have a non-uniform page height, then we have to shift further possibly. if (!hasUniformPageLogicalHeight && !pushToNextPageWithMinimumLogicalHeight(remainingLogicalHeight, logicalOffset, lineHeight)) return; @@ -7170,7 +7660,9 @@ void RenderBlock::adjustLinePositionForPagination(RootInlineBox* lineBox, Layout } LayoutUnit totalLogicalHeight = lineHeight + max<LayoutUnit>(0, logicalOffset); LayoutUnit pageLogicalHeightAtNewOffset = hasUniformPageLogicalHeight ? pageLogicalHeight : pageLogicalHeightForOffset(logicalOffset + remainingLogicalHeight); - if (lineBox == firstRootBox() && totalLogicalHeight < pageLogicalHeightAtNewOffset && !isOutOfFlowPositioned() && !isTableCell()) + setPageBreak(logicalOffset, lineHeight - remainingLogicalHeight); + if (((lineBox == firstRootBox() && totalLogicalHeight < pageLogicalHeightAtNewOffset) || (!style()->hasAutoOrphans() && style()->orphans() >= lineCount(lineBox))) + && !isOutOfFlowPositioned() && !isTableCell()) setPaginationStrut(remainingLogicalHeight + max<LayoutUnit>(0, logicalOffset)); else { delta += remainingLogicalHeight; @@ -7215,6 +7707,21 @@ LayoutUnit RenderBlock::adjustBlockChildForPagination(LayoutUnit logicalTopAfter // If the object has a page or column break value of "before", then we should shift to the top of the next page. LayoutUnit result = applyBeforeBreak(child, logicalTopAfterClear); + if (pageLogicalHeightForOffset(result)) { + LayoutUnit remainingLogicalHeight = pageRemainingLogicalHeightForOffset(result, ExcludePageBoundary); + LayoutUnit spaceShortage = child->logicalHeight() - remainingLogicalHeight; + if (spaceShortage > 0) { + // If the child crosses a column boundary, report a break, in case nothing inside it has already + // done so. The column balancer needs to know how much it has to stretch the columns to make more + // content fit. If no breaks are reported (but do occur), the balancer will have no clue. FIXME: + // This should be improved, though, because here we just pretend that the child is + // unsplittable. A splittable child, on the other hand, has break opportunities at every position + // where there's no child content, border or padding. In other words, we risk stretching more + // than necessary. + setPageBreak(result, spaceShortage); + } + } + // For replaced elements and scrolled elements, we want to shift them to the next page if they don't fit on the current one. LayoutUnit logicalTopBeforeUnsplittableAdjustment = result; LayoutUnit logicalTopAfterUnsplittableAdjustment = adjustForUnsplittableChild(child, result); @@ -7247,19 +7754,16 @@ LayoutUnit RenderBlock::adjustBlockChildForPagination(LayoutUnit logicalTopAfter return result; } -bool RenderBlock::lineWidthForPaginatedLineChanged(RootInlineBox* rootBox, LayoutUnit lineDelta) const +bool RenderBlock::lineWidthForPaginatedLineChanged(RootInlineBox* rootBox, LayoutUnit lineDelta, RenderFlowThread* flowThread) const { - if (!inRenderFlowThread()) + if (!flowThread) return false; RenderRegion* currentRegion = regionAtBlockOffset(rootBox->lineTopWithLeading() + lineDelta); - // Just bail if we still don't have a region. - if (!rootBox->hasContainingRegion() && !currentRegion) - return false; // Just bail if the region didn't change. - if (rootBox->hasContainingRegion() && rootBox->containingRegion() == currentRegion) + if (rootBox->containingRegion() == currentRegion) return false; - return rootBox->paginatedLineWidth() != availableLogicalWidthForContent(currentRegion, offsetFromLogicalTopOfFirstPage()); + return rootBox->paginatedLineWidth() != availableLogicalWidthForContent(currentRegion); } LayoutUnit RenderBlock::offsetFromLogicalTopOfFirstPage() const @@ -7267,94 +7771,71 @@ LayoutUnit RenderBlock::offsetFromLogicalTopOfFirstPage() const LayoutState* layoutState = view()->layoutState(); if (layoutState && !layoutState->isPaginated()) return 0; + + RenderFlowThread* flowThread = flowThreadContainingBlock(); + if (flowThread) + return flowThread->offsetFromLogicalTopOfFirstRegion(this); + if (layoutState) { - // FIXME: Sanity check that the renderer in the layout state is ours, since otherwise the computation will be off. - // Right now this assert gets hit inside computeLogicalHeight for percentage margins, since they're computed using - // widths which can vary in each region. Until we patch that, we can't have this assert. - // ASSERT(layoutState->m_renderer == this); + ASSERT(layoutState->m_renderer == this); LayoutSize offsetDelta = layoutState->m_layoutOffset - layoutState->m_pageOffset; return isHorizontalWritingMode() ? offsetDelta.height() : offsetDelta.width(); } - // FIXME: Right now, this assert is hit outside layout, from logicalLeftSelectionOffset in selectionGapRectsForRepaint (called from FrameSelection::selectAll). - // ASSERT(inRenderFlowThread()); - - // FIXME: This is a slower path that doesn't use layout state and relies on getting your logical top inside the enclosing flow thread. It doesn't - // work with columns or pages currently, but it should once they have been switched over to using flow threads. - if (!inRenderFlowThread()) - return 0; - - const RenderBlock* currentBlock = this; - LayoutRect blockRect(0, 0, width(), height()); - - while (currentBlock && !currentBlock->isRenderFlowThread()) { - RenderBlock* containerBlock = currentBlock->containingBlock(); - ASSERT(containerBlock); - if (!containerBlock) - return 0; - LayoutPoint currentBlockLocation = currentBlock->location(); - - if (containerBlock->style()->writingMode() != currentBlock->style()->writingMode()) { - // We have to put the block rect in container coordinates - // and we have to take into account both the container and current block flipping modes - if (containerBlock->style()->isFlippedBlocksWritingMode()) { - if (containerBlock->isHorizontalWritingMode()) - blockRect.setY(currentBlock->height() - blockRect.maxY()); - else - blockRect.setX(currentBlock->width() - blockRect.maxX()); - } - currentBlock->flipForWritingMode(blockRect); - } - blockRect.moveBy(currentBlockLocation); - currentBlock = containerBlock; - }; - return currentBlock->isHorizontalWritingMode() ? blockRect.y() : blockRect.x(); + + ASSERT_NOT_REACHED(); + return 0; } RenderRegion* RenderBlock::regionAtBlockOffset(LayoutUnit blockOffset) const { - if (!inRenderFlowThread()) - return 0; - - RenderFlowThread* flowThread = enclosingRenderFlowThread(); + RenderFlowThread* flowThread = flowThreadContainingBlock(); if (!flowThread || !flowThread->hasValidRegionInfo()) return 0; return flowThread->regionAtBlockOffset(offsetFromLogicalTopOfFirstPage() + blockOffset, true); } +void RenderBlock::updateStaticInlinePositionForChild(RenderBox* child, LayoutUnit logicalTop) +{ + if (child->style()->isOriginalDisplayInlineType()) + setStaticInlinePositionForChild(child, logicalTop, startAlignedOffsetForLine(logicalTop, false)); + else + setStaticInlinePositionForChild(child, logicalTop, startOffsetForContent(logicalTop)); +} + void RenderBlock::setStaticInlinePositionForChild(RenderBox* child, LayoutUnit blockOffset, LayoutUnit inlinePosition) { - if (inRenderFlowThread()) { + if (flowThreadContainingBlock()) { // Shift the inline position to exclude the region offset. inlinePosition += startOffsetForContent() - startOffsetForContent(blockOffset); } child->layer()->setStaticInlinePosition(inlinePosition); } -bool RenderBlock::logicalWidthChangedInRegions() const +bool RenderBlock::logicalWidthChangedInRegions(RenderFlowThread* flowThread) const { - if (!inRenderFlowThread()) - return false; - - RenderFlowThread* flowThread = enclosingRenderFlowThread(); if (!flowThread || !flowThread->hasValidRegionInfo()) - return 0; + return false; - return flowThread->logicalWidthChangedInRegions(this, offsetFromLogicalTopOfFirstPage()); + return flowThread->logicalWidthChangedInRegionsForBlock(this); } RenderRegion* RenderBlock::clampToStartAndEndRegions(RenderRegion* region) const { - ASSERT(region && inRenderFlowThread()); - + RenderFlowThread* flowThread = flowThreadContainingBlock(); + + ASSERT(isRenderView() || (region && flowThread)); + if (isRenderView()) + return region; + // We need to clamp to the block, since we want any lines or blocks that overflow out of the // logical top or logical bottom of the block to size as though the border box in the first and // last regions extended infinitely. Otherwise the lines are going to size according to the regions // they overflow into, which makes no sense when this block doesn't exist in |region| at all. RenderRegion* startRegion; RenderRegion* endRegion; - enclosingRenderFlowThread()->getRegionRangeForBox(this, startRegion, endRegion); + flowThread->getRegionRangeForBox(this, startRegion, endRegion); if (startRegion && region->logicalTopForFlowThreadContent() < startRegion->logicalTopForFlowThreadContent()) return startRegion; @@ -7398,6 +7879,40 @@ LayoutUnit RenderBlock::collapsedMarginAfterForChild(const RenderBox* child) co return marginAfterForChild(child); } +bool RenderBlock::hasMarginBeforeQuirk(const RenderBox* child) const +{ + // If the child has the same directionality as we do, then we can just return its + // margin quirk. + if (!child->isWritingModeRoot()) + return child->isRenderBlock() ? toRenderBlock(child)->hasMarginBeforeQuirk() : child->style()->hasMarginBeforeQuirk(); + + // The child has a different directionality. If the child is parallel, then it's just + // flipped relative to us. We can use the opposite edge. + if (child->isHorizontalWritingMode() == isHorizontalWritingMode()) + return child->isRenderBlock() ? toRenderBlock(child)->hasMarginAfterQuirk() : child->style()->hasMarginAfterQuirk(); + + // The child is perpendicular to us and box sides are never quirky in html.css, and we don't really care about + // whether or not authors specified quirky ems, since they're an implementation detail. + return false; +} + +bool RenderBlock::hasMarginAfterQuirk(const RenderBox* child) const +{ + // If the child has the same directionality as we do, then we can just return its + // margin quirk. + if (!child->isWritingModeRoot()) + return child->isRenderBlock() ? toRenderBlock(child)->hasMarginAfterQuirk() : child->style()->hasMarginAfterQuirk(); + + // The child has a different directionality. If the child is parallel, then it's just + // flipped relative to us. We can use the opposite edge. + if (child->isHorizontalWritingMode() == isHorizontalWritingMode()) + return child->isRenderBlock() ? toRenderBlock(child)->hasMarginBeforeQuirk() : child->style()->hasMarginBeforeQuirk(); + + // The child is perpendicular to us and box sides are never quirky in html.css, and we don't really care about + // whether or not authors specified quirky ems, since they're an implementation detail. + return false; +} + RenderBlock::MarginValues RenderBlock::marginValuesForChild(RenderBox* child) const { LayoutUnit childBeforePositive = 0; @@ -7467,23 +7982,40 @@ const char* RenderBlock::renderName() const return "RenderBlock (floating)"; if (isOutOfFlowPositioned()) return "RenderBlock (positioned)"; - if (isAnonymousColumnsBlock()) + if (style() && isAnonymousColumnsBlock()) return "RenderBlock (anonymous multi-column)"; - if (isAnonymousColumnSpanBlock()) + if (style() && isAnonymousColumnSpanBlock()) return "RenderBlock (anonymous multi-column span)"; - if (isAnonymousBlock()) + if (style() && isAnonymousBlock()) return "RenderBlock (anonymous)"; - else if (isAnonymous()) + // FIXME: Temporary hack while the new generated content system is being implemented. + if (isPseudoElement()) + return "RenderBlock (generated)"; + if (isAnonymous()) return "RenderBlock (generated)"; if (isRelPositioned()) return "RenderBlock (relative positioned)"; if (isStickyPositioned()) return "RenderBlock (sticky positioned)"; - if (isRunIn()) + if (style() && isRunIn()) return "RenderBlock (run-in)"; return "RenderBlock"; } +inline RenderBlock::FloatingObjects::FloatingObjects(const RenderBlock* renderer, bool horizontalWritingMode) + : m_placedFloatsTree(UninitializedTree) + , m_leftObjectsCount(0) + , m_rightObjectsCount(0) + , m_horizontalWritingMode(horizontalWritingMode) + , m_renderer(renderer) +{ +} + +void RenderBlock::createFloatingObjects() +{ + m_floatingObjects = adoptPtr(new FloatingObjects(this, isHorizontalWritingMode())); +} + inline void RenderBlock::FloatingObjects::clear() { m_set.clear(); @@ -7511,8 +8043,8 @@ inline void RenderBlock::FloatingObjects::decreaseObjectsCount(FloatingObject::T inline RenderBlock::FloatingObjectInterval RenderBlock::FloatingObjects::intervalForFloatingObject(FloatingObject* floatingObject) { if (m_horizontalWritingMode) - return RenderBlock::FloatingObjectInterval(floatingObject->frameRect().pixelSnappedY(), floatingObject->frameRect().pixelSnappedMaxY(), floatingObject); - return RenderBlock::FloatingObjectInterval(floatingObject->frameRect().pixelSnappedX(), floatingObject->frameRect().pixelSnappedMaxX(), floatingObject); + return RenderBlock::FloatingObjectInterval(floatingObject->frameRect().y(), floatingObject->frameRect().maxY(), floatingObject); + return RenderBlock::FloatingObjectInterval(floatingObject->frameRect().x(), floatingObject->frameRect().maxX(), floatingObject); } void RenderBlock::FloatingObjects::addPlacedObject(FloatingObject* floatingObject) @@ -7660,15 +8192,18 @@ TextRun RenderBlock::constructTextRun(RenderObject* context, const Font& font, c RenderBlock* RenderBlock::createAnonymousWithParentRendererAndDisplay(const RenderObject* parent, EDisplay display) { - // FIXME: Do we need to cover the new flex box here ? // FIXME: Do we need to convert all our inline displays to block-type in the anonymous logic ? EDisplay newDisplay; RenderBlock* newBox = 0; if (display == BOX || display == INLINE_BOX) { - newBox = new (parent->renderArena()) RenderDeprecatedFlexibleBox(parent->document() /* anonymous box */); + // FIXME: Remove this case once we have eliminated all internal users of old flexbox + newBox = RenderDeprecatedFlexibleBox::createAnonymous(parent->document()); newDisplay = BOX; + } else if (display == FLEX || display == INLINE_FLEX) { + newBox = RenderFlexibleBox::createAnonymous(parent->document()); + newDisplay = FLEX; } else { - newBox = new (parent->renderArena()) RenderBlock(parent->document() /* anonymous box */); + newBox = RenderBlock::createAnonymous(parent->document()); newDisplay = BLOCK; } @@ -7682,7 +8217,7 @@ RenderBlock* RenderBlock::createAnonymousColumnsWithParentRenderer(const RenderO RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyleWithDisplay(parent->style(), BLOCK); newStyle->inheritColumnPropertiesFrom(parent->style()); - RenderBlock* newBox = new (parent->renderArena()) RenderBlock(parent->document() /* anonymous box */); + RenderBlock* newBox = RenderBlock::createAnonymous(parent->document()); newBox->setStyle(newStyle.release()); return newBox; } @@ -7692,7 +8227,7 @@ RenderBlock* RenderBlock::createAnonymousColumnSpanWithParentRenderer(const Rend RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyleWithDisplay(parent->style(), BLOCK); newStyle->setColumnSpan(ColumnSpanAll); - RenderBlock* newBox = new (parent->renderArena()) RenderBlock(parent->document() /* anonymous box */); + RenderBlock* newBox = RenderBlock::createAnonymous(parent->document()); newBox->setStyle(newStyle.release()); return newBox; } @@ -7727,7 +8262,7 @@ String ValueToString<int>::string(const int value) String ValueToString<RenderBlock::FloatingObject*>::string(const RenderBlock::FloatingObject* floatingObject) { - return String::format("%p (%dx%d %dx%d)", floatingObject, floatingObject->frameRect().pixelSnappedX(), floatingObject->frameRect().pixelSnappedY(), floatingObject->frameRect().pixelSnappedMaxX(), floatingObject->frameRect().pixelSnappedMaxY()); + return String::format("%p (%ix%i %ix%i)", floatingObject, floatingObject->frameRect().x().toInt(), floatingObject->frameRect().y().toInt(), floatingObject->frameRect().maxX().toInt(), floatingObject->frameRect().maxY().toInt()); } #endif diff --git a/Source/WebCore/rendering/RenderBlock.h b/Source/WebCore/rendering/RenderBlock.h index 9f008477b..de9e9ec6e 100644 --- a/Source/WebCore/rendering/RenderBlock.h +++ b/Source/WebCore/rendering/RenderBlock.h @@ -34,9 +34,9 @@ #include <wtf/OwnPtr.h> #include <wtf/ListHashSet.h> -#if ENABLE(CSS_EXCLUSIONS) -#include "ExclusionShapeInsideInfo.h" -#include "ExclusionShapeValue.h" +#if ENABLE(CSS_SHAPES) +#include "ShapeInsideInfo.h" +#include "ShapeValue.h" #endif namespace WebCore { @@ -46,6 +46,7 @@ class InlineIterator; class LayoutStateMaintainer; class LineLayoutState; class LineWidth; +class LogicalSelectionOffsetCaches; class RenderInline; class RenderText; @@ -53,6 +54,9 @@ struct BidiRun; struct PaintInfo; class LineInfo; class RenderRubyRun; +#if ENABLE(CSS_SHAPES) +class BasicShape; +#endif class TextLayout; class WordMeasurement; @@ -62,8 +66,8 @@ template <class Iterator> struct MidpointState; typedef BidiResolver<InlineIterator, BidiRun> InlineBidiResolver; typedef MidpointState<InlineIterator> LineMidpointState; typedef WTF::ListHashSet<RenderBox*, 16> TrackedRendererListHashSet; -typedef WTF::HashMap<const RenderBlock*, TrackedRendererListHashSet*> TrackedDescendantsMap; -typedef WTF::HashMap<const RenderBox*, HashSet<RenderBlock*>*> TrackedContainerMap; +typedef WTF::HashMap<const RenderBlock*, OwnPtr<TrackedRendererListHashSet> > TrackedDescendantsMap; +typedef WTF::HashMap<const RenderBox*, OwnPtr<HashSet<RenderBlock*> > > TrackedContainerMap; typedef Vector<WordMeasurement, 64> WordMeasurements; enum CaretType { CursorCaret, DragCaret }; @@ -85,9 +89,11 @@ public: template <class> friend struct ValueToString; #endif - RenderBlock(Node*); + explicit RenderBlock(ContainerNode*); virtual ~RenderBlock(); + static RenderBlock* createAnonymous(Document*); + RenderObject* firstChild() const { ASSERT(children() == virtualChildren()); return children()->firstChild(); } RenderObject* lastChild() const { ASSERT(children() == virtualChildren()); return children()->lastChild(); } @@ -137,6 +143,15 @@ public: void setHasMarkupTruncation(bool b) { m_hasMarkupTruncation = b; } bool hasMarkupTruncation() const { return m_hasMarkupTruncation; } + void setHasMarginBeforeQuirk(bool b) { m_hasMarginBeforeQuirk = b; } + void setHasMarginAfterQuirk(bool b) { m_hasMarginAfterQuirk = b; } + + bool hasMarginBeforeQuirk() const { return m_hasMarginBeforeQuirk; } + bool hasMarginAfterQuirk() const { return m_hasMarginAfterQuirk; } + + bool hasMarginBeforeQuirk(const RenderBox* child) const; + bool hasMarginAfterQuirk(const RenderBox* child) const; + RootInlineBox* createAndAppendRootInlineBox(); bool generatesLineBoxesForInlineChild(RenderObject*); @@ -151,65 +166,66 @@ public: // Versions that can compute line offsets with the region and page offset passed in. Used for speed to avoid having to // compute the region all over again when you already know it. - LayoutUnit availableLogicalWidthForLine(LayoutUnit position, bool firstLine, RenderRegion* region, LayoutUnit offsetFromLogicalTopOfFirstPage, LayoutUnit logicalHeight = 0) const + LayoutUnit availableLogicalWidthForLine(LayoutUnit position, bool shouldIndentText, RenderRegion* region, LayoutUnit logicalHeight = 0) const { - return max<LayoutUnit>(0, logicalRightOffsetForLine(position, firstLine, region, offsetFromLogicalTopOfFirstPage, logicalHeight) - - logicalLeftOffsetForLine(position, firstLine, region, offsetFromLogicalTopOfFirstPage, logicalHeight)); + return max<LayoutUnit>(0, logicalRightOffsetForLine(position, shouldIndentText, region, logicalHeight) + - logicalLeftOffsetForLine(position, shouldIndentText, region, logicalHeight)); } - LayoutUnit logicalRightOffsetForLine(LayoutUnit position, bool firstLine, RenderRegion* region, LayoutUnit offsetFromLogicalTopOfFirstPage, LayoutUnit logicalHeight = 0) const + LayoutUnit logicalRightOffsetForLine(LayoutUnit position, bool shouldIndentText, RenderRegion* region, LayoutUnit logicalHeight = 0) const { - return logicalRightOffsetForLine(position, logicalRightOffsetForContent(region, offsetFromLogicalTopOfFirstPage), firstLine, 0, logicalHeight); + return logicalRightOffsetForLine(position, logicalRightOffsetForContent(region), shouldIndentText, 0, logicalHeight); } - LayoutUnit logicalLeftOffsetForLine(LayoutUnit position, bool firstLine, RenderRegion* region, LayoutUnit offsetFromLogicalTopOfFirstPage, LayoutUnit logicalHeight = 0) const + LayoutUnit logicalLeftOffsetForLine(LayoutUnit position, bool shouldIndentText, RenderRegion* region, LayoutUnit logicalHeight = 0) const { - return logicalLeftOffsetForLine(position, logicalLeftOffsetForContent(region, offsetFromLogicalTopOfFirstPage), firstLine, 0, logicalHeight); + return logicalLeftOffsetForLine(position, logicalLeftOffsetForContent(region), shouldIndentText, 0, logicalHeight); } - LayoutUnit startOffsetForLine(LayoutUnit position, bool firstLine, RenderRegion* region, LayoutUnit offsetFromLogicalTopOfFirstPage, LayoutUnit logicalHeight = 0) const + LayoutUnit startOffsetForLine(LayoutUnit position, bool shouldIndentText, RenderRegion* region, LayoutUnit logicalHeight = 0) const { - return style()->isLeftToRightDirection() ? logicalLeftOffsetForLine(position, firstLine, region, offsetFromLogicalTopOfFirstPage, logicalHeight) - : logicalWidth() - logicalRightOffsetForLine(position, firstLine, region, offsetFromLogicalTopOfFirstPage, logicalHeight); + return style()->isLeftToRightDirection() ? logicalLeftOffsetForLine(position, shouldIndentText, region, logicalHeight) + : logicalWidth() - logicalRightOffsetForLine(position, shouldIndentText, region, logicalHeight); } - LayoutUnit endOffsetForLine(LayoutUnit position, bool firstLine, RenderRegion* region, LayoutUnit offsetFromLogicalTopOfFirstPage, LayoutUnit logicalHeight = 0) const + LayoutUnit endOffsetForLine(LayoutUnit position, bool shouldIndentText, RenderRegion* region, LayoutUnit logicalHeight = 0) const { - return !style()->isLeftToRightDirection() ? logicalLeftOffsetForLine(position, firstLine, region, offsetFromLogicalTopOfFirstPage, logicalHeight) - : logicalWidth() - logicalRightOffsetForLine(position, firstLine, region, offsetFromLogicalTopOfFirstPage, logicalHeight); + return !style()->isLeftToRightDirection() ? logicalLeftOffsetForLine(position, shouldIndentText, region, logicalHeight) + : logicalWidth() - logicalRightOffsetForLine(position, shouldIndentText, region, logicalHeight); } - LayoutUnit availableLogicalWidthForLine(LayoutUnit position, bool firstLine, LayoutUnit logicalHeight = 0) const + LayoutUnit availableLogicalWidthForLine(LayoutUnit position, bool shouldIndentText, LayoutUnit logicalHeight = 0) const { - return availableLogicalWidthForLine(position, firstLine, regionAtBlockOffset(position), offsetFromLogicalTopOfFirstPage(), logicalHeight); + return availableLogicalWidthForLine(position, shouldIndentText, regionAtBlockOffset(position), logicalHeight); } - LayoutUnit logicalRightOffsetForLine(LayoutUnit position, bool firstLine, LayoutUnit logicalHeight = 0) const + LayoutUnit logicalRightOffsetForLine(LayoutUnit position, bool shouldIndentText, LayoutUnit logicalHeight = 0) const { - return logicalRightOffsetForLine(position, logicalRightOffsetForContent(position), firstLine, 0, logicalHeight); + return logicalRightOffsetForLine(position, logicalRightOffsetForContent(position), shouldIndentText, 0, logicalHeight); } - LayoutUnit logicalLeftOffsetForLine(LayoutUnit position, bool firstLine, LayoutUnit logicalHeight = 0) const + LayoutUnit logicalLeftOffsetForLine(LayoutUnit position, bool shouldIndentText, LayoutUnit logicalHeight = 0) const { - return logicalLeftOffsetForLine(position, logicalLeftOffsetForContent(position), firstLine, 0, logicalHeight); + return logicalLeftOffsetForLine(position, logicalLeftOffsetForContent(position), shouldIndentText, 0, logicalHeight); } - LayoutUnit pixelSnappedLogicalLeftOffsetForLine(LayoutUnit position, bool firstLine, LayoutUnit logicalHeight = 0) const + LayoutUnit pixelSnappedLogicalLeftOffsetForLine(LayoutUnit position, bool shouldIndentText, LayoutUnit logicalHeight = 0) const { - return roundToInt(logicalLeftOffsetForLine(position, firstLine, logicalHeight)); + return roundToInt(logicalLeftOffsetForLine(position, shouldIndentText, logicalHeight)); } - LayoutUnit pixelSnappedLogicalRightOffsetForLine(LayoutUnit position, bool firstLine, LayoutUnit logicalHeight = 0) const + LayoutUnit pixelSnappedLogicalRightOffsetForLine(LayoutUnit position, bool shouldIndentText, LayoutUnit logicalHeight = 0) const { // FIXME: Multicolumn layouts break carrying over subpixel values to the logical right offset because the lines may be shifted // by a subpixel value for all but the first column. This can lead to the actual pixel snapped width of the column being off // by one pixel when rendered versus layed out, which can result in the line being clipped. For now, we have to floor. - return floorToInt(logicalRightOffsetForLine(position, firstLine, logicalHeight)); + // https://bugs.webkit.org/show_bug.cgi?id=105461 + return floorToInt(logicalRightOffsetForLine(position, shouldIndentText, logicalHeight)); } - LayoutUnit startOffsetForLine(LayoutUnit position, bool firstLine, LayoutUnit logicalHeight = 0) const + LayoutUnit startOffsetForLine(LayoutUnit position, bool shouldIndentText, LayoutUnit logicalHeight = 0) const { - return style()->isLeftToRightDirection() ? logicalLeftOffsetForLine(position, firstLine, logicalHeight) - : logicalWidth() - logicalRightOffsetForLine(position, firstLine, logicalHeight); + return style()->isLeftToRightDirection() ? logicalLeftOffsetForLine(position, shouldIndentText, logicalHeight) + : logicalWidth() - logicalRightOffsetForLine(position, shouldIndentText, logicalHeight); } - LayoutUnit endOffsetForLine(LayoutUnit position, bool firstLine, LayoutUnit logicalHeight = 0) const + LayoutUnit endOffsetForLine(LayoutUnit position, bool shouldIndentText, LayoutUnit logicalHeight = 0) const { - return !style()->isLeftToRightDirection() ? logicalLeftOffsetForLine(position, firstLine, logicalHeight) - : logicalWidth() - logicalRightOffsetForLine(position, firstLine, logicalHeight); + return !style()->isLeftToRightDirection() ? logicalLeftOffsetForLine(position, shouldIndentText, logicalHeight) + : logicalWidth() - logicalRightOffsetForLine(position, shouldIndentText, logicalHeight); } - LayoutUnit startAlignedOffsetForLine(LayoutUnit position, bool firstLine); + LayoutUnit startAlignedOffsetForLine(LayoutUnit position, bool shouldIndentText); LayoutUnit textIndentOffset() const; virtual VisiblePosition positionForPoint(const LayoutPoint&); @@ -227,17 +243,17 @@ public: GapRects selectionGapRectsForRepaint(const RenderLayerModelObject* repaintContainer); LayoutRect logicalLeftSelectionGap(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, - RenderObject* selObj, LayoutUnit logicalLeft, LayoutUnit logicalTop, LayoutUnit logicalHeight, const PaintInfo*); + RenderObject* selObj, LayoutUnit logicalLeft, LayoutUnit logicalTop, LayoutUnit logicalHeight, const LogicalSelectionOffsetCaches&, const PaintInfo*); LayoutRect logicalRightSelectionGap(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, - RenderObject* selObj, LayoutUnit logicalRight, LayoutUnit logicalTop, LayoutUnit logicalHeight, const PaintInfo*); + RenderObject* selObj, LayoutUnit logicalRight, LayoutUnit logicalTop, LayoutUnit logicalHeight, const LogicalSelectionOffsetCaches&, const PaintInfo*); void getSelectionGapInfo(SelectionState, bool& leftGap, bool& rightGap); RenderBlock* blockBeforeWithinSelectionRoot(LayoutSize& offset) const; LayoutRect logicalRectToPhysicalRect(const LayoutPoint& physicalPosition, const LayoutRect& logicalRect); - + // Helper methods for computing line counts and heights for line counts. - RootInlineBox* lineAtIndex(int); - int lineCount(); + RootInlineBox* lineAtIndex(int) const; + int lineCount(const RootInlineBox* = 0, bool* = 0) const; int heightForLineCount(int); void clearTruncation(); @@ -262,6 +278,7 @@ public: RenderBlock* createAnonymousBlock(EDisplay display = BLOCK) const { return createAnonymousWithParentRendererAndDisplay(this, display); } RenderBlock* createAnonymousColumnsBlock() const { return createAnonymousColumnsWithParentRenderer(this); } RenderBlock* createAnonymousColumnSpanBlock() const { return createAnonymousColumnSpanWithParentRenderer(this); } + static void collapseAnonymousBoxChild(RenderBlock* parent, RenderObject* child); virtual RenderBox* createAnonymousBoxWithSameTypeAs(const RenderObject* parent) const OVERRIDE; @@ -297,13 +314,21 @@ public: void updateColumnInfoFromStyle(RenderStyle*); + LayoutUnit initialBlockOffsetForPainting() const; + LayoutUnit blockDeltaForPaintingNextColumn() const; + // These two functions take the ColumnInfo* to avoid repeated lookups of the info in the global HashMap. unsigned columnCount(ColumnInfo*) const; LayoutRect columnRectAt(ColumnInfo*, unsigned) const; LayoutUnit paginationStrut() const { return m_rareData ? m_rareData->m_paginationStrut : LayoutUnit(); } void setPaginationStrut(LayoutUnit); - + + bool shouldBreakAtLineToAvoidWidow() const { return m_rareData && m_rareData->m_shouldBreakAtLineToAvoidWidow; } + void clearShouldBreakAtLineToAvoidWidow() const; + RootInlineBox* lineBreakToAvoidWidow() const { return m_rareData ? m_rareData->m_lineBreakToAvoidWidow : 0; } + void setBreakAtLineToAvoidWidow(RootInlineBox*); + // The page logical offset is the object's offset from the top of the page in the page progression // direction (so an x-offset in vertical text and a y-offset for horizontal text). LayoutUnit pageLogicalOffset() const { return m_rareData ? m_rareData->m_pageLogicalOffset : LayoutUnit(); } @@ -371,41 +396,38 @@ public: virtual void scrollbarsChanged(bool /*horizontalScrollbarChanged*/, bool /*verticalScrollbarChanged*/) { }; - LayoutUnit logicalLeftOffsetForContent(RenderRegion*, LayoutUnit offsetFromLogicalTopOfFirstPage) const; - LayoutUnit logicalRightOffsetForContent(RenderRegion*, LayoutUnit offsetFromLogicalTopOfFirstPage) const; - LayoutUnit availableLogicalWidthForContent(RenderRegion* region, LayoutUnit offsetFromLogicalTopOfFirstPage) const + LayoutUnit logicalLeftOffsetForContent(RenderRegion*) const; + LayoutUnit logicalRightOffsetForContent(RenderRegion*) const; + LayoutUnit availableLogicalWidthForContent(RenderRegion* region) const { - return max<LayoutUnit>(0, logicalRightOffsetForContent(region, offsetFromLogicalTopOfFirstPage) - - logicalLeftOffsetForContent(region, offsetFromLogicalTopOfFirstPage)); } - LayoutUnit startOffsetForContent(RenderRegion* region, LayoutUnit offsetFromLogicalTopOfFirstPage) const + return max<LayoutUnit>(0, logicalRightOffsetForContent(region) - logicalLeftOffsetForContent(region)); } + LayoutUnit startOffsetForContent(RenderRegion* region) const { - return style()->isLeftToRightDirection() ? logicalLeftOffsetForContent(region, offsetFromLogicalTopOfFirstPage) - : logicalWidth() - logicalRightOffsetForContent(region, offsetFromLogicalTopOfFirstPage); + return style()->isLeftToRightDirection() ? logicalLeftOffsetForContent(region) : logicalWidth() - logicalRightOffsetForContent(region); } - LayoutUnit endOffsetForContent(RenderRegion* region, LayoutUnit offsetFromLogicalTopOfFirstPage) const + LayoutUnit endOffsetForContent(RenderRegion* region) const { - return !style()->isLeftToRightDirection() ? logicalLeftOffsetForContent(region, offsetFromLogicalTopOfFirstPage) - : logicalWidth() - logicalRightOffsetForContent(region, offsetFromLogicalTopOfFirstPage); + return !style()->isLeftToRightDirection() ? logicalLeftOffsetForContent(region) : logicalWidth() - logicalRightOffsetForContent(region); } LayoutUnit logicalLeftOffsetForContent(LayoutUnit blockOffset) const { - return logicalLeftOffsetForContent(regionAtBlockOffset(blockOffset), offsetFromLogicalTopOfFirstPage()); + return logicalLeftOffsetForContent(regionAtBlockOffset(blockOffset)); } LayoutUnit logicalRightOffsetForContent(LayoutUnit blockOffset) const { - return logicalRightOffsetForContent(regionAtBlockOffset(blockOffset), offsetFromLogicalTopOfFirstPage()); + return logicalRightOffsetForContent(regionAtBlockOffset(blockOffset)); } LayoutUnit availableLogicalWidthForContent(LayoutUnit blockOffset) const { - return availableLogicalWidthForContent(regionAtBlockOffset(blockOffset), offsetFromLogicalTopOfFirstPage()); + return availableLogicalWidthForContent(regionAtBlockOffset(blockOffset)); } LayoutUnit startOffsetForContent(LayoutUnit blockOffset) const { - return startOffsetForContent(regionAtBlockOffset(blockOffset), offsetFromLogicalTopOfFirstPage()); + return startOffsetForContent(regionAtBlockOffset(blockOffset)); } LayoutUnit endOffsetForContent(LayoutUnit blockOffset) const { - return endOffsetForContent(regionAtBlockOffset(blockOffset), offsetFromLogicalTopOfFirstPage()); + return endOffsetForContent(regionAtBlockOffset(blockOffset)); } LayoutUnit logicalLeftOffsetForContent() const { return isHorizontalWritingMode() ? borderLeft() + paddingLeft() : borderTop() + paddingTop(); } LayoutUnit logicalRightOffsetForContent() const { return logicalLeftOffsetForContent() + availableLogicalWidth(); } @@ -413,10 +435,11 @@ public: LayoutUnit endOffsetForContent() const { return !style()->isLeftToRightDirection() ? logicalLeftOffsetForContent() : logicalWidth() - logicalRightOffsetForContent(); } void setStaticInlinePositionForChild(RenderBox*, LayoutUnit blockOffset, LayoutUnit inlinePosition); + void updateStaticInlinePositionForChild(RenderBox*, LayoutUnit logicalTop); - LayoutUnit computeStartPositionDeltaForChildAvoidingFloats(const RenderBox* child, LayoutUnit childMarginStart, RenderRegion* = 0, LayoutUnit offsetFromLogicalTopOfFirstPage = 0); + LayoutUnit computeStartPositionDeltaForChildAvoidingFloats(const RenderBox* child, LayoutUnit childMarginStart, RenderRegion* = 0); - void placeRunInIfNeeded(RenderObject* newChild, PlaceGeneratedRunInFlag); + void placeRunInIfNeeded(RenderObject* newChild); bool runInIsPlacedIntoSiblingBlock(RenderObject* runIn); #ifndef NDEBUG @@ -424,11 +447,29 @@ public: void showLineTreeAndMark(const InlineBox* = 0, const char* = 0, const InlineBox* = 0, const char* = 0, const RenderObject* = 0) const; #endif -#if ENABLE(CSS_EXCLUSIONS) - ExclusionShapeInsideInfo* exclusionShapeInsideInfo() const +#if ENABLE(CSS_SHAPES) + ShapeInsideInfo* ensureShapeInsideInfo() { - return style()->shapeInside() && ExclusionShapeInsideInfo::isExclusionShapeInsideInfoEnabledForRenderBlock(this) ? ExclusionShapeInsideInfo::exclusionShapeInsideInfoForRenderBlock(this) : 0; + if (!m_rareData || !m_rareData->m_shapeInsideInfo) + setShapeInsideInfo(ShapeInsideInfo::createInfo(this)); + return m_rareData->m_shapeInsideInfo.get(); + } + + ShapeInsideInfo* shapeInsideInfo() const + { + if (!m_rareData || !m_rareData->m_shapeInsideInfo) + return 0; + return ShapeInsideInfo::isEnabledFor(this) ? m_rareData->m_shapeInsideInfo.get() : 0; + } + void setShapeInsideInfo(PassOwnPtr<ShapeInsideInfo> value) + { + if (!m_rareData) + m_rareData = adoptPtr(new RenderBlockRareData(this)); + m_rareData->m_shapeInsideInfo = value; } + void markShapeInsideDescendantsForLayout(); + ShapeInsideInfo* layoutShapeInsideInfo() const; + bool allowsShapeInsideInfoSharing() const { return !isInline() && !isFloating(); } #endif protected: @@ -442,33 +483,64 @@ protected: void setMaxMarginBeforeValues(LayoutUnit pos, LayoutUnit neg); void setMaxMarginAfterValues(LayoutUnit pos, LayoutUnit neg); + void setMustDiscardMarginBefore(bool = true); + void setMustDiscardMarginAfter(bool = true); + + bool mustDiscardMarginBefore() const; + bool mustDiscardMarginAfter() const; + + bool mustDiscardMarginBeforeForChild(const RenderBox*) const; + bool mustDiscardMarginAfterForChild(const RenderBox*) const; + + bool mustSeparateMarginBeforeForChild(const RenderBox*) const; + bool mustSeparateMarginAfterForChild(const RenderBox*) const; + void initMaxMarginValues() { if (m_rareData) { m_rareData->m_margins = MarginValues(RenderBlockRareData::positiveMarginBeforeDefault(this) , RenderBlockRareData::negativeMarginBeforeDefault(this), RenderBlockRareData::positiveMarginAfterDefault(this), RenderBlockRareData::negativeMarginAfterDefault(this)); m_rareData->m_paginationStrut = 0; + + m_rareData->m_discardMarginBefore = false; + m_rareData->m_discardMarginAfter = false; } } virtual void layout(); - void layoutPositionedObjects(bool relayoutChildren); + void layoutPositionedObjects(bool relayoutChildren, bool fixedPositionObjectsOnly = false); + void markFixedPositionObjectForLayoutIfNeeded(RenderObject* child); virtual void paint(PaintInfo&, const LayoutPoint&); virtual void paintObject(PaintInfo&, const LayoutPoint&); virtual void paintChildren(PaintInfo& forSelf, const LayoutPoint&, PaintInfo& forChild, bool usePrintRect); bool paintChild(RenderBox*, PaintInfo& forSelf, const LayoutPoint&, PaintInfo& forChild, bool usePrintRect); - LayoutUnit logicalRightOffsetForLine(LayoutUnit position, LayoutUnit fixedOffset, bool applyTextIndent, LayoutUnit* logicalHeightRemaining = 0, LayoutUnit logicalHeight = 0) const; - LayoutUnit logicalLeftOffsetForLine(LayoutUnit position, LayoutUnit fixedOffset, bool applyTextIndent, LayoutUnit* logicalHeightRemaining = 0, LayoutUnit logicalHeight = 0) const; + LayoutUnit logicalRightOffsetForLine(LayoutUnit logicalTop, LayoutUnit fixedOffset, bool applyTextIndent, LayoutUnit* heightRemaining = 0, LayoutUnit logicalHeight = 0) const + { + return adjustLogicalRightOffsetForLine(logicalRightFloatOffsetForLine(logicalTop, fixedOffset, heightRemaining, logicalHeight, ShapeOutsideFloatShapeOffset), applyTextIndent); + } + LayoutUnit logicalLeftOffsetForLine(LayoutUnit logicalTop, LayoutUnit fixedOffset, bool applyTextIndent, LayoutUnit* heightRemaining = 0, LayoutUnit logicalHeight = 0) const + { + return adjustLogicalLeftOffsetForLine(logicalLeftFloatOffsetForLine(logicalTop, fixedOffset, heightRemaining, logicalHeight, ShapeOutsideFloatShapeOffset), applyTextIndent); + } + LayoutUnit logicalRightOffsetForLineIgnoringShapeOutside(LayoutUnit logicalTop, LayoutUnit fixedOffset, bool applyTextIndent, LayoutUnit* heightRemaining = 0, LayoutUnit logicalHeight = 0) const + { + return adjustLogicalRightOffsetForLine(logicalRightFloatOffsetForLine(logicalTop, fixedOffset, heightRemaining, logicalHeight, ShapeOutsideFloatMarginBoxOffset), applyTextIndent); + } + LayoutUnit logicalLeftOffsetForLineIgnoringShapeOutside(LayoutUnit logicalTop, LayoutUnit fixedOffset, bool applyTextIndent, LayoutUnit* heightRemaining = 0, LayoutUnit logicalHeight = 0) const + { + return adjustLogicalLeftOffsetForLine(logicalLeftFloatOffsetForLine(logicalTop, fixedOffset, heightRemaining, logicalHeight, ShapeOutsideFloatMarginBoxOffset), applyTextIndent); + } virtual ETextAlign textAlignmentForLine(bool endsWithSoftBreak) const; virtual void adjustInlineDirectionLineBounds(int /* expansionOpportunityCount */, float& /* logicalLeft */, float& /* logicalWidth */) const { } virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction) OVERRIDE; - virtual void computePreferredLogicalWidths(); + virtual void computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const OVERRIDE; + virtual void computePreferredLogicalWidths() OVERRIDE; virtual int firstLineBoxBaseline() const; virtual int inlineBlockBaseline(LineDirectionMode) const OVERRIDE; @@ -492,11 +564,14 @@ protected: virtual bool hasLineIfEmpty() const; bool simplifiedLayout(); - void simplifiedNormalFlowLayout(); + virtual void simplifiedNormalFlowLayout(); void setDesiredColumnCountAndWidth(int, LayoutUnit); +public: void computeOverflow(LayoutUnit oldClientAfterEdge, bool recomputeFloats = false); + void clearLayoutOverflow(); +protected: virtual void addOverflowFromChildren(); void addOverflowFromFloats(); void addOverflowFromPositionedObjects(); @@ -504,7 +579,7 @@ protected: void addOverflowFromInlineChildren(); void addVisualOverflowFromTheme(); - virtual void addFocusRingRects(Vector<IntRect>&, const LayoutPoint&); + virtual void addFocusRingRects(Vector<IntRect>&, const LayoutPoint& additionalOffset, const RenderLayerModelObject* paintContainer = 0) OVERRIDE; #if ENABLE(SVG) // Only used by RenderSVGText, which explicitely overrides RenderBlock::layoutBlock(), do NOT use for anything else. @@ -517,15 +592,25 @@ protected: } #endif - void updateRegionsAndExclusionsLogicalSize(); - void computeRegionRangeForBlock(); + bool updateRegionsAndShapesBeforeChildLayout(RenderFlowThread*); + void updateRegionsAndShapesAfterChildLayout(RenderFlowThread*, bool heightChanged = false); + void computeRegionRangeForBlock(RenderFlowThread*); + + void updateBlockChildDirtyBitsBeforeLayout(bool relayoutChildren, RenderBox*); virtual void checkForPaginationLogicalHeightChange(LayoutUnit& pageLogicalHeight, bool& pageLogicalHeightChanged, bool& hasSpecifiedPageLogicalHeight); private: -#if ENABLE(CSS_EXCLUSIONS) - void computeExclusionShapeSize(); - void updateExclusionShapeInsideInfoAfterStyleChange(const ExclusionShapeValue*, const ExclusionShapeValue* oldExclusionShape); + enum ShapeOutsideFloatOffsetMode { ShapeOutsideFloatShapeOffset, ShapeOutsideFloatMarginBoxOffset }; + + LayoutUnit logicalRightFloatOffsetForLine(LayoutUnit logicalTop, LayoutUnit fixedOffset, LayoutUnit* heightRemaining, LayoutUnit logicalHeight, ShapeOutsideFloatOffsetMode) const; + LayoutUnit logicalLeftFloatOffsetForLine(LayoutUnit logicalTop, LayoutUnit fixedOffset, LayoutUnit* heightRemaining, LayoutUnit logicalHeight, ShapeOutsideFloatOffsetMode) const; + LayoutUnit adjustLogicalRightOffsetForLine(LayoutUnit offsetFromFloats, bool applyTextIndent) const; + LayoutUnit adjustLogicalLeftOffsetForLine(LayoutUnit offsetFromFloats, bool applyTextIndent) const; + +#if ENABLE(CSS_SHAPES) + void computeShapeSize(); + void updateShapeInsideInfoAfterStyleChange(const ShapeValue*, const ShapeValue* oldShape); #endif virtual RenderObjectChildList* virtualChildren() { return children(); } virtual const RenderObjectChildList* virtualChildren() const { return children(); } @@ -539,7 +624,7 @@ private: void makeChildrenNonInline(RenderObject* insertionPoint = 0); virtual void removeLeftoverAnonymousBlock(RenderBlock* child); - static void collapseAnonymousBoxChild(RenderBlock* parent, RenderObject* child); + void moveAllChildrenIncludingFloatsTo(RenderBlock* toBlock, bool fullRemoveInsert); virtual void dirtyLinesFromChangedChild(RenderObject* child) { m_lineBoxes.dirtyLinesFromChangedChild(this, child); } @@ -554,7 +639,7 @@ private: virtual LayoutUnit collapsedMarginBefore() const { return maxPositiveMarginBefore() - maxNegativeMarginBefore(); } virtual LayoutUnit collapsedMarginAfter() const { return maxPositiveMarginAfter() - maxNegativeMarginAfter(); } - virtual void repaintOverhangingFloats(bool paintAllDescendants); + virtual void repaintOverhangingFloats(bool paintAllDescendants) OVERRIDE; void layoutBlockChildren(bool relayoutChildren, LayoutUnit& maxFloatLogicalBottom); void layoutInlineChildren(bool relayoutChildren, LayoutUnit& repaintLogicalTop, LayoutUnit& repaintLogicalBottom); @@ -563,10 +648,6 @@ private: void insertIntoTrackedRendererMaps(RenderBox* descendant, TrackedDescendantsMap*&, TrackedContainerMap*&); static void removeFromTrackedRendererMaps(RenderBox* descendant, TrackedDescendantsMap*&, TrackedContainerMap*&); - virtual void borderFitAdjust(LayoutRect&) const; // Shrink the box in which the border paints if border-fit is set. - - virtual void updateBeforeAfterContent(PseudoId); - virtual RootInlineBox* createRootInlineBox(); // Subclassed by SVG and Ruby. // Called to lay out the legend for a fieldset or the ruby text of a ruby run. @@ -629,6 +710,18 @@ private: { } + FloatingObject* clone() const + { + FloatingObject* cloneObject = new FloatingObject(type(), m_frameRect); + cloneObject->m_renderer = m_renderer; + cloneObject->m_originatingLine = m_originatingLine; + cloneObject->m_paginationStrut = m_paginationStrut; + cloneObject->m_shouldPaint = m_shouldPaint; + cloneObject->m_isDescendant = m_isDescendant; + cloneObject->m_isPlaced = m_isPlaced; + return cloneObject; + } + Type type() const { return static_cast<Type>(m_type); } RenderBox* renderer() const { return m_renderer; } @@ -763,6 +856,7 @@ private: private: void reset(); + InlineIterator nextSegmentBreak(InlineBidiResolver&, LineInfo&, RenderTextInfo&, FloatingObject* lastFloatFromPreviousLine, unsigned consecutiveHyphenatedLines, WordMeasurements&); void skipTrailingWhitespace(InlineIterator&, const LineInfo&); void skipLeadingWhitespace(InlineBidiResolver&, LineInfo&, FloatingObject* lastFloatFromPreviousLine, LineWidth&); @@ -779,10 +873,12 @@ private: bool checkPaginationAndFloatsAtEndLine(LineLayoutState&); RootInlineBox* constructLine(BidiRunList<BidiRun>&, const LineInfo&); - InlineFlowBox* createLineBoxes(RenderObject*, const LineInfo&, InlineBox* childBox); + InlineFlowBox* createLineBoxes(RenderObject*, const LineInfo&, InlineBox* childBox, bool startsNewSegment); void setMarginsForRubyRun(BidiRun*, RenderRubyRun*, RenderObject*, const LineInfo&); + BidiRun* computeInlineDirectionPositionsForSegment(RootInlineBox*, const LineInfo&, ETextAlign, float& logicalLeft, + float& availableLogicalWidth, BidiRun* firstRun, BidiRun* trailingSpaceRun, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache&, WordMeasurements&); void computeInlineDirectionPositionsForLine(RootInlineBox*, const LineInfo&, BidiRun* firstRun, BidiRun* trailingSpaceRun, bool reachedEnd, GlyphOverflowAndFallbackFontsMap&, VerticalPositionCache&, WordMeasurements&); void computeBlockDirectionPositionsForLine(RootInlineBox*, BidiRun*, GlyphOverflowAndFallbackFontsMap&, VerticalPositionCache&); void deleteEllipsisLineBoxes(); @@ -830,8 +926,9 @@ private: virtual bool isPointInOverflowControl(HitTestResult&, const LayoutPoint& locationInContainer, const LayoutPoint& accumulatedOffset); - void computeInlinePreferredLogicalWidths(); - void computeBlockPreferredLogicalWidths(); + // FIXME: Make this method const so we can remove the const_cast in computeIntrinsicLogicalWidths. + void computeInlinePreferredLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth); + void computeBlockPreferredLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const; // Obtains the nearest enclosing block (including this block) that contributes a first-line style to our inline // children. @@ -851,15 +948,17 @@ private: virtual bool shouldPaintSelectionGaps() const; bool isSelectionRoot() const; GapRects selectionGaps(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, - LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const PaintInfo* = 0); + LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const LogicalSelectionOffsetCaches&, const PaintInfo* = 0); GapRects inlineSelectionGaps(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, - LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const PaintInfo*); + LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const LogicalSelectionOffsetCaches&, const PaintInfo*); GapRects blockSelectionGaps(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, - LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const PaintInfo*); + LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const LogicalSelectionOffsetCaches&, const PaintInfo*); LayoutRect blockSelectionGap(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, - LayoutUnit lastLogicalTop, LayoutUnit lastLogicalLeft, LayoutUnit lastLogicalRight, LayoutUnit logicalBottom, const PaintInfo*); - LayoutUnit logicalLeftSelectionOffset(RenderBlock* rootBlock, LayoutUnit position); - LayoutUnit logicalRightSelectionOffset(RenderBlock* rootBlock, LayoutUnit position); + LayoutUnit lastLogicalTop, LayoutUnit lastLogicalLeft, LayoutUnit lastLogicalRight, LayoutUnit logicalBottom, const LogicalSelectionOffsetCaches&, const PaintInfo*); + LayoutUnit logicalLeftSelectionOffset(RenderBlock* rootBlock, LayoutUnit position, const LogicalSelectionOffsetCaches&); + LayoutUnit logicalRightSelectionOffset(RenderBlock* rootBlock, LayoutUnit position, const LogicalSelectionOffsetCaches&); + + friend class LogicalSelectionOffsetCaches; virtual void absoluteRects(Vector<IntRect>&, const LayoutPoint& accumulatedOffset) const; virtual void absoluteQuads(Vector<FloatQuad>&, bool* wasFixed) const; @@ -872,6 +971,8 @@ private: virtual LayoutRect localCaretRect(InlineBox*, int caretOffset, LayoutUnit* extraWidthToEndOfLine = 0); void adjustPointToColumnContents(LayoutPoint&) const; + + void fitBorderToLinesIfNeeded(); // Shrink the box in which the border paints if border-fit is set. void adjustForBorderFit(LayoutUnit x, LayoutUnit& left, LayoutUnit& right) const; // Helper function for borderFitAdjust void markLinesDirtyInBlockRange(LayoutUnit logicalTop, LayoutUnit logicalBottom, RootInlineBox* highest = 0); @@ -917,10 +1018,12 @@ private: // These variables are used to detect quirky margins that we need to collapse away (in table cells // and in the body element). - bool m_marginBeforeQuirk : 1; - bool m_marginAfterQuirk : 1; + bool m_hasMarginBeforeQuirk : 1; + bool m_hasMarginAfterQuirk : 1; bool m_determinedMarginBeforeQuirk : 1; + bool m_discardMargin : 1; + // These flags track the previous maximal positive and negative margins. LayoutUnit m_positiveMargin; LayoutUnit m_negativeMargin; @@ -935,45 +1038,46 @@ private: m_positiveMargin = 0; m_negativeMargin = 0; } - void setMarginBeforeQuirk(bool b) { m_marginBeforeQuirk = b; } - void setMarginAfterQuirk(bool b) { m_marginAfterQuirk = b; } + void setHasMarginBeforeQuirk(bool b) { m_hasMarginBeforeQuirk = b; } + void setHasMarginAfterQuirk(bool b) { m_hasMarginAfterQuirk = b; } void setDeterminedMarginBeforeQuirk(bool b) { m_determinedMarginBeforeQuirk = b; } - void setPositiveMargin(LayoutUnit p) { m_positiveMargin = p; } - void setNegativeMargin(LayoutUnit n) { m_negativeMargin = n; } + void setPositiveMargin(LayoutUnit p) { ASSERT(!m_discardMargin); m_positiveMargin = p; } + void setNegativeMargin(LayoutUnit n) { ASSERT(!m_discardMargin); m_negativeMargin = n; } void setPositiveMarginIfLarger(LayoutUnit p) { + ASSERT(!m_discardMargin); if (p > m_positiveMargin) m_positiveMargin = p; } void setNegativeMarginIfLarger(LayoutUnit n) { + ASSERT(!m_discardMargin); if (n > m_negativeMargin) m_negativeMargin = n; } - void setMargin(LayoutUnit p, LayoutUnit n) { m_positiveMargin = p; m_negativeMargin = n; } + void setMargin(LayoutUnit p, LayoutUnit n) { ASSERT(!m_discardMargin); m_positiveMargin = p; m_negativeMargin = n; } + void setCanCollapseMarginAfterWithChildren(bool collapse) { m_canCollapseMarginAfterWithChildren = collapse; } + void setDiscardMargin(bool value) { m_discardMargin = value; } bool atBeforeSideOfBlock() const { return m_atBeforeSideOfBlock; } bool canCollapseWithMarginBefore() const { return m_atBeforeSideOfBlock && m_canCollapseMarginBeforeWithChildren; } bool canCollapseWithMarginAfter() const { return m_atAfterSideOfBlock && m_canCollapseMarginAfterWithChildren; } bool canCollapseMarginBeforeWithChildren() const { return m_canCollapseMarginBeforeWithChildren; } bool canCollapseMarginAfterWithChildren() const { return m_canCollapseMarginAfterWithChildren; } - void setCanCollapseMarginAfterWithChildren(bool collapse) { m_canCollapseMarginAfterWithChildren = collapse; } bool quirkContainer() const { return m_quirkContainer; } bool determinedMarginBeforeQuirk() const { return m_determinedMarginBeforeQuirk; } - bool marginBeforeQuirk() const { return m_marginBeforeQuirk; } - bool marginAfterQuirk() const { return m_marginAfterQuirk; } + bool hasMarginBeforeQuirk() const { return m_hasMarginBeforeQuirk; } + bool hasMarginAfterQuirk() const { return m_hasMarginAfterQuirk; } LayoutUnit positiveMargin() const { return m_positiveMargin; } LayoutUnit negativeMargin() const { return m_negativeMargin; } + bool discardMargin() const { return m_discardMargin; } LayoutUnit margin() const { return m_positiveMargin - m_negativeMargin; } }; void layoutBlockChild(RenderBox* child, MarginInfo&, LayoutUnit& previousFloatLogicalBottom, LayoutUnit& maxFloatLogicalBottom); void adjustPositionedBlock(RenderBox* child, const MarginInfo&); void adjustFloatingBlock(const MarginInfo&); - bool handleSpecialChild(RenderBox* child, const MarginInfo&); - bool handleFloatingChild(RenderBox* child, const MarginInfo&); - bool handlePositionedChild(RenderBox* child, const MarginInfo&); RenderBoxModelObject* createReplacementRunIn(RenderBoxModelObject* runIn); void moveRunInUnderSiblingBlockIfNeeded(RenderObject* runIn); @@ -982,8 +1086,7 @@ private: LayoutUnit collapseMargins(RenderBox* child, MarginInfo&); LayoutUnit clearFloatsIfNeeded(RenderBox* child, MarginInfo&, LayoutUnit oldTopPosMargin, LayoutUnit oldTopNegMargin, LayoutUnit yPos); LayoutUnit estimateLogicalTopPosition(RenderBox* child, const MarginInfo&, LayoutUnit& estimateWithoutPagination); - void marginBeforeEstimateForChild(RenderBox* child, LayoutUnit& positiveMarginBefore, LayoutUnit& negativeMarginBefore) const; - void determineLogicalLeftPositionForChild(RenderBox* child); + void marginBeforeEstimateForChild(RenderBox*, LayoutUnit&, LayoutUnit&, bool&) const; void handleAfterSideOfBlock(LayoutUnit top, LayoutUnit bottom, MarginInfo&); void setCollapsedBottomMargin(const MarginInfo&); // End helper functions and structs used by layoutBlockChildren. @@ -992,10 +1095,20 @@ private: RootInlineBox* createLineBoxesFromBidiRuns(BidiRunList<BidiRun>&, const InlineIterator& end, LineInfo&, VerticalPositionCache&, BidiRun* trailingSpaceRun, WordMeasurements&); void layoutRunsAndFloats(LineLayoutState&, bool hasInlineChild); void layoutRunsAndFloatsInRange(LineLayoutState&, InlineBidiResolver&, const InlineIterator& cleanLineStart, const BidiStatus& cleanLineBidiStatus, unsigned consecutiveHyphenatedLines); +#if ENABLE(CSS_SHAPES) + void updateShapeAndSegmentsForCurrentLine(ShapeInsideInfo*&, LayoutUnit&, LineLayoutState&); + void updateShapeAndSegmentsForCurrentLineInFlowThread(ShapeInsideInfo*&, LineLayoutState&); + bool adjustLogicalLineTopAndLogicalHeightIfNeeded(ShapeInsideInfo*, LayoutUnit, LineLayoutState&, InlineBidiResolver&, FloatingObject*, InlineIterator&, WordMeasurements&); +#endif + const InlineIterator& restartLayoutRunsAndFloatsInRange(LayoutUnit oldLogicalHeight, LayoutUnit newLogicalHeight, FloatingObject* lastFloatFromPreviousLine, InlineBidiResolver&, const InlineIterator&); void linkToEndLineIfNeeded(LineLayoutState&); static void repaintDirtyFloats(Vector<FloatWithRect>& floats); protected: + void dirtyForLayoutFromPercentageHeightDescendants(); + + void determineLogicalLeftPositionForChild(RenderBox* child, ApplyLayoutDeltaMode = DoNotApplyLayoutDelta); + // Pagination routines. virtual bool relayoutForPagination(bool hasSpecifiedPageLogicalHeight, LayoutUnit pageLogicalHeight, LayoutStateMaintainer&); @@ -1021,8 +1134,16 @@ public: protected: bool pushToNextPageWithMinimumLogicalHeight(LayoutUnit& adjustment, LayoutUnit logicalOffset, LayoutUnit minimumLogicalHeight) const; + // A page break is required at some offset due to space shortage in the current fragmentainer. + void setPageBreak(LayoutUnit offset, LayoutUnit spaceShortage); + + // Update minimum page height required to avoid fragmentation where it shouldn't occur (inside + // unbreakable content, between orphans and widows, etc.). This will be used as a hint to the + // column balancer to help set a good minimum column height. + void updateMinimumPageHeight(LayoutUnit offset, LayoutUnit minHeight); + LayoutUnit adjustForUnsplittableChild(RenderBox* child, LayoutUnit logicalOffset, bool includeMargins = false); // If the child is unsplittable and can't fit on the current page, return the top of the next page/column. - void adjustLinePositionForPagination(RootInlineBox*, LayoutUnit& deltaOffset); // Computes a deltaOffset value that put a line at the top of the next page if it doesn't fit on the current page. + void adjustLinePositionForPagination(RootInlineBox*, LayoutUnit& deltaOffset, RenderFlowThread*); // Computes a deltaOffset value that put a line at the top of the next page if it doesn't fit on the current page. LayoutUnit adjustBlockChildForPagination(LayoutUnit logicalTopAfterClear, LayoutUnit estimateWithoutPagination, RenderBox* child, bool atBeforeSideOfBlock); // Adjust from painting offsets to the local coords of this renderer @@ -1031,9 +1152,9 @@ protected: // This function is called to test a line box that has moved in the block direction to see if it has ended up in a new // region/page/column that has a different available line width than the old one. Used to know when you have to dirty a // line, i.e., that it can't be re-used. - bool lineWidthForPaginatedLineChanged(RootInlineBox*, LayoutUnit lineDelta = 0) const; + bool lineWidthForPaginatedLineChanged(RootInlineBox*, LayoutUnit lineDelta, RenderFlowThread*) const; - bool logicalWidthChangedInRegions() const; + bool logicalWidthChangedInRegions(RenderFlowThread*) const; virtual bool requiresColumns(int desiredColumnCount) const; @@ -1042,7 +1163,7 @@ protected: virtual bool canCollapseAnonymousBlockChild() const { return true; } public: - LayoutUnit offsetFromLogicalTopOfFirstPage() const; + virtual LayoutUnit offsetFromLogicalTopOfFirstPage() const; RenderRegion* regionAtBlockOffset(LayoutUnit) const; RenderRegion* clampToStartAndEndRegions(RenderRegion*) const; @@ -1073,6 +1194,9 @@ protected: , m_highValue(highValue) , m_offset(offset) , m_heightRemaining(heightRemaining) +#if ENABLE(CSS_SHAPES) + , m_last(0) +#endif { } @@ -1080,25 +1204,39 @@ protected: inline int highValue() const { return m_highValue; } void collectIfNeeded(const IntervalType&) const; +#if ENABLE(CSS_SHAPES) + // When computing the offset caused by the floats on a given line, if + // the outermost float on that line has a shape-outside, the inline + // content that butts up against that float must be positioned using + // the contours of the shape, not the margin box of the float. + // We save the last float encountered so that the offset can be + // computed correctly by the code using this adapter. + const FloatingObject* lastFloat() const { return m_last; } +#endif + private: const RenderBlock* m_renderer; int m_lowValue; int m_highValue; LayoutUnit& m_offset; LayoutUnit* m_heightRemaining; +#if ENABLE(CSS_SHAPES) + // This member variable is mutable because the collectIfNeeded method + // is declared as const, even though it doesn't actually respect that + // contract. It modifies other member variables via loopholes in the + // const behavior. Instead of using loopholes, I decided it was better + // to make the fact that this is modified in a const method explicit. + mutable const FloatingObject* m_last; +#endif }; + void createFloatingObjects(); + +public: + class FloatingObjects { + WTF_MAKE_NONCOPYABLE(FloatingObjects); WTF_MAKE_FAST_ALLOCATED; public: - FloatingObjects(const RenderBlock* renderer, bool horizontalWritingMode) - : m_placedFloatsTree(UninitializedTree) - , m_leftObjectsCount(0) - , m_rightObjectsCount(0) - , m_horizontalWritingMode(horizontalWritingMode) - , m_renderer(renderer) - { - } - void clear(); void add(FloatingObject*); void remove(FloatingObject*); @@ -1115,6 +1253,7 @@ protected: return m_placedFloatsTree; } private: + FloatingObjects(const RenderBlock*, bool horizontalWritingMode); void computePlacedFloatsTree(); inline void computePlacedFloatsTreeIfNeeded() { @@ -1131,8 +1270,9 @@ protected: unsigned m_rightObjectsCount; bool m_horizontalWritingMode; const RenderBlock* m_renderer; + + friend void RenderBlock::createFloatingObjects(); }; - OwnPtr<FloatingObjects> m_floatingObjects; // Allocated only when some of these fields have non-default values struct RenderBlockRareData { @@ -1143,6 +1283,10 @@ protected: , m_paginationStrut(0) , m_pageLogicalOffset(0) , m_lineGridBox(0) + , m_lineBreakToAvoidWidow(0) + , m_shouldBreakAtLineToAvoidWidow(false) + , m_discardMarginBefore(false) + , m_discardMarginAfter(false) { } @@ -1150,7 +1294,6 @@ protected: { return std::max<LayoutUnit>(block->marginBefore(), 0); } - static LayoutUnit negativeMarginBeforeDefault(const RenderBlock* block) { return std::max<LayoutUnit>(-block->marginBefore(), 0); @@ -1169,16 +1312,30 @@ protected: LayoutUnit m_pageLogicalOffset; RootInlineBox* m_lineGridBox; + + RootInlineBox* m_lineBreakToAvoidWidow; +#if ENABLE(CSS_SHAPES) + OwnPtr<ShapeInsideInfo> m_shapeInsideInfo; +#endif + bool m_shouldBreakAtLineToAvoidWidow : 1; + bool m_discardMarginBefore : 1; + bool m_discardMarginAfter : 1; }; +protected: + + OwnPtr<FloatingObjects> m_floatingObjects; OwnPtr<RenderBlockRareData> m_rareData; RenderObjectChildList m_children; RenderLineBoxList m_lineBoxes; // All of the root line boxes created for this block flow. For example, <div>Hello<br>world.</div> will have two total lines for the <div>. - mutable signed m_lineHeight : 30; + mutable signed m_lineHeight : 27; + unsigned m_hasMarginBeforeQuirk : 1; // Note these quirk values can't be put in RenderBlockRareData since they are set too frequently. + unsigned m_hasMarginAfterQuirk : 1; unsigned m_beingDestroyed : 1; unsigned m_hasMarkupTruncation : 1; + unsigned m_hasBorderOrPaddingLogicalWidthChanged : 1; // RenderRubyBase objects need to be able to split and merge, moving their children around // (calling moveChildTo, moveAllChildrenTo, and makeChildrenNonInline). @@ -1192,13 +1349,13 @@ private: inline RenderBlock* toRenderBlock(RenderObject* object) { - ASSERT(!object || object->isRenderBlock()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isRenderBlock()); return static_cast<RenderBlock*>(object); } inline const RenderBlock* toRenderBlock(const RenderObject* object) { - ASSERT(!object || object->isRenderBlock()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isRenderBlock()); return static_cast<const RenderBlock*>(object); } diff --git a/Source/WebCore/rendering/RenderBlockLineLayout.cpp b/Source/WebCore/rendering/RenderBlockLineLayout.cpp index 970fbc346..8550ca495 100644 --- a/Source/WebCore/rendering/RenderBlockLineLayout.cpp +++ b/Source/WebCore/rendering/RenderBlockLineLayout.cpp @@ -2,6 +2,7 @@ * Copyright (C) 2000 Lars Knoll (knoll@kde.org) * Copyright (C) 2003, 2004, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All right reserved. * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2013 ChangSeok Oh <shivamidow@gmail.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -29,24 +30,25 @@ #include "Logging.h" #include "RenderArena.h" #include "RenderCombineText.h" +#include "RenderCounter.h" #include "RenderFlowThread.h" #include "RenderInline.h" #include "RenderLayer.h" #include "RenderListMarker.h" +#include "RenderRegion.h" #include "RenderRubyRun.h" #include "RenderView.h" #include "Settings.h" #include "TrailingFloatsRootInlineBox.h" #include "VerticalPositionCache.h" #include "break_lines.h" -#include <wtf/AlwaysInline.h> #include <wtf/RefCountedLeakCounter.h> #include <wtf/StdLibExtras.h> #include <wtf/Vector.h> #include <wtf/unicode/CharacterNames.h> -#if ENABLE(CSS_EXCLUSIONS) -#include "ExclusionShapeInsideInfo.h" +#if ENABLE(CSS_SHAPES) +#include "ShapeInsideInfo.h" #endif #if ENABLE(SVG) @@ -63,47 +65,75 @@ namespace WebCore { // We don't let our line box tree for a single line get any deeper than this. const unsigned cMaxLineDepth = 200; -#if ENABLE(CSS_EXCLUSIONS) -static inline ExclusionShapeInsideInfo* layoutExclusionShapeInsideInfo(const RenderBlock* block) +static LayoutUnit logicalHeightForLine(const RenderBlock* block, bool isFirstLine, LayoutUnit replacedHeight = 0) { - return block->view()->layoutState()->exclusionShapeInsideInfo(); + if (!block->document()->inNoQuirksMode() && replacedHeight) + return replacedHeight; + + if (!(block->style(isFirstLine)->lineBoxContain() & LineBoxContainBlock)) + return 0; + + return max<LayoutUnit>(replacedHeight, block->lineHeight(isFirstLine, block->isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes)); +} + +#if ENABLE(CSS_SHAPES) +ShapeInsideInfo* RenderBlock::layoutShapeInsideInfo() const +{ + ShapeInsideInfo* shapeInsideInfo = view()->layoutState()->shapeInsideInfo(); + + if (!shapeInsideInfo && flowThreadContainingBlock() && allowsShapeInsideInfoSharing()) { + // regionAtBlockOffset returns regions like an array first={0,N-1}, second={N,M-1}, ... + LayoutUnit offset = logicalHeight() + logicalHeightForLine(this, false) - LayoutUnit(1); + RenderRegion* region = regionAtBlockOffset(offset); + if (region) + shapeInsideInfo = region->shapeInsideInfo(); + } + + return shapeInsideInfo; } #endif +enum IndentTextOrNot { DoNotIndentText, IndentText }; + class LineWidth { public: - LineWidth(RenderBlock* block, bool isFirstLine) + LineWidth(RenderBlock* block, bool isFirstLine, IndentTextOrNot shouldIndentText) : m_block(block) , m_uncommittedWidth(0) , m_committedWidth(0) , m_overhangWidth(0) + , m_trailingWhitespaceWidth(0) + , m_trailingCollapsedWhitespaceWidth(0) , m_left(0) , m_right(0) , m_availableWidth(0) -#if ENABLE(CSS_EXCLUSIONS) +#if ENABLE(CSS_SHAPES) , m_segment(0) #endif , m_isFirstLine(isFirstLine) + , m_shouldIndentText(shouldIndentText) { ASSERT(block); -#if ENABLE(CSS_EXCLUSIONS) - ExclusionShapeInsideInfo* exclusionShapeInsideInfo = layoutExclusionShapeInsideInfo(m_block); - // FIXME: Bug 91878: Add support for multiple segments, currently we only support one - if (exclusionShapeInsideInfo && exclusionShapeInsideInfo->hasSegments()) - m_segment = &exclusionShapeInsideInfo->segments()[0]; +#if ENABLE(CSS_SHAPES) + if (ShapeInsideInfo* shapeInsideInfo = m_block->layoutShapeInsideInfo()) + m_segment = shapeInsideInfo->currentSegment(); #endif updateAvailableWidth(); } - bool fitsOnLine() const { return currentWidth() <= m_availableWidth; } - bool fitsOnLine(float extra) const { return currentWidth() + extra <= m_availableWidth; } - float currentWidth() const { return m_committedWidth + m_uncommittedWidth; } + bool fitsOnLine(bool ignoringTrailingSpace = false) + { + return ignoringTrailingSpace ? fitsOnLineExcludingTrailingCollapsedWhitespace() : fitsOnLineIncludingExtraWidth(0); + } + bool fitsOnLineIncludingExtraWidth(float extra) const { return currentWidth() + extra <= m_availableWidth; } + bool fitsOnLineExcludingTrailingWhitespace(float extra) const { return currentWidth() - m_trailingWhitespaceWidth + extra <= m_availableWidth; } + float currentWidth() const { return m_committedWidth + m_uncommittedWidth; } // FIXME: We should eventually replace these three functions by ones that work on a higher abstraction. float uncommittedWidth() const { return m_uncommittedWidth; } float committedWidth() const { return m_committedWidth; } float availableWidth() const { return m_availableWidth; } - void updateAvailableWidth(); + void updateAvailableWidth(LayoutUnit minimumHeight = 0); void shrinkAvailableWidthForNewFloatIfNeeded(RenderBlock::FloatingObject*); void addUncommittedWidth(float delta) { m_uncommittedWidth += delta; } void commit() @@ -113,49 +143,42 @@ public: } void applyOverhang(RenderRubyRun*, RenderObject* startRenderer, RenderObject* endRenderer); void fitBelowFloats(); + void setTrailingWhitespaceWidth(float collapsedWhitespace, float borderPaddingMargin = 0) { m_trailingCollapsedWhitespaceWidth = collapsedWhitespace; m_trailingWhitespaceWidth = collapsedWhitespace + borderPaddingMargin; } + + bool shouldIndentText() const { return m_shouldIndentText == IndentText; } private: void computeAvailableWidthFromLeftAndRight() { m_availableWidth = max(0.0f, m_right - m_left) + m_overhangWidth; } + bool fitsOnLineExcludingTrailingCollapsedWhitespace() const { return currentWidth() - m_trailingCollapsedWhitespaceWidth <= m_availableWidth; } private: RenderBlock* m_block; float m_uncommittedWidth; float m_committedWidth; float m_overhangWidth; // The amount by which |m_availableWidth| has been inflated to account for possible contraction due to ruby overhang. + float m_trailingWhitespaceWidth; + float m_trailingCollapsedWhitespaceWidth; float m_left; float m_right; float m_availableWidth; -#if ENABLE(CSS_EXCLUSIONS) +#if ENABLE(CSS_SHAPES) const LineSegment* m_segment; #endif bool m_isFirstLine; + IndentTextOrNot m_shouldIndentText; }; -static LayoutUnit logicalHeightForLine(RenderBlock* block) -{ - InlineFlowBox* lineBox = block->firstRootBox(); - LayoutUnit logicalHeight = 0; - if (!lineBox) - return logicalHeight; - - if (lineBox->firstChild() && lineBox->firstChild()->renderer() && lineBox->firstChild()->renderer()->isRenderBlock()) - logicalHeight = toRenderBlock(lineBox->firstChild()->renderer())->logicalHeight(); - else - logicalHeight = lineBox->height(); - return logicalHeight; -} - -inline void LineWidth::updateAvailableWidth() +inline void LineWidth::updateAvailableWidth(LayoutUnit replacedHeight) { LayoutUnit height = m_block->logicalHeight(); - LayoutUnit logicalHeight = logicalHeightForLine(m_block); - m_left = m_block->logicalLeftOffsetForLine(height, m_isFirstLine, logicalHeight); - m_right = m_block->logicalRightOffsetForLine(height, m_isFirstLine, logicalHeight); + LayoutUnit logicalHeight = logicalHeightForLine(m_block, m_isFirstLine, replacedHeight); + m_left = m_block->logicalLeftOffsetForLine(height, shouldIndentText(), logicalHeight); + m_right = m_block->logicalRightOffsetForLine(height, shouldIndentText(), logicalHeight); -#if ENABLE(CSS_EXCLUSIONS) +#if ENABLE(CSS_SHAPES) if (m_segment) { m_left = max<float>(m_segment->logicalLeft, m_left); m_right = min<float>(m_segment->logicalRight, m_right); @@ -171,14 +194,55 @@ inline void LineWidth::shrinkAvailableWidthForNewFloatIfNeeded(RenderBlock::Floa if (height < m_block->logicalTopForFloat(newFloat) || height >= m_block->logicalBottomForFloat(newFloat)) return; +#if ENABLE(CSS_SHAPES) + // When floats with shape outside are stacked, the floats are positioned based on the margin box of the float, + // not the shape's contour. Since we computed the width based on the shape contour when we added the float, + // when we add a subsequent float on the same line, we need to undo the shape delta in order to position + // based on the margin box. In order to do this, we need to walk back through the floating object list to find + // the first previous float that is on the same side as our newFloat. + ShapeOutsideInfo* previousShapeOutsideInfo = 0; + const RenderBlock::FloatingObjectSet& floatingObjectSet = m_block->m_floatingObjects->set(); + RenderBlock::FloatingObjectSetIterator it = floatingObjectSet.end(); + RenderBlock::FloatingObjectSetIterator begin = floatingObjectSet.begin(); + for (--it; it != begin; --it) { + RenderBlock::FloatingObject* previousFloat = *it; + if (previousFloat != newFloat && previousFloat->type() == newFloat->type()) { + previousShapeOutsideInfo = previousFloat->renderer()->shapeOutsideInfo(); + if (previousShapeOutsideInfo) { + previousShapeOutsideInfo->computeSegmentsForContainingBlockLine(m_block->logicalHeight(), m_block->logicalTopForFloat(previousFloat), logicalHeightForLine(m_block, m_isFirstLine)); + } + break; + } + } + + ShapeOutsideInfo* shapeOutsideInfo = newFloat->renderer()->shapeOutsideInfo(); + if (shapeOutsideInfo) { + shapeOutsideInfo->computeSegmentsForContainingBlockLine(m_block->logicalHeight(), m_block->logicalTopForFloat(newFloat), logicalHeightForLine(m_block, m_isFirstLine)); + } +#endif + if (newFloat->type() == RenderBlock::FloatingObject::FloatLeft) { float newLeft = m_block->logicalRightForFloat(newFloat); - if (m_isFirstLine && m_block->style()->isLeftToRightDirection()) +#if ENABLE(CSS_SHAPES) + if (previousShapeOutsideInfo) + newLeft -= previousShapeOutsideInfo->rightSegmentMarginBoxDelta(); + if (shapeOutsideInfo) + newLeft += shapeOutsideInfo->rightSegmentMarginBoxDelta(); +#endif + + if (shouldIndentText() && m_block->style()->isLeftToRightDirection()) newLeft += floorToInt(m_block->textIndentOffset()); m_left = max<float>(m_left, newLeft); } else { float newRight = m_block->logicalLeftForFloat(newFloat); - if (m_isFirstLine && !m_block->style()->isLeftToRightDirection()) +#if ENABLE(CSS_SHAPES) + if (previousShapeOutsideInfo) + newRight -= previousShapeOutsideInfo->leftSegmentMarginBoxDelta(); + if (shapeOutsideInfo) + newRight += shapeOutsideInfo->leftSegmentMarginBoxDelta(); +#endif + + if (shouldIndentText() && !m_block->style()->isLeftToRightDirection()) newRight -= floorToInt(m_block->textIndentOffset()); m_right = min<float>(m_right, newRight); } @@ -215,8 +279,8 @@ void LineWidth::fitBelowFloats() if (floatLogicalBottom <= lastFloatLogicalBottom) break; - newLineLeft = m_block->logicalLeftOffsetForLine(floatLogicalBottom, m_isFirstLine); - newLineRight = m_block->logicalRightOffsetForLine(floatLogicalBottom, m_isFirstLine); + newLineLeft = m_block->logicalLeftOffsetForLine(floatLogicalBottom, shouldIndentText()); + newLineRight = m_block->logicalRightOffsetForLine(floatLogicalBottom, shouldIndentText()); newLineWidth = max(0.0f, newLineRight - newLineLeft); lastFloatLogicalBottom = floatLogicalBottom; if (newLineWidth >= m_uncommittedWidth) @@ -287,27 +351,37 @@ static inline LayoutUnit borderPaddingMarginEnd(RenderInline* child) return child->marginEnd() + child->paddingEnd() + child->borderEnd(); } -static bool shouldAddBorderPaddingMargin(RenderObject* child, bool &checkSide) +static inline bool shouldAddBorderPaddingMargin(RenderObject* child) { - if (!child || (child->isText() && !toRenderText(child)->textLength())) - return true; - checkSide = false; - return checkSide; + // When deciding whether we're at the edge of an inline, adjacent collapsed whitespace is the same as no sibling at all. + return !child || (child->isText() && !toRenderText(child)->textLength()); +} + +static RenderObject* previousInFlowSibling(RenderObject* child) +{ + child = child->previousSibling(); + while (child && child->isOutOfFlowPositioned()) + child = child->previousSibling(); + return child; } -static LayoutUnit inlineLogicalWidth(RenderObject* child, bool start = true, bool end = true) +static LayoutUnit inlineLogicalWidth(RenderObject* child, bool checkStartEdge = true, bool checkEndEdge = true) { unsigned lineDepth = 1; LayoutUnit extraWidth = 0; RenderObject* parent = child->parent(); while (parent->isRenderInline() && lineDepth++ < cMaxLineDepth) { RenderInline* parentAsRenderInline = toRenderInline(parent); - if (start && shouldAddBorderPaddingMargin(child->previousSibling(), start)) - extraWidth += borderPaddingMarginStart(parentAsRenderInline); - if (end && shouldAddBorderPaddingMargin(child->nextSibling(), end)) - extraWidth += borderPaddingMarginEnd(parentAsRenderInline); - if (!start && !end) - return extraWidth; + if (!isEmptyInline(parentAsRenderInline)) { + checkStartEdge = checkStartEdge && shouldAddBorderPaddingMargin(previousInFlowSibling(child)); + if (checkStartEdge) + extraWidth += borderPaddingMarginStart(parentAsRenderInline); + checkEndEdge = checkEndEdge && shouldAddBorderPaddingMargin(child->nextSibling()); + if (checkEndEdge) + extraWidth += borderPaddingMarginEnd(parentAsRenderInline); + if (!checkStartEdge && !checkEndEdge) + return extraWidth; + } child = parent; parent = child->parent(); } @@ -355,7 +429,8 @@ static void checkMidpoints(LineMidpointState& lineMidpointState, InlineIterator& } } -static void addMidpoint(LineMidpointState& lineMidpointState, const InlineIterator& midpoint) +// Don't call this directly. Use one of the descriptive helper functions below. +static void deprecatedAddMidpoint(LineMidpointState& lineMidpointState, const InlineIterator& midpoint) { if (lineMidpointState.midpoints.size() <= lineMidpointState.numMidpoints) lineMidpointState.midpoints.grow(lineMidpointState.numMidpoints + 10); @@ -364,6 +439,35 @@ static void addMidpoint(LineMidpointState& lineMidpointState, const InlineIterat midpoints[lineMidpointState.numMidpoints++] = midpoint; } +static inline void startIgnoringSpaces(LineMidpointState& lineMidpointState, const InlineIterator& midpoint) +{ + ASSERT(!(lineMidpointState.numMidpoints % 2)); + deprecatedAddMidpoint(lineMidpointState, midpoint); +} + +static inline void stopIgnoringSpaces(LineMidpointState& lineMidpointState, const InlineIterator& midpoint) +{ + ASSERT(lineMidpointState.numMidpoints % 2); + deprecatedAddMidpoint(lineMidpointState, midpoint); +} + +// When ignoring spaces, this needs to be called for objects that need line boxes such as RenderInlines or +// hard line breaks to ensure that they're not ignored. +static inline void ensureLineBoxInsideIgnoredSpaces(LineMidpointState& lineMidpointState, RenderObject* renderer) +{ + InlineIterator midpoint(0, renderer, 0); + stopIgnoringSpaces(lineMidpointState, midpoint); + startIgnoringSpaces(lineMidpointState, midpoint); +} + +// Adding a pair of midpoints before a character will split it out into a new line box. +static inline void ensureCharacterGetsLineBox(LineMidpointState& lineMidpointState, InlineIterator& textParagraphSeparator) +{ + InlineIterator midpoint(0, textParagraphSeparator.m_obj, textParagraphSeparator.m_pos); + startIgnoringSpaces(lineMidpointState, InlineIterator(0, textParagraphSeparator.m_obj, textParagraphSeparator.m_pos - 1)); + stopIgnoringSpaces(lineMidpointState, InlineIterator(0, textParagraphSeparator.m_obj, textParagraphSeparator.m_pos)); +} + static inline BidiRun* createRun(int start, int end, RenderObject* obj, InlineBidiResolver& resolver) { return new (obj->renderArena()) BidiRun(start, end, obj, resolver.context(), resolver.dir()); @@ -430,11 +534,20 @@ static inline InlineBox* createInlineBoxForRenderer(RenderObject* obj, bool isRo return toRenderInline(obj)->createAndAppendInlineFlowBox(); } +// FIXME: Don't let counters mark themselves as needing pref width recalcs during layout +// so we don't need this hack. +static inline void updateCounterIfNeeded(RenderText* o) +{ + if (!o->preferredLogicalWidthsDirty() || !o->isCounter()) + return; + toRenderCounter(o)->updateCounter(); +} + static inline void dirtyLineBoxesForRenderer(RenderObject* o, bool fullLayout) { if (o->isText()) { RenderText* renderText = toRenderText(o); - renderText->updateTextIfNeeded(); // FIXME: Counters depend on this hack. No clue why. Should be investigated and removed. + updateCounterIfNeeded(renderText); renderText->dirtyLineBoxes(fullLayout); } else toRenderInline(o)->dirtyLineBoxes(fullLayout); @@ -450,7 +563,7 @@ static bool parentIsConstructedOrHaveNext(InlineFlowBox* parentBox) return false; } -InlineFlowBox* RenderBlock::createLineBoxes(RenderObject* obj, const LineInfo& lineInfo, InlineBox* childBox) +InlineFlowBox* RenderBlock::createLineBoxes(RenderObject* obj, const LineInfo& lineInfo, InlineBox* childBox, bool startNewSegment) { // See if we have an unconstructed line box for this object that is also // the last item on the line. @@ -459,7 +572,7 @@ InlineFlowBox* RenderBlock::createLineBoxes(RenderObject* obj, const LineInfo& l InlineFlowBox* result = 0; bool hasDefaultLineBoxContain = style()->lineBoxContain() == RenderStyle::initialLineBoxContain(); do { - ASSERT(obj->isRenderInline() || obj == this); + ASSERT_WITH_SECURITY_IMPLICATION(obj->isRenderInline() || obj == this); RenderInline* inlineFlow = (obj != this) ? toRenderInline(obj) : 0; @@ -473,12 +586,13 @@ InlineFlowBox* RenderBlock::createLineBoxes(RenderObject* obj, const LineInfo& l // the same line (this can happen with very fancy language mixtures). bool constructedNewBox = false; bool allowedToConstructNewBox = !hasDefaultLineBoxContain || !inlineFlow || inlineFlow->alwaysCreateLineBoxes(); - bool canUseExistingParentBox = parentBox && !parentIsConstructedOrHaveNext(parentBox); + bool mustCreateBoxesToRoot = startNewSegment && !(parentBox && parentBox->isRootInlineBox()); + bool canUseExistingParentBox = parentBox && !parentIsConstructedOrHaveNext(parentBox) && !mustCreateBoxesToRoot; if (allowedToConstructNewBox && !canUseExistingParentBox) { // We need to make a new box for this render object. Once // made, we need to place it at the end of the current line. InlineBox* newBox = createInlineBoxForRenderer(obj, obj == this); - ASSERT(newBox->isInlineFlowBox()); + ASSERT_WITH_SECURITY_IMPLICATION(newBox->isInlineFlowBox()); parentBox = toInlineFlowBox(newBox); parentBox->setFirstLineStyleBit(lineInfo.isFirstLine()); parentBox->setIsHorizontal(isHorizontalWritingMode()); @@ -570,10 +684,16 @@ RootInlineBox* RenderBlock::constructLine(BidiRunList<BidiRun>& bidiRuns, const // If we have no parent box yet, or if the run is not simply a sibling, // then we need to construct inline boxes as necessary to properly enclose the - // run's inline box. - if (!parentBox || parentBox->renderer() != r->m_object->parent()) + // run's inline box. Segments can only be siblings at the root level, as + // they are positioned separately. +#if ENABLE(CSS_SHAPES) + bool runStartsSegment = r->m_startsSegment; +#else + bool runStartsSegment = false; +#endif + if (!parentBox || parentBox->renderer() != r->m_object->parent() || runStartsSegment) // Create new inline boxes all the way back to the appropriate insertion point. - parentBox = createLineBoxes(r->m_object->parent(), lineInfo, box); + parentBox = createLineBoxes(r->m_object->parent(), lineInfo, box, runStartsSegment); else { // Append the inline box to this line. parentBox->addToLine(box); @@ -692,10 +812,10 @@ void RenderBlock::setMarginsForRubyRun(BidiRun* run, RenderRubyRun* renderer, Re setMarginEndForChild(renderer, -endOverhang); } -static inline float measureHyphenWidth(RenderText* renderer, const Font& font) +static inline float measureHyphenWidth(RenderText* renderer, const Font& font, HashSet<const SimpleFontData*>* fallbackFonts = 0) { RenderStyle* style = renderer->style(); - return font.width(RenderBlock::constructTextRun(renderer, font, style->hyphenString().string(), style)); + return font.width(RenderBlock::constructTextRun(renderer, font, style->hyphenString().string(), style), fallbackFonts); } class WordMeasurement { @@ -716,11 +836,8 @@ public: }; static inline void setLogicalWidthForTextRun(RootInlineBox* lineBox, BidiRun* run, RenderText* renderer, float xPos, const LineInfo& lineInfo, - GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache& verticalPositionCache, WordMeasurements& wordMeasurements) + GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache& verticalPositionCache, WordMeasurements& wordMeasurements) { -#if !(PLATFORM(CHROMIUM) && OS(DARWIN)) - UNUSED_PARAM(wordMeasurements); -#endif HashSet<const SimpleFontData*> fallbackFonts; GlyphOverflow glyphOverflow; @@ -742,29 +859,33 @@ static inline void setLogicalWidthForTextRun(RootInlineBox* lineBox, BidiRun* ru LayoutUnit hyphenWidth = 0; if (toInlineTextBox(run->m_box)->hasHyphen()) { const Font& font = renderer->style(lineInfo.isFirstLine())->font(); - hyphenWidth = measureHyphenWidth(renderer, font); + hyphenWidth = measureHyphenWidth(renderer, font, &fallbackFonts); } float measuredWidth = 0; -#if !(PLATFORM(CHROMIUM) && OS(DARWIN)) bool kerningIsEnabled = font.typesettingFeatures() & Kerning; + bool canUseSimpleFontCodePath = renderer->canUseSimpleFontCodePath(); // Since we don't cache glyph overflows, we need to re-measure the run if // the style is linebox-contain: glyph. - if (!lineBox->fitsToGlyphs() && renderer->canUseSimpleFontCodePath()) { + if (!lineBox->fitsToGlyphs() && canUseSimpleFontCodePath) { int lastEndOffset = run->m_start; for (size_t i = 0, size = wordMeasurements.size(); i < size && lastEndOffset < run->m_stop; ++i) { - const WordMeasurement& wordMeasurement = wordMeasurements[i]; - if (wordMeasurement.width <=0 || wordMeasurement.startOffset == wordMeasurement.endOffset) + WordMeasurement& wordMeasurement = wordMeasurements[i]; + if (wordMeasurement.width <= 0 || wordMeasurement.startOffset == wordMeasurement.endOffset) continue; if (wordMeasurement.renderer != renderer || wordMeasurement.startOffset != lastEndOffset || wordMeasurement.endOffset > run->m_stop) continue; lastEndOffset = wordMeasurement.endOffset; if (kerningIsEnabled && lastEndOffset == run->m_stop) { - measuredWidth += renderer->width(wordMeasurement.startOffset, lastEndOffset - wordMeasurement.startOffset, xPos, lineInfo.isFirstLine()); - if (i > 0) + int wordLength = lastEndOffset - wordMeasurement.startOffset; + GlyphOverflow overflow; + measuredWidth += renderer->width(wordMeasurement.startOffset, wordLength, xPos + measuredWidth, lineInfo.isFirstLine(), + &wordMeasurement.fallbackFonts, &overflow); + UChar c = renderer->characterAt(wordMeasurement.startOffset); + if (i > 0 && wordLength == 1 && (c == ' ' || c == '\t')) measuredWidth += renderer->style()->wordSpacing(); } else measuredWidth += wordMeasurement.width; @@ -780,7 +901,6 @@ static inline void setLogicalWidthForTextRun(RootInlineBox* lineBox, BidiRun* ru fallbackFonts.clear(); } } -#endif if (!measuredWidth) measuredWidth = renderer->width(run->m_start, run->m_stop - run->m_start, xPos, lineInfo.isFirstLine(), &fallbackFonts, &glyphOverflow); @@ -808,6 +928,11 @@ static inline void computeExpansionForJustifiedText(BidiRun* firstRun, BidiRun* size_t i = 0; for (BidiRun* r = firstRun; r; r = r->next()) { +#if ENABLE(CSS_SHAPES) + // This method is called once per segment, do not move past the current segment. + if (r->m_startsSegment) + break; +#endif if (!r->m_box || r == trailingSpaceRun) continue; @@ -874,26 +999,97 @@ void RenderBlock::updateLogicalWidthForAlignment(const ETextAlign& textAlign, Bi } } +static IndentTextOrNot requiresIndent(bool isFirstLine, bool isAfterHardLineBreak, RenderStyle* style) +{ + IndentTextOrNot shouldIndentText = DoNotIndentText; + if (isFirstLine) + shouldIndentText = IndentText; +#if ENABLE(CSS3_TEXT) + else if (isAfterHardLineBreak && style->textIndentLine() == TextIndentEachLine) + shouldIndentText = IndentText; + + if (style->textIndentType() == TextIndentHanging) + shouldIndentText = shouldIndentText == IndentText ? DoNotIndentText : IndentText; +#else + UNUSED_PARAM(isAfterHardLineBreak); + UNUSED_PARAM(style); +#endif + return shouldIndentText; +} + +static void updateLogicalInlinePositions(RenderBlock* block, float& lineLogicalLeft, float& lineLogicalRight, float& availableLogicalWidth, bool firstLine, IndentTextOrNot shouldIndentText, LayoutUnit boxLogicalHeight) +{ + LayoutUnit lineLogicalHeight = logicalHeightForLine(block, firstLine, boxLogicalHeight); + lineLogicalLeft = block->pixelSnappedLogicalLeftOffsetForLine(block->logicalHeight(), shouldIndentText == IndentText, lineLogicalHeight); + lineLogicalRight = block->pixelSnappedLogicalRightOffsetForLine(block->logicalHeight(), shouldIndentText == IndentText, lineLogicalHeight); + availableLogicalWidth = lineLogicalRight - lineLogicalLeft; +} + void RenderBlock::computeInlineDirectionPositionsForLine(RootInlineBox* lineBox, const LineInfo& lineInfo, BidiRun* firstRun, BidiRun* trailingSpaceRun, bool reachedEnd, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache& verticalPositionCache, WordMeasurements& wordMeasurements) { ETextAlign textAlign = textAlignmentForLine(!reachedEnd && !lineBox->endsWithBreak()); - LayoutUnit lineLogicalHeight = logicalHeightForLine(this); // CSS 2.1: "'Text-indent' only affects a line if it is the first formatted line of an element. For example, the first line of an anonymous block // box is only affected if it is the first child of its parent element." - bool firstLine = lineInfo.isFirstLine() && !(isAnonymousBlock() && parent()->firstChild() != this); - float logicalLeft = pixelSnappedLogicalLeftOffsetForLine(logicalHeight(), firstLine, lineLogicalHeight); - float logicalRight = pixelSnappedLogicalRightOffsetForLine(logicalHeight(), firstLine, lineLogicalHeight); -#if ENABLE(CSS_EXCLUSIONS) - ExclusionShapeInsideInfo* exclusionShapeInsideInfo = layoutExclusionShapeInsideInfo(this); - if (exclusionShapeInsideInfo && exclusionShapeInsideInfo->hasSegments()) { - logicalLeft = max<float>(roundToInt(exclusionShapeInsideInfo->segments()[0].logicalLeft), logicalLeft); - logicalRight = min<float>(floorToInt(exclusionShapeInsideInfo->segments()[0].logicalRight), logicalRight); + // CSS3 "text-indent", "-webkit-each-line" affects the first line of the block container as well as each line after a forced line break, + // but does not affect lines after a soft wrap break. + bool isFirstLine = lineInfo.isFirstLine() && !(isAnonymousBlock() && parent()->firstChild() != this); + bool isAfterHardLineBreak = lineBox->prevRootBox() && lineBox->prevRootBox()->endsWithBreak(); + IndentTextOrNot shouldIndentText = requiresIndent(isFirstLine, isAfterHardLineBreak, style()); + float lineLogicalLeft; + float lineLogicalRight; + float availableLogicalWidth; + updateLogicalInlinePositions(this, lineLogicalLeft, lineLogicalRight, availableLogicalWidth, isFirstLine, shouldIndentText, 0); + bool needsWordSpacing; +#if ENABLE(CSS_SHAPES) + ShapeInsideInfo* shapeInsideInfo = layoutShapeInsideInfo(); + if (shapeInsideInfo && shapeInsideInfo->hasSegments()) { + BidiRun* segmentStart = firstRun; + const SegmentList& segments = shapeInsideInfo->segments(); + float logicalLeft = max<float>(roundToInt(segments[0].logicalLeft), lineLogicalLeft); + float logicalRight = min<float>(floorToInt(segments[0].logicalRight), lineLogicalRight); + float startLogicalLeft = logicalLeft; + float endLogicalRight = logicalLeft; + float minLogicalLeft = logicalLeft; + float maxLogicalRight = logicalLeft; + lineBox->beginPlacingBoxRangesInInlineDirection(logicalLeft); + for (size_t i = 0; i < segments.size(); i++) { + if (i) { + logicalLeft = max<float>(roundToInt(segments[i].logicalLeft), lineLogicalLeft); + logicalRight = min<float>(floorToInt(segments[i].logicalRight), lineLogicalRight); + } + availableLogicalWidth = logicalRight - logicalLeft; + BidiRun* newSegmentStart = computeInlineDirectionPositionsForSegment(lineBox, lineInfo, textAlign, logicalLeft, availableLogicalWidth, segmentStart, trailingSpaceRun, textBoxDataMap, verticalPositionCache, wordMeasurements); + needsWordSpacing = false; + endLogicalRight = lineBox->placeBoxRangeInInlineDirection(segmentStart->m_box, newSegmentStart ? newSegmentStart->m_box : 0, logicalLeft, minLogicalLeft, maxLogicalRight, needsWordSpacing, textBoxDataMap); + if (!newSegmentStart || !newSegmentStart->next()) + break; + ASSERT(newSegmentStart->m_startsSegment); + // Discard the empty segment start marker bidi runs + segmentStart = newSegmentStart->next(); + } + lineBox->endPlacingBoxRangesInInlineDirection(startLogicalLeft, endLogicalRight, minLogicalLeft, maxLogicalRight); + return; } #endif - float availableLogicalWidth = logicalRight - logicalLeft; + if (firstRun && firstRun->m_object->isReplaced()) { + RenderBox* renderBox = toRenderBox(firstRun->m_object); + updateLogicalInlinePositions(this, lineLogicalLeft, lineLogicalRight, availableLogicalWidth, isFirstLine, shouldIndentText, renderBox->logicalHeight()); + } + + computeInlineDirectionPositionsForSegment(lineBox, lineInfo, textAlign, lineLogicalLeft, availableLogicalWidth, firstRun, trailingSpaceRun, textBoxDataMap, verticalPositionCache, wordMeasurements); + // The widths of all runs are now known. We can now place every inline box (and + // compute accurate widths for the inline flow boxes). + needsWordSpacing = false; + lineBox->placeBoxesInInlineDirection(lineLogicalLeft, needsWordSpacing, textBoxDataMap); +} + +BidiRun* RenderBlock::computeInlineDirectionPositionsForSegment(RootInlineBox* lineBox, const LineInfo& lineInfo, ETextAlign textAlign, float& logicalLeft, + float& availableLogicalWidth, BidiRun* firstRun, BidiRun* trailingSpaceRun, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache& verticalPositionCache, + WordMeasurements& wordMeasurements) +{ bool needsWordSpacing = false; float totalLogicalWidth = lineBox->getFlowSpacingLogicalWidth(); unsigned expansionOpportunityCount = 0; @@ -901,7 +1097,14 @@ void RenderBlock::computeInlineDirectionPositionsForLine(RootInlineBox* lineBox, Vector<unsigned, 16> expansionOpportunities; RenderObject* previousObject = 0; - for (BidiRun* r = firstRun; r; r = r->next()) { + BidiRun* r = firstRun; + for (; r; r = r->next()) { +#if ENABLE(CSS_SHAPES) + // Once we have reached the start of the next segment, we have finished + // computing the positions for this segment's contents. + if (r->m_startsSegment) + break; +#endif if (!r->m_box || r->m_object->isOutOfFlowPositioned() || r->m_box->isLineBreak()) continue; // Positioned objects are only participating to figure out their // correct static x position. They have no effect on the width. @@ -951,10 +1154,7 @@ void RenderBlock::computeInlineDirectionPositionsForLine(RootInlineBox* lineBox, computeExpansionForJustifiedText(firstRun, trailingSpaceRun, expansionOpportunities, expansionOpportunityCount, totalLogicalWidth, availableLogicalWidth); - // The widths of all runs are now known. We can now place every inline box (and - // compute accurate widths for the inline flow boxes). - needsWordSpacing = false; - lineBox->placeBoxesInInlineDirection(logicalLeft, needsWordSpacing, textBoxDataMap); + return r; } void RenderBlock::computeBlockDirectionPositionsForLine(RootInlineBox* lineBox, BidiRun* firstRun, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, @@ -1010,11 +1210,7 @@ static void setStaticPositions(RenderBlock* block, RenderBox* child) toRenderInline(containerBlock)->layer()->setStaticInlinePosition(block->startAlignedOffsetForLine(blockHeight, false)); toRenderInline(containerBlock)->layer()->setStaticBlockPosition(blockHeight); } - - if (child->style()->isOriginalDisplayInlineType()) - block->setStaticInlinePositionForChild(child, blockHeight, block->startAlignedOffsetForLine(blockHeight, false)); - else - block->setStaticInlinePositionForChild(child, blockHeight, block->startOffsetForContent(blockHeight)); + block->updateStaticInlinePositionForChild(child, blockHeight); child->layer()->setStaticBlockPosition(blockHeight); } @@ -1101,13 +1297,14 @@ static inline BidiStatus statusWithDirection(TextDirection textDirection, bool i } // FIXME: BidiResolver should have this logic. -static inline void constructBidiRuns(InlineBidiResolver& topResolver, BidiRunList<BidiRun>& bidiRuns, const InlineIterator& endOfLine, VisualDirectionOverride override, bool previousLineBrokeCleanly) +static inline void constructBidiRunsForSegment(InlineBidiResolver& topResolver, BidiRunList<BidiRun>& bidiRuns, const InlineIterator& endOfRuns, VisualDirectionOverride override, bool previousLineBrokeCleanly) { // FIXME: We should pass a BidiRunList into createBidiRunsForLine instead // of the resolver owning the runs. ASSERT(&topResolver.runs() == &bidiRuns); + ASSERT(topResolver.position() != endOfRuns); RenderObject* currentRoot = topResolver.position().root(); - topResolver.createBidiRunsForLine(endOfLine, override, previousLineBrokeCleanly); + topResolver.createBidiRunsForLine(endOfRuns, override, previousLineBrokeCleanly); while (!topResolver.isolatedRuns().isEmpty()) { // It does not matter which order we resolve the runs as long as we resolve them all. @@ -1147,7 +1344,7 @@ static inline void constructBidiRuns(InlineBidiResolver& topResolver, BidiRunLis // We stop at the next end of line; we may re-enter this isolate in the next call to constructBidiRuns(). // FIXME: What should end and previousLineBrokeCleanly be? // rniwa says previousLineBrokeCleanly is just a WinIE hack and could always be false here? - isolatedResolver.createBidiRunsForLine(endOfLine, NoVisualOverride, previousLineBrokeCleanly); + isolatedResolver.createBidiRunsForLine(endOfRuns, NoVisualOverride, previousLineBrokeCleanly); // Note that we do not delete the runs from the resolver. // We're not guaranteed to get any BidiRuns in the previous step. If we don't, we allow the placeholder // itself to be turned into an InlineBox. We can't remove it here without potentially losing track of @@ -1158,12 +1355,48 @@ static inline void constructBidiRuns(InlineBidiResolver& topResolver, BidiRunLis // If we encountered any nested isolate runs, just move them // to the top resolver's list for later processing. if (!isolatedResolver.isolatedRuns().isEmpty()) { - topResolver.isolatedRuns().append(isolatedResolver.isolatedRuns()); + topResolver.isolatedRuns().appendVector(isolatedResolver.isolatedRuns()); isolatedResolver.isolatedRuns().clear(); } } } +static inline void constructBidiRunsForLine(const RenderBlock* block, InlineBidiResolver& topResolver, BidiRunList<BidiRun>& bidiRuns, const InlineIterator& endOfLine, VisualDirectionOverride override, bool previousLineBrokeCleanly) +{ +#if !ENABLE(CSS_SHAPES) + UNUSED_PARAM(block); + constructBidiRunsForSegment(topResolver, bidiRuns, endOfLine, override, previousLineBrokeCleanly); +#else + ShapeInsideInfo* shapeInsideInfo = block->layoutShapeInsideInfo(); + if (!shapeInsideInfo || !shapeInsideInfo->hasSegments()) { + constructBidiRunsForSegment(topResolver, bidiRuns, endOfLine, override, previousLineBrokeCleanly); + return; + } + + const SegmentRangeList& segmentRanges = shapeInsideInfo->segmentRanges(); + ASSERT(segmentRanges.size()); + + for (size_t i = 0; i < segmentRanges.size(); i++) { + LineSegmentIterator iterator = segmentRanges[i].start; + InlineIterator segmentStart(iterator.root, iterator.object, iterator.offset); + iterator = segmentRanges[i].end; + InlineIterator segmentEnd(iterator.root, iterator.object, iterator.offset); + if (i) { + ASSERT(segmentStart.m_obj); + BidiRun* segmentMarker = createRun(segmentStart.m_pos, segmentStart.m_pos, segmentStart.m_obj, topResolver); + segmentMarker->m_startsSegment = true; + bidiRuns.addRun(segmentMarker); + // Do not collapse midpoints between segments + topResolver.midpointState().betweenMidpoints = false; + } + if (segmentStart == segmentEnd) + continue; + topResolver.setPosition(segmentStart, numberOfIsolateAncestors(segmentStart)); + constructBidiRunsForSegment(topResolver, bidiRuns, segmentEnd, override, previousLineBrokeCleanly); + } +#endif +} + // This function constructs line boxes for all of the text runs in the resolver and computes their position. RootInlineBox* RenderBlock::createLineBoxesFromBidiRuns(BidiRunList<BidiRun>& bidiRuns, const InlineIterator& end, LineInfo& lineInfo, VerticalPositionCache& verticalPositionCache, BidiRun* trailingSpaceRun, WordMeasurements& wordMeasurements) { @@ -1221,7 +1454,7 @@ RootInlineBox* RenderBlock::createLineBoxesFromBidiRuns(BidiRunList<BidiRun>& bi // during an entire linebox tree layout pass (aka layoutInlineChildren). class LineLayoutState { public: - LineLayoutState(bool fullLayout, LayoutUnit& repaintLogicalTop, LayoutUnit& repaintLogicalBottom) + LineLayoutState(bool fullLayout, LayoutUnit& repaintLogicalTop, LayoutUnit& repaintLogicalBottom, RenderFlowThread* flowThread) : m_lastFloat(0) , m_endLine(0) , m_floatIndex(0) @@ -1231,7 +1464,9 @@ public: , m_isFullLayout(fullLayout) , m_repaintLogicalTop(repaintLogicalTop) , m_repaintLogicalBottom(repaintLogicalBottom) + , m_adjustedLogicalLineTop(0) , m_usesRepaintBounds(false) + , m_flowThread(flowThread) { } void markForFullLayout() { m_isFullLayout = true; } @@ -1275,6 +1510,12 @@ public: unsigned floatIndex() const { return m_floatIndex; } void setFloatIndex(unsigned floatIndex) { m_floatIndex = floatIndex; } + LayoutUnit adjustedLogicalLineTop() const { return m_adjustedLogicalLineTop; } + void setAdjustedLogicalLineTop(LayoutUnit value) { m_adjustedLogicalLineTop = value; } + + RenderFlowThread* flowThread() const { return m_flowThread; } + void setFlowThread(RenderFlowThread* thread) { m_flowThread = thread; } + private: Vector<RenderBlock::FloatWithRect> m_floats; RenderBlock::FloatingObject* m_lastFloat; @@ -1291,7 +1532,11 @@ private: LayoutUnit& m_repaintLogicalTop; LayoutUnit& m_repaintLogicalBottom; + LayoutUnit m_adjustedLogicalLineTop; + bool m_usesRepaintBounds; + + RenderFlowThread* m_flowThread; }; static void deleteLineRange(LineLayoutState& layoutState, RenderArena* arena, RootInlineBox* startLine, RootInlineBox* stopLine = 0) @@ -1334,7 +1579,7 @@ void RenderBlock::layoutRunsAndFloats(LineLayoutState& layoutState, bool hasInli } } - if (m_floatingObjects && !m_floatingObjects->set().isEmpty()) + if (containsFloats()) layoutState.setLastFloat(m_floatingObjects->set().last()); // We also find the first clean line and extract these lines. We will add them back @@ -1381,6 +1626,167 @@ RenderBlock::RenderTextInfo::~RenderTextInfo() { } +// Before restarting the layout loop with a new logicalHeight, remove all floats that were added and reset the resolver. +inline const InlineIterator& RenderBlock::restartLayoutRunsAndFloatsInRange(LayoutUnit oldLogicalHeight, LayoutUnit newLogicalHeight, FloatingObject* lastFloatFromPreviousLine, InlineBidiResolver& resolver, const InlineIterator& oldEnd) +{ + removeFloatingObjectsBelow(lastFloatFromPreviousLine, oldLogicalHeight); + setLogicalHeight(newLogicalHeight); + resolver.setPositionIgnoringNestedIsolates(oldEnd); + return oldEnd; +} + +#if ENABLE(CSS_SHAPES) +static inline float firstPositiveWidth(const WordMeasurements& wordMeasurements) +{ + for (size_t i = 0; i < wordMeasurements.size(); ++i) { + if (wordMeasurements[i].width > 0) + return wordMeasurements[i].width; + } + return 0; +} + +static inline LayoutUnit adjustLogicalLineTop(ShapeInsideInfo* shapeInsideInfo, InlineIterator start, InlineIterator end, const WordMeasurements& wordMeasurements) +{ + if (!shapeInsideInfo || end != start) + return 0; + + float minWidth = firstPositiveWidth(wordMeasurements); + ASSERT(minWidth || wordMeasurements.isEmpty()); + if (minWidth > 0 && shapeInsideInfo->adjustLogicalLineTop(minWidth)) + return shapeInsideInfo->logicalLineTop(); + + return shapeInsideInfo->shapeLogicalBottom(); +} + +static inline void pushShapeContentOverflowBelowTheContentBox(RenderBlock* block, ShapeInsideInfo* shapeInsideInfo, LayoutUnit lineTop, LayoutUnit lineHeight) +{ + ASSERT(shapeInsideInfo); + + LayoutUnit logicalLineBottom = lineTop + lineHeight; + LayoutUnit shapeLogicalBottom = shapeInsideInfo->shapeLogicalBottom(); + LayoutUnit shapeContainingBlockHeight = shapeInsideInfo->shapeContainingBlockHeight(); + + bool isOverflowPositionedAlready = (shapeContainingBlockHeight - shapeInsideInfo->owner()->borderAndPaddingAfter() + lineHeight) <= lineTop; + + // If the last line overlaps with the shape, we don't need the segments anymore + if (lineTop < shapeLogicalBottom && shapeLogicalBottom < logicalLineBottom) + shapeInsideInfo->clearSegments(); + + if (logicalLineBottom <= shapeLogicalBottom || !shapeContainingBlockHeight || isOverflowPositionedAlready) + return; + + LayoutUnit newLogicalHeight = block->logicalHeight() + (shapeContainingBlockHeight - (lineTop + shapeInsideInfo->owner()->borderAndPaddingAfter())); + block->setLogicalHeight(newLogicalHeight); +} + +void RenderBlock::updateShapeAndSegmentsForCurrentLine(ShapeInsideInfo*& shapeInsideInfo, LayoutUnit& absoluteLogicalTop, LineLayoutState& layoutState) +{ + if (layoutState.flowThread()) + return updateShapeAndSegmentsForCurrentLineInFlowThread(shapeInsideInfo, layoutState); + + if (!shapeInsideInfo) + return; + + LayoutUnit lineTop = logicalHeight() + absoluteLogicalTop; + LayoutUnit lineHeight = this->lineHeight(layoutState.lineInfo().isFirstLine(), isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes); + + // FIXME: Bug 95361: It is possible for a line to grow beyond lineHeight, in which case these segments may be incorrect. + shapeInsideInfo->computeSegmentsForLine(lineTop, lineHeight); + + pushShapeContentOverflowBelowTheContentBox(this, shapeInsideInfo, lineTop, lineHeight); +} + +void RenderBlock::updateShapeAndSegmentsForCurrentLineInFlowThread(ShapeInsideInfo*& shapeInsideInfo, LineLayoutState& layoutState) +{ + ASSERT(layoutState.flowThread()); + + LayoutUnit lineHeight = this->lineHeight(layoutState.lineInfo().isFirstLine(), isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes); + + RenderRegion* currentRegion = regionAtBlockOffset(logicalHeight()); + if (!currentRegion) + return; + + shapeInsideInfo = currentRegion->shapeInsideInfo(); + + LayoutUnit logicalLineTopInFlowThread = logicalHeight() + offsetFromLogicalTopOfFirstPage(); + LayoutUnit logicalLineBottomInFlowThread = logicalLineTopInFlowThread + lineHeight; + LayoutUnit logicalRegionTopInFlowThread = currentRegion->logicalTopForFlowThreadContent(); + LayoutUnit logicalRegionBottomInFlowThread = logicalRegionTopInFlowThread + currentRegion->logicalHeight() - currentRegion->borderAndPaddingBefore() - currentRegion->borderAndPaddingAfter(); + + // We only want to deal regions with shapes, so we look up for the next region whether it has a shape + if (!shapeInsideInfo && !currentRegion->isLastRegion()) { + LayoutUnit deltaToNextRegion = logicalHeight() + logicalRegionBottomInFlowThread - logicalLineTopInFlowThread; + RenderRegion* lookupForNextRegion = regionAtBlockOffset(logicalHeight() + deltaToNextRegion); + if (!lookupForNextRegion->shapeInsideInfo()) + return; + } + + LayoutUnit shapeBottomInFlowThread = LayoutUnit::max(); + if (shapeInsideInfo) + shapeBottomInFlowThread = shapeInsideInfo->shapeLogicalBottom() + currentRegion->logicalTopForFlowThreadContent(); + + // If the line is between two shapes/regions we position the line to the top of the next shape/region + RenderRegion* nextRegion = regionAtBlockOffset(logicalHeight() + lineHeight); + if ((currentRegion != nextRegion && (logicalLineBottomInFlowThread > logicalRegionBottomInFlowThread)) || (!currentRegion->isLastRegion() && shapeBottomInFlowThread < logicalLineBottomInFlowThread)) { + LayoutUnit deltaToNextRegion = logicalRegionBottomInFlowThread - logicalLineTopInFlowThread; + nextRegion = regionAtBlockOffset(logicalHeight() + deltaToNextRegion); + + ASSERT(currentRegion != nextRegion); + + shapeInsideInfo = nextRegion->shapeInsideInfo(); + setLogicalHeight(logicalHeight() + deltaToNextRegion); + + currentRegion = nextRegion; + + logicalLineTopInFlowThread = logicalHeight() + offsetFromLogicalTopOfFirstPage(); + logicalLineBottomInFlowThread = logicalLineTopInFlowThread + lineHeight; + logicalRegionTopInFlowThread = currentRegion->logicalTopForFlowThreadContent(); + logicalRegionBottomInFlowThread = logicalRegionTopInFlowThread + currentRegion->logicalHeight() - currentRegion->borderAndPaddingBefore() - currentRegion->borderAndPaddingAfter(); + } + + if (!shapeInsideInfo) + return; + + // We position the first line to the top of the shape in the region or to the previously adjusted position in the shape + if (logicalLineBottomInFlowThread <= (logicalRegionTopInFlowThread + lineHeight) || (logicalLineTopInFlowThread - logicalRegionTopInFlowThread) < (layoutState.adjustedLogicalLineTop() - currentRegion->borderAndPaddingBefore())) { + LayoutUnit shapeTopOffset = layoutState.adjustedLogicalLineTop(); + if (!shapeTopOffset) + shapeTopOffset = shapeInsideInfo->shapeLogicalTop(); + + LayoutUnit shapePositionInFlowThread = currentRegion->logicalTopForFlowThreadContent() + shapeTopOffset; + LayoutUnit shapeTopLineTopDelta = shapePositionInFlowThread - logicalLineTopInFlowThread - currentRegion->borderAndPaddingBefore(); + + setLogicalHeight(logicalHeight() + shapeTopLineTopDelta); + logicalLineTopInFlowThread += shapeTopLineTopDelta; + layoutState.setAdjustedLogicalLineTop(0); + } + + LayoutUnit lineTop = logicalLineTopInFlowThread - currentRegion->logicalTopForFlowThreadContent() + currentRegion->borderAndPaddingBefore(); + shapeInsideInfo->computeSegmentsForLine(lineTop, lineHeight); + + if (currentRegion->isLastRegion()) + pushShapeContentOverflowBelowTheContentBox(this, shapeInsideInfo, lineTop, lineHeight); +} + +bool RenderBlock::adjustLogicalLineTopAndLogicalHeightIfNeeded(ShapeInsideInfo* shapeInsideInfo, LayoutUnit absoluteLogicalTop, LineLayoutState& layoutState, InlineBidiResolver& resolver, FloatingObject* lastFloatFromPreviousLine, InlineIterator& end, WordMeasurements& wordMeasurements) +{ + LayoutUnit adjustedLogicalLineTop = adjustLogicalLineTop(shapeInsideInfo, resolver.position(), end, wordMeasurements); + if (!adjustedLogicalLineTop) + return false; + + LayoutUnit newLogicalHeight = adjustedLogicalLineTop - absoluteLogicalTop; + + if (layoutState.flowThread()) { + layoutState.setAdjustedLogicalLineTop(adjustedLogicalLineTop); + newLogicalHeight = logicalHeight(); + } + + + end = restartLayoutRunsAndFloatsInRange(logicalHeight(), newLogicalHeight, lastFloatFromPreviousLine, resolver, end); + return true; +} +#endif + void RenderBlock::layoutRunsAndFloatsInRange(LineLayoutState& layoutState, InlineBidiResolver& resolver, const InlineIterator& cleanLineStart, const BidiStatus& cleanLineBidiStatus, unsigned consecutiveHyphenatedLines) { RenderStyle* styleToUse = style(); @@ -1393,18 +1799,23 @@ void RenderBlock::layoutRunsAndFloatsInRange(LineLayoutState& layoutState, Inlin LineBreaker lineBreaker(this); -#if ENABLE(CSS_EXCLUSIONS) +#if ENABLE(CSS_SHAPES) LayoutUnit absoluteLogicalTop; - ExclusionShapeInsideInfo* exclusionShapeInsideInfo = layoutExclusionShapeInsideInfo(this); - if (exclusionShapeInsideInfo) { - if (exclusionShapeInsideInfo != this->exclusionShapeInsideInfo()) { + ShapeInsideInfo* shapeInsideInfo = layoutShapeInsideInfo(); + if (shapeInsideInfo) { + ASSERT(shapeInsideInfo->owner() == this || allowsShapeInsideInfoSharing()); + if (shapeInsideInfo != this->shapeInsideInfo()) { // FIXME Bug 100284: If subsequent LayoutStates are pushed, we will have to add // their offsets from the original shape-inside container. absoluteLogicalTop = logicalTop(); } // Begin layout at the logical top of our shape inside. - if (logicalHeight() + absoluteLogicalTop < exclusionShapeInsideInfo->shapeLogicalTop()) - setLogicalHeight(exclusionShapeInsideInfo->shapeLogicalTop() - absoluteLogicalTop); + if (logicalHeight() + absoluteLogicalTop < shapeInsideInfo->shapeLogicalTop()) { + LayoutUnit logicalHeight = shapeInsideInfo->shapeLogicalTop() - absoluteLogicalTop; + if (layoutState.flowThread()) + logicalHeight -= shapeInsideInfo->owner()->borderAndPaddingBefore(); + setLogicalHeight(logicalHeight); + } } #endif @@ -1425,17 +1836,14 @@ void RenderBlock::layoutRunsAndFloatsInRange(LineLayoutState& layoutState, Inlin const InlineIterator oldEnd = end; bool isNewUBAParagraph = layoutState.lineInfo().previousLineBrokeCleanly(); - FloatingObject* lastFloatFromPreviousLine = (m_floatingObjects && !m_floatingObjects->set().isEmpty()) ? m_floatingObjects->set().last() : 0; -#if ENABLE(CSS_EXCLUSIONS) - // FIXME: Bug 95361: It is possible for a line to grow beyond lineHeight, in which - // case these segments may be incorrect. - if (exclusionShapeInsideInfo) { - LayoutUnit lineTop = logicalHeight() + absoluteLogicalTop; - exclusionShapeInsideInfo->computeSegmentsForLine(lineTop, lineHeight(layoutState.lineInfo().isFirstLine(), isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes)); - } + FloatingObject* lastFloatFromPreviousLine = (containsFloats()) ? m_floatingObjects->set().last() : 0; + +#if ENABLE(CSS_SHAPES) + updateShapeAndSegmentsForCurrentLine(shapeInsideInfo, absoluteLogicalTop, layoutState); #endif WordMeasurements wordMeasurements; end = lineBreaker.nextLineBreak(resolver, layoutState.lineInfo(), renderTextInfo, lastFloatFromPreviousLine, consecutiveHyphenatedLines, wordMeasurements); + renderTextInfo.m_lineBreakIterator.resetPriorContext(); if (resolver.position().atEnd()) { // FIXME: We shouldn't be creating any runs in nextLineBreak to begin with! // Once BidiRunList is separated from BidiResolver this will not be needed. @@ -1445,6 +1853,11 @@ void RenderBlock::layoutRunsAndFloatsInRange(LineLayoutState& layoutState, Inlin resolver.setPosition(InlineIterator(resolver.position().root(), 0, 0), 0); break; } + +#if ENABLE(CSS_SHAPES) + if (adjustLogicalLineTopAndLogicalHeightIfNeeded(shapeInsideInfo, absoluteLogicalTop, layoutState, resolver, lastFloatFromPreviousLine, end, wordMeasurements)) + continue; +#endif ASSERT(end != resolver.position()); // This is a short-cut for empty lines. @@ -1461,7 +1874,7 @@ void RenderBlock::layoutRunsAndFloatsInRange(LineLayoutState& layoutState, Inlin } // FIXME: This ownership is reversed. We should own the BidiRunList and pass it to createBidiRunsForLine. BidiRunList<BidiRun>& bidiRuns = resolver.runs(); - constructBidiRuns(resolver, bidiRuns, end, override, layoutState.lineInfo().previousLineBrokeCleanly()); + constructBidiRunsForLine(this, resolver, bidiRuns, end, override, layoutState.lineInfo().previousLineBrokeCleanly()); ASSERT(resolver.position() == end); BidiRun* trailingSpaceRun = !layoutState.lineInfo().previousLineBrokeCleanly() ? handleTrailingSpaces(bidiRuns, resolver.context()) : 0; @@ -1489,7 +1902,7 @@ void RenderBlock::layoutRunsAndFloatsInRange(LineLayoutState& layoutState, Inlin if (paginated) { LayoutUnit adjustment = 0; - adjustLinePositionForPagination(lineBox, adjustment); + adjustLinePositionForPagination(lineBox, adjustment, layoutState.flowThread()); if (adjustment) { LayoutUnit oldLineWidth = availableLogicalWidthForLine(oldLogicalHeight, layoutState.lineInfo().isFirstLine()); lineBox->adjustBlockDirectionPosition(adjustment); @@ -1499,24 +1912,23 @@ void RenderBlock::layoutRunsAndFloatsInRange(LineLayoutState& layoutState, Inlin if (availableLogicalWidthForLine(oldLogicalHeight + adjustment, layoutState.lineInfo().isFirstLine()) != oldLineWidth) { // We have to delete this line, remove all floats that got added, and let line layout re-run. lineBox->deleteLine(renderArena()); - removeFloatingObjectsBelow(lastFloatFromPreviousLine, oldLogicalHeight); - setLogicalHeight(oldLogicalHeight + adjustment); - resolver.setPositionIgnoringNestedIsolates(oldEnd); - end = oldEnd; + end = restartLayoutRunsAndFloatsInRange(oldLogicalHeight, oldLogicalHeight + adjustment, lastFloatFromPreviousLine, resolver, oldEnd); continue; } setLogicalHeight(lineBox->lineBottomWithLeading()); } - if (inRenderFlowThread()) + if (layoutState.flowThread()) lineBox->setContainingRegion(regionAtBlockOffset(lineBox->lineTopWithLeading())); } } + } - for (size_t i = 0; i < lineBreaker.positionedObjects().size(); ++i) - setStaticPositions(this, lineBreaker.positionedObjects()[i]); + for (size_t i = 0; i < lineBreaker.positionedObjects().size(); ++i) + setStaticPositions(this, lineBreaker.positionedObjects()[i]); + if (!layoutState.lineInfo().isEmpty()) { layoutState.lineInfo().setFirstLine(false); newLine(lineBreaker.clear()); } @@ -1546,6 +1958,63 @@ void RenderBlock::layoutRunsAndFloatsInRange(LineLayoutState& layoutState, Inlin lineMidpointState.reset(); resolver.setPosition(end, numberOfIsolateAncestors(end)); } + + if (paginated && !style()->hasAutoWidows()) { + // Check the line boxes to make sure we didn't create unacceptable widows. + // However, we'll prioritize orphans - so nothing we do here should create + // a new orphan. + + RootInlineBox* lineBox = lastRootBox(); + + // Count from the end of the block backwards, to see how many hanging + // lines we have. + RootInlineBox* firstLineInBlock = firstRootBox(); + int numLinesHanging = 1; + while (lineBox && lineBox != firstLineInBlock && !lineBox->isFirstAfterPageBreak()) { + ++numLinesHanging; + lineBox = lineBox->prevRootBox(); + } + + // If there were no breaks in the block, we didn't create any widows. + if (!lineBox || !lineBox->isFirstAfterPageBreak() || lineBox == firstLineInBlock) + return; + + if (numLinesHanging < style()->widows()) { + // We have detected a widow. Now we need to work out how many + // lines there are on the previous page, and how many we need + // to steal. + int numLinesNeeded = style()->widows() - numLinesHanging; + RootInlineBox* currentFirstLineOfNewPage = lineBox; + + // Count the number of lines in the previous page. + lineBox = lineBox->prevRootBox(); + int numLinesInPreviousPage = 1; + while (lineBox && lineBox != firstLineInBlock && !lineBox->isFirstAfterPageBreak()) { + ++numLinesInPreviousPage; + lineBox = lineBox->prevRootBox(); + } + + // If there was an explicit value for orphans, respect that. If not, we still + // shouldn't create a situation where we make an orphan bigger than the initial value. + // This means that setting widows implies we also care about orphans, but given + // the specification says the initial orphan value is non-zero, this is ok. The + // author is always free to set orphans explicitly as well. + int orphans = style()->hasAutoOrphans() ? style()->initialOrphans() : style()->orphans(); + int numLinesAvailable = numLinesInPreviousPage - orphans; + if (numLinesAvailable <= 0) + return; + + int numLinesToTake = min(numLinesAvailable, numLinesNeeded); + // Wind back from our first widowed line. + lineBox = currentFirstLineOfNewPage; + for (int i = 0; i < numLinesToTake; ++i) + lineBox = lineBox->prevRootBox(); + + // We now want to break at this line. Remember for next layout and trigger relayout. + setBreakAtLineToAvoidWidow(lineBox); + markLinesDirtyInBlockRange(lastRootBox()->lineBottomWithLeading(), lineBox->lineBottomWithLeading(), lineBox); + } + } } void RenderBlock::linkToEndLineIfNeeded(LineLayoutState& layoutState) @@ -1559,13 +2028,13 @@ void RenderBlock::linkToEndLineIfNeeded(LineLayoutState& layoutState) line->attachLine(); if (paginated) { delta -= line->paginationStrut(); - adjustLinePositionForPagination(line, delta); + adjustLinePositionForPagination(line, delta, layoutState.flowThread()); } if (delta) { layoutState.updateRepaintRangeFromBox(line, delta); line->adjustBlockDirectionPosition(delta); } - if (inRenderFlowThread()) + if (layoutState.flowThread()) line->setContainingRegion(regionAtBlockOffset(line->lineTopWithLeading())); if (Vector<RenderBox*>* cleanLineFloats = line->floatsPtr()) { Vector<RenderBox*>::iterator end = cleanLineFloats->end(); @@ -1604,7 +2073,7 @@ void RenderBlock::linkToEndLineIfNeeded(LineLayoutState& layoutState) LayoutRect logicalLayoutOverflow(0, blockLogicalHeight, 1, bottomLayoutOverflow - blockLogicalHeight); LayoutRect logicalVisualOverflow(0, blockLogicalHeight, 1, bottomVisualOverflow - blockLogicalHeight); trailingFloatsLineBox->setOverflowFromLogicalRects(logicalLayoutOverflow, logicalVisualOverflow, trailingFloatsLineBox->lineTop(), trailingFloatsLineBox->lineBottom()); - if (inRenderFlowThread()) + if (layoutState.flowThread()) trailingFloatsLineBox->setContainingRegion(regionAtBlockOffset(trailingFloatsLineBox->lineTopWithLeading())); } @@ -1640,28 +2109,31 @@ void RenderBlock::repaintDirtyFloats(Vector<FloatWithRect>& floats) void RenderBlock::layoutInlineChildren(bool relayoutChildren, LayoutUnit& repaintLogicalTop, LayoutUnit& repaintLogicalBottom) { - m_overflow.clear(); - - setLogicalHeight(borderBefore() + paddingBefore()); + setLogicalHeight(borderAndPaddingBefore()); // Lay out our hypothetical grid line as though it occurs at the top of the block. if (view()->layoutState() && view()->layoutState()->lineGrid() == this) layoutLineGridBox(); + RenderFlowThread* flowThread = flowThreadContainingBlock(); + bool clearLinesForPagination = firstLineBox() && flowThread && !flowThread->hasRegions(); + // Figure out if we should clear out our line boxes. // FIXME: Handle resize eventually! - bool isFullLayout = !firstLineBox() || selfNeedsLayout() || relayoutChildren; - LineLayoutState layoutState(isFullLayout, repaintLogicalTop, repaintLogicalBottom); + bool isFullLayout = !firstLineBox() || selfNeedsLayout() || relayoutChildren || clearLinesForPagination; + LineLayoutState layoutState(isFullLayout, repaintLogicalTop, repaintLogicalBottom, flowThread); if (isFullLayout) lineBoxes()->deleteLineBoxes(renderArena()); - // Text truncation only kicks in if your overflow isn't visible and your text-overflow-mode isn't - // clip. + // Text truncation kicks in in two cases: + // 1) If your overflow isn't visible and your text-overflow-mode isn't clip. + // 2) If you're an anonymous block with a block parent that satisfies #1. // FIXME: CSS3 says that descendants that are clipped must also know how to truncate. This is insanely - // difficult to figure out (especially in the middle of doing layout), and is really an esoteric pile of nonsense - // anyway, so we won't worry about following the draft here. - bool hasTextOverflow = style()->textOverflow() && hasOverflowClip(); + // difficult to figure out in general (especially in the middle of doing layout), so we only handle the + // simple case of an anonymous block truncating when it's parent is clipped. + bool hasTextOverflow = (style()->textOverflow() && hasOverflowClip()) + || (isAnonymousBlock() && parent() && parent()->isRenderBlock() && parent()->style()->textOverflow() && parent()->hasOverflowClip()); // Walk all the lines and delete our ellipsis line boxes if they exist. if (hasTextOverflow) @@ -1728,7 +2200,7 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, LayoutUnit& repain } // Now add in the bottom border/padding. - setLogicalHeight(logicalHeight() + lastLineAnnotationsAdjustment + borderAfter() + paddingAfter() + scrollbarLogicalHeight()); + setLogicalHeight(logicalHeight() + lastLineAnnotationsAdjustment + borderAndPaddingAfter() + scrollbarLogicalHeight()); if (!firstLineBox() && hasLineIfEmpty()) setLogicalHeight(logicalHeight() + lineHeight(true, isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes)); @@ -1750,7 +2222,7 @@ void RenderBlock::checkFloatsInCleanLine(RootInlineBox* line, Vector<FloatWithRe RenderBox* floatingBox = *it; floatingBox->layoutIfNeeded(); LayoutSize newSize(floatingBox->width() + floatingBox->marginWidth(), floatingBox->height() + floatingBox->marginHeight()); - ASSERT(floatIndex < floats.size()); + ASSERT_WITH_SECURITY_IMPLICATION(floatIndex < floats.size()); if (floats[floatIndex].object != floatingBox) { encounteredNewFloat = true; return; @@ -1784,12 +2256,12 @@ RootInlineBox* RenderBlock::determineStartPosition(LineLayoutState& layoutState, size_t floatIndex = 0; for (curr = firstRootBox(); curr && !curr->isDirty(); curr = curr->nextRootBox()) { if (paginated) { - if (lineWidthForPaginatedLineChanged(curr)) { + if (lineWidthForPaginatedLineChanged(curr, 0, layoutState.flowThread())) { curr->markDirty(); break; } paginationDelta -= curr->paginationStrut(); - adjustLinePositionForPagination(curr, paginationDelta); + adjustLinePositionForPagination(curr, paginationDelta, layoutState.flowThread()); if (paginationDelta) { if (containsFloats() || !layoutState.floats().isEmpty()) { // FIXME: Do better eventually. For now if we ever shift because of pagination and floats are present just go to a full layout. @@ -1800,7 +2272,7 @@ RootInlineBox* RenderBlock::determineStartPosition(LineLayoutState& layoutState, layoutState.updateRepaintRangeFromBox(curr, paginationDelta); curr->adjustBlockDirectionPosition(paginationDelta); } - if (inRenderFlowThread()) + if (layoutState.flowThread()) curr->setContainingRegion(regionAtBlockOffset(curr->lineTopWithLeading())); } @@ -1819,16 +2291,9 @@ RootInlineBox* RenderBlock::determineStartPosition(LineLayoutState& layoutState, } if (layoutState.isFullLayout()) { - // FIXME: This should just call deleteLineBoxTree, but that causes - // crashes for fast/repaint tests. - RenderArena* arena = renderArena(); - curr = firstRootBox(); - while (curr) { - // Note: This uses nextRootBox() insted of nextLineBox() like deleteLineBoxTree does. - RootInlineBox* next = curr->nextRootBox(); - curr->deleteLine(arena); - curr = next; - } + m_lineBoxes.deleteLineBoxTree(renderArena()); + curr = 0; + ASSERT(!firstLineBox() && !lastLineBox()); } else { if (curr) { @@ -1936,7 +2401,7 @@ bool RenderBlock::checkPaginationAndFloatsAtEndLine(LineLayoutState& layoutState LayoutUnit lineDelta = logicalHeight() - layoutState.endLineLogicalTop(); bool paginated = view()->layoutState() && view()->layoutState()->isPaginated(); - if (paginated && inRenderFlowThread()) { + if (paginated && layoutState.flowThread()) { // Check all lines from here to the end, and see if the hypothetical new position for the lines will result // in a different available line width. for (RootInlineBox* lineBox = layoutState.endLine(); lineBox; lineBox = lineBox->nextRootBox()) { @@ -1945,10 +2410,10 @@ bool RenderBlock::checkPaginationAndFloatsAtEndLine(LineLayoutState& layoutState // strut yet. LayoutUnit oldPaginationStrut = lineBox->paginationStrut(); lineDelta -= oldPaginationStrut; - adjustLinePositionForPagination(lineBox, lineDelta); + adjustLinePositionForPagination(lineBox, lineDelta, layoutState.flowThread()); lineBox->setPaginationStrut(oldPaginationStrut); } - if (lineWidthForPaginatedLineChanged(lineBox, lineDelta)) + if (lineWidthForPaginatedLineChanged(lineBox, lineDelta, layoutState.flowThread())) return false; } } @@ -2050,12 +2515,24 @@ static bool requiresLineBoxForContent(RenderInline* flow, const LineInfo& lineIn return false; } -static bool alwaysRequiresLineBox(RenderInline* flow) +static bool hasInlineDirectionBordersPaddingOrMargin(RenderInline* flow) +{ + // Where an empty inline is split across anonymous blocks we should only give lineboxes to the 'sides' of the + // inline that have borders, padding or margin. + bool shouldApplyStartBorderPaddingOrMargin = !flow->parent()->isAnonymousBlock() || !flow->isInlineElementContinuation(); + if (shouldApplyStartBorderPaddingOrMargin && (flow->borderStart() || flow->marginStart() || flow->paddingStart())) + return true; + + bool shouldApplyEndBorderPaddingOrMargin = !flow->parent()->isAnonymousBlock() || flow->isInlineElementContinuation() || !flow->inlineElementContinuation(); + return shouldApplyEndBorderPaddingOrMargin && (flow->borderEnd() || flow->marginEnd() || flow->paddingEnd()); +} + +static bool alwaysRequiresLineBox(RenderObject* flow) { // FIXME: Right now, we only allow line boxes for inlines that are truly empty. // We need to fix this, though, because at the very least, inlines containing only // ignorable whitespace should should also have line boxes. - return !flow->firstChild() && flow->hasInlineDirectionBordersPaddingOrMargin(); + return isEmptyInline(flow) && hasInlineDirectionBordersPaddingOrMargin(toRenderInline(flow)); } static bool requiresLineBox(const InlineIterator& it, const LineInfo& lineInfo = LineInfo(), WhitespacePosition whitespacePosition = LeadingWhitespace) @@ -2063,15 +2540,15 @@ static bool requiresLineBox(const InlineIterator& it, const LineInfo& lineInfo = if (it.m_obj->isFloatingOrOutOfFlowPositioned()) return false; - if (it.m_obj->isRenderInline() && !alwaysRequiresLineBox(toRenderInline(it.m_obj)) && !requiresLineBoxForContent(toRenderInline(it.m_obj), lineInfo)) + if (it.m_obj->isRenderInline() && !alwaysRequiresLineBox(it.m_obj) && !requiresLineBoxForContent(toRenderInline(it.m_obj), lineInfo)) return false; if (!shouldCollapseWhiteSpace(it.m_obj->style(), lineInfo, whitespacePosition) || it.m_obj->isBR()) return true; UChar current = it.current(); - return current != ' ' && current != '\t' && current != softHyphen && (current != '\n' || it.m_obj->preservesNewline()) - && !skipNonBreakingSpace(it, lineInfo); + bool notJustWhitespace = current != ' ' && current != '\t' && current != softHyphen && (current != '\n' || it.m_obj->preservesNewline()) && !skipNonBreakingSpace(it, lineInfo); + return notJustWhitespace || isEmptyInline(it.m_obj); } bool RenderBlock::generatesLineBoxesForInlineChild(RenderObject* inlineObj) @@ -2115,9 +2592,15 @@ void RenderBlock::LineBreaker::skipLeadingWhitespace(InlineBidiResolver& resolve resolver.runs().addRun(createRun(0, 1, object, resolver)); lineInfo.incrementRunsFromLeadingWhitespace(); } - } else if (object->isFloating()) + } else if (object->isFloating()) { + // The top margin edge of a self-collapsing block that clears a float intrudes up into it by the height of the margin, + // so in order to place this first child float at the top content edge of the self-collapsing block add the margin back in before placement. + LayoutUnit marginOffset = (!object->previousSibling() && m_block->isSelfCollapsingBlock() && m_block->style()->clear() && m_block->getClearDelta(m_block, LayoutUnit())) ? m_block->collapsedMarginBeforeForChild(m_block) : LayoutUnit(); + LayoutUnit oldLogicalHeight = m_block->logicalHeight(); + m_block->setLogicalHeight(oldLogicalHeight + marginOffset); m_block->positionNewFloatOnLine(m_block->insertFloatingObject(toRenderBox(object)), lastFloatFromPreviousLine, lineInfo, width); - else if (object->isText() && object->style()->hasTextCombine() && object->isCombineText() && !toRenderCombineText(object)->isCombined()) { + m_block->setLogicalHeight(oldLogicalHeight); + } else if (object->isText() && object->style()->hasTextCombine() && object->isCombineText() && !toRenderCombineText(object)->isCombined()) { toRenderCombineText(object)->combineText(); if (toRenderCombineText(object)->isCombined()) continue; @@ -2132,11 +2615,14 @@ void RenderBlock::LineBreaker::skipLeadingWhitespace(InlineBidiResolver& resolve static bool shouldSkipWhitespaceAfterStartObject(RenderBlock* block, RenderObject* o, LineMidpointState& lineMidpointState) { RenderObject* next = bidiNextSkippingEmptyInlines(block, o); + while (next && next->isFloatingOrOutOfFlowPositioned()) + next = bidiNextSkippingEmptyInlines(block, next); + if (next && !next->isBR() && next->isText() && toRenderText(next)->textLength() > 0) { RenderText* nextText = toRenderText(next); UChar nextChar = nextText->characterAt(0); if (nextText->style()->isCollapsibleWhiteSpace(nextChar)) { - addMidpoint(lineMidpointState, InlineIterator(0, o, 0)); + startIgnoringSpaces(lineMidpointState, InlineIterator(0, o, 0)); return true; } } @@ -2144,14 +2630,14 @@ static bool shouldSkipWhitespaceAfterStartObject(RenderBlock* block, RenderObjec return false; } -static ALWAYS_INLINE float textWidth(RenderText* text, unsigned from, unsigned len, const Font& font, float xPos, bool isFixedPitch, bool collapseWhiteSpace, HashSet<const SimpleFontData*>* fallbackFonts = 0, TextLayout* layout = 0) +static ALWAYS_INLINE float textWidth(RenderText* text, unsigned from, unsigned len, const Font& font, float xPos, bool isFixedPitch, bool collapseWhiteSpace, HashSet<const SimpleFontData*>& fallbackFonts, TextLayout* layout = 0) { GlyphOverflow glyphOverflow; if (isFixedPitch || (!from && len == text->textLength()) || text->style()->hasTextCombine()) - return text->width(from, len, font, xPos, fallbackFonts, &glyphOverflow); + return text->width(from, len, font, xPos, &fallbackFonts, &glyphOverflow); if (layout) - return Font::width(*layout, from, len, fallbackFonts); + return Font::width(*layout, from, len, &fallbackFonts); TextRun run = RenderBlock::constructTextRun(text, font, text, from, len, text->style()); run.setCharactersLength(text->textLength() - from); @@ -2160,7 +2646,7 @@ static ALWAYS_INLINE float textWidth(RenderText* text, unsigned from, unsigned l run.setCharacterScanForCodePath(!text->canUseSimpleFontCodePath()); run.setTabSize(!collapseWhiteSpace, text->style()->tabSize()); run.setXPos(xPos); - return font.width(run, fallbackFonts, &glyphOverflow); + return font.width(run, &fallbackFonts, &glyphOverflow); } static void tryHyphenating(RenderText* text, const Font& font, const AtomicString& localeIdentifier, unsigned consecutiveHyphenatedLines, int consecutiveHyphenatedLinesLimit, int minimumPrefixLimit, int minimumSuffixLimit, unsigned lastSpace, unsigned pos, float xPos, int availableWidth, bool isFixedPitch, bool collapseWhiteSpace, int lastSpaceWordSpacing, InlineIterator& lineBreak, int nextBreakable, bool& hyphenated) @@ -2219,7 +2705,8 @@ static void tryHyphenating(RenderText* text, const Font& font, const AtomicStrin ASSERT(pos - lastSpace - prefixLength >= minimumSuffixLength); #if !ASSERT_DISABLED - float prefixWidth = hyphenWidth + textWidth(text, lastSpace, prefixLength, font, xPos, isFixedPitch, collapseWhiteSpace) + lastSpaceWordSpacing; + HashSet<const SimpleFontData*> fallbackFonts; + float prefixWidth = hyphenWidth + textWidth(text, lastSpace, prefixLength, font, xPos, isFixedPitch, collapseWhiteSpace, fallbackFonts) + lastSpaceWordSpacing; ASSERT(xPos + prefixWidth <= availableWidth); #else UNUSED_PARAM(isFixedPitch); @@ -2234,7 +2721,7 @@ public: TrailingObjects(); void setTrailingWhitespace(RenderText*); void clear(); - void appendBoxIfNeeded(RenderBox*); + void appendBoxIfNeeded(RenderBoxModelObject*); enum CollapseFirstSpaceOrNot { DoNotCollapseFirstSpace, CollapseFirstSpace }; @@ -2242,7 +2729,7 @@ public: private: RenderText* m_whitespace; - Vector<RenderBox*, 4> m_boxes; + Vector<RenderBoxModelObject*, 4> m_boxes; }; TrailingObjects::TrailingObjects() @@ -2259,10 +2746,10 @@ inline void TrailingObjects::setTrailingWhitespace(RenderText* whitespace) inline void TrailingObjects::clear() { m_whitespace = 0; - m_boxes.clear(); + m_boxes.shrink(0); // Use shrink(0) instead of clear() to retain our capacity. } -inline void TrailingObjects::appendBoxIfNeeded(RenderBox* box) +inline void TrailingObjects::appendBoxIfNeeded(RenderBoxModelObject* box) { if (m_whitespace) m_boxes.append(box); @@ -2289,9 +2776,7 @@ void TrailingObjects::updateMidpointsForTrailingBoxes(LineMidpointState& lineMid for (size_t i = 0; i < m_boxes.size(); ++i) { if (currentMidpoint >= lineMidpointState.numMidpoints) { // We don't have a midpoint for this box yet. - InlineIterator ignoreStart(0, m_boxes[i], 0); - addMidpoint(lineMidpointState, ignoreStart); // Stop ignoring. - addMidpoint(lineMidpointState, ignoreStart); // Start ignoring again. + ensureLineBoxInsideIgnoredSpaces(lineMidpointState, m_boxes[i]); } else { ASSERT(lineMidpointState.midpoints[currentMidpoint].m_obj == m_boxes[i]); ASSERT(lineMidpointState.midpoints[currentMidpoint + 1].m_obj == m_boxes[i]); @@ -2305,11 +2790,9 @@ void TrailingObjects::updateMidpointsForTrailingBoxes(LineMidpointState& lineMid unsigned length = m_whitespace->textLength(); unsigned pos = length >= 2 ? length - 2 : UINT_MAX; InlineIterator endMid(0, m_whitespace, pos); - addMidpoint(lineMidpointState, endMid); + startIgnoringSpaces(lineMidpointState, endMid); for (size_t i = 0; i < m_boxes.size(); ++i) { - InlineIterator ignoreStart(0, m_boxes[i], 0); - addMidpoint(lineMidpointState, ignoreStart); // Stop ignoring spaces. - addMidpoint(lineMidpointState, ignoreStart); // Start ignoring again. + ensureLineBoxInsideIgnoredSpaces(lineMidpointState, m_boxes[i]); } } } @@ -2323,6 +2806,115 @@ void RenderBlock::LineBreaker::reset() InlineIterator RenderBlock::LineBreaker::nextLineBreak(InlineBidiResolver& resolver, LineInfo& lineInfo, RenderTextInfo& renderTextInfo, FloatingObject* lastFloatFromPreviousLine, unsigned consecutiveHyphenatedLines, WordMeasurements& wordMeasurements) { +#if !ENABLE(CSS_SHAPES) + return nextSegmentBreak(resolver, lineInfo, renderTextInfo, lastFloatFromPreviousLine, consecutiveHyphenatedLines, wordMeasurements); +#else + ShapeInsideInfo* shapeInsideInfo = m_block->layoutShapeInsideInfo(); + + if (!shapeInsideInfo || !shapeInsideInfo->lineOverlapsShapeBounds()) + return nextSegmentBreak(resolver, lineInfo, renderTextInfo, lastFloatFromPreviousLine, consecutiveHyphenatedLines, wordMeasurements); + + InlineIterator end = resolver.position(); + InlineIterator oldEnd = end; + + if (!shapeInsideInfo->hasSegments()) { + end = nextSegmentBreak(resolver, lineInfo, renderTextInfo, lastFloatFromPreviousLine, consecutiveHyphenatedLines, wordMeasurements); + resolver.setPositionIgnoringNestedIsolates(oldEnd); + return oldEnd; + } + + const SegmentList& segments = shapeInsideInfo->segments(); + SegmentRangeList& segmentRanges = shapeInsideInfo->segmentRanges(); + + for (unsigned i = 0; i < segments.size() && !end.atEnd(); i++) { + InlineIterator segmentStart = resolver.position(); + end = nextSegmentBreak(resolver, lineInfo, renderTextInfo, lastFloatFromPreviousLine, consecutiveHyphenatedLines, wordMeasurements); + + ASSERT(segmentRanges.size() == i); + if (resolver.position().atEnd()) { + segmentRanges.append(LineSegmentRange(segmentStart, end)); + break; + } + if (resolver.position() == end) { + // Nothing fit this segment + end = segmentStart; + segmentRanges.append(LineSegmentRange(segmentStart, segmentStart)); + resolver.setPositionIgnoringNestedIsolates(segmentStart); + } else { + // Note that resolver.position is already skipping some of the white space at the beginning of the line, + // so that's why segmentStart might be different than resolver.position(). + LineSegmentRange range(resolver.position(), end); + segmentRanges.append(range); + resolver.setPosition(end, numberOfIsolateAncestors(end)); + + if (lineInfo.previousLineBrokeCleanly()) { + // If we hit a new line break, just stop adding anything to this line. + break; + } + } + } + resolver.setPositionIgnoringNestedIsolates(oldEnd); + return end; +#endif +} + +static inline bool iteratorIsBeyondEndOfRenderCombineText(const InlineIterator& iter, RenderCombineText* renderer) +{ + return iter.m_obj == renderer && iter.m_pos >= renderer->textLength(); +} + +static inline void commitLineBreakAtCurrentWidth(LineWidth& width, InlineIterator& lBreak, RenderObject* object, unsigned offset = 0, int nextBreak = -1) +{ + width.commit(); + lBreak.moveTo(object, offset, nextBreak); +} + +static bool textBeginsWithBreakablePosition(RenderObject* next) +{ + ASSERT(next->isText()); + RenderText* nextText = toRenderText(next); + if (nextText->isWordBreak()) + return true; + if (!nextText->textLength()) + return false; + UChar c = nextText->characterAt(0); + return c == ' ' || c == '\t' || (c == '\n' && !nextText->preservesNewline()); +} + +static bool canBreakAtThisPosition(bool autoWrap, LineWidth& width, InlineIterator& lBreak, RenderObject* next, const InlineIterator& current, EWhiteSpace currWS, bool currentCharacterIsSpace, bool autoWrapWasEverTrueOnLine) +{ + // If we are no-wrap and have found a line-breaking opportunity already then we should take it. + if (width.committedWidth() && !width.fitsOnLine(currentCharacterIsSpace) && currWS == NOWRAP) + return true; + + // Avoid breaking before empty inlines. + if (next && isEmptyInline(next)) + return false; + + // Return early if we autowrap and the current character is a space as we will always want to break at such a position. + if (autoWrap && currentCharacterIsSpace) + return true; + + bool nextIsText = (next && (current.m_obj->isText() || isEmptyInline(current.m_obj)) && next->isText() && !next->isBR() && (autoWrap || next->style()->autoWrap())); + if (!nextIsText) + return autoWrap; + + bool canBreakHere = !currentCharacterIsSpace && textBeginsWithBreakablePosition(next); + + // See if attempting to fit below floats creates more available width on the line. + if (!width.fitsOnLine() && !width.committedWidth()) + width.fitBelowFloats(); + + bool canPlaceOnLine = width.fitsOnLine() || !autoWrapWasEverTrueOnLine; + + if (canPlaceOnLine && canBreakHere) + commitLineBreakAtCurrentWidth(width, lBreak, next); + + return canBreakHere; +} + +InlineIterator RenderBlock::LineBreaker::nextSegmentBreak(InlineBidiResolver& resolver, LineInfo& lineInfo, RenderTextInfo& renderTextInfo, FloatingObject* lastFloatFromPreviousLine, unsigned consecutiveHyphenatedLines, WordMeasurements& wordMeasurements) +{ reset(); ASSERT(resolver.position().root() == m_block); @@ -2331,7 +2923,7 @@ InlineIterator RenderBlock::LineBreaker::nextLineBreak(InlineBidiResolver& resol bool includeEndWidth = true; LineMidpointState& lineMidpointState = resolver.midpointState(); - LineWidth width(m_block, lineInfo.isFirstLine()); + LineWidth width(m_block, lineInfo.isFirstLine(), requiresIndent(lineInfo.isFirstLine(), lineInfo.previousLineBrokeCleanly(), m_block->style())); skipLeadingWhitespace(resolver, lineInfo, lastFloatFromPreviousLine, width); @@ -2410,10 +3002,8 @@ InlineIterator RenderBlock::LineBreaker::nextLineBreak(InlineBidiResolver& resol // A <br> with clearance always needs a linebox in case the lines below it get dirtied later and // need to check for floats to clear - so if we're ignoring spaces, stop ignoring them and add a // run for this object. - if (ignoringSpaces && currentStyle->clear() != CNONE) { - addMidpoint(lineMidpointState, InlineIterator(0, current.m_obj, 0)); // Stop ignoring spaces. - addMidpoint(lineMidpointState, InlineIterator(0, current.m_obj, 0)); // Start ignoring again. - } + if (ignoringSpaces && currentStyle->clear() != CNONE) + ensureLineBoxInsideIgnoredSpaces(lineMidpointState, current.m_obj); if (!lineInfo.isEmpty()) m_clear = currentStyle->clear(); @@ -2437,22 +3027,22 @@ InlineIterator RenderBlock::LineBreaker::nextLineBreak(InlineBidiResolver& resol // If we're ignoring spaces, we have to stop and include this object and // then start ignoring spaces again. if (isInlineType || current.m_obj->container()->isRenderInline()) { - if (ignoringSpaces) { - ignoreStart.m_obj = current.m_obj; - ignoreStart.m_pos = 0; - addMidpoint(lineMidpointState, ignoreStart); // Stop ignoring spaces. - addMidpoint(lineMidpointState, ignoreStart); // Start ignoring again. - } + if (ignoringSpaces) + ensureLineBoxInsideIgnoredSpaces(lineMidpointState, current.m_obj); trailingObjects.appendBoxIfNeeded(box); } else m_positionedObjects.append(box); + width.addUncommittedWidth(inlineLogicalWidth(current.m_obj)); + // Reset prior line break context characters. + renderTextInfo.m_lineBreakIterator.resetPriorContext(); } else if (current.m_obj->isFloating()) { RenderBox* floatBox = toRenderBox(current.m_obj); FloatingObject* f = m_block->insertFloatingObject(floatBox); // check if it fits in the current line. // If it does, position it now, otherwise, position // it after moving to next line (in newLine() func) - if (floatsFitOnLine && width.fitsOnLine(m_block->logicalWidthForFloat(f))) { + // FIXME: Bug 110372: Properly position multiple stacked floats with non-rectangular shape outside. + if (floatsFitOnLine && width.fitsOnLineExcludingTrailingWhitespace(m_block->logicalWidthForFloat(f))) { m_block->positionNewFloatOnLine(f, lastFloatFromPreviousLine, lineInfo, width); if (lBreak.m_obj == current.m_obj) { ASSERT(!lBreak.m_pos); @@ -2460,9 +3050,11 @@ InlineIterator RenderBlock::LineBreaker::nextLineBreak(InlineBidiResolver& resol } } else floatsFitOnLine = false; + // Update prior line break context characters, using U+FFFD (OBJECT REPLACEMENT CHARACTER) for floating element. + renderTextInfo.m_lineBreakIterator.updatePriorContext(replacementCharacter); } else if (current.m_obj->isRenderInline()) { // Right now, we should only encounter empty inlines here. - ASSERT(!current.m_obj->firstChild()); + ASSERT(isEmptyInline(current.m_obj)); RenderInline* flowBox = toRenderInline(current.m_obj); @@ -2470,7 +3062,7 @@ InlineIterator RenderBlock::LineBreaker::nextLineBreak(InlineBidiResolver& resol // to make sure that we stop to include this object and then start ignoring spaces again. // If this object is at the start of the line, we need to behave like list markers and // start ignoring spaces. - bool requiresLineBox = alwaysRequiresLineBox(flowBox); + bool requiresLineBox = alwaysRequiresLineBox(current.m_obj); if (requiresLineBox || requiresLineBoxForContent(flowBox, lineInfo)) { // An empty inline that only has line-height, vertical-align or font-metrics will only get a // line box to affect the height of the line if the rest of the line is not empty. @@ -2478,8 +3070,7 @@ InlineIterator RenderBlock::LineBreaker::nextLineBreak(InlineBidiResolver& resol lineInfo.setEmpty(false, m_block, &width); if (ignoringSpaces) { trailingObjects.clear(); - addMidpoint(lineMidpointState, InlineIterator(0, current.m_obj, 0)); // Stop ignoring spaces. - addMidpoint(lineMidpointState, InlineIterator(0, current.m_obj, 0)); // Start ignoring again. + ensureLineBoxInsideIgnoredSpaces(lineMidpointState, current.m_obj); } else if (blockStyle->collapseWhiteSpace() && resolver.position().m_obj == current.m_obj && shouldSkipWhitespaceAfterStartObject(m_block, current.m_obj, lineMidpointState)) { // Like with list markers, we start ignoring spaces to make sure that any @@ -2487,21 +3078,24 @@ InlineIterator RenderBlock::LineBreaker::nextLineBreak(InlineBidiResolver& resol currentCharacterIsSpace = true; currentCharacterIsWS = true; ignoringSpaces = true; + } else { + trailingObjects.appendBoxIfNeeded(flowBox); } } - width.addUncommittedWidth(borderPaddingMarginStart(flowBox) + borderPaddingMarginEnd(flowBox)); + width.addUncommittedWidth(inlineLogicalWidth(current.m_obj) + borderPaddingMarginStart(flowBox) + borderPaddingMarginEnd(flowBox)); } else if (current.m_obj->isReplaced()) { RenderBox* replacedBox = toRenderBox(current.m_obj); + if (atStart) + width.updateAvailableWidth(replacedBox->logicalHeight()); + // Break on replaced elements if either has normal white-space. - if ((autoWrap || RenderStyle::autoWrap(lastWS)) && (!current.m_obj->isImage() || allowImagesToBreak)) { - width.commit(); - lBreak.moveToStartOf(current.m_obj); - } + if ((autoWrap || RenderStyle::autoWrap(lastWS)) && (!current.m_obj->isImage() || allowImagesToBreak)) + commitLineBreakAtCurrentWidth(width, lBreak, current.m_obj); if (ignoringSpaces) - addMidpoint(lineMidpointState, InlineIterator(0, current.m_obj, 0)); + stopIgnoringSpaces(lineMidpointState, InlineIterator(0, current.m_obj, 0)); lineInfo.setEmpty(false, m_block, &width); ignoringSpaces = false; @@ -2526,6 +3120,8 @@ InlineIterator RenderBlock::LineBreaker::nextLineBreak(InlineBidiResolver& resol width.addUncommittedWidth(replacedLogicalWidth); if (current.m_obj->isRubyRun()) width.applyOverhang(toRenderRubyRun(current.m_obj), last, next); + // Update prior line break context characters, using U+FFFD (OBJECT REPLACEMENT CHARACTER) for replaced element. + renderTextInfo.m_lineBreakIterator.updatePriorContext(replacementCharacter); } else if (current.m_obj->isText()) { if (!current.m_pos) appliedStartWidth = false; @@ -2536,8 +3132,21 @@ InlineIterator RenderBlock::LineBreaker::nextLineBreak(InlineBidiResolver& resol bool isSVGText = t->isSVGInlineText(); #endif - if (t->style()->hasTextCombine() && current.m_obj->isCombineText() && !toRenderCombineText(current.m_obj)->isCombined()) - toRenderCombineText(current.m_obj)->combineText(); + // If we have left a no-wrap inline and entered an autowrap inline while ignoring spaces + // then we need to mark the start of the autowrap inline as a potential linebreak now. + if (autoWrap && !RenderStyle::autoWrap(lastWS) && ignoringSpaces) + commitLineBreakAtCurrentWidth(width, lBreak, current.m_obj); + + if (t->style()->hasTextCombine() && current.m_obj->isCombineText() && !toRenderCombineText(current.m_obj)->isCombined()) { + RenderCombineText* combineRenderer = toRenderCombineText(current.m_obj); + combineRenderer->combineText(); + // The length of the renderer's text may have changed. Increment stale iterator positions + if (iteratorIsBeyondEndOfRenderCombineText(lBreak, combineRenderer)) { + ASSERT(iteratorIsBeyondEndOfRenderCombineText(resolver.position(), combineRenderer)); + lBreak.increment(); + resolver.increment(); + } + } RenderStyle* style = t->style(lineInfo.isFirstLine()); const Font& f = style->font(); @@ -2558,19 +3167,24 @@ InlineIterator RenderBlock::LineBreaker::nextLineBreak(InlineBidiResolver& resol bool midWordBreak = false; bool breakAll = currentStyle->wordBreak() == BreakAllWordBreak && autoWrap; float hyphenWidth = 0; +#if ENABLE(SVG) + if (isSVGText) { + breakWords = false; + breakAll = false; + } +#endif if (t->isWordBreak()) { - width.commit(); - lBreak.moveToStartOf(current.m_obj); + commitLineBreakAtCurrentWidth(width, lBreak, current.m_obj); ASSERT(current.m_pos == t->textLength()); } if (renderTextInfo.m_text != t) { - t->updateTextIfNeeded(); + updateCounterIfNeeded(t); renderTextInfo.m_text = t; renderTextInfo.m_font = &f; renderTextInfo.m_layout = f.createLayout(t, width.currentWidth(), collapseWhiteSpace); - renderTextInfo.m_lineBreakIterator.reset(t->text(), style->locale()); + renderTextInfo.m_lineBreakIterator.resetStringAndReleaseIterator(t->text(), style->locale()); } else if (renderTextInfo.m_layout && renderTextInfo.m_font != &f) { renderTextInfo.m_font = &f; renderTextInfo.m_layout = f.createLayout(t, width.currentWidth(), collapseWhiteSpace); @@ -2580,8 +3194,11 @@ InlineIterator RenderBlock::LineBreaker::nextLineBreak(InlineBidiResolver& resol // Non-zero only when kerning is enabled and TextLayout isn't used, in which case we measure // words with their trailing space, then subtract its width. - float wordTrailingSpaceWidth = (f.typesettingFeatures() & Kerning) && !textLayout ? f.width(constructTextRun(t, f, &space, 1, style)) + wordSpacing : 0; + HashSet<const SimpleFontData*> fallbackFonts; + float wordTrailingSpaceWidth = (f.typesettingFeatures() & Kerning) && !textLayout ? f.width(constructTextRun(t, f, &space, 1, style), &fallbackFonts) + wordSpacing : 0; + UChar lastCharacter = renderTextInfo.m_lineBreakIterator.lastCharacter(); + UChar secondToLastCharacter = renderTextInfo.m_lineBreakIterator.secondToLastCharacter(); for (; current.m_pos < t->textLength(); current.fastIncrementInTextNode()) { bool previousCharacterIsSpace = currentCharacterIsSpace; bool previousCharacterIsWS = currentCharacterIsWS; @@ -2592,7 +3209,7 @@ InlineIterator RenderBlock::LineBreaker::nextLineBreak(InlineBidiResolver& resol lineInfo.setEmpty(false, m_block, &width); if (c == softHyphen && autoWrap && !hyphenWidth && style->hyphens() != HyphensNone) { - hyphenWidth = measureHyphenWidth(t, f); + hyphenWidth = measureHyphenWidth(t, f, &fallbackFonts); width.addUncommittedWidth(hyphenWidth); } @@ -2603,7 +3220,7 @@ InlineIterator RenderBlock::LineBreaker::nextLineBreak(InlineBidiResolver& resol if ((breakAll || breakWords) && !midWordBreak) { wrapW += charWidth; bool midWordBreakIsBeforeSurrogatePair = U16_IS_LEAD(c) && current.m_pos + 1 < t->textLength() && U16_IS_TRAIL(t->characters()[current.m_pos + 1]); - charWidth = textWidth(t, current.m_pos, midWordBreakIsBeforeSurrogatePair ? 2 : 1, f, width.committedWidth() + wrapW, isFixedPitch, collapseWhiteSpace, 0, textLayout); + charWidth = textWidth(t, current.m_pos, midWordBreakIsBeforeSurrogatePair ? 2 : 1, f, width.committedWidth() + wrapW, isFixedPitch, collapseWhiteSpace, fallbackFonts, textLayout); midWordBreak = width.committedWidth() + wrapW + charWidth > width.availableWidth(); } @@ -2613,18 +3230,18 @@ InlineIterator RenderBlock::LineBreaker::nextLineBreak(InlineBidiResolver& resol if (betweenWords || midWordBreak) { bool stoppedIgnoringSpaces = false; if (ignoringSpaces) { + lastSpaceWordSpacing = 0; if (!currentCharacterIsSpace) { // Stop ignoring spaces and begin at this // new point. ignoringSpaces = false; - lastSpaceWordSpacing = 0; wordSpacingForWordMeasurement = 0; lastSpace = current.m_pos; // e.g., "Foo goo", don't add in any of the ignored spaces. - addMidpoint(lineMidpointState, InlineIterator(0, current.m_obj, current.m_pos)); + stopIgnoringSpaces(lineMidpointState, InlineIterator(0, current.m_obj, current.m_pos)); stoppedIgnoringSpaces = true; } else { // Just keep ignoring these spaces. - continue; + goto nextCharacter; } } @@ -2635,15 +3252,23 @@ InlineIterator RenderBlock::LineBreaker::nextLineBreak(InlineBidiResolver& resol wordMeasurement.endOffset = current.m_pos; wordMeasurement.startOffset = lastSpace; - float additionalTmpW; + float additionalTempWidth; if (wordTrailingSpaceWidth && c == ' ') - additionalTmpW = textWidth(t, lastSpace, current.m_pos + 1 - lastSpace, f, width.currentWidth(), isFixedPitch, collapseWhiteSpace, &wordMeasurement.fallbackFonts, textLayout) - wordTrailingSpaceWidth; + additionalTempWidth = textWidth(t, lastSpace, current.m_pos + 1 - lastSpace, f, width.currentWidth(), isFixedPitch, collapseWhiteSpace, wordMeasurement.fallbackFonts, textLayout) - wordTrailingSpaceWidth; else - additionalTmpW = textWidth(t, lastSpace, current.m_pos - lastSpace, f, width.currentWidth(), isFixedPitch, collapseWhiteSpace, &wordMeasurement.fallbackFonts, textLayout); + additionalTempWidth = textWidth(t, lastSpace, current.m_pos - lastSpace, f, width.currentWidth(), isFixedPitch, collapseWhiteSpace, wordMeasurement.fallbackFonts, textLayout); + + if (wordMeasurement.fallbackFonts.isEmpty() && !fallbackFonts.isEmpty()) + wordMeasurement.fallbackFonts.swap(fallbackFonts); + fallbackFonts.clear(); + + wordMeasurement.width = additionalTempWidth + wordSpacingForWordMeasurement; + additionalTempWidth += lastSpaceWordSpacing; + width.addUncommittedWidth(additionalTempWidth); + + if (collapseWhiteSpace && previousCharacterIsSpace && currentCharacterIsSpace && additionalTempWidth) + width.setTrailingWhitespaceWidth(additionalTempWidth); - wordMeasurement.width = additionalTmpW + wordSpacingForWordMeasurement; - additionalTmpW += lastSpaceWordSpacing; - width.addUncommittedWidth(additionalTmpW); if (!appliedStartWidth) { width.addUncommittedWidth(inlineLogicalWidth(current.m_obj, true, false)); appliedStartWidth = true; @@ -2659,13 +3284,13 @@ InlineIterator RenderBlock::LineBreaker::nextLineBreak(InlineBidiResolver& resol // as candidate width for this line. bool lineWasTooWide = false; if (width.fitsOnLine() && currentCharacterIsWS && currentStyle->breakOnlyAfterWhiteSpace() && !midWordBreak) { - float charWidth = textWidth(t, current.m_pos, 1, f, width.currentWidth(), isFixedPitch, collapseWhiteSpace, &wordMeasurement.fallbackFonts, textLayout) + (applyWordSpacing ? wordSpacing : 0); + float charWidth = textWidth(t, current.m_pos, 1, f, width.currentWidth(), isFixedPitch, collapseWhiteSpace, wordMeasurement.fallbackFonts, textLayout) + (applyWordSpacing ? wordSpacing : 0); // Check if line is too big even without the extra space // at the end of the line. If it is not, do nothing. // If the line needs the extra whitespace to be too long, // then move the line break to the space and skip all // additional whitespace. - if (!width.fitsOnLine(charWidth)) { + if (!width.fitsOnLineIncludingExtraWidth(charWidth)) { lineWasTooWide = true; lBreak.moveTo(current.m_obj, current.m_pos, current.m_nextBreakablePosition); skipTrailingWhitespace(lBreak, lineInfo); @@ -2673,16 +3298,13 @@ InlineIterator RenderBlock::LineBreaker::nextLineBreak(InlineBidiResolver& resol } if (lineWasTooWide || !width.fitsOnLine()) { if (canHyphenate && !width.fitsOnLine()) { - tryHyphenating(t, f, style->locale(), consecutiveHyphenatedLines, blockStyle->hyphenationLimitLines(), style->hyphenationLimitBefore(), style->hyphenationLimitAfter(), lastSpace, current.m_pos, width.currentWidth() - additionalTmpW, width.availableWidth(), isFixedPitch, collapseWhiteSpace, lastSpaceWordSpacing, lBreak, current.m_nextBreakablePosition, m_hyphenated); + tryHyphenating(t, f, style->locale(), consecutiveHyphenatedLines, blockStyle->hyphenationLimitLines(), style->hyphenationLimitBefore(), style->hyphenationLimitAfter(), lastSpace, current.m_pos, width.currentWidth() - additionalTempWidth, width.availableWidth(), isFixedPitch, collapseWhiteSpace, lastSpaceWordSpacing, lBreak, current.m_nextBreakablePosition, m_hyphenated); if (m_hyphenated) goto end; } if (lBreak.atTextParagraphSeparator()) { - if (!stoppedIgnoringSpaces && current.m_pos > 0) { - // We need to stop right before the newline and then start up again. - addMidpoint(lineMidpointState, InlineIterator(0, current.m_obj, current.m_pos - 1)); // Stop - addMidpoint(lineMidpointState, InlineIterator(0, current.m_obj, current.m_pos)); // Start - } + if (!stoppedIgnoringSpaces && current.m_pos > 0) + ensureCharacterGetsLineBox(lineMidpointState, current); lBreak.increment(); lineInfo.setPreviousLineBrokeCleanly(true); wordMeasurement.endOffset = lBreak.m_pos; @@ -2695,10 +3317,12 @@ InlineIterator RenderBlock::LineBreaker::nextLineBreak(InlineBidiResolver& resol wordMeasurement.width = charWidth; } } - goto end; // Didn't fit. Jump to the end. + // Didn't fit. Jump to the end unless there's still an opportunity to collapse whitespace. + if (ignoringSpaces || !collapseWhiteSpace || !currentCharacterIsSpace || !previousCharacterIsSpace) + goto end; } else { if (!betweenWords || (midWordBreak && !autoWrap)) - width.addUncommittedWidth(-additionalTmpW); + width.addUncommittedWidth(-additionalTempWidth); if (hyphenWidth) { // Subtract the width of the soft hyphen out since we fit on a line. width.addUncommittedWidth(-hyphenWidth); @@ -2708,11 +3332,8 @@ InlineIterator RenderBlock::LineBreaker::nextLineBreak(InlineBidiResolver& resol } if (c == '\n' && preserveNewline) { - if (!stoppedIgnoringSpaces && current.m_pos > 0) { - // We need to stop right before the newline and then start up again. - addMidpoint(lineMidpointState, InlineIterator(0, current.m_obj, current.m_pos - 1)); // Stop - addMidpoint(lineMidpointState, InlineIterator(0, current.m_obj, current.m_pos)); // Start - } + if (!stoppedIgnoringSpaces && current.m_pos > 0) + ensureCharacterGetsLineBox(lineMidpointState, current); lBreak.moveTo(current.m_obj, current.m_pos, current.m_nextBreakablePosition); lBreak.increment(); lineInfo.setPreviousLineBrokeCleanly(true); @@ -2720,9 +3341,8 @@ InlineIterator RenderBlock::LineBreaker::nextLineBreak(InlineBidiResolver& resol } if (autoWrap && betweenWords) { - width.commit(); wrapW = 0; - lBreak.moveTo(current.m_obj, current.m_pos, current.m_nextBreakablePosition); + commitLineBreakAtCurrentWidth(width, lBreak, current.m_obj, current.m_pos, current.m_nextBreakablePosition); // Auto-wrapping text should not wrap in the middle of a word once it has had an // opportunity to break after a word. breakWords = false; @@ -2751,7 +3371,7 @@ InlineIterator RenderBlock::LineBreaker::nextLineBreak(InlineBidiResolver& resol // We just entered a mode where we are ignoring // spaces. Create a midpoint to terminate the run // before the second space. - addMidpoint(lineMidpointState, ignoreStart); + startIgnoringSpaces(lineMidpointState, ignoreStart); trailingObjects.updateMidpointsForTrailingBoxes(lineMidpointState, InlineIterator(), TrailingObjects::DoNotCollapseFirstSpace); } } @@ -2762,16 +3382,14 @@ InlineIterator RenderBlock::LineBreaker::nextLineBreak(InlineBidiResolver& resol lastSpaceWordSpacing = applyWordSpacing ? wordSpacing : 0; wordSpacingForWordMeasurement = (applyWordSpacing && wordMeasurements.last().width) ? wordSpacing : 0; lastSpace = current.m_pos; // e.g., "Foo goo", don't add in any of the ignored spaces. - addMidpoint(lineMidpointState, InlineIterator(0, current.m_obj, current.m_pos)); + stopIgnoringSpaces(lineMidpointState, InlineIterator(0, current.m_obj, current.m_pos)); } #if ENABLE(SVG) if (isSVGText && current.m_pos > 0) { // Force creation of new InlineBoxes for each absolute positioned character (those that start new text chunks). - if (toRenderSVGInlineText(t)->characterStartsNewTextChunk(current.m_pos)) { - addMidpoint(lineMidpointState, InlineIterator(0, current.m_obj, current.m_pos - 1)); - addMidpoint(lineMidpointState, InlineIterator(0, current.m_obj, current.m_pos)); - } + if (toRenderSVGInlineText(t)->characterStartsNewTextChunk(current.m_pos)) + ensureCharacterGetsLineBox(lineMidpointState, current); } #endif @@ -2791,24 +3409,39 @@ InlineIterator RenderBlock::LineBreaker::nextLineBreak(InlineBidiResolver& resol trailingObjects.clear(); atStart = false; + nextCharacter: + secondToLastCharacter = lastCharacter; + lastCharacter = c; } + renderTextInfo.m_lineBreakIterator.setPriorContext(lastCharacter, secondToLastCharacter); + wordMeasurements.grow(wordMeasurements.size() + 1); WordMeasurement& wordMeasurement = wordMeasurements.last(); wordMeasurement.renderer = t; // IMPORTANT: current.m_pos is > length here! - float additionalTmpW = ignoringSpaces ? 0 : textWidth(t, lastSpace, current.m_pos - lastSpace, f, width.currentWidth(), isFixedPitch, collapseWhiteSpace, &wordMeasurement.fallbackFonts, textLayout); + float additionalTempWidth = ignoringSpaces ? 0 : textWidth(t, lastSpace, current.m_pos - lastSpace, f, width.currentWidth(), isFixedPitch, collapseWhiteSpace, wordMeasurement.fallbackFonts, textLayout); wordMeasurement.startOffset = lastSpace; wordMeasurement.endOffset = current.m_pos; - wordMeasurement.width = ignoringSpaces ? 0 : additionalTmpW + wordSpacingForWordMeasurement; - additionalTmpW += lastSpaceWordSpacing; - width.addUncommittedWidth(additionalTmpW + inlineLogicalWidth(current.m_obj, !appliedStartWidth, includeEndWidth)); + wordMeasurement.width = ignoringSpaces ? 0 : additionalTempWidth + wordSpacingForWordMeasurement; + additionalTempWidth += lastSpaceWordSpacing; + + float inlineLogicalTempWidth = inlineLogicalWidth(current.m_obj, !appliedStartWidth, includeEndWidth); + width.addUncommittedWidth(additionalTempWidth + inlineLogicalTempWidth); + + if (wordMeasurement.fallbackFonts.isEmpty() && !fallbackFonts.isEmpty()) + wordMeasurement.fallbackFonts.swap(fallbackFonts); + fallbackFonts.clear(); + + if (collapseWhiteSpace && currentCharacterIsSpace && additionalTempWidth) + width.setTrailingWhitespaceWidth(additionalTempWidth, inlineLogicalTempWidth); + includeEndWidth = false; if (!width.fitsOnLine()) { if (canHyphenate) - tryHyphenating(t, f, style->locale(), consecutiveHyphenatedLines, blockStyle->hyphenationLimitLines(), style->hyphenationLimitBefore(), style->hyphenationLimitAfter(), lastSpace, current.m_pos, width.currentWidth() - additionalTmpW, width.availableWidth(), isFixedPitch, collapseWhiteSpace, lastSpaceWordSpacing, lBreak, current.m_nextBreakablePosition, m_hyphenated); + tryHyphenating(t, f, style->locale(), consecutiveHyphenatedLines, blockStyle->hyphenationLimitLines(), style->hyphenationLimitBefore(), style->hyphenationLimitAfter(), lastSpace, current.m_pos, width.currentWidth() - additionalTempWidth, width.availableWidth(), isFixedPitch, collapseWhiteSpace, lastSpaceWordSpacing, lBreak, current.m_nextBreakablePosition, m_hyphenated); if (!m_hyphenated && lBreak.previousInSameNode() == softHyphen && style->hyphens() != HyphensNone) m_hyphenated = true; @@ -2819,35 +3452,8 @@ InlineIterator RenderBlock::LineBreaker::nextLineBreak(InlineBidiResolver& resol } else ASSERT_NOT_REACHED(); - bool checkForBreak = autoWrap || blockStyle->autoWrap(); - if (width.committedWidth() && !width.fitsOnLine() && lBreak.m_obj && currWS == NOWRAP) - checkForBreak = true; - else if (next && current.m_obj->isText() && next->isText() && !next->isBR() && (autoWrap || (next->style()->autoWrap()))) { - if (currentCharacterIsSpace) - checkForBreak = true; - else { - RenderText* nextText = toRenderText(next); - if (nextText->textLength()) { - UChar c = nextText->characterAt(0); - checkForBreak = (c == ' ' || c == '\t' || (c == '\n' && !next->preservesNewline())); - // If the next item on the line is text, and if we did not end with - // a space, then the next text run continues our word (and so it needs to - // keep adding to |tmpW|. Just update and continue. - } else if (nextText->isWordBreak()) - checkForBreak = true; - - if (!width.fitsOnLine() && !width.committedWidth()) - width.fitBelowFloats(); - - bool canPlaceOnLine = width.fitsOnLine() || !autoWrapWasEverTrueOnLine; - if (canPlaceOnLine && checkForBreak) { - width.commit(); - lBreak.moveToStartOf(next); - } - } - } - - if (checkForBreak && !width.fitsOnLine()) { + bool canBreakHere = canBreakAtThisPosition(autoWrap, width, lBreak, next, current, currWS, currentCharacterIsSpace, autoWrapWasEverTrueOnLine); + if (canBreakHere && !width.fitsOnLine(ignoringSpaces)) { // if we have floats, try to get below them. if (currentCharacterIsSpace && !ignoringSpaces && currentStyle->collapseWhiteSpace()) trailingObjects.clear(); @@ -2860,16 +3466,18 @@ InlineIterator RenderBlock::LineBreaker::nextLineBreak(InlineBidiResolver& resol // |width| may have been adjusted because we got shoved down past a float (thus // giving us more room), so we need to retest, and only jump to // the end label if we still don't fit on the line. -dwh - if (!width.fitsOnLine()) + if (!width.fitsOnLine(ignoringSpaces)) goto end; + } else if (blockStyle->autoWrap() && !width.fitsOnLine() && !width.committedWidth()) { + // If the container autowraps but the current child does not then we still need to ensure that it + // wraps and moves below any floats. + width.fitBelowFloats(); } if (!current.m_obj->isFloatingOrOutOfFlowPositioned()) { last = current.m_obj; - if (last->isReplaced() && autoWrap && (!last->isImage() || allowImagesToBreak) && (!last->isListMarker() || toRenderListMarker(last)->isInside())) { - width.commit(); - lBreak.moveToStartOf(next); - } + if (last->isReplaced() && autoWrap && (!last->isImage() || allowImagesToBreak) && (!last->isListMarker() || toRenderListMarker(last)->isInside())) + commitLineBreakAtCurrentWidth(width, lBreak, next); } // Clear out our character space bool, since inline <pre>s don't collapse whitespace @@ -2881,33 +3489,42 @@ InlineIterator RenderBlock::LineBreaker::nextLineBreak(InlineBidiResolver& resol atStart = false; } - if (width.fitsOnLine() || lastWS == NOWRAP) + if (width.fitsOnLine(true) || lastWS == NOWRAP) lBreak.clear(); end: - if (lBreak == resolver.position() && (!lBreak.m_obj || !lBreak.m_obj->isBR())) { - // we just add as much as possible - if (blockStyle->whiteSpace() == PRE) { - // FIXME: Don't really understand this case. - if (current.m_pos) { - // FIXME: This should call moveTo which would clear m_nextBreakablePosition - // this code as-is is likely wrong. - lBreak.m_obj = current.m_obj; - lBreak.m_pos = current.m_pos - 1; - } else - lBreak.moveTo(last, last->isText() ? last->length() : 0); - } else if (lBreak.m_obj) { - // Don't ever break in the middle of a word if we can help it. - // There's no room at all. We just have to be on this line, - // even though we'll spill out. - lBreak.moveTo(current.m_obj, current.m_pos); +#if ENABLE(CSS_SHAPES) + ShapeInsideInfo* shapeInfo = m_block->layoutShapeInsideInfo(); + bool segmentAllowsOverflow = !shapeInfo || !shapeInfo->hasSegments(); +#else + bool segmentAllowsOverflow = true; +#endif + + if (segmentAllowsOverflow) { + if (lBreak == resolver.position()) { + if (!lBreak.m_obj || !lBreak.m_obj->isBR()) { + // we just add as much as possible + if (blockStyle->whiteSpace() == PRE && !current.m_pos) { + lBreak.moveTo(last, last->isText() ? last->length() : 0); + } else if (lBreak.m_obj) { + // Don't ever break in the middle of a word if we can help it. + // There's no room at all. We just have to be on this line, + // even though we'll spill out. + lBreak.moveTo(current.m_obj, current.m_pos); + } + } + // make sure we consume at least one char/object. + if (lBreak == resolver.position()) + lBreak.increment(); + } else if (!width.committedWidth() && (!current.m_obj || !current.m_obj->isBR()) && !current.m_pos) { + // Do not push the current object to the next line, when this line has some content, but it is still considered empty. + // Empty inline elements like <span></span> can produce such lines and now we just ignore these break opportunities + // at the start of a line, if no width has been committed yet. + // Behave as if it was actually empty and consume at least one object. + lBreak.increment(); } } - // make sure we consume at least one char/object. - if (lBreak == resolver.position()) - lBreak.increment(); - // Sanity check our midpoints. checkMidpoints(lineMidpointState, lBreak); @@ -2980,9 +3597,11 @@ void RenderBlock::checkLinesForTextOverflow() ETextAlign textAlign = style()->textAlign(); bool firstLine = true; for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) { - LayoutUnit blockRightEdge = logicalRightOffsetForLine(curr->lineTop(), firstLine); - LayoutUnit blockLeftEdge = logicalLeftOffsetForLine(curr->lineTop(), firstLine); - LayoutUnit lineBoxEdge = ltr ? curr->x() + curr->logicalWidth() : curr->x(); + // FIXME: Use pixelSnappedLogicalRightOffsetForLine instead of snapping it ourselves once the column workaround in said method has been fixed. + // https://bugs.webkit.org/show_bug.cgi?id=105461 + int blockRightEdge = snapSizeToPixel(logicalRightOffsetForLine(curr->lineTop(), firstLine), curr->x()); + int blockLeftEdge = pixelSnappedLogicalLeftOffsetForLine(curr->lineTop(), firstLine); + int lineBoxEdge = ltr ? snapSizeToPixel(curr->x() + curr->logicalWidth(), curr->x()) : snapSizeToPixel(curr->x(), 0); if ((ltr && lineBoxEdge > blockRightEdge) || (!ltr && lineBoxEdge < blockLeftEdge)) { // This line spills out of our box in the appropriate direction. Now we need to see if the line // can be truncated. In order for truncation to be possible, the line must have sufficient space to @@ -2994,7 +3613,7 @@ void RenderBlock::checkLinesForTextOverflow() if (curr->lineCanAccommodateEllipsis(ltr, blockEdge, lineBoxEdge, width)) { float totalLogicalWidth = curr->placeEllipsis(ellipsisStr, ltr, blockLeftEdge, blockRightEdge, width); - float logicalLeft = 0; // We are only intersted in the delta from the base position. + float logicalLeft = 0; // We are only interested in the delta from the base position. float truncatedWidth = pixelSnappedLogicalRightOffsetForLine(curr->lineTop(), firstLine); updateLogicalWidthForAlignment(textAlign, 0, logicalLeft, totalLogicalWidth, truncatedWidth, 0); if (ltr) diff --git a/Source/WebCore/rendering/RenderBox.cpp b/Source/WebCore/rendering/RenderBox.cpp index 6c341ddb2..03b81ac9a 100644 --- a/Source/WebCore/rendering/RenderBox.cpp +++ b/Source/WebCore/rendering/RenderBox.cpp @@ -25,19 +25,19 @@ #include "config.h" #include "RenderBox.h" -#include "CachedImage.h" #include "Chrome.h" #include "ChromeClient.h" #include "Document.h" +#include "FloatQuad.h" +#include "Frame.h" #include "FrameView.h" #include "GraphicsContext.h" -#include "HitTestResult.h" -#include "htmlediting.h" #include "HTMLElement.h" +#include "HTMLFrameOwnerElement.h" +#include "HTMLInputElement.h" #include "HTMLNames.h" -#include "ImageBuffer.h" -#include "FloatQuad.h" -#include "Frame.h" +#include "HTMLTextAreaElement.h" +#include "HitTestResult.h" #include "Page.h" #include "PaintInfo.h" #include "RenderArena.h" @@ -47,15 +47,19 @@ #include "RenderGeometryMap.h" #include "RenderInline.h" #include "RenderLayer.h" -#include "RenderPart.h" #include "RenderRegion.h" #include "RenderTableCell.h" #include "RenderTheme.h" #include "RenderView.h" -#include "ScrollbarTheme.h" #include "TransformState.h" +#include "htmlediting.h" #include <algorithm> #include <math.h> +#include <wtf/StackStats.h> + +#if USE(ACCELERATED_COMPOSITING) +#include "RenderLayerCompositor.h" +#endif using namespace std; @@ -72,9 +76,26 @@ static OverrideSizeMap* gOverrideWidthMap = 0; static OverrideSizeMap* gOverrideContainingBlockLogicalHeightMap = 0; static OverrideSizeMap* gOverrideContainingBlockLogicalWidthMap = 0; + +// Size of border belt for autoscroll. When mouse pointer in border belt, +// autoscroll is started. +static const int autoscrollBeltSize = 20; +static const unsigned backgroundObscurationTestMaxDepth = 4; + bool RenderBox::s_hadOverflowClip = false; -RenderBox::RenderBox(Node* node) +static bool skipBodyBackground(const RenderBox* bodyElementRenderer) +{ + ASSERT(bodyElementRenderer->isBody()); + // The <body> only paints its background if the root element has defined a background independent of the body, + // or if the <body>'s parent is not the document element's renderer (e.g. inside SVG foreignObject). + RenderObject* documentElementRenderer = bodyElementRenderer->document()->documentElement()->renderer(); + return documentElementRenderer + && !documentElementRenderer->hasBackground() + && (documentElementRenderer == bodyElementRenderer->parent()); +} + +RenderBox::RenderBox(ContainerNode* node) : RenderBoxModelObject(node) , m_minPreferredLogicalWidth(-1) , m_maxPreferredLogicalWidth(-1) @@ -87,13 +108,13 @@ RenderBox::~RenderBox() { } -LayoutRect RenderBox::borderBoxRectInRegion(RenderRegion* region, LayoutUnit offsetFromTopOfFirstPage, RenderBoxRegionInfoFlags cacheFlag) const +LayoutRect RenderBox::borderBoxRectInRegion(RenderRegion* region, RenderBoxRegionInfoFlags cacheFlag) const { if (!region) return borderBoxRect(); // Compute the logical width and placement in this region. - RenderBoxRegionInfo* boxInfo = renderBoxRegionInfo(region, offsetFromTopOfFirstPage, cacheFlag); + RenderBoxRegionInfo* boxInfo = renderBoxRegionInfo(region, cacheFlag); if (!boxInfo) return borderBoxRect(); @@ -104,17 +125,15 @@ LayoutRect RenderBox::borderBoxRectInRegion(RenderRegion* region, LayoutUnit off // Now apply the parent inset since it is cumulative whenever anything in the containing block chain shifts. // FIXME: Doesn't work right with perpendicular writing modes. const RenderBlock* currentBox = containingBlock(); - offsetFromTopOfFirstPage -= logicalTop(); - RenderBoxRegionInfo* currentBoxInfo = currentBox->renderBoxRegionInfo(region, offsetFromTopOfFirstPage); + RenderBoxRegionInfo* currentBoxInfo = currentBox->renderBoxRegionInfo(region); while (currentBoxInfo && currentBoxInfo->isShifted()) { if (currentBox->style()->direction() == LTR) logicalLeft += currentBoxInfo->logicalLeft(); else logicalLeft -= (currentBox->logicalWidth() - currentBoxInfo->logicalWidth()) - currentBoxInfo->logicalLeft(); - offsetFromTopOfFirstPage -= logicalTop(); currentBox = currentBox->containingBlock(); region = currentBox->clampToStartAndEndRegions(region); - currentBoxInfo = currentBox->renderBoxRegionInfo(region, offsetFromTopOfFirstPage); + currentBoxInfo = currentBox->renderBoxRegionInfo(region); } if (cacheFlag == DoNotCacheRenderBoxRegionInfo) @@ -127,11 +146,12 @@ LayoutRect RenderBox::borderBoxRectInRegion(RenderRegion* region, LayoutUnit off void RenderBox::clearRenderBoxRegionInfo() { - if (!inRenderFlowThread() || isRenderFlowThread()) + if (isRenderFlowThread()) return; - RenderFlowThread* flowThread = enclosingRenderFlowThread(); - flowThread->removeRenderBoxRegionInfo(this); + RenderFlowThread* flowThread = flowThreadContainingBlock(); + if (flowThread) + flowThread->removeRenderBoxRegionInfo(this); } void RenderBox::willBeDestroyed() @@ -141,6 +161,10 @@ void RenderBox::willBeDestroyed() RenderBlock::removePercentHeightDescendantIfNeeded(this); +#if ENABLE(CSS_SHAPES) + ShapeOutsideInfo::removeInfo(this); +#endif + RenderBoxModelObject::willBeDestroyed(); } @@ -184,8 +208,14 @@ void RenderBox::styleWillChange(StyleDifference diff, const RenderStyle* newStyl // The background of the root element or the body element could propagate up to // the canvas. Just dirty the entire canvas when our style changes substantially. if (diff >= StyleDifferenceRepaint && node() && - (node()->hasTagName(htmlTag) || node()->hasTagName(bodyTag))) + (node()->hasTagName(htmlTag) || node()->hasTagName(bodyTag))) { view()->repaint(); + +#if USE(ACCELERATED_COMPOSITING) + if (oldStyle->hasEntirelyFixedBackground() != newStyle->hasEntirelyFixedBackground()) + view()->compositor()->rootFixedBackgroundsChanged(); +#endif + } // When a layout hint happens and an object's position style changes, we have to do a layout // to dirty the render tree using the old position value now. @@ -242,6 +272,15 @@ void RenderBox::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle } } + // Our opaqueness might have changed without triggering layout. + if (diff >= StyleDifferenceRepaint && diff <= StyleDifferenceRepaintLayer) { + RenderObject* parentToInvalidate = parent(); + for (unsigned i = 0; i < backgroundObscurationTestMaxDepth && parentToInvalidate; ++i) { + parentToInvalidate->invalidateBackgroundObscurationStatus(); + parentToInvalidate = parentToInvalidate->parent(); + } + } + bool isBodyRenderer = isBody(); bool isRootRenderer = isRoot(); @@ -253,6 +292,7 @@ void RenderBox::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle // Propagate the new writing mode and direction up to the RenderView. RenderView* viewRenderer = view(); RenderStyle* viewStyle = viewRenderer->style(); + bool viewChangedWritingMode = false; if (viewStyle->direction() != newStyle->direction() && (isRootRenderer || !document()->directionSetOnDocumentElement())) { viewStyle->setDirection(newStyle->direction()); if (isBodyRenderer) @@ -262,6 +302,7 @@ void RenderBox::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle if (viewStyle->writingMode() != newStyle->writingMode() && (isRootRenderer || !document()->writingModeSetOnDocumentElement())) { viewStyle->setWritingMode(newStyle->writingMode()); + viewChangedWritingMode = true; viewRenderer->setHorizontalWritingMode(newStyle->isHorizontalWritingMode()); viewRenderer->markAllDescendantsWithFloatsForLayout(); if (isBodyRenderer) { @@ -272,9 +313,35 @@ void RenderBox::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle } frame()->view()->recalculateScrollbarOverlayStyle(); + + const Pagination& pagination = frame()->view()->pagination(); + if (viewChangedWritingMode && pagination.mode != Pagination::Unpaginated) { + viewStyle->setColumnStylesFromPaginationMode(pagination.mode); + if (viewRenderer->hasColumns()) + viewRenderer->updateColumnInfoFromStyle(viewStyle); + } } + +#if ENABLE(CSS_SHAPES) + updateShapeOutsideInfoAfterStyleChange(style()->shapeOutside(), oldStyle ? oldStyle->shapeOutside() : 0); +#endif } +#if ENABLE(CSS_SHAPES) +void RenderBox::updateShapeOutsideInfoAfterStyleChange(const ShapeValue* shapeOutside, const ShapeValue* oldShapeOutside) +{ + // FIXME: A future optimization would do a deep comparison for equality. (bug 100811) + if (shapeOutside == oldShapeOutside) + return; + + if (shapeOutside) { + ShapeOutsideInfo* shapeOutsideInfo = ShapeOutsideInfo::ensureInfo(this); + shapeOutsideInfo->dirtyShapeSize(); + } else + ShapeOutsideInfo::removeInfo(this); +} +#endif + void RenderBox::updateFromStyle() { RenderBoxModelObject::updateFromStyle(); @@ -287,7 +354,6 @@ void RenderBox::updateFromStyle() if (isRootObject || isViewObject) setHasBoxDecorations(true); - setPositioned(styleToUse->hasOutOfFlowPosition()); setFloating(!isOutOfFlowPositioned() && styleToUse->isFloating()); // We also handle <body> and <html>, whose overflow applies to the viewport. @@ -336,6 +402,7 @@ void RenderBox::layout() child = child->nextSibling(); } statePusher.pop(); + invalidateBackgroundObscurationStatus(); setNeedsLayout(false); } @@ -361,6 +428,16 @@ int RenderBox::pixelSnappedClientHeight() const return snapSizeToPixel(clientHeight(), y() + clientTop()); } +int RenderBox::pixelSnappedOffsetWidth() const +{ + return snapSizeToPixel(offsetWidth(), x() + clientLeft()); +} + +int RenderBox::pixelSnappedOffsetHeight() const +{ + return snapSizeToPixel(offsetHeight(), y() + clientTop()); +} + int RenderBox::scrollWidth() const { if (hasOverflowClip()) @@ -420,23 +497,34 @@ void RenderBox::updateLayerTransform() layer()->updateTransform(); } -LayoutUnit RenderBox::constrainLogicalWidthInRegionByMinMax(LayoutUnit logicalWidth, LayoutUnit availableWidth, RenderBlock* cb, RenderRegion* region, LayoutUnit offsetFromLogicalTopOfFirstPage) const +LayoutUnit RenderBox::constrainLogicalWidthInRegionByMinMax(LayoutUnit logicalWidth, LayoutUnit availableWidth, RenderBlock* cb, RenderRegion* region) const { RenderStyle* styleToUse = style(); if (!styleToUse->logicalMaxWidth().isUndefined()) - logicalWidth = min(logicalWidth, computeLogicalWidthInRegionUsing(MaxSize, availableWidth, cb, region, offsetFromLogicalTopOfFirstPage)); - return max(logicalWidth, computeLogicalWidthInRegionUsing(MinSize, availableWidth, cb, region, offsetFromLogicalTopOfFirstPage)); + logicalWidth = min(logicalWidth, computeLogicalWidthInRegionUsing(MaxSize, styleToUse->logicalMaxWidth(), availableWidth, cb, region)); + return max(logicalWidth, computeLogicalWidthInRegionUsing(MinSize, styleToUse->logicalMinWidth(), availableWidth, cb, region)); } LayoutUnit RenderBox::constrainLogicalHeightByMinMax(LayoutUnit logicalHeight) const { RenderStyle* styleToUse = style(); if (!styleToUse->logicalMaxHeight().isUndefined()) { - LayoutUnit maxH = computeLogicalHeightUsing(MaxSize, styleToUse->logicalMaxHeight()); + LayoutUnit maxH = computeLogicalHeightUsing(styleToUse->logicalMaxHeight()); if (maxH != -1) logicalHeight = min(logicalHeight, maxH); } - return max(logicalHeight, computeLogicalHeightUsing(MinSize, styleToUse->logicalMinHeight())); + return max(logicalHeight, computeLogicalHeightUsing(styleToUse->logicalMinHeight())); +} + +LayoutUnit RenderBox::constrainContentBoxLogicalHeightByMinMax(LayoutUnit logicalHeight) const +{ + RenderStyle* styleToUse = style(); + if (!styleToUse->logicalMaxHeight().isUndefined()) { + LayoutUnit maxH = computeContentLogicalHeight(styleToUse->logicalMaxHeight()); + if (maxH != -1) + logicalHeight = min(logicalHeight, maxH); + } + return max(logicalHeight, computeContentLogicalHeight(styleToUse->logicalMinHeight())); } IntRect RenderBox::absoluteContentBox() const @@ -459,14 +547,16 @@ LayoutRect RenderBox::outlineBoundsForRepaint(const RenderLayerModelObject* repa LayoutRect box = borderBoundingBox(); adjustRectForOutlineAndShadow(box); - FloatQuad containerRelativeQuad; - if (geometryMap) - containerRelativeQuad = geometryMap->mapToContainer(box, repaintContainer); - else - containerRelativeQuad = localToContainerQuad(FloatRect(box), repaintContainer); - - box = containerRelativeQuad.enclosingBoundingBox(); + if (repaintContainer != this) { + FloatQuad containerRelativeQuad; + if (geometryMap) + containerRelativeQuad = geometryMap->mapToContainer(box, repaintContainer); + else + containerRelativeQuad = localToContainerQuad(FloatRect(box), repaintContainer); + box = containerRelativeQuad.enclosingBoundingBox(); + } + // FIXME: layoutDelta needs to be applied in parts before/after transforms and // repaint containers. https://bugs.webkit.org/show_bug.cgi?id=23308 box.move(view()->layoutDelta()); @@ -474,7 +564,7 @@ LayoutRect RenderBox::outlineBoundsForRepaint(const RenderLayerModelObject* repa return box; } -void RenderBox::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint& additionalOffset) +void RenderBox::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint& additionalOffset, const RenderLayerModelObject*) { if (!size().isEmpty()) rects.append(pixelSnappedIntRect(additionalOffset, size())); @@ -565,6 +655,24 @@ int RenderBox::horizontalScrollbarHeight() const return includeHorizontalScrollbarSize() ? layer()->horizontalScrollbarHeight() : 0; } +int RenderBox::instrinsicScrollbarLogicalWidth() const +{ + if (!hasOverflowClip()) + return 0; + + if (isHorizontalWritingMode() && style()->overflowY() == OSCROLL) { + ASSERT(layer()->hasVerticalScrollbar()); + return verticalScrollbarWidth(); + } + + if (!isHorizontalWritingMode() && style()->overflowX() == OSCROLL) { + ASSERT(layer()->hasHorizontalScrollbar()); + return horizontalScrollbarHeight(); + } + + return 0; +} + bool RenderBox::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier, Node** stopNode) { RenderLayer* l = layer(); @@ -628,10 +736,70 @@ bool RenderBox::usesCompositedScrolling() const return hasOverflowClip() && hasLayer() && layer()->usesCompositedScrolling(); } -void RenderBox::autoscroll() +void RenderBox::autoscroll(const IntPoint& position) { if (layer()) - layer()->autoscroll(); + layer()->autoscroll(position); +} + +// There are two kinds of renderer that can autoscroll. +bool RenderBox::canAutoscroll() const +{ + // Check for a box that can be scrolled in its own right. + if (canBeScrolledAndHasScrollableArea()) + return true; + + // Check for a box that represents the top level of a web page. + // This can be scrolled by calling Chrome::scrollRectIntoView. + // This only has an effect on the Mac platform in applications + // that put web views into scrolling containers, such as Mac OS X Mail. + // The code for this is in RenderLayer::scrollRectToVisible. + if (node() != document()) + return false; + Frame* frame = this->frame(); + if (!frame) + return false; + Page* page = frame->page(); + return page && page->mainFrame() == frame && frame->view()->isScrollable(); +} + +// If specified point is in border belt, returned offset denotes direction of +// scrolling. +IntSize RenderBox::calculateAutoscrollDirection(const IntPoint& windowPoint) const +{ + if (!frame()) + return IntSize(); + + FrameView* frameView = frame()->view(); + if (!frameView) + return IntSize(); + + IntSize offset; + IntPoint point = frameView->windowToContents(windowPoint); + IntRect box(absoluteBoundingBoxRect()); + + if (point.x() < box.x() + autoscrollBeltSize) + point.move(-autoscrollBeltSize, 0); + else if (point.x() > box.maxX() - autoscrollBeltSize) + point.move(autoscrollBeltSize, 0); + + if (point.y() < box.y() + autoscrollBeltSize) + point.move(0, -autoscrollBeltSize); + else if (point.y() > box.maxY() - autoscrollBeltSize) + point.move(0, autoscrollBeltSize); + return frameView->contentsToWindow(point) - windowPoint; +} + +RenderBox* RenderBox::findAutoscrollable(RenderObject* renderer) +{ + while (renderer && !(renderer->isBox() && toRenderBox(renderer)->canAutoscroll())) { + if (!renderer->parent() && renderer->node() == renderer->document() && renderer->document()->ownerElement()) + renderer = renderer->document()->ownerElement()->renderer(); + else + renderer = renderer->parent(); + } + + return renderer && renderer->isBox() ? toRenderBox(renderer) : 0; } void RenderBox::panScroll(const IntPoint& source) @@ -661,31 +829,49 @@ LayoutSize RenderBox::cachedSizeForOverflowClip() const void RenderBox::applyCachedClipAndScrollOffsetForRepaint(LayoutRect& paintRect) const { + flipForWritingMode(paintRect); paintRect.move(-scrolledContentOffset()); // For overflow:auto/scroll/hidden. // Do not clip scroll layer contents to reduce the number of repaints while scrolling. - if (usesCompositedScrolling()) + if (usesCompositedScrolling()) { + flipForWritingMode(paintRect); return; + } // height() is inaccurate if we're in the middle of a layout of this RenderBox, so use the // layer's size instead. Even if the layer's size is wrong, the layer itself will repaint // anyway if its size does change. LayoutRect clipRect(LayoutPoint(), cachedSizeForOverflowClip()); paintRect = intersection(paintRect, clipRect); + flipForWritingMode(paintRect); +} + +void RenderBox::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const +{ + minLogicalWidth = minPreferredLogicalWidth() - borderAndPaddingLogicalWidth(); + maxLogicalWidth = maxPreferredLogicalWidth() - borderAndPaddingLogicalWidth(); } LayoutUnit RenderBox::minPreferredLogicalWidth() const { - if (preferredLogicalWidthsDirty()) + if (preferredLogicalWidthsDirty()) { +#ifndef NDEBUG + SetLayoutNeededForbiddenScope layoutForbiddenScope(const_cast<RenderBox*>(this)); +#endif const_cast<RenderBox*>(this)->computePreferredLogicalWidths(); + } return m_minPreferredLogicalWidth; } LayoutUnit RenderBox::maxPreferredLogicalWidth() const { - if (preferredLogicalWidthsDirty()) + if (preferredLogicalWidthsDirty()) { +#ifndef NDEBUG + SetLayoutNeededForbiddenScope layoutForbiddenScope(const_cast<RenderBox*>(this)); +#endif const_cast<RenderBox*>(this)->computePreferredLogicalWidths(); + } return m_maxPreferredLogicalWidth; } @@ -784,6 +970,11 @@ void RenderBox::clearContainingBlockOverrideSize() { if (gOverrideContainingBlockLogicalWidthMap) gOverrideContainingBlockLogicalWidthMap->remove(this); + clearOverrideContainingBlockContentLogicalHeight(); +} + +void RenderBox::clearOverrideContainingBlockContentLogicalHeight() +{ if (gOverrideContainingBlockLogicalHeightMap) gOverrideContainingBlockLogicalHeightMap->remove(this); } @@ -851,13 +1042,16 @@ void RenderBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) LayoutPoint adjustedPaintOffset = paintOffset + location(); // default implementation. Just pass paint through to the children PaintInfo childInfo(paintInfo); - childInfo.updatePaintingRootForChildren(this); + childInfo.updateSubtreePaintRootForChildren(this); for (RenderObject* child = firstChild(); child; child = child->nextSibling()) child->paint(childInfo, adjustedPaintOffset); } void RenderBox::paintRootBoxFillLayers(const PaintInfo& paintInfo) { + if (paintInfo.skipRootBackground()) + return; + RenderObject* rootBackgroundRenderer = rendererForRootBackground(); const FillLayer* bgLayer = rootBackgroundRenderer->style()->backgroundLayers(); @@ -878,9 +1072,24 @@ BackgroundBleedAvoidance RenderBox::determineBackgroundBleedAvoidance(GraphicsCo AffineTransform ctm = context->getCTM(); FloatSize contextScaling(static_cast<float>(ctm.xScale()), static_cast<float>(ctm.yScale())); + + // Because RoundedRect uses IntRect internally the inset applied by the + // BackgroundBleedShrinkBackground strategy cannot be less than one integer + // layout coordinate, even with subpixel layout enabled. To take that into + // account, we clamp the contextScaling to 1.0 for the following test so + // that borderObscuresBackgroundEdge can only return true if the border + // widths are greater than 2 in both layout coordinates and screen + // coordinates. + // This precaution will become obsolete if RoundedRect is ever promoted to + // a sub-pixel representation. + if (contextScaling.width() > 1) + contextScaling.setWidth(1); + if (contextScaling.height() > 1) + contextScaling.setHeight(1); + if (borderObscuresBackgroundEdge(contextScaling)) return BackgroundBleedShrinkBackground; - if (!style->hasAppearance() && borderObscuresBackground() && backgroundIsSingleOpaqueLayer()) + if (!style->hasAppearance() && borderObscuresBackground() && backgroundHasOpaqueTopLayer()) return BackgroundBleedBackgroundOverBorder; return BackgroundBleedUseTransparencyLayer; @@ -894,10 +1103,6 @@ void RenderBox::paintBoxDecorations(PaintInfo& paintInfo, const LayoutPoint& pai LayoutRect paintRect = borderBoxRectInRegion(paintInfo.renderRegion); paintRect.moveBy(paintOffset); - // border-fit can adjust where we paint our border and background. If set, we snugly fit our line box descendants. (The iChat - // balloon layout is an example of this). - borderFitAdjust(paintRect); - BackgroundBleedAvoidance bleedAvoidance = determineBackgroundBleedAvoidance(paintInfo.context); // FIXME: Should eventually give the theme control over whether the box shadow should paint, since controls could have @@ -912,7 +1117,7 @@ void RenderBox::paintBoxDecorations(PaintInfo& paintInfo, const LayoutPoint& pai // beginning the layer). RoundedRect border = style()->getRoundedBorderFor(paintRect, view()); stateSaver.save(); - paintInfo.context->addRoundedRectClip(border); + paintInfo.context->clipRoundedRect(border); paintInfo.context->beginTransparencyLayer(1); } @@ -941,33 +1146,168 @@ void RenderBox::paintBoxDecorations(PaintInfo& paintInfo, const LayoutPoint& pai void RenderBox::paintBackground(const PaintInfo& paintInfo, const LayoutRect& paintRect, BackgroundBleedAvoidance bleedAvoidance) { - if (isRoot()) + if (isRoot()) { paintRootBoxFillLayers(paintInfo); - else if (!isBody() - || (document()->documentElement()->renderer() && document()->documentElement()->renderer()->hasBackground()) - || (document()->documentElement()->renderer() != parent())) { - // The <body> only paints its background if the root element has defined a background independent of the body, - // or if the <body>'s parent is not the document element's renderer (e.g. inside SVG foreignObject). - if (!backgroundIsObscured()) - paintFillLayers(paintInfo, style()->visitedDependentColor(CSSPropertyBackgroundColor), style()->backgroundLayers(), paintRect, bleedAvoidance); + return; + } + if (isBody() && skipBodyBackground(this)) + return; + if (backgroundIsKnownToBeObscured() && !boxShadowShouldBeAppliedToBackground(bleedAvoidance)) + return; + paintFillLayers(paintInfo, style()->visitedDependentColor(CSSPropertyBackgroundColor), style()->backgroundLayers(), paintRect, bleedAvoidance); +} + +bool RenderBox::getBackgroundPaintedExtent(LayoutRect& paintedExtent) const +{ + ASSERT(hasBackground()); + LayoutRect backgroundRect = pixelSnappedIntRect(borderBoxRect()); + + Color backgroundColor = style()->visitedDependentColor(CSSPropertyBackgroundColor); + if (backgroundColor.isValid() && backgroundColor.alpha()) { + paintedExtent = backgroundRect; + return true; + } + + if (!style()->backgroundLayers()->image() || style()->backgroundLayers()->next()) { + paintedExtent = backgroundRect; + return true; + } + + BackgroundImageGeometry geometry; + calculateBackgroundImageGeometry(0, style()->backgroundLayers(), backgroundRect, geometry); + paintedExtent = geometry.destRect(); + return !geometry.hasNonLocalGeometry(); +} + +bool RenderBox::backgroundIsKnownToBeOpaqueInRect(const LayoutRect& localRect) const +{ + if (isBody() && skipBodyBackground(this)) + return false; + + Color backgroundColor = style()->visitedDependentColor(CSSPropertyBackgroundColor); + if (!backgroundColor.isValid() || backgroundColor.hasAlpha()) + return false; + + // If the element has appearance, it might be painted by theme. + // We cannot be sure if theme paints the background opaque. + // In this case it is safe to not assume opaqueness. + // FIXME: May be ask theme if it paints opaque. + if (style()->hasAppearance()) + return false; + // FIXME: Check the opaqueness of background images. + + // FIXME: Use rounded rect if border radius is present. + if (style()->hasBorderRadius()) + return false; + // FIXME: The background color clip is defined by the last layer. + if (style()->backgroundLayers()->next()) + return false; + LayoutRect backgroundRect; + switch (style()->backgroundClip()) { + case BorderFillBox: + backgroundRect = borderBoxRect(); + break; + case PaddingFillBox: + backgroundRect = paddingBoxRect(); + break; + case ContentFillBox: + backgroundRect = contentBoxRect(); + break; + default: + break; + } + return backgroundRect.contains(localRect); +} + +static bool isCandidateForOpaquenessTest(RenderBox* childBox) +{ + RenderStyle* childStyle = childBox->style(); + if (childStyle->position() != StaticPosition && childBox->containingBlock() != childBox->parent()) + return false; + if (childStyle->visibility() != VISIBLE || childStyle->shapeOutside()) + return false; + if (!childBox->width() || !childBox->height()) + return false; + if (RenderLayer* childLayer = childBox->layer()) { +#if USE(ACCELERATED_COMPOSITING) + if (childLayer->isComposited()) + return false; +#endif + // FIXME: Deal with z-index. + if (!childStyle->hasAutoZIndex()) + return false; + if (childLayer->hasTransform() || childLayer->isTransparent() || childLayer->hasFilter()) + return false; + } + return true; +} + +bool RenderBox::foregroundIsKnownToBeOpaqueInRect(const LayoutRect& localRect, unsigned maxDepthToTest) const +{ + if (!maxDepthToTest) + return false; + for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { + if (!child->isBox()) + continue; + RenderBox* childBox = toRenderBox(child); + if (!isCandidateForOpaquenessTest(childBox)) + continue; + LayoutPoint childLocation = childBox->location(); + if (childBox->isRelPositioned()) + childLocation.move(childBox->relativePositionOffset()); + LayoutRect childLocalRect = localRect; + childLocalRect.moveBy(-childLocation); + if (childLocalRect.y() < 0 || childLocalRect.x() < 0) { + // If there is unobscured area above/left of a static positioned box then the rect is probably not covered. + if (childBox->style()->position() == StaticPosition) + return false; + continue; + } + if (childLocalRect.maxY() > childBox->height() || childLocalRect.maxX() > childBox->width()) + continue; + if (childBox->backgroundIsKnownToBeOpaqueInRect(childLocalRect)) + return true; + if (childBox->foregroundIsKnownToBeOpaqueInRect(childLocalRect, maxDepthToTest - 1)) + return true; } + return false; } -bool RenderBox::backgroundIsSingleOpaqueLayer() const +bool RenderBox::computeBackgroundIsKnownToBeObscured() +{ + // Test to see if the children trivially obscure the background. + // FIXME: This test can be much more comprehensive. + if (!hasBackground()) + return false; + // Table and root background painting is special. + if (isTable() || isRoot()) + return false; + + LayoutRect backgroundRect; + if (!getBackgroundPaintedExtent(backgroundRect)) + return false; + return foregroundIsKnownToBeOpaqueInRect(backgroundRect, backgroundObscurationTestMaxDepth); +} + +bool RenderBox::backgroundHasOpaqueTopLayer() const { const FillLayer* fillLayer = style()->backgroundLayers(); - if (!fillLayer || fillLayer->next() || fillLayer->clip() != BorderFillBox || fillLayer->composite() != CompositeSourceOver) + if (!fillLayer || fillLayer->clip() != BorderFillBox) return false; // Clipped with local scrolling if (hasOverflowClip() && fillLayer->attachment() == LocalBackgroundAttachment) return false; - Color bgColor = style()->visitedDependentColor(CSSPropertyBackgroundColor); - if (bgColor.isValid() && bgColor.alpha() == 255) + if (fillLayer->hasOpaqueImage(this) && fillLayer->hasRepeatXY() && fillLayer->image()->canRender(this, style()->effectiveZoom())) return true; - - // FIXME: return true if a background image is present and is opaque + + // If there is only one layer and no image, check whether the background color is opaque + if (!fillLayer->next() && !fillLayer->hasImage()) { + Color bgColor = style()->visitedDependentColor(CSSPropertyBackgroundColor); + if (bgColor.isValid() && bgColor.alpha() == 255) + return true; + } return false; } @@ -978,11 +1318,6 @@ void RenderBox::paintMask(PaintInfo& paintInfo, const LayoutPoint& paintOffset) return; LayoutRect paintRect = LayoutRect(paintOffset, size()); - - // border-fit can adjust where we paint our border and background. If set, we snugly fit our line box descendants. (The iChat - // balloon layout is an example of this). - borderFitAdjust(paintRect); - paintMaskImages(paintInfo, paintRect); } @@ -1038,7 +1373,8 @@ LayoutRect RenderBox::maskClipRect() for (const FillLayer* maskLayer = style()->maskLayers(); maskLayer; maskLayer = maskLayer->next()) { if (maskLayer->image()) { BackgroundImageGeometry geometry; - calculateBackgroundImageGeometry(maskLayer, borderBox, geometry); + // Masks should never have fixed attachment, so it's OK for paintContainer to be null. + calculateBackgroundImageGeometry(0, maskLayer, borderBox, geometry); result.unite(geometry.destRect()); } } @@ -1048,18 +1384,40 @@ LayoutRect RenderBox::maskClipRect() void RenderBox::paintFillLayers(const PaintInfo& paintInfo, const Color& c, const FillLayer* fillLayer, const LayoutRect& rect, BackgroundBleedAvoidance bleedAvoidance, CompositeOperator op, RenderObject* backgroundObject) { - if (!fillLayer) - return; + Vector<const FillLayer*, 8> layers; + const FillLayer* curLayer = fillLayer; + bool shouldDrawBackgroundInSeparateBuffer = false; + while (curLayer) { + layers.append(curLayer); + // Stop traversal when an opaque layer is encountered. + // FIXME : It would be possible for the following occlusion culling test to be more aggressive + // on layers with no repeat by testing whether the image covers the layout rect. + // Testing that here would imply duplicating a lot of calculations that are currently done in + // RenderBoxModelObject::paintFillLayerExtended. A more efficient solution might be to move + // the layer recursion into paintFillLayerExtended, or to compute the layer geometry here + // and pass it down. + + if (!shouldDrawBackgroundInSeparateBuffer && curLayer->blendMode() != BlendModeNormal) + shouldDrawBackgroundInSeparateBuffer = true; + + // The clipOccludesNextLayers condition must be evaluated first to avoid short-circuiting. + if (curLayer->clipOccludesNextLayers(curLayer == fillLayer) && curLayer->hasOpaqueImage(this) && curLayer->image()->canRender(this, style()->effectiveZoom()) && curLayer->hasRepeatXY() && curLayer->blendMode() == BlendModeNormal) + break; + curLayer = curLayer->next(); + } + + GraphicsContext* context = paintInfo.context; + if (!context) + shouldDrawBackgroundInSeparateBuffer = false; + if (shouldDrawBackgroundInSeparateBuffer) + context->beginTransparencyLayer(1); - // FIXME : It would be possible for the following occlusion culling test to be more aggressive - // on layers with no repeat by testing whether the image covers the layout rect. - // Testing that here would imply duplicating a lot of calculations that are currently done in - // RenderBoxModelOBject::paintFillLayerExtended. A more efficient solution might be to move - // the layer recursion into paintFillLayerExtended, or to compute the layer geometry here - // and pass it down. - if (fillLayer->next() && (!fillLayer->hasOpaqueImage(this) || !fillLayer->image()->canRender(this, style()->effectiveZoom()) || !fillLayer->hasRepeatXY())) - paintFillLayers(paintInfo, c, fillLayer->next(), rect, bleedAvoidance, op, backgroundObject); - paintFillLayer(paintInfo, c, fillLayer, rect, bleedAvoidance, op, backgroundObject); + Vector<const FillLayer*>::const_reverse_iterator topLayer = layers.rend(); + for (Vector<const FillLayer*>::const_reverse_iterator it = layers.rbegin(); it != topLayer; ++it) + paintFillLayer(paintInfo, c, *it, rect, bleedAvoidance, op, backgroundObject); + + if (shouldDrawBackgroundInSeparateBuffer) + context->endTransparencyLayer(); } void RenderBox::paintFillLayer(const PaintInfo& paintInfo, const Color& c, const FillLayer* fillLayer, const LayoutRect& rect, @@ -1097,8 +1455,13 @@ void RenderBox::imageChanged(WrappedImagePtr image, const IntRect*) #if USE(ACCELERATED_COMPOSITING) - if (hasLayer() && layer()->hasCompositedMask() && layersUseImage(image, style()->maskLayers())) + if (!isComposited()) + return; + + if (layer()->hasCompositedMask() && layersUseImage(image, style()->maskLayers())) layer()->contentChanged(MaskImageChanged); + if (layersUseImage(image, style()->backgroundLayers())) + layer()->contentChanged(BackgroundImageChanged); #endif } @@ -1109,8 +1472,7 @@ bool RenderBox::repaintLayerRectsForImage(WrappedImagePtr image, const FillLayer for (const FillLayer* curLayer = layers; curLayer; curLayer = curLayer->next()) { if (curLayer->image() && image == curLayer->image()->data() && curLayer->image()->canRender(this, style()->effectiveZoom())) { - // Now that we know this image is being used, compute the renderer and the rect - // if we haven't already + // Now that we know this image is being used, compute the renderer and the rect if we haven't already. if (!layerRenderer) { bool drawingRootBackground = drawingBackground && (isRoot() || (isBody() && !document()->documentElement()->renderer()->hasBackground())); if (drawingRootBackground) { @@ -1137,7 +1499,14 @@ bool RenderBox::repaintLayerRectsForImage(WrappedImagePtr image, const FillLayer } BackgroundImageGeometry geometry; - layerRenderer->calculateBackgroundImageGeometry(curLayer, rendererRect, geometry); + layerRenderer->calculateBackgroundImageGeometry(0, curLayer, rendererRect, geometry); + if (geometry.hasNonLocalGeometry()) { + // Rather than incur the costs of computing the paintContainer for renderers with fixed backgrounds + // in order to get the right destRect, just repaint the entire renderer. + layerRenderer->repaint(); + return true; + } + layerRenderer->repaintRectangle(geometry.destRect()); if (geometry.destRect() == rendererRect) return true; @@ -1162,10 +1531,10 @@ void RenderBox::paintCustomHighlight(const LayoutPoint& paintOffset, const Atomi if (r) { FloatRect rootRect(paintOffset.x() + r->x(), paintOffset.y() + r->selectionTop(), r->logicalWidth(), r->selectionHeight()); FloatRect imageRect(paintOffset.x() + x(), rootRect.y(), width(), rootRect.height()); - page->chrome()->client()->paintCustomHighlight(node(), type, imageRect, rootRect, behindText, false); + page->chrome().client()->paintCustomHighlight(node(), type, imageRect, rootRect, behindText, false); } else { FloatRect imageRect(paintOffset.x() + x(), paintOffset.y() + y(), width(), height()); - page->chrome()->client()->paintCustomHighlight(node(), type, imageRect, imageRect, behindText, false); + page->chrome().client()->paintCustomHighlight(node(), type, imageRect, imageRect, behindText, false); } } @@ -1189,10 +1558,10 @@ bool RenderBox::pushContentsClip(PaintInfo& paintInfo, const LayoutPoint& accumu paintObject(paintInfo, accumulatedOffset); paintInfo.phase = PaintPhaseChildBlockBackgrounds; } - IntRect clipRect = pixelSnappedIntRect(isControlClip ? controlClipRect(accumulatedOffset) : overflowClipRect(accumulatedOffset, paintInfo.renderRegion)); + IntRect clipRect = pixelSnappedIntRect(isControlClip ? controlClipRect(accumulatedOffset) : overflowClipRect(accumulatedOffset, paintInfo.renderRegion, IgnoreOverlayScrollbarSize, paintInfo.phase)); paintInfo.context->save(); if (style()->hasBorderRadius()) - paintInfo.context->addRoundedRectClip(style()->getRoundedInnerBorderFor(LayoutRect(accumulatedOffset, size()))); + paintInfo.context->clipRoundedRect(style()->getRoundedInnerBorderFor(LayoutRect(accumulatedOffset, size()))); paintInfo.context->clip(clipRect); return true; } @@ -1210,7 +1579,7 @@ void RenderBox::popContentsClip(PaintInfo& paintInfo, PaintPhase originalPhase, paintInfo.phase = originalPhase; } -LayoutRect RenderBox::overflowClipRect(const LayoutPoint& location, RenderRegion* region, OverlayScrollbarSizeRelevancy relevancy) +LayoutRect RenderBox::overflowClipRect(const LayoutPoint& location, RenderRegion* region, OverlayScrollbarSizeRelevancy relevancy, PaintPhase) { // FIXME: When overflow-clip (CSS3) is implemented, we'll obtain the property // here. @@ -1258,18 +1627,17 @@ LayoutRect RenderBox::clipRect(const LayoutPoint& location, RenderRegion* region return clipRect; } -LayoutUnit RenderBox::shrinkLogicalWidthToAvoidFloats(LayoutUnit childMarginStart, LayoutUnit childMarginEnd, const RenderBlock* cb, RenderRegion* region, LayoutUnit offsetFromLogicalTopOfFirstPage) const +LayoutUnit RenderBox::shrinkLogicalWidthToAvoidFloats(LayoutUnit childMarginStart, LayoutUnit childMarginEnd, const RenderBlock* cb, RenderRegion* region) const { RenderRegion* containingBlockRegion = 0; LayoutUnit logicalTopPosition = logicalTop(); - LayoutUnit adjustedPageOffsetForContainingBlock = offsetFromLogicalTopOfFirstPage - logicalTop(); if (region) { - LayoutUnit offsetFromLogicalTopOfRegion = region ? region->logicalTopForFlowThreadContent() - offsetFromLogicalTopOfFirstPage : LayoutUnit(); + LayoutUnit offsetFromLogicalTopOfRegion = region ? region->logicalTopForFlowThreadContent() - offsetFromLogicalTopOfFirstPage() : LayoutUnit(); logicalTopPosition = max(logicalTopPosition, logicalTopPosition + offsetFromLogicalTopOfRegion); containingBlockRegion = cb->clampToStartAndEndRegions(region); } - LayoutUnit result = cb->availableLogicalWidthForLine(logicalTopPosition, false, containingBlockRegion, adjustedPageOffsetForContainingBlock) - childMarginStart - childMarginEnd; + LayoutUnit result = cb->availableLogicalWidthForLine(logicalTopPosition, false, containingBlockRegion) - childMarginStart - childMarginEnd; // We need to see if margins on either the start side or the end side can contain the floats in question. If they can, // then just using the line width is inaccurate. In the case where a float completely fits, we don't need to use the line @@ -1277,9 +1645,9 @@ LayoutUnit RenderBox::shrinkLogicalWidthToAvoidFloats(LayoutUnit childMarginStar // doesn't fit, we can use the line offset, but we need to grow it by the margin to reflect the fact that the margin was // "consumed" by the float. Negative margins aren't consumed by the float, and so we ignore them. if (childMarginStart > 0) { - LayoutUnit startContentSide = cb->startOffsetForContent(containingBlockRegion, adjustedPageOffsetForContainingBlock); + LayoutUnit startContentSide = cb->startOffsetForContent(containingBlockRegion); LayoutUnit startContentSideWithMargin = startContentSide + childMarginStart; - LayoutUnit startOffset = cb->startOffsetForLine(logicalTopPosition, false, containingBlockRegion, adjustedPageOffsetForContainingBlock); + LayoutUnit startOffset = cb->startOffsetForLine(logicalTopPosition, false, containingBlockRegion); if (startOffset > startContentSideWithMargin) result += childMarginStart; else @@ -1287,9 +1655,9 @@ LayoutUnit RenderBox::shrinkLogicalWidthToAvoidFloats(LayoutUnit childMarginStar } if (childMarginEnd > 0) { - LayoutUnit endContentSide = cb->endOffsetForContent(containingBlockRegion, adjustedPageOffsetForContainingBlock); + LayoutUnit endContentSide = cb->endOffsetForContent(containingBlockRegion); LayoutUnit endContentSideWithMargin = endContentSide + childMarginEnd; - LayoutUnit endOffset = cb->endOffsetForLine(logicalTopPosition, false, containingBlockRegion, adjustedPageOffsetForContainingBlock); + LayoutUnit endOffset = cb->endOffsetForLine(logicalTopPosition, false, containingBlockRegion); if (endOffset > endContentSideWithMargin) result += childMarginEnd; else @@ -1308,16 +1676,16 @@ LayoutUnit RenderBox::containingBlockLogicalWidthForContent() const return cb->availableLogicalWidth(); } -LayoutUnit RenderBox::containingBlockLogicalHeightForContent() const +LayoutUnit RenderBox::containingBlockLogicalHeightForContent(AvailableLogicalHeightType heightType) const { if (hasOverrideContainingBlockLogicalHeight()) return overrideContainingBlockContentLogicalHeight(); RenderBlock* cb = containingBlock(); - return cb->availableLogicalHeight(); + return cb->availableLogicalHeight(heightType); } -LayoutUnit RenderBox::containingBlockLogicalWidthForContentInRegion(RenderRegion* region, LayoutUnit offsetFromLogicalTopOfFirstPage) const +LayoutUnit RenderBox::containingBlockLogicalWidthForContentInRegion(RenderRegion* region) const { if (!region) return containingBlockLogicalWidthForContent(); @@ -1327,24 +1695,23 @@ LayoutUnit RenderBox::containingBlockLogicalWidthForContentInRegion(RenderRegion // FIXME: It's unclear if a region's content should use the containing block's override logical width. // If it should, the following line should call containingBlockLogicalWidthForContent. LayoutUnit result = cb->availableLogicalWidth(); - RenderBoxRegionInfo* boxInfo = cb->renderBoxRegionInfo(containingBlockRegion, offsetFromLogicalTopOfFirstPage - logicalTop()); + RenderBoxRegionInfo* boxInfo = cb->renderBoxRegionInfo(containingBlockRegion); if (!boxInfo) return result; return max<LayoutUnit>(0, result - (cb->logicalWidth() - boxInfo->logicalWidth())); } -LayoutUnit RenderBox::containingBlockAvailableLineWidthInRegion(RenderRegion* region, LayoutUnit offsetFromLogicalTopOfFirstPage) const +LayoutUnit RenderBox::containingBlockAvailableLineWidthInRegion(RenderRegion* region) const { RenderBlock* cb = containingBlock(); RenderRegion* containingBlockRegion = 0; LayoutUnit logicalTopPosition = logicalTop(); - LayoutUnit adjustedPageOffsetForContainingBlock = offsetFromLogicalTopOfFirstPage - logicalTop(); if (region) { - LayoutUnit offsetFromLogicalTopOfRegion = region ? region->logicalTopForFlowThreadContent() - offsetFromLogicalTopOfFirstPage : LayoutUnit(); + LayoutUnit offsetFromLogicalTopOfRegion = region ? region->logicalTopForFlowThreadContent() - offsetFromLogicalTopOfFirstPage() : LayoutUnit(); logicalTopPosition = max(logicalTopPosition, logicalTopPosition + offsetFromLogicalTopOfRegion); containingBlockRegion = cb->clampToStartAndEndRegions(region); } - return cb->availableLogicalWidthForLine(logicalTopPosition, false, containingBlockRegion, adjustedPageOffsetForContainingBlock, availableLogicalHeight()); + return cb->availableLogicalWidthForLine(logicalTopPosition, false, containingBlockRegion, availableLogicalHeight(IncludeMarginBorderPadding)); } LayoutUnit RenderBox::perpendicularContainingBlockLogicalHeight() const @@ -1358,15 +1725,14 @@ LayoutUnit RenderBox::perpendicularContainingBlockLogicalHeight() const RenderStyle* containingBlockStyle = cb->style(); Length logicalHeightLength = containingBlockStyle->logicalHeight(); - + // FIXME: For now just support fixed heights. Eventually should support percentage heights as well. if (!logicalHeightLength.isFixed()) { - // Rather than making the child be completely unconstrained, WinIE uses the viewport width and height - // as a constraint. We do that for now as well even though it's likely being unconstrained is what the spec - // will decide. - return containingBlockStyle->isHorizontalWritingMode() ? view()->frameView()->visibleHeight() : view()->frameView()->visibleWidth(); + LayoutUnit fillFallbackExtent = containingBlockStyle->isHorizontalWritingMode() ? view()->frameView()->visibleHeight() : view()->frameView()->visibleWidth(); + LayoutUnit fillAvailableExtent = containingBlock()->availableLogicalHeight(ExcludeMarginBorderPadding); + return min(fillAvailableExtent, fillFallbackExtent); } - + // Use the content box logical height as specified by the style. return cb->adjustContentBoxLogicalHeightForBoxSizing(logicalHeightLength.value()); } @@ -1405,8 +1771,6 @@ void RenderBox::mapLocalToContainer(const RenderLayerModelObject* repaintContain *wasFixed = mode & IsFixed; LayoutSize containerOffset = offsetFromContainer(o, roundedLayoutPoint(transformState.mappedPoint())); - if (mode & SnapOffsetForTransforms) - containerOffset = roundedIntSize(containerOffset); bool preserve3D = mode & UseTransforms && (o->style()->preserves3D() || style()->preserves3D()); if (mode & UseTransforms && shouldUseTransformFromContainer(o)) { @@ -1425,13 +1789,6 @@ void RenderBox::mapLocalToContainer(const RenderLayerModelObject* repaintContain } mode &= ~ApplyContainerFlip; - if (o->isRenderFlowThread()) { - // Transform from render flow coordinates into region coordinates. - RenderRegion* region = toRenderFlowThread(o)->mapFromFlowToRegion(transformState); - if (region) - region->mapLocalToContainer(region->containerForRepaint(), transformState, mode, wasFixed); - return; - } o->mapLocalToContainer(repaintContainer, transformState, mode, wasFixed); } @@ -1457,12 +1814,7 @@ const RenderObject* RenderBox::pushMappingToContainer(const RenderLayerModelObje bool offsetDependsOnPoint = false; LayoutSize containerOffset = offsetFromContainer(container, LayoutPoint(), &offsetDependsOnPoint); - if (geometryMap.mapCoordinatesFlags() & SnapOffsetForTransforms) - containerOffset = roundedIntSize(containerOffset); - if (container->isRenderFlowThread()) - offsetDependsOnPoint = true; - bool preserve3D = container->style()->preserves3D() || style()->preserves3D(); if (shouldUseTransformFromContainer(container)) { TransformationMatrix t; @@ -1494,7 +1846,8 @@ void RenderBox::mapAbsoluteToLocalPoint(MapCoordinatesFlags mode, TransformState LayoutSize RenderBox::offsetFromContainer(RenderObject* o, const LayoutPoint& point, bool* offsetDependsOnPoint) const { - ASSERT(o == container()); + // A region "has" boxes inside it without being their container. + ASSERT(o == container() || o->isRenderRegion()); LayoutSize offset; if (isInFlowPositioned()) @@ -1523,6 +1876,9 @@ LayoutSize RenderBox::offsetFromContainer(RenderObject* o, const LayoutPoint& po if (style()->position() == AbsolutePosition && o->isInFlowPositioned() && o->isRenderInline()) offset += toRenderInline(o)->offsetForInFlowPositionedInline(this); + if (offsetDependsOnPoint) + *offsetDependsOnPoint |= o->isRenderFlowThread(); + return offset; } @@ -1680,7 +2036,7 @@ void RenderBox::computeRectForRepaint(const RenderLayerModelObject* repaintConta if (position == AbsolutePosition && o->isInFlowPositioned() && o->isRenderInline()) topLeft += toRenderInline(o)->offsetForInFlowPositionedInline(this); - else if ((position == RelativePosition || position == StickyPosition) && layer()) { + else if (styleToUse->hasInFlowPosition() && layer()) { // Apply the relative position offset when invalidating a rectangle. The layer // is translated, but the render box isn't, so we need to do this to get the // right dirty rect. Since this is called from RenderObject::setStyle, the relative position @@ -1730,6 +2086,10 @@ void RenderBox::repaintDuringLayoutIfMoved(const LayoutRect& oldRect) } } +void RenderBox::repaintOverhangingFloats(bool) +{ +} + void RenderBox::updateLogicalWidth() { LogicalExtentComputedValues computedValues; @@ -1741,7 +2101,7 @@ void RenderBox::updateLogicalWidth() setMarginEnd(computedValues.m_margins.m_end); } -void RenderBox::computeLogicalWidthInRegion(LogicalExtentComputedValues& computedValues, RenderRegion* region, LayoutUnit offsetFromLogicalTopOfFirstPage) const +void RenderBox::computeLogicalWidthInRegion(LogicalExtentComputedValues& computedValues, RenderRegion* region) const { computedValues.m_extent = logicalWidth(); computedValues.m_position = logicalLeft(); @@ -1751,7 +2111,7 @@ void RenderBox::computeLogicalWidthInRegion(LogicalExtentComputedValues& compute if (isOutOfFlowPositioned()) { // FIXME: This calculation is not patched for block-flow yet. // https://bugs.webkit.org/show_bug.cgi?id=46500 - computePositionedLogicalWidth(computedValues, region, offsetFromLogicalTopOfFirstPage); + computePositionedLogicalWidth(computedValues, region); return; } @@ -1763,7 +2123,7 @@ void RenderBox::computeLogicalWidthInRegion(LogicalExtentComputedValues& compute // width. Use the width from the style context. // FIXME: Account for block-flow in flexible boxes. // https://bugs.webkit.org/show_bug.cgi?id=46418 - if (hasOverrideWidth() && parent()->isFlexibleBoxIncludingDeprecated()) { + if (hasOverrideWidth() && (style()->borderFit() == BorderFitLines || parent()->isFlexibleBoxIncludingDeprecated())) { computedValues.m_extent = overrideLogicalContentWidth() + borderAndPaddingLogicalWidth(); return; } @@ -1778,11 +2138,8 @@ void RenderBox::computeLogicalWidthInRegion(LogicalExtentComputedValues& compute Length logicalWidthLength = treatAsReplaced ? Length(computeReplacedLogicalWidth(), Fixed) : styleToUse->logicalWidth(); RenderBlock* cb = containingBlock(); - LayoutUnit containerLogicalWidth = max<LayoutUnit>(0, containingBlockLogicalWidthForContentInRegion(region, offsetFromLogicalTopOfFirstPage)); + LayoutUnit containerLogicalWidth = max<LayoutUnit>(0, containingBlockLogicalWidthForContentInRegion(region)); bool hasPerpendicularContainingBlock = cb->isHorizontalWritingMode() != isHorizontalWritingMode(); - LayoutUnit containerWidthInInlineDirection = containerLogicalWidth; - if (hasPerpendicularContainingBlock) - containerWidthInInlineDirection = perpendicularContainingBlockLogicalHeight(); if (isInline() && !isInlineBlockOrInlineTable()) { // just calculate margins @@ -1798,14 +2155,13 @@ void RenderBox::computeLogicalWidthInRegion(LogicalExtentComputedValues& compute if (treatAsReplaced) computedValues.m_extent = logicalWidthLength.value() + borderAndPaddingLogicalWidth(); else { - LayoutUnit preferredWidth = computeLogicalWidthInRegionUsing(MainOrPreferredSize, containerWidthInInlineDirection, cb, region, offsetFromLogicalTopOfFirstPage); - computedValues.m_extent = constrainLogicalWidthInRegionByMinMax(preferredWidth, containerWidthInInlineDirection, cb, region, offsetFromLogicalTopOfFirstPage); + LayoutUnit containerWidthInInlineDirection = containerLogicalWidth; + if (hasPerpendicularContainingBlock) + containerWidthInInlineDirection = perpendicularContainingBlockLogicalHeight(); + LayoutUnit preferredWidth = computeLogicalWidthInRegionUsing(MainOrPreferredSize, styleToUse->logicalWidth(), containerWidthInInlineDirection, cb, region); + computedValues.m_extent = constrainLogicalWidthInRegionByMinMax(preferredWidth, containerWidthInInlineDirection, cb, region); } - // Fieldsets are currently the only objects that stretch to their minimum width. - if (stretchesToMinIntrinsicLogicalWidth()) - computedValues.m_extent = max(computedValues.m_extent, minPreferredLogicalWidth()); - // Margin calculations. if (hasPerpendicularContainingBlock || isFloating() || isInline()) { RenderView* renderView = view(); @@ -1814,7 +2170,7 @@ void RenderBox::computeLogicalWidthInRegion(LogicalExtentComputedValues& compute } else { LayoutUnit containerLogicalWidthForAutoMargins = containerLogicalWidth; if (avoidsFloats() && cb->containsFloats()) - containerLogicalWidthForAutoMargins = containingBlockAvailableLineWidthInRegion(region, offsetFromLogicalTopOfFirstPage); + containerLogicalWidthForAutoMargins = containingBlockAvailableLineWidthInRegion(region); bool hasInvertedDirection = cb->style()->isLeftToRightDirection() != style()->isLeftToRightDirection(); computeInlineDirectionMargins(cb, containerLogicalWidthForAutoMargins, computedValues.m_extent, hasInvertedDirection ? computedValues.m_margins.m_end : computedValues.m_margins.m_start, @@ -1822,7 +2178,7 @@ void RenderBox::computeLogicalWidthInRegion(LogicalExtentComputedValues& compute } if (!hasPerpendicularContainingBlock && containerLogicalWidth && containerLogicalWidth != (computedValues.m_extent + computedValues.m_margins.m_start + computedValues.m_margins.m_end) - && !isFloating() && !isInline() && !cb->isFlexibleBoxIncludingDeprecated()) { + && !isFloating() && !isInline() && !cb->isFlexibleBoxIncludingDeprecated() && !cb->isRenderGrid()) { LayoutUnit newMargin = containerLogicalWidth - computedValues.m_extent - cb->marginStartForChild(this); bool hasInvertedDirection = cb->style()->isLeftToRightDirection() != style()->isLeftToRightDirection(); if (hasInvertedDirection) @@ -1832,44 +2188,65 @@ void RenderBox::computeLogicalWidthInRegion(LogicalExtentComputedValues& compute } } -LayoutUnit RenderBox::computeLogicalWidthInRegionUsing(SizeType widthType, LayoutUnit availableLogicalWidth, - const RenderBlock* cb, RenderRegion* region, LayoutUnit offsetFromLogicalTopOfFirstPage) const +LayoutUnit RenderBox::fillAvailableMeasure(LayoutUnit availableLogicalWidth) const { - RenderStyle* styleToUse = style(); - Length logicalWidth; - if (widthType == MainOrPreferredSize) - logicalWidth = styleToUse->logicalWidth(); - else if (widthType == MinSize) - logicalWidth = styleToUse->logicalMinWidth(); - else - logicalWidth = styleToUse->logicalMaxWidth(); + LayoutUnit marginStart = 0; + LayoutUnit marginEnd = 0; + return fillAvailableMeasure(availableLogicalWidth, marginStart, marginEnd); +} - ASSERT(!logicalWidth.isUndefined()); +LayoutUnit RenderBox::fillAvailableMeasure(LayoutUnit availableLogicalWidth, LayoutUnit& marginStart, LayoutUnit& marginEnd) const +{ + RenderView* renderView = view(); + marginStart = minimumValueForLength(style()->marginStart(), availableLogicalWidth, renderView); + marginEnd = minimumValueForLength(style()->marginEnd(), availableLogicalWidth, renderView); + return availableLogicalWidth - marginStart - marginEnd; +} - if (widthType == MinSize && logicalWidth.isAuto()) - return adjustBorderBoxLogicalWidthForBoxSizing(0); - +LayoutUnit RenderBox::computeIntrinsicLogicalWidthUsing(Length logicalWidthLength, LayoutUnit availableLogicalWidth, LayoutUnit borderAndPadding) const +{ + if (logicalWidthLength.type() == FillAvailable) + return fillAvailableMeasure(availableLogicalWidth); + + LayoutUnit minLogicalWidth = 0; + LayoutUnit maxLogicalWidth = 0; + computeIntrinsicLogicalWidths(minLogicalWidth, maxLogicalWidth); + + if (logicalWidthLength.type() == MinContent) + return minLogicalWidth + borderAndPadding; + + if (logicalWidthLength.type() == MaxContent) + return maxLogicalWidth + borderAndPadding; + + if (logicalWidthLength.type() == FitContent) { + minLogicalWidth += borderAndPadding; + maxLogicalWidth += borderAndPadding; + return max(minLogicalWidth, min(maxLogicalWidth, fillAvailableMeasure(availableLogicalWidth))); + } + + ASSERT_NOT_REACHED(); + return 0; +} + +LayoutUnit RenderBox::computeLogicalWidthInRegionUsing(SizeType widthType, Length logicalWidth, LayoutUnit availableLogicalWidth, + const RenderBlock* cb, RenderRegion* region) const +{ if (!logicalWidth.isIntrinsicOrAuto()) { // FIXME: If the containing block flow is perpendicular to our direction we need to use the available logical height instead. return adjustBorderBoxLogicalWidthForBoxSizing(valueForLength(logicalWidth, availableLogicalWidth, view())); } - if (logicalWidth.type() == MinContent) - return minPreferredLogicalWidth(); - if (logicalWidth.type() == MaxContent) - return maxPreferredLogicalWidth(); + if (logicalWidth.isIntrinsic()) + return computeIntrinsicLogicalWidthUsing(logicalWidth, availableLogicalWidth, borderAndPaddingLogicalWidth()); - RenderView* renderView = view(); - LayoutUnit marginStart = minimumValueForLength(styleToUse->marginStart(), availableLogicalWidth, renderView); - LayoutUnit marginEnd = minimumValueForLength(styleToUse->marginEnd(), availableLogicalWidth, renderView); - LayoutUnit logicalWidthResult = availableLogicalWidth - marginStart - marginEnd; + LayoutUnit marginStart = 0; + LayoutUnit marginEnd = 0; + LayoutUnit logicalWidthResult = fillAvailableMeasure(availableLogicalWidth, marginStart, marginEnd); - // shrinkToAvoidFloats() is only true for width: auto so the below code works correctly for - // width: fill-available since no case matches and it returns the logicalWidthResult from above. if (shrinkToAvoidFloats() && cb->containsFloats()) - logicalWidthResult = min(logicalWidthResult, shrinkLogicalWidthToAvoidFloats(marginStart, marginEnd, cb, region, offsetFromLogicalTopOfFirstPage)); + logicalWidthResult = min(logicalWidthResult, shrinkLogicalWidthToAvoidFloats(marginStart, marginEnd, cb, region)); - if (logicalWidth.type() == FitContent || (logicalWidth.type() != FillAvailable && sizesLogicalWidthToFitContent(widthType))) + if (widthType == MainOrPreferredSize && sizesLogicalWidthToFitContent(widthType)) return max(minPreferredLogicalWidth(), min(maxPreferredLogicalWidth(), logicalWidthResult)); return logicalWidthResult; } @@ -1938,7 +2315,7 @@ bool RenderBox::sizesLogicalWidthToFitContent(SizeType widthType) const // stretching column flexbox. // FIXME: Think about block-flow here. // https://bugs.webkit.org/show_bug.cgi?id=46473 - if (logicalWidth.type() == Auto && !isStretchingColumnFlexItem(this) && node() && (node()->hasTagName(inputTag) || node()->hasTagName(selectTag) || node()->hasTagName(buttonTag) || node()->hasTagName(textareaTag) || node()->hasTagName(legendTag))) + if (logicalWidth.type() == Auto && !isStretchingColumnFlexItem(this) && node() && (isHTMLInputElement(node()) || node()->hasTagName(selectTag) || node()->hasTagName(buttonTag) || isHTMLTextAreaElement(node()) || node()->hasTagName(legendTag))) return true; if (isHorizontalWritingMode() != containingBlock()->isHorizontalWritingMode()) @@ -1995,7 +2372,7 @@ void RenderBox::computeInlineDirectionMargins(RenderBlock* containingBlock, Layo marginEnd = minimumValueForLength(marginEndLength, containerWidth, renderView); } -RenderBoxRegionInfo* RenderBox::renderBoxRegionInfo(RenderRegion* region, LayoutUnit offsetFromLogicalTopOfFirstPage, RenderBoxRegionInfoFlags cacheFlag) const +RenderBoxRegionInfo* RenderBox::renderBoxRegionInfo(RenderRegion* region, RenderBoxRegionInfoFlags cacheFlag) const { // Make sure nobody is trying to call this with a null region. if (!region) @@ -2010,22 +2387,17 @@ RenderBoxRegionInfo* RenderBox::renderBoxRegionInfo(RenderRegion* region, Layout // No cached value was found, so we have to compute our insets in this region. // FIXME: For now we limit this computation to normal RenderBlocks. Future patches will expand // support to cover all boxes. - if (!inRenderFlowThread() || isFloating() || isReplaced() || isInline() || hasColumns() - || isTableCell() || !isBlockFlow() || isRenderFlowThread()) - return 0; - - RenderFlowThread* flowThread = enclosingRenderFlowThread(); - if (flowThread->style()->writingMode() != style()->writingMode()) + RenderFlowThread* flowThread = flowThreadContainingBlock(); + if (isRenderFlowThread() || !flowThread || !canHaveBoxInfoInRegion() || flowThread->style()->writingMode() != style()->writingMode()) return 0; LogicalExtentComputedValues computedValues; - computeLogicalWidthInRegion(computedValues, region, offsetFromLogicalTopOfFirstPage); + computeLogicalWidthInRegion(computedValues, region); // Now determine the insets based off where this object is supposed to be positioned. RenderBlock* cb = containingBlock(); RenderRegion* clampedContainingBlockRegion = cb->clampToStartAndEndRegions(region); - RenderBoxRegionInfo* containingBlockInfo = cb->renderBoxRegionInfo(clampedContainingBlockRegion, - offsetFromLogicalTopOfFirstPage - logicalTop()); + RenderBoxRegionInfo* containingBlockInfo = cb->renderBoxRegionInfo(clampedContainingBlockRegion); LayoutUnit containingBlockLogicalWidth = cb->logicalWidth(); LayoutUnit containingBlockLogicalWidthInRegion = containingBlockInfo ? containingBlockInfo->logicalWidth() : containingBlockLogicalWidth; @@ -2042,7 +2414,7 @@ RenderBoxRegionInfo* RenderBox::renderBoxRegionInfo(RenderRegion* region, Layout LayoutUnit logicalLeftOffset = 0; if (!isOutOfFlowPositioned() && avoidsFloats() && cb->containsFloats()) { - LayoutUnit startPositionDelta = cb->computeStartPositionDeltaForChildAvoidingFloats(this, marginStartInRegion, region, offsetFromLogicalTopOfFirstPage); + LayoutUnit startPositionDelta = cb->computeStartPositionDeltaForChildAvoidingFloats(this, marginStartInRegion, region); if (cb->style()->isLeftToRightDirection()) logicalLeftDelta += startPositionDelta; else @@ -2167,7 +2539,7 @@ void RenderBox::computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logica LayoutUnit heightResult; if (checkMinMaxHeight) { - heightResult = computeLogicalHeightUsing(MainOrPreferredSize, style()->logicalHeight()); + heightResult = computeLogicalHeightUsing(style()->logicalHeight()); if (heightResult == -1) heightResult = computedValues.m_extent; heightResult = constrainLogicalHeightByMinMax(heightResult); @@ -2194,49 +2566,37 @@ void RenderBox::computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logica // height since we don't set a height in RenderView when we're printing. So without this quirk, the // height has nothing to be a percentage of, and it ends up being 0. That is bad. bool paginatedContentNeedsBaseHeight = document()->printing() && h.isPercent() - && (isRoot() || (isBody() && document()->documentElement()->renderer()->style()->logicalHeight().isPercent())); + && (isRoot() || (isBody() && document()->documentElement()->renderer()->style()->logicalHeight().isPercent())) && !isInline(); if (stretchesToViewport() || paginatedContentNeedsBaseHeight) { - // FIXME: Finish accounting for block flow here. - // https://bugs.webkit.org/show_bug.cgi?id=46603 LayoutUnit margins = collapsedMarginBefore() + collapsedMarginAfter(); - LayoutUnit visHeight; - if (document()->printing()) - visHeight = static_cast<LayoutUnit>(view()->pageLogicalHeight()); - else { - if (isHorizontalWritingMode()) - visHeight = view()->viewHeight(); - else - visHeight = view()->viewWidth(); - } + LayoutUnit visibleHeight = view()->pageOrViewLogicalHeight(); if (isRoot()) - computedValues.m_extent = max(computedValues.m_extent, visHeight - margins); + computedValues.m_extent = max(computedValues.m_extent, visibleHeight - margins); else { LayoutUnit marginsBordersPadding = margins + parentBox()->marginBefore() + parentBox()->marginAfter() + parentBox()->borderAndPaddingLogicalHeight(); - computedValues.m_extent = max(computedValues.m_extent, visHeight - marginsBordersPadding); + computedValues.m_extent = max(computedValues.m_extent, visibleHeight - marginsBordersPadding); } } } -LayoutUnit RenderBox::computeLogicalHeightUsing(SizeType heightType, const Length& height) const +LayoutUnit RenderBox::computeLogicalHeightUsing(const Length& height) const { - LayoutUnit logicalHeight = computeContentAndScrollbarLogicalHeightUsing(heightType, height); + LayoutUnit logicalHeight = computeContentAndScrollbarLogicalHeightUsing(height); if (logicalHeight != -1) logicalHeight = adjustBorderBoxLogicalHeightForBoxSizing(logicalHeight); return logicalHeight; } -LayoutUnit RenderBox::computeContentLogicalHeight(SizeType heightType, const Length& height) +LayoutUnit RenderBox::computeContentLogicalHeight(const Length& height) const { - LayoutUnit heightIncludingScrollbar = computeContentAndScrollbarLogicalHeightUsing(heightType, height); + LayoutUnit heightIncludingScrollbar = computeContentAndScrollbarLogicalHeightUsing(height); if (heightIncludingScrollbar == -1) return -1; return std::max<LayoutUnit>(0, adjustContentBoxLogicalHeightForBoxSizing(heightIncludingScrollbar) - scrollbarLogicalHeight()); } -LayoutUnit RenderBox::computeContentAndScrollbarLogicalHeightUsing(SizeType heightType, const Length& height) const +LayoutUnit RenderBox::computeContentAndScrollbarLogicalHeightUsing(const Length& height) const { - if (height.isAuto()) - return heightType == MinSize ? 0 : -1; if (height.isFixed()) return height.value(); if (height.isPercent()) @@ -2276,9 +2636,7 @@ LayoutUnit RenderBox::computePercentageLogicalHeight(const Length& height) const // A positioned element that specified both top/bottom or that specifies height should be treated as though it has a height // explicitly specified that can be used for any percentage computations. - // FIXME: We can't just check top/bottom here. - // https://bugs.webkit.org/show_bug.cgi?id=46500 - bool isOutOfFlowPositionedWithSpecifiedHeight = cb->isOutOfFlowPositioned() && (!cbstyle->logicalHeight().isAuto() || (!cbstyle->top().isAuto() && !cbstyle->bottom().isAuto())); + bool isOutOfFlowPositionedWithSpecifiedHeight = cb->isOutOfFlowPositioned() && (!cbstyle->logicalHeight().isAuto() || (!cbstyle->logicalTop().isAuto() && !cbstyle->logicalBottom().isAuto())); bool includeBorderPadding = isTable(); @@ -2308,22 +2666,28 @@ LayoutUnit RenderBox::computePercentageLogicalHeight(const Length& height) const includeBorderPadding = true; } } else if (cbstyle->logicalHeight().isFixed()) { - LayoutUnit contentBoxHeightWithScrollbar = cb->adjustContentBoxLogicalHeightForBoxSizing(cbstyle->logicalHeight().value()); - availableHeight = max<LayoutUnit>(0, contentBoxHeightWithScrollbar - cb->scrollbarLogicalHeight()); + LayoutUnit contentBoxHeight = cb->adjustContentBoxLogicalHeightForBoxSizing(cbstyle->logicalHeight().value()); + availableHeight = max<LayoutUnit>(0, cb->constrainContentBoxLogicalHeightByMinMax(contentBoxHeight - cb->scrollbarLogicalHeight())); } else if (cbstyle->logicalHeight().isPercent() && !isOutOfFlowPositionedWithSpecifiedHeight) { // We need to recur and compute the percentage height for our containing block. LayoutUnit heightWithScrollbar = cb->computePercentageLogicalHeight(cbstyle->logicalHeight()); if (heightWithScrollbar != -1) { LayoutUnit contentBoxHeightWithScrollbar = cb->adjustContentBoxLogicalHeightForBoxSizing(heightWithScrollbar); - availableHeight = max<LayoutUnit>(0, contentBoxHeightWithScrollbar - cb->scrollbarLogicalHeight()); + // We need to adjust for min/max height because this method does not + // handle the min/max of the current block, its caller does. So the + // return value from the recursive call will not have been adjusted + // yet. + LayoutUnit contentBoxHeight = cb->constrainContentBoxLogicalHeightByMinMax(contentBoxHeightWithScrollbar - cb->scrollbarLogicalHeight()); + availableHeight = max<LayoutUnit>(0, contentBoxHeight); } - } else if (cb->isRenderView() || isOutOfFlowPositionedWithSpecifiedHeight) { + } else if (isOutOfFlowPositionedWithSpecifiedHeight) { // Don't allow this to affect the block' height() member variable, since this // can get called while the block is still laying out its kids. LogicalExtentComputedValues computedValues; cb->computeLogicalHeight(cb->logicalHeight(), 0, computedValues); availableHeight = computedValues.m_extent - cb->borderAndPaddingLogicalHeight() - cb->scrollbarLogicalHeight(); - } + } else if (cb->isRenderView()) + availableHeight = view()->pageOrViewLogicalHeight(); if (availableHeight == -1) return availableHeight; @@ -2342,30 +2706,36 @@ LayoutUnit RenderBox::computePercentageLogicalHeight(const Length& height) const return result; } -LayoutUnit RenderBox::computeReplacedLogicalWidth(bool includeMaxWidth) const +LayoutUnit RenderBox::computeReplacedLogicalWidth(ShouldComputePreferred shouldComputePreferred) const { - return computeReplacedLogicalWidthRespectingMinMaxWidth(computeReplacedLogicalWidthUsing(MainOrPreferredSize, style()->logicalWidth()), includeMaxWidth); + return computeReplacedLogicalWidthRespectingMinMaxWidth(computeReplacedLogicalWidthUsing(style()->logicalWidth()), shouldComputePreferred); } -LayoutUnit RenderBox::computeReplacedLogicalWidthRespectingMinMaxWidth(LayoutUnit logicalWidth, bool includeMaxWidth) const +LayoutUnit RenderBox::computeReplacedLogicalWidthRespectingMinMaxWidth(LayoutUnit logicalWidth, ShouldComputePreferred shouldComputePreferred) const { - LayoutUnit minLogicalWidth = computeReplacedLogicalWidthUsing(MinSize, style()->logicalMinWidth()); - LayoutUnit maxLogicalWidth = !includeMaxWidth || style()->logicalMaxWidth().isUndefined() ? logicalWidth : computeReplacedLogicalWidthUsing(MaxSize, style()->logicalMaxWidth()); + LayoutUnit minLogicalWidth = (shouldComputePreferred == ComputePreferred && style()->logicalMinWidth().isPercent()) || style()->logicalMinWidth().isUndefined() ? logicalWidth : computeReplacedLogicalWidthUsing(style()->logicalMinWidth()); + LayoutUnit maxLogicalWidth = (shouldComputePreferred == ComputePreferred && style()->logicalMaxWidth().isPercent()) || style()->logicalMaxWidth().isUndefined() ? logicalWidth : computeReplacedLogicalWidthUsing(style()->logicalMaxWidth()); return max(minLogicalWidth, min(logicalWidth, maxLogicalWidth)); } -LayoutUnit RenderBox::computeReplacedLogicalWidthUsing(SizeType sizeType, Length logicalWidth) const +LayoutUnit RenderBox::computeReplacedLogicalWidthUsing(Length logicalWidth) const { - if (sizeType == MinSize && logicalWidth.isAuto()) - return adjustContentBoxLogicalWidthForBoxSizing(0); - switch (logicalWidth.type()) { case Fixed: return adjustContentBoxLogicalWidthForBoxSizing(logicalWidth.value()); + case MinContent: + case MaxContent: { + // MinContent/MaxContent don't need the availableLogicalWidth argument. + LayoutUnit availableLogicalWidth = 0; + return computeIntrinsicLogicalWidthUsing(logicalWidth, availableLogicalWidth, borderAndPaddingLogicalWidth()) - borderAndPaddingLogicalWidth(); + } case ViewportPercentageWidth: case ViewportPercentageHeight: case ViewportPercentageMin: + case ViewportPercentageMax: return adjustContentBoxLogicalWidthForBoxSizing(valueForLength(logicalWidth, 0, view())); + case FitContent: + case FillAvailable: case Percent: case Calculated: { // FIXME: containingBlockLogicalWidthForContent() is wrong if the replaced element's block-flow is perpendicular to the @@ -2375,32 +2745,38 @@ LayoutUnit RenderBox::computeReplacedLogicalWidthUsing(SizeType sizeType, Length Length containerLogicalWidth = containingBlock()->style()->logicalWidth(); // FIXME: Handle cases when containing block width is calculated or viewport percent. // https://bugs.webkit.org/show_bug.cgi?id=91071 + if (logicalWidth.isIntrinsic()) + return computeIntrinsicLogicalWidthUsing(logicalWidth, cw, borderAndPaddingLogicalWidth()) - borderAndPaddingLogicalWidth(); if (cw > 0 || (!cw && (containerLogicalWidth.isFixed() || containerLogicalWidth.isPercent()))) return adjustContentBoxLogicalWidthForBoxSizing(minimumValueForLength(logicalWidth, cw)); } // fall through - default: + case Intrinsic: + case MinIntrinsic: + case Auto: + case Relative: + case Undefined: return intrinsicLogicalWidth(); - } + } + + ASSERT_NOT_REACHED(); + return 0; } LayoutUnit RenderBox::computeReplacedLogicalHeight() const { - return computeReplacedLogicalHeightRespectingMinMaxHeight(computeReplacedLogicalHeightUsing(MainOrPreferredSize, style()->logicalHeight())); + return computeReplacedLogicalHeightRespectingMinMaxHeight(computeReplacedLogicalHeightUsing(style()->logicalHeight())); } LayoutUnit RenderBox::computeReplacedLogicalHeightRespectingMinMaxHeight(LayoutUnit logicalHeight) const { - LayoutUnit minLogicalHeight = computeReplacedLogicalHeightUsing(MinSize, style()->logicalMinHeight()); - LayoutUnit maxLogicalHeight = style()->logicalMaxHeight().isUndefined() ? logicalHeight : computeReplacedLogicalHeightUsing(MaxSize, style()->logicalMaxHeight()); + LayoutUnit minLogicalHeight = computeReplacedLogicalHeightUsing(style()->logicalMinHeight()); + LayoutUnit maxLogicalHeight = style()->logicalMaxHeight().isUndefined() ? logicalHeight : computeReplacedLogicalHeightUsing(style()->logicalMaxHeight()); return max(minLogicalHeight, min(logicalHeight, maxLogicalHeight)); } -LayoutUnit RenderBox::computeReplacedLogicalHeightUsing(SizeType sizeType, Length logicalHeight) const +LayoutUnit RenderBox::computeReplacedLogicalHeightUsing(Length logicalHeight) const { - if (sizeType == MinSize && logicalHeight.isAuto()) - return adjustContentBoxLogicalHeightForBoxSizing(0); - switch (logicalHeight.type()) { case Fixed: return adjustContentBoxLogicalHeightForBoxSizing(logicalHeight.value()); @@ -2416,7 +2792,7 @@ LayoutUnit RenderBox::computeReplacedLogicalHeightUsing(SizeType sizeType, Lengt // FIXME: This calculation is not patched for block-flow yet. // https://bugs.webkit.org/show_bug.cgi?id=46500 if (cb->isOutOfFlowPositioned() && cb->style()->height().isAuto() && !(cb->style()->top().isAuto() || cb->style()->bottom().isAuto())) { - ASSERT(cb->isRenderBlock()); + ASSERT_WITH_SECURITY_IMPLICATION(cb->isRenderBlock()); RenderBlock* block = toRenderBlock(cb); LogicalExtentComputedValues computedValues; block->computeLogicalHeight(block->logicalHeight(), 0, computedValues); @@ -2432,7 +2808,7 @@ LayoutUnit RenderBox::computeReplacedLogicalHeightUsing(SizeType sizeType, Lengt if (isOutOfFlowPositioned()) availableHeight = containingBlockLogicalHeightForPositioned(toRenderBoxModelObject(cb)); else { - availableHeight = containingBlockLogicalHeightForContent(); + availableHeight = containingBlockLogicalHeightForContent(IncludeMarginBorderPadding); // It is necessary to use the border-box to match WinIE's broken // box model. This is essential for sizing inside // table cells using percentage heights. @@ -2454,18 +2830,19 @@ LayoutUnit RenderBox::computeReplacedLogicalHeightUsing(SizeType sizeType, Lengt case ViewportPercentageWidth: case ViewportPercentageHeight: case ViewportPercentageMin: + case ViewportPercentageMax: return adjustContentBoxLogicalHeightForBoxSizing(valueForLength(logicalHeight, 0, view())); default: return intrinsicLogicalHeight(); } } -LayoutUnit RenderBox::availableLogicalHeight() const +LayoutUnit RenderBox::availableLogicalHeight(AvailableLogicalHeightType heightType) const { - return availableLogicalHeightUsing(style()->logicalHeight()); + return constrainLogicalHeightByMinMax(availableLogicalHeightUsing(style()->logicalHeight(), heightType)); } -LayoutUnit RenderBox::availableLogicalHeightUsing(const Length& h) const +LayoutUnit RenderBox::availableLogicalHeightUsing(const Length& h, AvailableLogicalHeightType heightType) const { if (isRenderView()) return isHorizontalWritingMode() ? toRenderView(this)->frameView()->visibleHeight() : toRenderView(this)->frameView()->visibleWidth(); @@ -2479,13 +2856,13 @@ LayoutUnit RenderBox::availableLogicalHeightUsing(const Length& h) const return logicalHeight() - borderAndPaddingLogicalHeight(); } - if (h.isPercent() && isOutOfFlowPositioned()) { + if (h.isPercent() && isOutOfFlowPositioned() && !isRenderFlowThread()) { // FIXME: This is wrong if the containingBlock has a perpendicular writing mode. LayoutUnit availableHeight = containingBlockLogicalHeightForPositioned(containingBlock()); return adjustContentBoxLogicalHeightForBoxSizing(valueForLength(h, availableHeight)); } - LayoutUnit heightIncludingScrollbar = computeContentAndScrollbarLogicalHeightUsing(MainOrPreferredSize, h); + LayoutUnit heightIncludingScrollbar = computeContentAndScrollbarLogicalHeightUsing(h); if (heightIncludingScrollbar != -1) return std::max<LayoutUnit>(0, adjustContentBoxLogicalHeightForBoxSizing(heightIncludingScrollbar) - scrollbarLogicalHeight()); @@ -2500,7 +2877,12 @@ LayoutUnit RenderBox::availableLogicalHeightUsing(const Length& h) const } // FIXME: This is wrong if the containingBlock has a perpendicular writing mode. - return containingBlockLogicalHeightForContent(); + LayoutUnit availableHeight = containingBlockLogicalHeightForContent(heightType); + if (heightType == ExcludeMarginBorderPadding) { + // FIXME: Margin collapsing hasn't happened yet, so this incorrectly removes collapsed margins. + availableHeight -= marginBefore() + marginAfter() + borderAndPaddingLogicalHeight(); + } + return availableHeight; } void RenderBox::computeBlockDirectionMargins(const RenderBlock* containingBlock, LayoutUnit& marginBefore, LayoutUnit& marginAfter) const @@ -2531,8 +2913,7 @@ void RenderBox::computeAndSetBlockDirectionMargins(const RenderBlock* containing containingBlock->setMarginAfterForChild(this, marginAfter); } -LayoutUnit RenderBox::containingBlockLogicalWidthForPositioned(const RenderBoxModelObject* containingBlock, RenderRegion* region, - LayoutUnit offsetFromLogicalTopOfFirstPage, bool checkForPerpendicularWritingMode) const +LayoutUnit RenderBox::containingBlockLogicalWidthForPositioned(const RenderBoxModelObject* containingBlock, RenderRegion* region, bool checkForPerpendicularWritingMode) const { // Container for position:fixed is the frame. Frame* frame = view() ? view()->frame(): 0; @@ -2544,29 +2925,28 @@ LayoutUnit RenderBox::containingBlockLogicalWidthForPositioned(const RenderBoxMo return containingBlockLogicalHeightForPositioned(containingBlock, false); if (containingBlock->isBox()) { + RenderFlowThread* flowThread = flowThreadContainingBlock(); + if (!flowThread) + return toRenderBox(containingBlock)->clientLogicalWidth(); + const RenderBlock* cb = toRenderBlock(containingBlock); - LayoutUnit result = cb->clientLogicalWidth(); - if (inRenderFlowThread()) { - RenderBoxRegionInfo* boxInfo = 0; - if (!region) { - if (containingBlock->isRenderFlowThread() && !checkForPerpendicularWritingMode) - return toRenderFlowThread(containingBlock)->contentLogicalWidthOfFirstRegion(); - if (isWritingModeRoot()) { - LayoutUnit cbPageOffset = offsetFromLogicalTopOfFirstPage - logicalTop(); - RenderRegion* cbRegion = cb->regionAtBlockOffset(cbPageOffset); - if (cbRegion) { - cbRegion = cb->clampToStartAndEndRegions(cbRegion); - boxInfo = cb->renderBoxRegionInfo(cbRegion, cbPageOffset); - } + RenderBoxRegionInfo* boxInfo = 0; + if (!region) { + if (containingBlock->isRenderFlowThread() && !checkForPerpendicularWritingMode) + return toRenderFlowThread(containingBlock)->contentLogicalWidthOfFirstRegion(); + if (isWritingModeRoot()) { + LayoutUnit cbPageOffset = cb->offsetFromLogicalTopOfFirstPage(); + RenderRegion* cbRegion = cb->regionAtBlockOffset(cbPageOffset); + if (cbRegion) { + cbRegion = cb->clampToStartAndEndRegions(cbRegion); + boxInfo = cb->renderBoxRegionInfo(cbRegion); } - } else if (region && enclosingRenderFlowThread()->isHorizontalWritingMode() == containingBlock->isHorizontalWritingMode()) { - RenderRegion* containingBlockRegion = cb->clampToStartAndEndRegions(region); - boxInfo = cb->renderBoxRegionInfo(containingBlockRegion, offsetFromLogicalTopOfFirstPage - logicalTop()); } - if (boxInfo) - return max<LayoutUnit>(0, result - (cb->logicalWidth() - boxInfo->logicalWidth())); + } else if (region && flowThread->isHorizontalWritingMode() == containingBlock->isHorizontalWritingMode()) { + RenderRegion* containingBlockRegion = cb->clampToStartAndEndRegions(region); + boxInfo = cb->renderBoxRegionInfo(containingBlockRegion); } - return result; + return (boxInfo) ? max<LayoutUnit>(0, cb->clientLogicalWidth() - (cb->logicalWidth() - boxInfo->logicalWidth())) : cb->clientLogicalWidth(); } ASSERT(containingBlock->isRenderInline() && containingBlock->isInFlowPositioned()); @@ -2600,12 +2980,13 @@ LayoutUnit RenderBox::containingBlockLogicalHeightForPositioned(const RenderBoxM return (view()->isHorizontalWritingMode() ? frameView->visibleHeight() : frameView->visibleWidth()) / frame->frameScaleFactor(); if (checkForPerpendicularWritingMode && containingBlock->isHorizontalWritingMode() != isHorizontalWritingMode()) - return containingBlockLogicalWidthForPositioned(containingBlock, 0, 0, false); + return containingBlockLogicalWidthForPositioned(containingBlock, 0, false); if (containingBlock->isBox()) { const RenderBlock* cb = toRenderBlock(containingBlock); LayoutUnit result = cb->clientLogicalHeight(); - if (inRenderFlowThread() && containingBlock->isRenderFlowThread() && enclosingRenderFlowThread()->isHorizontalWritingMode() == containingBlock->isHorizontalWritingMode()) + RenderFlowThread* flowThread = flowThreadContainingBlock(); + if (flowThread && containingBlock->isRenderFlowThread() && flowThread->isHorizontalWritingMode() == containingBlock->isHorizontalWritingMode()) return toRenderFlowThread(containingBlock)->contentLogicalHeightOfFirstRegion(); return result; } @@ -2644,7 +3025,7 @@ static void computeInlineStaticDistance(Length& logicalLeft, Length& logicalRigh if (region && curr->isRenderBlock()) { const RenderBlock* cb = toRenderBlock(curr); region = cb->clampToStartAndEndRegions(region); - RenderBoxRegionInfo* boxInfo = cb->renderBoxRegionInfo(region, region->logicalTopForFlowThreadContent()); + RenderBoxRegionInfo* boxInfo = cb->renderBoxRegionInfo(region); if (boxInfo) staticPosition += boxInfo->logicalLeft(); } @@ -2661,10 +3042,10 @@ static void computeInlineStaticDistance(Length& logicalLeft, Length& logicalRigh if (curr == enclosingBox) staticPosition -= enclosingBox->logicalWidth(); if (region && curr->isRenderBlock()) { - const RenderBlock* cb = toRenderBlock(curr); - region = cb->clampToStartAndEndRegions(region); - RenderBoxRegionInfo* boxInfo = cb->renderBoxRegionInfo(region, region->logicalTopForFlowThreadContent()); - if (boxInfo) { + const RenderBlock* cb = toRenderBlock(curr); + region = cb->clampToStartAndEndRegions(region); + RenderBoxRegionInfo* boxInfo = cb->renderBoxRegionInfo(region); + if (boxInfo) { if (curr != containerBlock) staticPosition -= cb->logicalWidth() - (boxInfo->logicalLeft() + boxInfo->logicalWidth()); if (curr == enclosingBox) @@ -2679,17 +3060,13 @@ static void computeInlineStaticDistance(Length& logicalLeft, Length& logicalRigh } } -void RenderBox::computePositionedLogicalWidth(LogicalExtentComputedValues& computedValues, RenderRegion* region, LayoutUnit offsetFromLogicalTopOfFirstPage) const +void RenderBox::computePositionedLogicalWidth(LogicalExtentComputedValues& computedValues, RenderRegion* region) const { if (isReplaced()) { - // FIXME: For regions with width auto, we want to compute width using the normal block sizing code. - // For now, regions are replaced elements and this code can be removed once the RenderRegion - // will inherit from RenderBlock instead of RenderReplaced. - // (see https://bugs.webkit.org/show_bug.cgi?id=74132 ) - if (!isRenderRegion() || (isRenderRegion() && shouldComputeSizeAsReplaced())) { - computePositionedLogicalWidthReplaced(computedValues); // FIXME: Patch for regions when we add replaced element support. - return; - } + // FIXME: Positioned replaced elements inside a flow thread are not working properly + // with variable width regions (see https://bugs.webkit.org/show_bug.cgi?id=69896 ). + computePositionedLogicalWidthReplaced(computedValues); + return; } // QUESTIONS @@ -2712,7 +3089,7 @@ void RenderBox::computePositionedLogicalWidth(LogicalExtentComputedValues& compu // relative positioned inline. const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container()); - const LayoutUnit containerLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock, region, offsetFromLogicalTopOfFirstPage); + const LayoutUnit containerLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock, region); // Use the container block's direction except when calculating the static distance // This conforms with the reference results for abspos-replaced-width-margin-000.htm @@ -2756,7 +3133,7 @@ void RenderBox::computePositionedLogicalWidth(LogicalExtentComputedValues& compu computeInlineStaticDistance(logicalLeftLength, logicalRightLength, this, containerBlock, containerLogicalWidth, region); // Calculate constraint equation values for 'width' case. - computePositionedLogicalWidthUsing(MainOrPreferredSize, style()->logicalWidth(), containerBlock, containerDirection, + computePositionedLogicalWidthUsing(style()->logicalWidth(), containerBlock, containerDirection, containerLogicalWidth, bordersPlusPadding, logicalLeftLength, logicalRightLength, marginLogicalLeft, marginLogicalRight, computedValues); @@ -2765,7 +3142,7 @@ void RenderBox::computePositionedLogicalWidth(LogicalExtentComputedValues& compu if (!style()->logicalMaxWidth().isUndefined()) { LogicalExtentComputedValues maxValues; - computePositionedLogicalWidthUsing(MaxSize, style()->logicalMaxWidth(), containerBlock, containerDirection, + computePositionedLogicalWidthUsing(style()->logicalMaxWidth(), containerBlock, containerDirection, containerLogicalWidth, bordersPlusPadding, logicalLeftLength, logicalRightLength, marginLogicalLeft, marginLogicalRight, maxValues); @@ -2779,10 +3156,10 @@ void RenderBox::computePositionedLogicalWidth(LogicalExtentComputedValues& compu } // Calculate constraint equation values for 'min-width' case. - if (!style()->logicalMinWidth().isZero()) { + if (!style()->logicalMinWidth().isZero() || style()->logicalMinWidth().isIntrinsic()) { LogicalExtentComputedValues minValues; - computePositionedLogicalWidthUsing(MinSize, style()->logicalMinWidth(), containerBlock, containerDirection, + computePositionedLogicalWidthUsing(style()->logicalMinWidth(), containerBlock, containerDirection, containerLogicalWidth, bordersPlusPadding, logicalLeftLength, logicalRightLength, marginLogicalLeft, marginLogicalRight, minValues); @@ -2795,24 +3172,20 @@ void RenderBox::computePositionedLogicalWidth(LogicalExtentComputedValues& compu } } - if (stretchesToMinIntrinsicLogicalWidth() && computedValues.m_extent < minPreferredLogicalWidth() - bordersPlusPadding) { - computePositionedLogicalWidthUsing(MainOrPreferredSize, Length(minPreferredLogicalWidth() - bordersPlusPadding, Fixed), containerBlock, containerDirection, - containerLogicalWidth, bordersPlusPadding, - logicalLeftLength, logicalRightLength, marginLogicalLeft, marginLogicalRight, - computedValues); - } - computedValues.m_extent += bordersPlusPadding; // Adjust logicalLeft if we need to for the flipped version of our writing mode in regions. - if (inRenderFlowThread() && !region && isWritingModeRoot() && isHorizontalWritingMode() == containerBlock->isHorizontalWritingMode()) { + // FIXME: Add support for other types of objects as containerBlock, not only RenderBlock. + RenderFlowThread* flowThread = flowThreadContainingBlock(); + if (flowThread && !region && isWritingModeRoot() && isHorizontalWritingMode() == containerBlock->isHorizontalWritingMode() && containerBlock->isRenderBlock()) { + ASSERT(containerBlock->canHaveBoxInfoInRegion()); LayoutUnit logicalLeftPos = computedValues.m_position; const RenderBlock* cb = toRenderBlock(containerBlock); - LayoutUnit cbPageOffset = offsetFromLogicalTopOfFirstPage - logicalTop(); + LayoutUnit cbPageOffset = cb->offsetFromLogicalTopOfFirstPage(); RenderRegion* cbRegion = cb->regionAtBlockOffset(cbPageOffset); if (cbRegion) { cbRegion = cb->clampToStartAndEndRegions(cbRegion); - RenderBoxRegionInfo* boxInfo = cb->renderBoxRegionInfo(cbRegion, cbPageOffset); + RenderBoxRegionInfo* boxInfo = cb->renderBoxRegionInfo(cbRegion); if (boxInfo) { logicalLeftPos += boxInfo->logicalLeft(); computedValues.m_position = logicalLeftPos; @@ -2832,13 +3205,13 @@ static void computeLogicalLeftPositionedOffset(LayoutUnit& logicalLeftPos, const logicalLeftPos += (child->isHorizontalWritingMode() ? containerBlock->borderLeft() : containerBlock->borderTop()); } -void RenderBox::computePositionedLogicalWidthUsing(SizeType widthSizeType, Length logicalWidth, const RenderBoxModelObject* containerBlock, TextDirection containerDirection, +void RenderBox::computePositionedLogicalWidthUsing(Length logicalWidth, const RenderBoxModelObject* containerBlock, TextDirection containerDirection, LayoutUnit containerLogicalWidth, LayoutUnit bordersPlusPadding, Length logicalLeft, Length logicalRight, Length marginLogicalLeft, Length marginLogicalRight, LogicalExtentComputedValues& computedValues) const { - if (widthSizeType == MinSize && logicalWidth.isAuto()) - logicalWidth = Length(0, Fixed); + if (logicalWidth.isIntrinsic()) + logicalWidth = Length(computeIntrinsicLogicalWidthUsing(logicalWidth, containerLogicalWidth, bordersPlusPadding) - bordersPlusPadding, Fixed); // 'left' and 'right' cannot both be 'auto' because one would of been // converted to the static position already @@ -2846,6 +3219,8 @@ void RenderBox::computePositionedLogicalWidthUsing(SizeType widthSizeType, Lengt LayoutUnit logicalLeftValue = 0; + const LayoutUnit containerRelativeLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock, 0, false); + bool logicalWidthIsAuto = logicalWidth.isIntrinsicOrAuto(); bool logicalLeftIsAuto = logicalLeft.isAuto(); bool logicalRightIsAuto = logicalRight.isAuto(); @@ -2893,16 +3268,16 @@ void RenderBox::computePositionedLogicalWidthUsing(SizeType widthSizeType, Lengt } } else if (marginLogicalLeft.isAuto()) { // Solve for left margin - marginLogicalRightValue = valueForLength(marginLogicalRight, containerLogicalWidth, renderView); + marginLogicalRightValue = valueForLength(marginLogicalRight, containerRelativeLogicalWidth, renderView); marginLogicalLeftValue = availableSpace - marginLogicalRightValue; } else if (marginLogicalRight.isAuto()) { // Solve for right margin - marginLogicalLeftValue = valueForLength(marginLogicalLeft, containerLogicalWidth, renderView); + marginLogicalLeftValue = valueForLength(marginLogicalLeft, containerRelativeLogicalWidth, renderView); marginLogicalRightValue = availableSpace - marginLogicalLeftValue; } else { // Over-constrained, solve for left if direction is RTL - marginLogicalLeftValue = valueForLength(marginLogicalLeft, containerLogicalWidth, renderView); - marginLogicalRightValue = valueForLength(marginLogicalRight, containerLogicalWidth, renderView); + marginLogicalLeftValue = valueForLength(marginLogicalLeft, containerRelativeLogicalWidth, renderView); + marginLogicalRightValue = valueForLength(marginLogicalRight, containerRelativeLogicalWidth, renderView); // Use the containing block's direction rather than the parent block's // per CSS 2.1 reference test abspos-non-replaced-width-margin-000. @@ -2952,8 +3327,8 @@ void RenderBox::computePositionedLogicalWidthUsing(SizeType widthSizeType, Lengt // because the value is not used for any further calculations. // Calculate margins, 'auto' margins are ignored. - marginLogicalLeftValue = minimumValueForLength(marginLogicalLeft, containerLogicalWidth, renderView); - marginLogicalRightValue = minimumValueForLength(marginLogicalRight, containerLogicalWidth, renderView); + marginLogicalLeftValue = minimumValueForLength(marginLogicalLeft, containerRelativeLogicalWidth, renderView); + marginLogicalRightValue = minimumValueForLength(marginLogicalRight, containerRelativeLogicalWidth, renderView); const LayoutUnit availableSpace = containerLogicalWidth - (marginLogicalLeftValue + marginLogicalRightValue + bordersPlusPadding); @@ -3030,14 +3405,8 @@ static void computeBlockStaticDistance(Length& logicalTop, Length& logicalBottom void RenderBox::computePositionedLogicalHeight(LogicalExtentComputedValues& computedValues) const { if (isReplaced()) { - // FIXME: For regions with height auto, we want to compute width using the normal block sizing code. - // For now, regions are replaced elements and this code can be removed once the RenderRegion - // will inherit from RenderBlock instead of RenderReplaced. - // (see https://bugs.webkit.org/show_bug.cgi?id=74132 ) - if (!isRenderRegion() || (isRenderRegion() && shouldComputeSizeAsReplaced())) { - computePositionedLogicalHeightReplaced(computedValues); - return; - } + computePositionedLogicalHeightReplaced(computedValues); + return; } // The following is based off of the W3C Working Draft from April 11, 2006 of @@ -3082,7 +3451,7 @@ void RenderBox::computePositionedLogicalHeight(LogicalExtentComputedValues& comp // Calculate constraint equation values for 'height' case. LayoutUnit logicalHeight = computedValues.m_extent; - computePositionedLogicalHeightUsing(MainOrPreferredSize, styleToUse->logicalHeight(), containerBlock, containerLogicalHeight, bordersPlusPadding, logicalHeight, + computePositionedLogicalHeightUsing(styleToUse->logicalHeight(), containerBlock, containerLogicalHeight, bordersPlusPadding, logicalHeight, logicalTopLength, logicalBottomLength, marginBefore, marginAfter, computedValues); @@ -3093,7 +3462,7 @@ void RenderBox::computePositionedLogicalHeight(LogicalExtentComputedValues& comp if (!styleToUse->logicalMaxHeight().isUndefined()) { LogicalExtentComputedValues maxValues; - computePositionedLogicalHeightUsing(MaxSize, styleToUse->logicalMaxHeight(), containerBlock, containerLogicalHeight, bordersPlusPadding, logicalHeight, + computePositionedLogicalHeightUsing(styleToUse->logicalMaxHeight(), containerBlock, containerLogicalHeight, bordersPlusPadding, logicalHeight, logicalTopLength, logicalBottomLength, marginBefore, marginAfter, maxValues); @@ -3109,7 +3478,7 @@ void RenderBox::computePositionedLogicalHeight(LogicalExtentComputedValues& comp if (!styleToUse->logicalMinHeight().isZero()) { LogicalExtentComputedValues minValues; - computePositionedLogicalHeightUsing(MinSize, styleToUse->logicalMinHeight(), containerBlock, containerLogicalHeight, bordersPlusPadding, logicalHeight, + computePositionedLogicalHeightUsing(styleToUse->logicalMinHeight(), containerBlock, containerLogicalHeight, bordersPlusPadding, logicalHeight, logicalTopLength, logicalBottomLength, marginBefore, marginAfter, minValues); @@ -3125,14 +3494,17 @@ void RenderBox::computePositionedLogicalHeight(LogicalExtentComputedValues& comp computedValues.m_extent += bordersPlusPadding; // Adjust logicalTop if we need to for perpendicular writing modes in regions. - if (inRenderFlowThread() && isHorizontalWritingMode() != containerBlock->isHorizontalWritingMode()) { + // FIXME: Add support for other types of objects as containerBlock, not only RenderBlock. + RenderFlowThread* flowThread = flowThreadContainingBlock(); + if (flowThread && isHorizontalWritingMode() != containerBlock->isHorizontalWritingMode() && containerBlock->isRenderBlock()) { + ASSERT(containerBlock->canHaveBoxInfoInRegion()); LayoutUnit logicalTopPos = computedValues.m_position; const RenderBlock* cb = toRenderBlock(containerBlock); LayoutUnit cbPageOffset = cb->offsetFromLogicalTopOfFirstPage() - logicalLeft(); RenderRegion* cbRegion = cb->regionAtBlockOffset(cbPageOffset); if (cbRegion) { cbRegion = cb->clampToStartAndEndRegions(cbRegion); - RenderBoxRegionInfo* boxInfo = cb->renderBoxRegionInfo(cbRegion, cbPageOffset); + RenderBoxRegionInfo* boxInfo = cb->renderBoxRegionInfo(cbRegion); if (boxInfo) { logicalTopPos += boxInfo->logicalLeft(); computedValues.m_position = logicalTopPos; @@ -3163,14 +3535,11 @@ static void computeLogicalTopPositionedOffset(LayoutUnit& logicalTopPos, const R } } -void RenderBox::computePositionedLogicalHeightUsing(SizeType heightSizeType, Length logicalHeightLength, const RenderBoxModelObject* containerBlock, +void RenderBox::computePositionedLogicalHeightUsing(Length logicalHeightLength, const RenderBoxModelObject* containerBlock, LayoutUnit containerLogicalHeight, LayoutUnit bordersPlusPadding, LayoutUnit logicalHeight, Length logicalTop, Length logicalBottom, Length marginBefore, Length marginAfter, LogicalExtentComputedValues& computedValues) const { - if (heightSizeType == MinSize && logicalHeightLength.isAuto()) - logicalHeightLength = Length(0, Fixed); - // 'top' and 'bottom' cannot both be 'auto' because 'top would of been // converted to the static position in computePositionedLogicalHeight() ASSERT(!(logicalTop.isAuto() && logicalBottom.isAuto())); @@ -3178,6 +3547,8 @@ void RenderBox::computePositionedLogicalHeightUsing(SizeType heightSizeType, Len LayoutUnit logicalHeightValue; LayoutUnit contentLogicalHeight = logicalHeight - bordersPlusPadding; + const LayoutUnit containerRelativeLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock, 0, false); + LayoutUnit logicalTopValue = 0; bool logicalHeightIsAuto = logicalHeightLength.isAuto(); @@ -3216,16 +3587,16 @@ void RenderBox::computePositionedLogicalHeightUsing(SizeType heightSizeType, Len computedValues.m_margins.m_after = availableSpace - computedValues.m_margins.m_before; // account for odd valued differences } else if (marginBefore.isAuto()) { // Solve for top margin - computedValues.m_margins.m_after = valueForLength(marginAfter, containerLogicalHeight, renderView); + computedValues.m_margins.m_after = valueForLength(marginAfter, containerRelativeLogicalWidth, renderView); computedValues.m_margins.m_before = availableSpace - computedValues.m_margins.m_after; } else if (marginAfter.isAuto()) { // Solve for bottom margin - computedValues.m_margins.m_before = valueForLength(marginBefore, containerLogicalHeight, renderView); + computedValues.m_margins.m_before = valueForLength(marginBefore, containerRelativeLogicalWidth, renderView); computedValues.m_margins.m_after = availableSpace - computedValues.m_margins.m_before; } else { // Over-constrained, (no need solve for bottom) - computedValues.m_margins.m_before = valueForLength(marginBefore, containerLogicalHeight, renderView); - computedValues.m_margins.m_after = valueForLength(marginAfter, containerLogicalHeight, renderView); + computedValues.m_margins.m_before = valueForLength(marginBefore, containerRelativeLogicalWidth, renderView); + computedValues.m_margins.m_after = valueForLength(marginAfter, containerRelativeLogicalWidth, renderView); } } else { /*--------------------------------------------------------------------*\ @@ -3254,8 +3625,8 @@ void RenderBox::computePositionedLogicalHeightUsing(SizeType heightSizeType, Len // because the value is not used for any further calculations. // Calculate margins, 'auto' margins are ignored. - computedValues.m_margins.m_before = minimumValueForLength(marginBefore, containerLogicalHeight, renderView); - computedValues.m_margins.m_after = minimumValueForLength(marginAfter, containerLogicalHeight, renderView); + computedValues.m_margins.m_before = minimumValueForLength(marginBefore, containerRelativeLogicalWidth, renderView); + computedValues.m_margins.m_after = minimumValueForLength(marginAfter, containerRelativeLogicalWidth, renderView); const LayoutUnit availableSpace = containerLogicalHeight - (computedValues.m_margins.m_before + computedValues.m_margins.m_after + bordersPlusPadding); @@ -3302,6 +3673,7 @@ void RenderBox::computePositionedLogicalWidthReplaced(LogicalExtentComputedValue const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container()); const LayoutUnit containerLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock); + const LayoutUnit containerRelativeLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock, 0, false); // To match WinIE, in quirks mode use the parent's 'direction' property // instead of the the container block's. @@ -3386,28 +3758,28 @@ void RenderBox::computePositionedLogicalWidthReplaced(LogicalExtentComputedValue * that value. \*-----------------------------------------------------------------------*/ } else if (logicalLeft.isAuto()) { - marginLogicalLeftAlias = valueForLength(marginLogicalLeft, containerLogicalWidth, renderView); - marginLogicalRightAlias = valueForLength(marginLogicalRight, containerLogicalWidth, renderView); + marginLogicalLeftAlias = valueForLength(marginLogicalLeft, containerRelativeLogicalWidth, renderView); + marginLogicalRightAlias = valueForLength(marginLogicalRight, containerRelativeLogicalWidth, renderView); logicalRightValue = valueForLength(logicalRight, containerLogicalWidth, renderView); // Solve for 'left' logicalLeftValue = availableSpace - (logicalRightValue + marginLogicalLeftAlias + marginLogicalRightAlias); } else if (logicalRight.isAuto()) { - marginLogicalLeftAlias = valueForLength(marginLogicalLeft, containerLogicalWidth, renderView); - marginLogicalRightAlias = valueForLength(marginLogicalRight, containerLogicalWidth, renderView); + marginLogicalLeftAlias = valueForLength(marginLogicalLeft, containerRelativeLogicalWidth, renderView); + marginLogicalRightAlias = valueForLength(marginLogicalRight, containerRelativeLogicalWidth, renderView); logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth, renderView); // Solve for 'right' logicalRightValue = availableSpace - (logicalLeftValue + marginLogicalLeftAlias + marginLogicalRightAlias); } else if (marginLogicalLeft.isAuto()) { - marginLogicalRightAlias = valueForLength(marginLogicalRight, containerLogicalWidth, renderView); + marginLogicalRightAlias = valueForLength(marginLogicalRight, containerRelativeLogicalWidth, renderView); logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth, renderView); logicalRightValue = valueForLength(logicalRight, containerLogicalWidth, renderView); // Solve for 'margin-left' marginLogicalLeftAlias = availableSpace - (logicalLeftValue + logicalRightValue + marginLogicalRightAlias); } else if (marginLogicalRight.isAuto()) { - marginLogicalLeftAlias = valueForLength(marginLogicalLeft, containerLogicalWidth, renderView); + marginLogicalLeftAlias = valueForLength(marginLogicalLeft, containerRelativeLogicalWidth, renderView); logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth, renderView); logicalRightValue = valueForLength(logicalRight, containerLogicalWidth, renderView); @@ -3415,8 +3787,8 @@ void RenderBox::computePositionedLogicalWidthReplaced(LogicalExtentComputedValue marginLogicalRightAlias = availableSpace - (logicalLeftValue + logicalRightValue + marginLogicalLeftAlias); } else { // Nothing is 'auto', just calculate the values. - marginLogicalLeftAlias = valueForLength(marginLogicalLeft, containerLogicalWidth, renderView); - marginLogicalRightAlias = valueForLength(marginLogicalRight, containerLogicalWidth, renderView); + marginLogicalLeftAlias = valueForLength(marginLogicalLeft, containerRelativeLogicalWidth, renderView); + marginLogicalRightAlias = valueForLength(marginLogicalRight, containerRelativeLogicalWidth, renderView); logicalRightValue = valueForLength(logicalRight, containerLogicalWidth, renderView); logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth, renderView); // If the containing block is right-to-left, then push the left position as far to the right as possible @@ -3470,6 +3842,7 @@ void RenderBox::computePositionedLogicalHeightReplaced(LogicalExtentComputedValu const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container()); const LayoutUnit containerLogicalHeight = containingBlockLogicalHeightForPositioned(containerBlock); + const LayoutUnit containerRelativeLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock, 0, false); // Variables to solve. Length marginBefore = style()->marginBefore(); @@ -3536,29 +3909,29 @@ void RenderBox::computePositionedLogicalHeightReplaced(LogicalExtentComputedValu * for that value. \*-----------------------------------------------------------------------*/ } else if (logicalTop.isAuto()) { - marginBeforeAlias = valueForLength(marginBefore, containerLogicalHeight, renderView); - marginAfterAlias = valueForLength(marginAfter, containerLogicalHeight, renderView); + marginBeforeAlias = valueForLength(marginBefore, containerRelativeLogicalWidth, renderView); + marginAfterAlias = valueForLength(marginAfter, containerRelativeLogicalWidth, renderView); logicalBottomValue = valueForLength(logicalBottom, containerLogicalHeight, renderView); // Solve for 'top' logicalTopValue = availableSpace - (logicalBottomValue + marginBeforeAlias + marginAfterAlias); } else if (logicalBottom.isAuto()) { - marginBeforeAlias = valueForLength(marginBefore, containerLogicalHeight, renderView); - marginAfterAlias = valueForLength(marginAfter, containerLogicalHeight, renderView); + marginBeforeAlias = valueForLength(marginBefore, containerRelativeLogicalWidth, renderView); + marginAfterAlias = valueForLength(marginAfter, containerRelativeLogicalWidth, renderView); logicalTopValue = valueForLength(logicalTop, containerLogicalHeight, renderView); // Solve for 'bottom' // NOTE: It is not necessary to solve for 'bottom' because we don't ever // use the value. } else if (marginBefore.isAuto()) { - marginAfterAlias = valueForLength(marginAfter, containerLogicalHeight, renderView); + marginAfterAlias = valueForLength(marginAfter, containerRelativeLogicalWidth, renderView); logicalTopValue = valueForLength(logicalTop, containerLogicalHeight, renderView); logicalBottomValue = valueForLength(logicalBottom, containerLogicalHeight, renderView); // Solve for 'margin-top' marginBeforeAlias = availableSpace - (logicalTopValue + logicalBottomValue + marginAfterAlias); } else if (marginAfter.isAuto()) { - marginBeforeAlias = valueForLength(marginBefore, containerLogicalHeight, renderView); + marginBeforeAlias = valueForLength(marginBefore, containerRelativeLogicalWidth, renderView); logicalTopValue = valueForLength(logicalTop, containerLogicalHeight, renderView); logicalBottomValue = valueForLength(logicalBottom, containerLogicalHeight, renderView); @@ -3566,8 +3939,8 @@ void RenderBox::computePositionedLogicalHeightReplaced(LogicalExtentComputedValu marginAfterAlias = availableSpace - (logicalTopValue + logicalBottomValue + marginBeforeAlias); } else { // Nothing is 'auto', just calculate the values. - marginBeforeAlias = valueForLength(marginBefore, containerLogicalHeight, renderView); - marginAfterAlias = valueForLength(marginAfter, containerLogicalHeight, renderView); + marginBeforeAlias = valueForLength(marginBefore, containerRelativeLogicalWidth, renderView); + marginAfterAlias = valueForLength(marginAfter, containerRelativeLogicalWidth, renderView); logicalTopValue = valueForLength(logicalTop, containerLogicalHeight, renderView); // NOTE: It is not necessary to solve for 'bottom' because we don't ever // use the value. @@ -3594,7 +3967,6 @@ LayoutRect RenderBox::localCaretRect(InlineBox* box, int caretOffset, LayoutUnit // They never refer to children. // FIXME: Paint the carets inside empty blocks differently than the carets before/after elements. - // FIXME: What about border and padding? LayoutRect rect(location(), LayoutSize(caretWidth, height())); bool ltr = box ? box->isLeftToRightDirection() : style()->isLeftToRightDirection(); @@ -3626,6 +3998,14 @@ LayoutRect RenderBox::localCaretRect(InlineBox* box, int caretOffset, LayoutUnit // Move to local coords rect.moveBy(-location()); + // FIXME: Border/padding should be added for all elements but this workaround + // is needed because we use offsets inside an "atomic" element to represent + // positions before and after the element in deprecated editing offsets. + if (node() && !(editingIgnoresContent(node()) || isTableElement(node()))) { + rect.setX(rect.x() + borderLeft() + paddingLeft()); + rect.setY(rect.y() + paddingTop() + borderTop()); + } + if (!isHorizontalWritingMode()) return rect.transposedRect(); @@ -3636,16 +4016,16 @@ VisiblePosition RenderBox::positionForPoint(const LayoutPoint& point) { // no children...return this render object's element, if there is one, and offset 0 if (!firstChild()) - return createVisiblePosition(node() ? firstPositionInOrBeforeNode(node()) : Position()); + return createVisiblePosition(nonPseudoNode() ? firstPositionInOrBeforeNode(nonPseudoNode()) : Position()); - if (isTable() && node()) { + if (isTable() && nonPseudoNode()) { LayoutUnit right = contentWidth() + borderAndPaddingWidth(); LayoutUnit bottom = contentHeight() + borderAndPaddingHeight(); if (point.x() < 0 || point.x() > right || point.y() < 0 || point.y() > bottom) { if (point.x() <= right / 2) - return createVisiblePosition(firstPositionInOrBeforeNode(node())); - return createVisiblePosition(lastPositionInOrAfterNode(node())); + return createVisiblePosition(firstPositionInOrBeforeNode(nonPseudoNode())); + return createVisiblePosition(lastPositionInOrAfterNode(nonPseudoNode())); } } @@ -3713,7 +4093,7 @@ VisiblePosition RenderBox::positionForPoint(const LayoutPoint& point) if (closestRenderer) return closestRenderer->positionForPoint(adjustedPoint - closestRenderer->locationOffset()); - return createVisiblePosition(firstPositionInOrBeforeNode(node())); + return createVisiblePosition(firstPositionInOrBeforeNode(nonPseudoNode())); } bool RenderBox::shrinkToAvoidFloats() const @@ -3728,7 +4108,7 @@ bool RenderBox::shrinkToAvoidFloats() const bool RenderBox::avoidsFloats() const { - return isReplaced() || hasOverflowClip() || isHR() || isLegend() || isWritingModeRoot() || isDeprecatedFlexItem(); + return isReplaced() || hasOverflowClip() || isHR() || isLegend() || isWritingModeRoot() || isFlexItemIncludingDeprecated(); } void RenderBox::addVisualEffectOverflow() @@ -3778,6 +4158,10 @@ void RenderBox::addVisualEffectOverflow() void RenderBox::addOverflowFromChild(RenderBox* child, const LayoutSize& delta) { + // Never allow flow threads to propagate overflow up to a parent. + if (child->isRenderFlowThread()) + return; + // Only propagate layout overflow from the child if the child isn't clipping its overflow. If it is, then // its overflow is internal to it, and we don't care about it. layoutOverflowRectForPropagation takes care of this // and just propagates the border box rect instead. @@ -3810,7 +4194,7 @@ void RenderBox::addLayoutOverflow(const LayoutRect& rect) bool hasTopOverflow = !style()->isLeftToRightDirection() && !isHorizontalWritingMode(); bool hasLeftOverflow = !style()->isLeftToRightDirection() && isHorizontalWritingMode(); if (isFlexibleBox() && style()->isReverseFlexDirection()) { - RenderFlexibleBox* flexibleBox = static_cast<RenderFlexibleBox*>(this); + RenderFlexibleBox* flexibleBox = toRenderFlexibleBox(this); if (flexibleBox->isHorizontalFlow()) hasLeftOverflow = true; else @@ -3857,19 +4241,6 @@ void RenderBox::addVisualOverflow(const LayoutRect& rect) m_overflow->addVisualOverflow(rect); } -void RenderBox::clearLayoutOverflow() -{ - if (!m_overflow) - return; - - if (visualOverflowRect() == borderBoxRect()) { - m_overflow.clear(); - return; - } - - m_overflow->setLayoutOverflow(borderBoxRect()); -} - inline static bool percentageLogicalHeightIsResolvable(const RenderBox* box) { return RenderBox::percentageLogicalHeightIsResolvableFromBlock(box->containingBlock(), box->isOutOfFlowPositioned()); @@ -4168,6 +4539,13 @@ bool RenderBox::hasRelativeLogicalHeight() const || style()->logicalMaxHeight().isPercent(); } +bool RenderBox::hasViewportPercentageLogicalHeight() const +{ + return style()->logicalHeight().isViewportPercentage() + || style()->logicalMinHeight().isViewportPercentage() + || style()->logicalMaxHeight().isViewportPercentage(); +} + static void markBoxForRelayoutAfterSplit(RenderBox* box) { // FIXME: The table code should handle that automatically. If not, @@ -4218,4 +4596,14 @@ RenderObject* RenderBox::splitAnonymousBoxesAroundChild(RenderObject* beforeChil return beforeChild; } +LayoutUnit RenderBox::offsetFromLogicalTopOfFirstPage() const +{ + LayoutState* layoutState = view()->layoutState(); + if ((layoutState && !layoutState->isPaginated()) || (!layoutState && !flowThreadContainingBlock())) + return 0; + + RenderBlock* containerBlock = containingBlock(); + return containerBlock->offsetFromLogicalTopOfFirstPage() + logicalTop(); +} + } // namespace WebCore diff --git a/Source/WebCore/rendering/RenderBox.h b/Source/WebCore/rendering/RenderBox.h index c1de3e736..9756f9e8e 100644 --- a/Source/WebCore/rendering/RenderBox.h +++ b/Source/WebCore/rendering/RenderBox.h @@ -26,6 +26,9 @@ #include "RenderBoxModelObject.h" #include "RenderOverflow.h" #include "ScrollTypes.h" +#if ENABLE(CSS_SHAPES) +#include "ShapeOutsideInfo.h" +#endif namespace WebCore { @@ -34,17 +37,21 @@ class RenderRegion; struct PaintInfo; enum SizeType { MainOrPreferredSize, MinSize, MaxSize }; - +enum AvailableLogicalHeightType { ExcludeMarginBorderPadding, IncludeMarginBorderPadding }; enum OverlayScrollbarSizeRelevancy { IgnoreOverlayScrollbarSize, IncludeOverlayScrollbarSize }; +enum ShouldComputePreferred { ComputeActual, ComputePreferred }; + class RenderBox : public RenderBoxModelObject { public: - RenderBox(Node*); + explicit RenderBox(ContainerNode*); virtual ~RenderBox(); // hasAutoZIndex only returns true if the element is positioned or a flex-item since // position:static elements that are not flex-items get their z-index coerced to auto. - virtual bool requiresLayer() const OVERRIDE { return isRoot() || isPositioned() || createsGroup() || hasClipPath() || hasOverflowClip() || hasTransform() || hasHiddenBackface() || hasReflection() || style()->specifiesColumns() || !style()->hasAutoZIndex(); } + virtual bool requiresLayer() const OVERRIDE { return isRoot() || isPositioned() || createsGroup() || hasClipPath() || hasOverflowClip() || hasTransform() || hasHiddenBackface() || hasReflection() || style()->specifiesColumns() || !style()->hasAutoZIndex() || isFloatingWithShapeOutside(); } + + virtual bool backgroundIsKnownToBeOpaqueInRect(const LayoutRect& localRect) const OVERRIDE; // Use this with caution! No type checking is done! RenderBox* firstChildBox() const; @@ -75,8 +82,9 @@ public: LayoutUnit logicalWidth() const { return style()->isHorizontalWritingMode() ? width() : height(); } LayoutUnit logicalHeight() const { return style()->isHorizontalWritingMode() ? height() : width(); } - LayoutUnit constrainLogicalWidthInRegionByMinMax(LayoutUnit, LayoutUnit, RenderBlock*, RenderRegion* = 0, LayoutUnit offsetFromLogicalTopOfFirstPage = 0) const; + LayoutUnit constrainLogicalWidthInRegionByMinMax(LayoutUnit, LayoutUnit, RenderBlock*, RenderRegion* = 0) const; LayoutUnit constrainLogicalHeightByMinMax(LayoutUnit) const; + LayoutUnit constrainContentBoxLogicalHeightByMinMax(LayoutUnit) const; int pixelSnappedLogicalHeight() const { return style()->isHorizontalWritingMode() ? pixelSnappedHeight() : pixelSnappedWidth(); } int pixelSnappedLogicalWidth() const { return style()->isHorizontalWritingMode() ? pixelSnappedWidth() : pixelSnappedHeight(); } @@ -139,6 +147,7 @@ public: void setFrameRect(const LayoutRect& rect) { m_frameRect = rect; } LayoutRect borderBoxRect() const { return LayoutRect(LayoutPoint(), size()); } + LayoutRect paddingBoxRect() const { return LayoutRect(borderLeft(), borderTop(), contentWidth() + paddingLeft() + paddingRight(), contentHeight() + paddingTop() + paddingBottom()); } IntRect pixelSnappedBorderBoxRect() const { return IntRect(IntPoint(), m_frameRect.pixelSnappedSize()); } virtual IntRect borderBoundingBox() const { return pixelSnappedBorderBoxRect(); } @@ -155,7 +164,7 @@ public: // Bounds of the outline box in absolute coords. Respects transforms virtual LayoutRect outlineBoundsForRepaint(const RenderLayerModelObject* /*repaintContainer*/, const RenderGeometryMap*) const OVERRIDE; - virtual void addFocusRingRects(Vector<IntRect>&, const LayoutPoint&); + virtual void addFocusRingRects(Vector<IntRect>&, const LayoutPoint& additionalOffset, const RenderLayerModelObject* paintContainer = 0) OVERRIDE; // Use this with caution! No type checking is done! RenderBox* previousSiblingBox() const; @@ -167,8 +176,6 @@ public: // respectively are flipped when compared to their physical counterparts. For example minX is on the left in vertical-lr, // but it is on the right in vertical-rl. LayoutRect layoutOverflowRect() const { return m_overflow ? m_overflow->layoutOverflowRect() : clientBoxRect(); } - IntRect pixelSnappedLayoutOverflowRect() const { return pixelSnappedIntRect(layoutOverflowRect()); } - LayoutSize maxLayoutOverflow() const { return LayoutSize(layoutOverflowRect().maxX(), layoutOverflowRect().maxY()); } LayoutUnit logicalLeftLayoutOverflow() const { return style()->isHorizontalWritingMode() ? layoutOverflowRect().x() : layoutOverflowRect().y(); } LayoutUnit logicalRightLayoutOverflow() const { return style()->isHorizontalWritingMode() ? layoutOverflowRect().maxX() : layoutOverflowRect().maxY(); } @@ -184,7 +191,6 @@ public: void addVisualEffectOverflow(); void addOverflowFromChild(RenderBox* child) { addOverflowFromChild(child, child->locationOffset()); } void addOverflowFromChild(RenderBox* child, const LayoutSize& delta); - void clearLayoutOverflow(); void updateLayerTransform(); @@ -198,9 +204,8 @@ public: virtual LayoutUnit offsetWidth() const { return width(); } virtual LayoutUnit offsetHeight() const { return height(); } - // FIXME: The implementation for these functions will change once we move to subpixel layout. See bug 60318. - virtual int pixelSnappedOffsetWidth() const { return pixelSnappedWidth(); } - virtual int pixelSnappedOffsetHeight() const { return pixelSnappedHeight(); } + virtual int pixelSnappedOffsetWidth() const OVERRIDE; + virtual int pixelSnappedOffsetHeight() const OVERRIDE; // More IE extensions. clientWidth and clientHeight represent the interior of an object // excluding border and scrollbar. clientLeft/Top are just the borderLeftWidth and borderTopWidth. @@ -312,6 +317,7 @@ public: void setOverrideContainingBlockContentLogicalWidth(LayoutUnit); void setOverrideContainingBlockContentLogicalHeight(LayoutUnit); void clearContainingBlockOverrideSize(); + void clearOverrideContainingBlockContentLogicalHeight(); virtual LayoutSize offsetFromContainer(RenderObject*, const LayoutPoint&, bool* offsetDependsOnPoint = 0) const; @@ -320,8 +326,6 @@ public: LayoutUnit adjustContentBoxLogicalWidthForBoxSizing(LayoutUnit width) const; LayoutUnit adjustContentBoxLogicalHeightForBoxSizing(LayoutUnit height) const; - virtual void borderFitAdjust(LayoutRect&) const { } // Shrink the box in which the border paints if border-fit is set. - struct ComputedMarginValues { ComputedMarginValues() : m_before(0) @@ -355,8 +359,9 @@ public: void computeAndSetBlockDirectionMargins(const RenderBlock* containingBlock); enum RenderBoxRegionInfoFlags { CacheRenderBoxRegionInfo, DoNotCacheRenderBoxRegionInfo }; - LayoutRect borderBoxRectInRegion(RenderRegion*, LayoutUnit offsetFromLogicalTopOfFirstPage = 0, RenderBoxRegionInfoFlags = CacheRenderBoxRegionInfo) const; + LayoutRect borderBoxRectInRegion(RenderRegion*, RenderBoxRegionInfoFlags = CacheRenderBoxRegionInfo) const; void clearRenderBoxRegionInfo(); + virtual LayoutUnit offsetFromLogicalTopOfFirstPage() const; void positionLineBox(InlineBox*); @@ -372,26 +377,26 @@ public: virtual LayoutRect clippedOverflowRectForRepaint(const RenderLayerModelObject* repaintContainer) const OVERRIDE; virtual void computeRectForRepaint(const RenderLayerModelObject* repaintContainer, LayoutRect&, bool fixed = false) const OVERRIDE; - - virtual void repaintDuringLayoutIfMoved(const LayoutRect&); + void repaintDuringLayoutIfMoved(const LayoutRect&); + virtual void repaintOverhangingFloats(bool paintAllDescendants); virtual LayoutUnit containingBlockLogicalWidthForContent() const; - LayoutUnit containingBlockLogicalHeightForContent() const; + LayoutUnit containingBlockLogicalHeightForContent(AvailableLogicalHeightType) const; - LayoutUnit containingBlockLogicalWidthForContentInRegion(RenderRegion*, LayoutUnit offsetFromLogicalTopOfFirstPage) const; - LayoutUnit containingBlockAvailableLineWidthInRegion(RenderRegion*, LayoutUnit offsetFromLogicalTopOfFirstPage) const; + LayoutUnit containingBlockLogicalWidthForContentInRegion(RenderRegion*) const; + LayoutUnit containingBlockAvailableLineWidthInRegion(RenderRegion*) const; LayoutUnit perpendicularContainingBlockLogicalHeight() const; virtual void updateLogicalWidth(); virtual void updateLogicalHeight(); virtual void computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues&) const; - RenderBoxRegionInfo* renderBoxRegionInfo(RenderRegion*, LayoutUnit offsetFromLogicalTopOfFirstPage, RenderBoxRegionInfoFlags = CacheRenderBoxRegionInfo) const; - void computeLogicalWidthInRegion(LogicalExtentComputedValues&, RenderRegion* = 0, LayoutUnit offsetFromLogicalTopOfFirstPage = 0) const; + RenderBoxRegionInfo* renderBoxRegionInfo(RenderRegion*, RenderBoxRegionInfoFlags = CacheRenderBoxRegionInfo) const; + void computeLogicalWidthInRegion(LogicalExtentComputedValues&, RenderRegion* = 0) const; bool stretchesToViewport() const { - return document()->inQuirksMode() && style()->logicalHeight().isAuto() && !isFloatingOrOutOfFlowPositioned() && (isRoot() || isBody()) && !document()->shouldDisplaySeamlesslyWithParent(); + return document()->inQuirksMode() && style()->logicalHeight().isAuto() && !isFloatingOrOutOfFlowPositioned() && (isRoot() || isBody()) && !document()->shouldDisplaySeamlesslyWithParent() && !isInline(); } virtual LayoutSize intrinsicSize() const { return LayoutSize(); } @@ -401,20 +406,19 @@ public: // Whether or not the element shrinks to its intrinsic width (rather than filling the width // of a containing block). HTML4 buttons, <select>s, <input>s, legends, and floating/compact elements do this. bool sizesLogicalWidthToFitContent(SizeType) const; - virtual bool stretchesToMinIntrinsicLogicalWidth() const { return false; } - LayoutUnit shrinkLogicalWidthToAvoidFloats(LayoutUnit childMarginStart, LayoutUnit childMarginEnd, const RenderBlock* cb, RenderRegion*, LayoutUnit offsetFromLogicalTopOfFirstPage) const; + LayoutUnit shrinkLogicalWidthToAvoidFloats(LayoutUnit childMarginStart, LayoutUnit childMarginEnd, const RenderBlock* cb, RenderRegion*) const; - LayoutUnit computeLogicalWidthInRegionUsing(SizeType, LayoutUnit availableLogicalWidth, const RenderBlock* containingBlock, RenderRegion*, LayoutUnit offsetFromLogicalTopOfFirstPage) const; - LayoutUnit computeLogicalHeightUsing(SizeType, const Length& height) const; - LayoutUnit computeContentLogicalHeight(SizeType, const Length& height); - LayoutUnit computeContentAndScrollbarLogicalHeightUsing(SizeType, const Length& height) const; - LayoutUnit computeReplacedLogicalWidthUsing(SizeType, Length width) const; - LayoutUnit computeReplacedLogicalWidthRespectingMinMaxWidth(LayoutUnit logicalWidth, bool includeMaxWidth = true) const; - LayoutUnit computeReplacedLogicalHeightUsing(SizeType, Length height) const; + LayoutUnit computeLogicalWidthInRegionUsing(SizeType, Length logicalWidth, LayoutUnit availableLogicalWidth, const RenderBlock* containingBlock, RenderRegion*) const; + LayoutUnit computeLogicalHeightUsing(const Length& height) const; + LayoutUnit computeContentLogicalHeight(const Length& height) const; + LayoutUnit computeContentAndScrollbarLogicalHeightUsing(const Length& height) const; + LayoutUnit computeReplacedLogicalWidthUsing(Length width) const; + LayoutUnit computeReplacedLogicalWidthRespectingMinMaxWidth(LayoutUnit logicalWidth, ShouldComputePreferred = ComputeActual) const; + LayoutUnit computeReplacedLogicalHeightUsing(Length height) const; LayoutUnit computeReplacedLogicalHeightRespectingMinMaxHeight(LayoutUnit logicalHeight) const; - virtual LayoutUnit computeReplacedLogicalWidth(bool includeMaxWidth = true) const; + virtual LayoutUnit computeReplacedLogicalWidth(ShouldComputePreferred = ComputeActual) const; virtual LayoutUnit computeReplacedLogicalHeight() const; static bool percentageLogicalHeightIsResolvableFromBlock(const RenderBlock* containingBlock, bool outOfFlowPositioned); @@ -422,24 +426,29 @@ public: // Block flows subclass availableWidth/Height to handle multi column layout (shrinking the width/height available to children when laying out.) virtual LayoutUnit availableLogicalWidth() const { return contentLogicalWidth(); } - virtual LayoutUnit availableLogicalHeight() const; - LayoutUnit availableLogicalHeightUsing(const Length&) const; + virtual LayoutUnit availableLogicalHeight(AvailableLogicalHeightType) const; + LayoutUnit availableLogicalHeightUsing(const Length&, AvailableLogicalHeightType) const; // There are a few cases where we need to refer specifically to the available physical width and available physical height. // Relative positioning is one of those cases, since left/top offsets are physical. - LayoutUnit availableWidth() const { return style()->isHorizontalWritingMode() ? availableLogicalWidth() : availableLogicalHeight(); } - LayoutUnit availableHeight() const { return style()->isHorizontalWritingMode() ? availableLogicalHeight() : availableLogicalWidth(); } + LayoutUnit availableWidth() const { return style()->isHorizontalWritingMode() ? availableLogicalWidth() : availableLogicalHeight(IncludeMarginBorderPadding); } + LayoutUnit availableHeight() const { return style()->isHorizontalWritingMode() ? availableLogicalHeight(IncludeMarginBorderPadding) : availableLogicalWidth(); } virtual int verticalScrollbarWidth() const; int horizontalScrollbarHeight() const; + int instrinsicScrollbarLogicalWidth() const; int scrollbarLogicalHeight() const { return style()->isHorizontalWritingMode() ? horizontalScrollbarHeight() : verticalScrollbarWidth(); } virtual bool scroll(ScrollDirection, ScrollGranularity, float multiplier = 1, Node** stopNode = 0); virtual bool logicalScroll(ScrollLogicalDirection, ScrollGranularity, float multiplier = 1, Node** stopNode = 0); bool canBeScrolledAndHasScrollableArea() const; virtual bool canBeProgramaticallyScrolled() const; - virtual void autoscroll(); + virtual void autoscroll(const IntPoint&); + bool canAutoscroll() const; + IntSize calculateAutoscrollDirection(const IntPoint& windowPoint) const; + static RenderBox* findAutoscrollable(RenderObject*); virtual void stopAutoscroll() { } virtual void panScroll(const IntPoint&); + bool hasAutoVerticalScrollbar() const { return hasOverflowClip() && (style()->overflowY() == OAUTO || style()->overflowY() == OOVERLAY); } bool hasAutoHorizontalScrollbar() const { return hasOverflowClip() && (style()->overflowX() == OAUTO || style()->overflowX() == OOVERLAY); } bool scrollsOverflow() const { return scrollsOverflowX() || scrollsOverflowY(); } @@ -452,7 +461,8 @@ public: virtual LayoutRect localCaretRect(InlineBox*, int caretOffset, LayoutUnit* extraWidthToEndOfLine = 0); - virtual LayoutRect overflowClipRect(const LayoutPoint& location, RenderRegion*, OverlayScrollbarSizeRelevancy = IgnoreOverlayScrollbarSize); + virtual LayoutRect overflowClipRect(const LayoutPoint& location, RenderRegion*, OverlayScrollbarSizeRelevancy = IgnoreOverlayScrollbarSize, PaintPhase = PaintPhaseBlockBackground); + virtual LayoutRect overflowClipRectForChildLayers(const LayoutPoint& location, RenderRegion* region, OverlayScrollbarSizeRelevancy relevancy) { return overflowClipRect(location, region, relevancy); } LayoutRect clipRect(const LayoutPoint& location, RenderRegion*); virtual bool hasControlClip() const { return false; } virtual LayoutRect controlClipRect(const LayoutPoint&) const { return LayoutRect(); } @@ -496,6 +506,7 @@ public: bool isWritingModeRoot() const { return !parent() || parent()->style()->writingMode() != style()->writingMode(); } bool isDeprecatedFlexItem() const { return !isInline() && !isFloatingOrOutOfFlowPositioned() && parent() && parent()->isDeprecatedFlexibleBox(); } + bool isFlexItemIncludingDeprecated() const { return !isInline() && !isFloatingOrOutOfFlowPositioned() && parent() && parent()->isFlexibleBoxIncludingDeprecated(); } virtual LayoutUnit lineHeight(bool firstLine, LineDirectionMode, LinePositionMode = PositionOnContainingLine) const; virtual int baselinePosition(FontBaseline, bool firstLine, LineDirectionMode, LinePositionMode = PositionOnContainingLine) const OVERRIDE; @@ -521,7 +532,7 @@ public: LayoutRect logicalLayoutOverflowRectForPropagation(RenderStyle*) const; LayoutRect layoutOverflowRectForPropagation(RenderStyle*) const; - RenderOverflow* hasRenderOverflow() const { return m_overflow.get(); } + bool hasRenderOverflow() const { return m_overflow; } bool hasVisualOverflow() const { return m_overflow && !borderBoxRect().contains(m_overflow->visualOverflowRect()); } virtual bool needsPreferredWidthsRecalculation() const; @@ -533,27 +544,26 @@ public: virtual bool hasRelativeDimensions() const; virtual bool hasRelativeLogicalHeight() const; + virtual bool hasViewportPercentageLogicalHeight() const; bool hasHorizontalLayoutOverflow() const { - if (RenderOverflow* overflow = hasRenderOverflow()) { - LayoutRect layoutOverflowRect = overflow->layoutOverflowRect(); - flipForWritingMode(layoutOverflowRect); - return layoutOverflowRect.x() < x() || layoutOverflowRect.maxX() > x() + logicalWidth(); - } + if (!m_overflow) + return false; - return false; + LayoutRect layoutOverflowRect = m_overflow->layoutOverflowRect(); + flipForWritingMode(layoutOverflowRect); + return layoutOverflowRect.x() < x() || layoutOverflowRect.maxX() > x() + logicalWidth(); } bool hasVerticalLayoutOverflow() const { - if (RenderOverflow* overflow = hasRenderOverflow()) { - LayoutRect layoutOverflowRect = overflow->layoutOverflowRect(); - flipForWritingMode(layoutOverflowRect); - return layoutOverflowRect.y() < y() || layoutOverflowRect.maxY() > y() + logicalHeight(); - } + if (!m_overflow) + return false; - return false; + LayoutRect layoutOverflowRect = m_overflow->layoutOverflowRect(); + flipForWritingMode(layoutOverflowRect); + return layoutOverflowRect.y() < y() || layoutOverflowRect.maxY() > y() + logicalHeight(); } virtual RenderBox* createAnonymousBoxWithSameTypeAs(const RenderObject*) const @@ -564,6 +574,13 @@ public: bool hasSameDirectionAs(const RenderBox* object) const { return style()->direction() == object->style()->direction(); } +#if ENABLE(CSS_SHAPES) + ShapeOutsideInfo* shapeOutsideInfo() const + { + return isFloatingWithShapeOutside() && ShapeOutsideInfo::isEnabledFor(this) ? ShapeOutsideInfo::info(this) : 0; + } +#endif + protected: virtual void willBeDestroyed(); @@ -571,7 +588,11 @@ protected: virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); virtual void updateFromStyle() OVERRIDE; - virtual bool backgroundIsObscured() const { return false; } + // Returns false if it could not cheaply compute the extent (e.g. fixed background), in which case the returned rect may be incorrect. + bool getBackgroundPaintedExtent(LayoutRect&) const; + virtual bool foregroundIsKnownToBeOpaqueInRect(const LayoutRect& localRect, unsigned maxDepthToTest) const; + virtual bool computeBackgroundIsKnownToBeObscured() OVERRIDE; + void paintBackground(const PaintInfo&, const LayoutRect&, BackgroundBleedAvoidance = BackgroundBleedNone); void paintFillLayer(const PaintInfo&, const Color&, const FillLayer*, const LayoutRect&, BackgroundBleedAvoidance, CompositeOperator, RenderObject* backgroundObject); @@ -580,17 +601,19 @@ protected: void paintMaskImages(const PaintInfo&, const LayoutRect&); BackgroundBleedAvoidance determineBackgroundBleedAvoidance(GraphicsContext*) const; - bool backgroundIsSingleOpaqueLayer() const; + bool backgroundHasOpaqueTopLayer() const; #if PLATFORM(MAC) void paintCustomHighlight(const LayoutPoint&, const AtomicString& type, bool behindText); #endif - void computePositionedLogicalWidth(LogicalExtentComputedValues&, RenderRegion* = 0, LayoutUnit offsetFromLogicalTopOfFirstPage = 0) const; + void computePositionedLogicalWidth(LogicalExtentComputedValues&, RenderRegion* = 0) const; + + LayoutUnit computeIntrinsicLogicalWidthUsing(Length logicalWidthLength, LayoutUnit availableLogicalWidth, LayoutUnit borderAndPadding) const; virtual bool shouldComputeSizeAsReplaced() const { return isReplaced() && !isInlineBlockOrInlineTable(); } - virtual void mapLocalToContainer(const RenderLayerModelObject* repaintContainer, TransformState&, MapCoordinatesFlags = ApplyContainerFlip | SnapOffsetForTransforms, bool* wasFixed = 0) const OVERRIDE; + virtual void mapLocalToContainer(const RenderLayerModelObject* repaintContainer, TransformState&, MapCoordinatesFlags = ApplyContainerFlip, bool* wasFixed = 0) const OVERRIDE; virtual const RenderObject* pushMappingToContainer(const RenderLayerModelObject*, RenderGeometryMap&) const OVERRIDE; virtual void mapAbsoluteToLocalPoint(MapCoordinatesFlags, TransformState&) const; @@ -599,6 +622,10 @@ protected: RenderObject* splitAnonymousBoxesAroundChild(RenderObject* beforeChild); private: +#if ENABLE(CSS_SHAPES) + void updateShapeOutsideInfoAfterStyleChange(const ShapeValue* shapeOutside, const ShapeValue* oldShapeOutside); +#endif + bool fixedElementLaysOutRelativeToFrame(Frame*, FrameView*) const; bool includeVerticalScrollbarSize() const; @@ -609,16 +636,17 @@ private: bool skipContainingBlockForPercentHeightCalculation(const RenderBox* containingBlock) const; - LayoutUnit containingBlockLogicalWidthForPositioned(const RenderBoxModelObject* containingBlock, RenderRegion* = 0, - LayoutUnit offsetFromLogicalTopOfFirstPage = 0, bool checkForPerpendicularWritingMode = true) const; + LayoutUnit containingBlockLogicalWidthForPositioned(const RenderBoxModelObject* containingBlock, RenderRegion* = 0, bool checkForPerpendicularWritingMode = true) const; LayoutUnit containingBlockLogicalHeightForPositioned(const RenderBoxModelObject* containingBlock, bool checkForPerpendicularWritingMode = true) const; + LayoutUnit viewLogicalHeightForPercentages() const; + void computePositionedLogicalHeight(LogicalExtentComputedValues&) const; - void computePositionedLogicalWidthUsing(SizeType, Length logicalWidth, const RenderBoxModelObject* containerBlock, TextDirection containerDirection, + void computePositionedLogicalWidthUsing(Length logicalWidth, const RenderBoxModelObject* containerBlock, TextDirection containerDirection, LayoutUnit containerLogicalWidth, LayoutUnit bordersPlusPadding, Length logicalLeft, Length logicalRight, Length marginLogicalLeft, Length marginLogicalRight, LogicalExtentComputedValues&) const; - void computePositionedLogicalHeightUsing(SizeType, Length logicalHeightLength, const RenderBoxModelObject* containerBlock, + void computePositionedLogicalHeightUsing(Length logicalHeightLength, const RenderBoxModelObject* containerBlock, LayoutUnit containerLogicalHeight, LayoutUnit bordersPlusPadding, LayoutUnit logicalHeight, Length logicalTop, Length logicalBottom, Length marginLogicalTop, Length marginLogicalBottom, LogicalExtentComputedValues&) const; @@ -626,6 +654,11 @@ private: void computePositionedLogicalHeightReplaced(LogicalExtentComputedValues&) const; void computePositionedLogicalWidthReplaced(LogicalExtentComputedValues&) const; + LayoutUnit fillAvailableMeasure(LayoutUnit availableLogicalWidth) const; + LayoutUnit fillAvailableMeasure(LayoutUnit availableLogicalWidth, LayoutUnit& marginStart, LayoutUnit& marginEnd) const; + + virtual void computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const; + // This function calculates the minimum and maximum preferred widths for an object. // These values are used in shrink-to-fit layout systems. // These include tables, positioned objects, floats and flexible boxes. @@ -659,13 +692,13 @@ private: inline RenderBox* toRenderBox(RenderObject* object) { - ASSERT(!object || object->isBox()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isBox()); return static_cast<RenderBox*>(object); } inline const RenderBox* toRenderBox(const RenderObject* object) { - ASSERT(!object || object->isBox()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isBox()); return static_cast<const RenderBox*>(object); } diff --git a/Source/WebCore/rendering/RenderBoxModelObject.cpp b/Source/WebCore/rendering/RenderBoxModelObject.cpp index 6d63169b5..9d156ee37 100644 --- a/Source/WebCore/rendering/RenderBoxModelObject.cpp +++ b/Source/WebCore/rendering/RenderBoxModelObject.cpp @@ -35,11 +35,12 @@ #include "RenderBlock.h" #include "RenderInline.h" #include "RenderLayer.h" +#include "RenderNamedFlowThread.h" +#include "RenderRegion.h" #include "RenderView.h" #include "ScrollingConstraints.h" #include "Settings.h" #include "TransformState.h" -#include <wtf/CurrentTime.h> #if USE(ACCELERATED_COMPOSITING) #include "RenderLayerBacking.h" @@ -90,11 +91,13 @@ private: ObjectLayerSizeMap m_objectLayerSizeMap; Timer<ImageQualityController> m_timer; bool m_animatedResizeIsActive; + bool m_liveResizeOptimizationIsActive; }; ImageQualityController::ImageQualityController() : m_timer(this, &ImageQualityController::highQualityRepaintTimerFired) , m_animatedResizeIsActive(false) + , m_liveResizeOptimizationIsActive(false) { } @@ -129,11 +132,22 @@ void ImageQualityController::objectDestroyed(RenderBoxModelObject* object) void ImageQualityController::highQualityRepaintTimerFired(Timer<ImageQualityController>*) { - if (m_animatedResizeIsActive) { - m_animatedResizeIsActive = false; - for (ObjectLayerSizeMap::iterator it = m_objectLayerSizeMap.begin(); it != m_objectLayerSizeMap.end(); ++it) - it->key->repaint(); + if (!m_animatedResizeIsActive && !m_liveResizeOptimizationIsActive) + return; + m_animatedResizeIsActive = false; + + for (ObjectLayerSizeMap::iterator it = m_objectLayerSizeMap.begin(); it != m_objectLayerSizeMap.end(); ++it) { + if (Frame* frame = it->key->document()->frame()) { + // If this renderer's containing FrameView is in live resize, punt the timer and hold back for now. + if (frame->view() && frame->view()->inLiveResize()) { + restartTimer(); + return; + } + } + it->key->repaint(); } + + m_liveResizeOptimizationIsActive = false; } void ImageQualityController::restartTimer() @@ -148,9 +162,16 @@ bool ImageQualityController::shouldPaintAtLowQuality(GraphicsContext* context, R if (!image || !image->isBitmapImage() || context->paintingDisabled()) return false; - if (object->style()->imageRendering() == ImageRenderingOptimizeContrast) + switch (object->style()->imageRendering()) { + case ImageRenderingOptimizeSpeed: + case ImageRenderingCrispEdges: return true; - + case ImageRenderingOptimizeQuality: + return false; + case ImageRenderingAuto: + break; + } + // Make sure to use the unzoomed image size, since if a full page zoom is in effect, the image // is actually being scaled. IntSize imageSize(image->width(), image->height()); @@ -168,6 +189,22 @@ bool ImageQualityController::shouldPaintAtLowQuality(GraphicsContext* context, R } } + // If the containing FrameView is being resized, paint at low quality until resizing is finished. + if (Frame* frame = object->document()->frame()) { + bool frameViewIsCurrentlyInLiveResize = frame->view() && frame->view()->inLiveResize(); + if (frameViewIsCurrentlyInLiveResize) { + set(object, innerMap, layer, size); + restartTimer(); + m_liveResizeOptimizationIsActive = true; + return true; + } + if (m_liveResizeOptimizationIsActive) { + // Live resize has ended, paint in HQ and remove this object from the list. + removeLayer(object, innerMap, layer); + return false; + } + } + const AffineTransform& currentTransform = context->getCTM(); bool contextIsScaled = !currentTransform.isIdentityOrTranslationOrFlipped(); if (!contextIsScaled && size == imageSize) { @@ -310,7 +347,7 @@ bool RenderBoxModelObject::shouldPaintAtLowQuality(GraphicsContext* context, Ima return imageQualityController()->shouldPaintAtLowQuality(context, this, image, layer, size); } -RenderBoxModelObject::RenderBoxModelObject(Node* node) +RenderBoxModelObject::RenderBoxModelObject(ContainerNode* node) : RenderLayerModelObject(node) { } @@ -331,15 +368,6 @@ void RenderBoxModelObject::willBeDestroyed() // A continuation of this RenderObject should be destroyed at subclasses. ASSERT(!continuation()); - if (isPositioned()) { - if (RenderView* view = this->view()) { - if (FrameView* frameView = view->frameView()) { - if (style()->hasViewportConstrainedPosition()) - frameView->removeViewportConstrainedObject(this); - } - } - } - // If this is a first-letter object with a remaining text fragment then the // entry needs to be cleared from the map. if (firstLetterRemainingText()) @@ -357,8 +385,7 @@ void RenderBoxModelObject::updateFromStyle() RenderStyle* styleToUse = style(); setHasBoxDecorations(hasBackground() || styleToUse->hasBorder() || styleToUse->hasAppearance() || styleToUse->boxShadow()); setInline(styleToUse->isDisplayInlineType()); - setRelPositioned(styleToUse->position() == RelativePosition); - setStickyPositioned(styleToUse->position() == StickyPosition); + setPositionState(styleToUse->position()); setHorizontalWritingMode(styleToUse->isHorizontalWritingMode()); } @@ -378,6 +405,37 @@ static LayoutSize accumulateInFlowPositionOffsets(const RenderObject* child) return offset; } +bool RenderBoxModelObject::hasAutoHeightOrContainingBlockWithAutoHeight() const +{ + Length logicalHeightLength = style()->logicalHeight(); + if (logicalHeightLength.isAuto()) + return true; + + // For percentage heights: The percentage is calculated with respect to the height of the generated box's + // containing block. If the height of the containing block is not specified explicitly (i.e., it depends + // on content height), and this element is not absolutely positioned, the value computes to 'auto'. + if (!logicalHeightLength.isPercent() || isOutOfFlowPositioned() || document()->inQuirksMode()) + return false; + + // Anonymous block boxes are ignored when resolving percentage values that would refer to it: + // the closest non-anonymous ancestor box is used instead. + RenderBlock* cb = containingBlock(); + while (cb->isAnonymous()) + cb = cb->containingBlock(); + + // Matching RenderBox::percentageLogicalHeightIsResolvableFromBlock() by + // ignoring table cell's attribute value, where it says that table cells violate + // what the CSS spec says to do with heights. Basically we + // don't care if the cell specified a height or not. + if (cb->isTableCell()) + return false; + + if (!cb->style()->logicalHeight().isAuto() || (!cb->style()->logicalTop().isAuto() && !cb->style()->logicalBottom().isAuto())) + return false; + + return true; +} + LayoutSize RenderBoxModelObject::relativePositionOffset() const { LayoutSize offset = accumulateInFlowPositionOffsets(this); @@ -404,13 +462,13 @@ LayoutSize RenderBoxModelObject::relativePositionOffset() const // calculate the percent offset based on this height. // See <https://bugs.webkit.org/show_bug.cgi?id=26396>. if (!style()->top().isAuto() - && (!containingBlock->style()->height().isAuto() + && (!containingBlock->hasAutoHeightOrContainingBlockWithAutoHeight() || !style()->top().isPercent() || containingBlock->stretchesToViewport())) offset.expand(0, valueForLength(style()->top(), containingBlock->availableHeight(), view())); else if (!style()->bottom().isAuto() - && (!containingBlock->style()->height().isAuto() + && (!containingBlock->hasAutoHeightOrContainingBlockWithAutoHeight() || !style()->bottom().isPercent() || containingBlock->stretchesToViewport())) offset.expand(0, -valueForLength(style()->bottom(), containingBlock->availableHeight(), view())); @@ -434,20 +492,31 @@ LayoutPoint RenderBoxModelObject::adjustedPositionRelativeToOffsetParent(const L if (const RenderBoxModelObject* offsetParent = this->offsetParent()) { if (offsetParent->isBox() && !offsetParent->isBody()) referencePoint.move(-toRenderBox(offsetParent)->borderLeft(), -toRenderBox(offsetParent)->borderTop()); - if (!isOutOfFlowPositioned()) { + if (!isOutOfFlowPositioned() || flowThreadContainingBlock()) { if (isRelPositioned()) referencePoint.move(relativePositionOffset()); else if (isStickyPositioned()) referencePoint.move(stickyPositionOffset()); - const RenderObject* curr = parent(); - while (curr != offsetParent) { + + // CSS regions specification says that region flows should return the body element as their offsetParent. + // Since we will bypass the body’s renderer anyway, just end the loop if we encounter a region flow (named flow thread). + // See http://dev.w3.org/csswg/css-regions/#cssomview-offset-attributes + RenderObject* curr = parent(); + while (curr != offsetParent && !curr->isRenderNamedFlowThread()) { // FIXME: What are we supposed to do inside SVG content? - if (curr->isBox() && !curr->isTableRow()) - referencePoint.moveBy(toRenderBox(curr)->topLeftLocation()); - referencePoint.move(curr->parent()->offsetForColumns(referencePoint)); + if (!isOutOfFlowPositioned()) { + if (curr->isBox() && !curr->isTableRow()) + referencePoint.moveBy(toRenderBox(curr)->topLeftLocation()); + referencePoint.move(curr->parent()->offsetForColumns(referencePoint)); + } curr = curr->parent(); } - if (offsetParent->isBox() && offsetParent->isBody() && !offsetParent->isPositioned()) + + // Compute the offset position for elements inside named flow threads for which the offsetParent was the body. + // See https://bugs.webkit.org/show_bug.cgi?id=115899 + if (curr->isRenderNamedFlowThread()) + referencePoint = toRenderNamedFlowThread(curr)->adjustedPositionRelativeToOffsetParent(*this, referencePoint); + else if (offsetParent->isBox() && offsetParent->isBody() && !offsetParent->isPositioned()) referencePoint.moveBy(toRenderBox(offsetParent)->topLeftLocation()); } } @@ -455,63 +524,116 @@ LayoutPoint RenderBoxModelObject::adjustedPositionRelativeToOffsetParent(const L return referencePoint; } -void RenderBoxModelObject::computeStickyPositionConstraints(StickyPositionViewportConstraints& constraints, const FloatRect& viewportRect) const +void RenderBoxModelObject::computeStickyPositionConstraints(StickyPositionViewportConstraints& constraints, const FloatRect& constrainingRect) const { + constraints.setConstrainingRectAtLastLayout(constrainingRect); + RenderBlock* containingBlock = this->containingBlock(); + RenderLayer* enclosingClippingLayer = layer()->enclosingOverflowClipLayer(ExcludeSelf); + RenderBox* enclosingClippingBox = enclosingClippingLayer ? toRenderBox(enclosingClippingLayer->renderer()) : view(); - LayoutRect containerContentRect = containingBlock->contentBoxRect(); + LayoutRect containerContentRect; + if (!enclosingClippingLayer || (containingBlock != enclosingClippingBox)) + containerContentRect = containingBlock->contentBoxRect(); + else { + containerContentRect = containingBlock->layoutOverflowRect(); + LayoutPoint containerLocation = containerContentRect.location() + LayoutPoint(containingBlock->borderLeft() + containingBlock->paddingLeft(), + containingBlock->borderTop() + containingBlock->paddingTop()); + containerContentRect.setLocation(containerLocation); + } + + LayoutUnit maxWidth = containingBlock->availableLogicalWidth(); - LayoutUnit minLeftMargin = minimumValueForLength(style()->marginLeft(), containingBlock->availableLogicalWidth(), view()); - LayoutUnit minTopMargin = minimumValueForLength(style()->marginTop(), containingBlock->availableLogicalWidth(), view()); - LayoutUnit minRightMargin = minimumValueForLength(style()->marginRight(), containingBlock->availableLogicalWidth(), view()); - LayoutUnit minBottomMargin = minimumValueForLength(style()->marginBottom(), containingBlock->availableLogicalWidth(), view()); + // Sticky positioned element ignore any override logical width on the containing block (as they don't call + // containingBlockLogicalWidthForContent). It's unclear whether this is totally fine. + LayoutBoxExtent minMargin(minimumValueForLength(style()->marginTop(), maxWidth, view()), + minimumValueForLength(style()->marginRight(), maxWidth, view()), + minimumValueForLength(style()->marginBottom(), maxWidth, view()), + minimumValueForLength(style()->marginLeft(), maxWidth, view())); // Compute the container-relative area within which the sticky element is allowed to move. - containerContentRect.move(minLeftMargin, minTopMargin); - containerContentRect.contract(minLeftMargin + minRightMargin, minTopMargin + minBottomMargin); - constraints.setAbsoluteContainingBlockRect(containingBlock->localToAbsoluteQuad(FloatRect(containerContentRect), SnapOffsetForTransforms).boundingBox()); + containerContentRect.contract(minMargin); + // Finally compute container rect relative to the scrolling ancestor. + FloatRect containerRectRelativeToScrollingAncestor = containingBlock->localToContainerQuad(FloatRect(containerContentRect), enclosingClippingBox).boundingBox(); + if (enclosingClippingLayer) { + FloatPoint containerLocationRelativeToScrollingAncestor = containerRectRelativeToScrollingAncestor.location() - + FloatSize(enclosingClippingBox->borderLeft() + enclosingClippingBox->paddingLeft(), + enclosingClippingBox->borderTop() + enclosingClippingBox->paddingTop()); + if (enclosingClippingBox != containingBlock) + containerLocationRelativeToScrollingAncestor += enclosingClippingLayer->scrollOffset(); + containerRectRelativeToScrollingAncestor.setLocation(containerLocationRelativeToScrollingAncestor); + } + constraints.setContainingBlockRect(containerRectRelativeToScrollingAncestor); + + // Now compute the sticky box rect, also relative to the scrolling ancestor. LayoutRect stickyBoxRect = frameRectForStickyPositioning(); LayoutRect flippedStickyBoxRect = stickyBoxRect; containingBlock->flipForWritingMode(flippedStickyBoxRect); - LayoutPoint stickyLocation = flippedStickyBoxRect.location(); + FloatRect stickyBoxRelativeToScrollingAnecstor = flippedStickyBoxRect; - // FIXME: sucks to call localToAbsolute again, but we can't just offset from the previously computed rect if there are transforms. - FloatRect absContainerFrame = containingBlock->localToAbsoluteQuad(FloatRect(FloatPoint(), containingBlock->size()), SnapOffsetForTransforms).boundingBox(); - // We can't call localToAbsolute on |this| because that will recur. FIXME: For now, assume that |this| is not transformed. - FloatRect absoluteStickyBoxRect(absContainerFrame.location() + stickyLocation, flippedStickyBoxRect.size()); - constraints.setAbsoluteStickyBoxRect(absoluteStickyBoxRect); + // FIXME: sucks to call localToContainerQuad again, but we can't just offset from the previously computed rect if there are transforms. + // Map to the view to avoid including page scale factor. + FloatPoint stickyLocationRelativeToScrollingAncestor = flippedStickyBoxRect.location() + containingBlock->localToContainerQuad(FloatRect(FloatPoint(), containingBlock->size()), enclosingClippingBox).boundingBox().location(); + if (enclosingClippingLayer) { + stickyLocationRelativeToScrollingAncestor -= FloatSize(enclosingClippingBox->borderLeft() + enclosingClippingBox->paddingLeft(), + enclosingClippingBox->borderTop() + enclosingClippingBox->paddingTop()); + if (enclosingClippingBox != containingBlock) + stickyLocationRelativeToScrollingAncestor += enclosingClippingLayer->scrollOffset(); + } + // FIXME: For now, assume that |this| is not transformed. + stickyBoxRelativeToScrollingAnecstor.setLocation(stickyLocationRelativeToScrollingAncestor); + constraints.setStickyBoxRect(stickyBoxRelativeToScrollingAnecstor); if (!style()->left().isAuto()) { - constraints.setLeftOffset(valueForLength(style()->left(), viewportRect.width(), view())); + constraints.setLeftOffset(valueForLength(style()->left(), constrainingRect.width(), view())); constraints.addAnchorEdge(ViewportConstraints::AnchorEdgeLeft); } if (!style()->right().isAuto()) { - constraints.setRightOffset(valueForLength(style()->right(), viewportRect.width(), view())); + constraints.setRightOffset(valueForLength(style()->right(), constrainingRect.width(), view())); constraints.addAnchorEdge(ViewportConstraints::AnchorEdgeRight); } if (!style()->top().isAuto()) { - constraints.setTopOffset(valueForLength(style()->top(), viewportRect.height(), view())); + constraints.setTopOffset(valueForLength(style()->top(), constrainingRect.height(), view())); constraints.addAnchorEdge(ViewportConstraints::AnchorEdgeTop); } if (!style()->bottom().isAuto()) { - constraints.setBottomOffset(valueForLength(style()->bottom(), viewportRect.height(), view())); + constraints.setBottomOffset(valueForLength(style()->bottom(), constrainingRect.height(), view())); constraints.addAnchorEdge(ViewportConstraints::AnchorEdgeBottom); } } LayoutSize RenderBoxModelObject::stickyPositionOffset() const { - LayoutRect viewportRect = view()->frameView()->visibleContentRect(); + FloatRect constrainingRect; + ASSERT(hasLayer()); + RenderLayer* enclosingClippingLayer = layer()->enclosingOverflowClipLayer(ExcludeSelf); + if (enclosingClippingLayer) { + RenderBox* enclosingClippingBox = toRenderBox(enclosingClippingLayer->renderer()); + LayoutRect clipRect = enclosingClippingBox->overflowClipRect(LayoutPoint(), 0); // FIXME: make this work in regions. + constrainingRect = enclosingClippingBox->localToContainerQuad(FloatRect(clipRect), view()).boundingBox(); + + FloatPoint scrollOffset = FloatPoint() + enclosingClippingLayer->scrollOffset(); + constrainingRect.setLocation(scrollOffset); + } else { + LayoutRect viewportRect = view()->frameView()->viewportConstrainedVisibleContentRect(); + float scale = 1; + if (Frame* frame = view()->frameView()->frame()) + scale = frame->frameScaleFactor(); + + viewportRect.scale(1 / scale); + constrainingRect = viewportRect; + } + StickyPositionViewportConstraints constraints; - computeStickyPositionConstraints(constraints, viewportRect); + computeStickyPositionConstraints(constraints, constrainingRect); // The sticky offset is physical, so we can just return the delta computed in absolute coords (though it may be wrong with transforms). - return LayoutSize(constraints.computeStickyOffset(viewportRect)); + return LayoutSize(constraints.computeStickyOffset(constrainingRect)); } LayoutSize RenderBoxModelObject::offsetForInFlowPosition() const @@ -549,97 +671,12 @@ int RenderBoxModelObject::pixelSnappedOffsetHeight() const return snapSizeToPixel(offsetHeight(), offsetTop()); } -LayoutUnit RenderBoxModelObject::computedCSSPaddingTop() const -{ - LayoutUnit w = 0; - RenderView* renderView = 0; - Length padding = style()->paddingTop(); - if (padding.isPercent()) - w = containingBlock()->availableLogicalWidth(); - else if (padding.isViewportPercentage()) - renderView = view(); - return minimumValueForLength(padding, w, renderView); -} - -LayoutUnit RenderBoxModelObject::computedCSSPaddingBottom() const -{ - LayoutUnit w = 0; - RenderView* renderView = 0; - Length padding = style()->paddingBottom(); - if (padding.isPercent()) - w = containingBlock()->availableLogicalWidth(); - else if (padding.isViewportPercentage()) - renderView = view(); - return minimumValueForLength(padding, w, renderView); -} - -LayoutUnit RenderBoxModelObject::computedCSSPaddingLeft() const -{ - LayoutUnit w = 0; - RenderView* renderView = 0; - Length padding = style()->paddingLeft(); - if (padding.isPercent()) - w = containingBlock()->availableLogicalWidth(); - else if (padding.isViewportPercentage()) - renderView = view(); - return minimumValueForLength(padding, w, renderView); -} - -LayoutUnit RenderBoxModelObject::computedCSSPaddingRight() const +LayoutUnit RenderBoxModelObject::computedCSSPadding(Length padding) const { LayoutUnit w = 0; RenderView* renderView = 0; - Length padding = style()->paddingRight(); if (padding.isPercent()) - w = containingBlock()->availableLogicalWidth(); - else if (padding.isViewportPercentage()) - renderView = view(); - return minimumValueForLength(padding, w, renderView); -} - -LayoutUnit RenderBoxModelObject::computedCSSPaddingBefore() const -{ - LayoutUnit w = 0; - RenderView* renderView = 0; - Length padding = style()->paddingBefore(); - if (padding.isPercent()) - w = containingBlock()->availableLogicalWidth(); - else if (padding.isViewportPercentage()) - renderView = view(); - return minimumValueForLength(padding, w, renderView); -} - -LayoutUnit RenderBoxModelObject::computedCSSPaddingAfter() const -{ - LayoutUnit w = 0; - RenderView* renderView = 0; - Length padding = style()->paddingAfter(); - if (padding.isPercent()) - w = containingBlock()->availableLogicalWidth(); - else if (padding.isViewportPercentage()) - renderView = view(); - return minimumValueForLength(padding, w, renderView); -} - -LayoutUnit RenderBoxModelObject::computedCSSPaddingStart() const -{ - LayoutUnit w = 0; - RenderView* renderView = 0; - Length padding = style()->paddingStart(); - if (padding.isPercent()) - w = containingBlock()->availableLogicalWidth(); - else if (padding.isViewportPercentage()) - renderView = view(); - return minimumValueForLength(padding, w, renderView); -} - -LayoutUnit RenderBoxModelObject::computedCSSPaddingEnd() const -{ - LayoutUnit w = 0; - RenderView* renderView = 0; - Length padding = style()->paddingEnd(); - if (padding.isPercent()) - w = containingBlock()->availableLogicalWidth(); + w = containingBlockLogicalWidthForContent(); else if (padding.isViewportPercentage()) renderView = view(); return minimumValueForLength(padding, w, renderView); @@ -661,31 +698,31 @@ RoundedRect RenderBoxModelObject::getBackgroundRoundedRect(const LayoutRect& bor void RenderBoxModelObject::clipRoundedInnerRect(GraphicsContext * context, const LayoutRect& rect, const RoundedRect& clipRect) { if (clipRect.isRenderable()) - context->addRoundedRectClip(clipRect); + context->clipRoundedRect(clipRect); else { // We create a rounded rect for each of the corners and clip it, while making sure we clip opposing corners together. if (!clipRect.radii().topLeft().isEmpty() || !clipRect.radii().bottomRight().isEmpty()) { IntRect topCorner(clipRect.rect().x(), clipRect.rect().y(), rect.maxX() - clipRect.rect().x(), rect.maxY() - clipRect.rect().y()); RoundedRect::Radii topCornerRadii; topCornerRadii.setTopLeft(clipRect.radii().topLeft()); - context->addRoundedRectClip(RoundedRect(topCorner, topCornerRadii)); + context->clipRoundedRect(RoundedRect(topCorner, topCornerRadii)); IntRect bottomCorner(rect.x(), rect.y(), clipRect.rect().maxX() - rect.x(), clipRect.rect().maxY() - rect.y()); RoundedRect::Radii bottomCornerRadii; bottomCornerRadii.setBottomRight(clipRect.radii().bottomRight()); - context->addRoundedRectClip(RoundedRect(bottomCorner, bottomCornerRadii)); + context->clipRoundedRect(RoundedRect(bottomCorner, bottomCornerRadii)); } if (!clipRect.radii().topRight().isEmpty() || !clipRect.radii().bottomLeft().isEmpty()) { IntRect topCorner(rect.x(), clipRect.rect().y(), clipRect.rect().maxX() - rect.x(), rect.maxY() - clipRect.rect().y()); RoundedRect::Radii topCornerRadii; topCornerRadii.setTopRight(clipRect.radii().topRight()); - context->addRoundedRectClip(RoundedRect(topCorner, topCornerRadii)); + context->clipRoundedRect(RoundedRect(topCorner, topCornerRadii)); IntRect bottomCorner(clipRect.rect().x(), rect.y(), rect.maxX() - clipRect.rect().x(), clipRect.rect().maxY() - rect.y()); RoundedRect::Radii bottomCornerRadii; bottomCornerRadii.setBottomLeft(clipRect.radii().bottomLeft()); - context->addRoundedRectClip(RoundedRect(bottomCorner, bottomCornerRadii)); + context->clipRoundedRect(RoundedRect(bottomCorner, bottomCornerRadii)); } } } @@ -725,9 +762,9 @@ static void applyBoxShadowForBackground(GraphicsContext* context, RenderStyle* s FloatSize shadowOffset(boxShadow->x(), boxShadow->y()); if (!boxShadow->isWebkitBoxShadow()) - context->setShadow(shadowOffset, boxShadow->blur(), boxShadow->color(), style->colorSpace()); + context->setShadow(shadowOffset, boxShadow->radius(), boxShadow->color(), style->colorSpace()); else - context->setLegacyShadow(shadowOffset, boxShadow->blur(), boxShadow->color(), style->colorSpace()); + context->setLegacyShadow(shadowOffset, boxShadow->radius(), boxShadow->color(), style->colorSpace()); } void RenderBoxModelObject::paintFillLayerExtended(const PaintInfo& paintInfo, const Color& color, const FillLayer* bgLayer, const LayoutRect& rect, @@ -787,10 +824,17 @@ void RenderBoxModelObject::paintFillLayerExtended(const PaintInfo& paintInfo, co if (hasRoundedBorder && bleedAvoidance != BackgroundBleedUseTransparencyLayer) { RoundedRect border = backgroundRoundedRectAdjustedForBleedAvoidance(context, rect, bleedAvoidance, box, boxSize, includeLeftEdge, includeRightEdge); - context->fillRoundedRect(border, bgColor, style()->colorSpace()); + if (border.isRenderable()) + context->fillRoundedRect(border, bgColor, style()->colorSpace()); + else { + context->save(); + clipRoundedInnerRect(context, rect, border); + context->fillRect(border.rect(), bgColor, style()->colorSpace()); + context->restore(); + } } else context->fillRect(pixelSnappedIntRect(rect), bgColor, style()->colorSpace()); - + return; } @@ -861,7 +905,7 @@ void RenderBoxModelObject::paintFillLayerExtended(const PaintInfo& paintInfo, co // Now add the text to the clip. We do this by painting using a special paint phase that signals to // InlineTextBoxes that they should just add their contents to the clip. - PaintInfo info(maskImageContext, maskRect, PaintPhaseTextClip, true, 0, paintInfo.renderRegion, 0); + PaintInfo info(maskImageContext, maskRect, PaintPhaseTextClip, PaintBehaviorForceBlackText, 0, paintInfo.renderRegion); if (box) { RootInlineBox* root = box->root(); box->paint(info, LayoutPoint(scrolledPaintRect.x() - box->x(), scrolledPaintRect.y() - box->y()), root->lineTop(), root->lineBottom()); @@ -946,7 +990,7 @@ void RenderBoxModelObject::paintFillLayerExtended(const PaintInfo& paintInfo, co // no progressive loading of the background image if (shouldPaintBackgroundImage) { BackgroundImageGeometry geometry; - calculateBackgroundImageGeometry(bgLayer, scrolledPaintRect, geometry); + calculateBackgroundImageGeometry(paintInfo.paintContainer, bgLayer, scrolledPaintRect, geometry, backgroundObject); geometry.clip(paintInfo.rect); if (!geometry.destRect().isEmpty()) { CompositeOperator compositeOp = op == CompositeSourceOver ? bgLayer->composite() : op; @@ -954,7 +998,7 @@ void RenderBoxModelObject::paintFillLayerExtended(const PaintInfo& paintInfo, co RefPtr<Image> image = bgImage->image(clientForBackgroundImage, geometry.tileSize()); bool useLowQualityScaling = shouldPaintAtLowQuality(context, image.get(), bgLayer, geometry.tileSize()); context->drawTiledImage(image.get(), style()->colorSpace(), geometry.destRect(), geometry.relativePhase(), geometry.tileSize(), - compositeOp, useLowQualityScaling); + compositeOp, useLowQualityScaling, bgLayer->blendMode()); } } @@ -1085,7 +1129,7 @@ IntSize RenderBoxModelObject::calculateFillTileSize(const FillLayer* fillLayer, if (layerWidth.isFixed()) tileSize.setWidth(layerWidth.value()); - else if (layerWidth.isPercent() || layerHeight.isViewportPercentage()) + else if (layerWidth.isPercent() || layerWidth.isViewportPercentage()) tileSize.setWidth(valueForLength(layerWidth, positioningAreaSize.width(), renderView)); if (layerHeight.isFixed()) @@ -1165,8 +1209,27 @@ IntPoint RenderBoxModelObject::BackgroundImageGeometry::relativePhase() const return phase; } -void RenderBoxModelObject::calculateBackgroundImageGeometry(const FillLayer* fillLayer, const LayoutRect& paintRect, - BackgroundImageGeometry& geometry) +bool RenderBoxModelObject::fixedBackgroundPaintsInLocalCoordinates() const +{ +#if USE(ACCELERATED_COMPOSITING) + if (!isRoot()) + return false; + + if (view()->frameView() && view()->frameView()->paintBehavior() & PaintBehaviorFlattenCompositingLayers) + return false; + + RenderLayer* rootLayer = view()->layer(); + if (!rootLayer || !rootLayer->isComposited()) + return false; + + return rootLayer->backing()->backgroundLayerPaintsFixedRootBackground(); +#else + return false; +#endif +} + +void RenderBoxModelObject::calculateBackgroundImageGeometry(const RenderLayerModelObject* paintContainer, const FillLayer* fillLayer, const LayoutRect& paintRect, + BackgroundImageGeometry& geometry, RenderObject* backgroundObject) const { LayoutUnit left = 0; LayoutUnit top = 0; @@ -1175,8 +1238,9 @@ void RenderBoxModelObject::calculateBackgroundImageGeometry(const FillLayer* fil // Determine the background positioning area and set destRect to the background painting area. // destRect will be adjusted later if the background is non-repeating. + // FIXME: transforms spec says that fixed backgrounds behave like scroll inside transforms. https://bugs.webkit.org/show_bug.cgi?id=15679 bool fixedAttachment = fillLayer->attachment() == FixedBackgroundAttachment; - + #if ENABLE(FAST_MOBILE_SCROLLING) if (view()->frameView() && view()->frameView()->canBlitOnScroll()) { // As a side effect of an optimization to blit on scroll, we do not honor the CSS @@ -1216,29 +1280,71 @@ void RenderBoxModelObject::calculateBackgroundImageGeometry(const FillLayer* fil } else positioningAreaSize = pixelSnappedIntSize(paintRect.size() - LayoutSize(left + right, top + bottom), paintRect.location()); } else { - geometry.setDestRect(pixelSnappedIntRect(viewRect())); + geometry.setHasNonLocalGeometry(); + + IntRect viewportRect = pixelSnappedIntRect(viewRect()); + if (fixedBackgroundPaintsInLocalCoordinates()) + viewportRect.setLocation(IntPoint()); + else if (FrameView* frameView = view()->frameView()) + viewportRect.setLocation(IntPoint(frameView->scrollOffsetForFixedPosition())); + + if (paintContainer) { + IntPoint absoluteContainerOffset = roundedIntPoint(paintContainer->localToAbsolute(FloatPoint())); + viewportRect.moveBy(-absoluteContainerOffset); + } + + geometry.setDestRect(pixelSnappedIntRect(viewportRect)); positioningAreaSize = geometry.destRect().size(); } + const RenderObject* clientForBackgroundImage = backgroundObject ? backgroundObject : this; IntSize fillTileSize = calculateFillTileSize(fillLayer, positioningAreaSize); - fillLayer->image()->setContainerSizeForRenderer(this, fillTileSize, style()->effectiveZoom()); + fillLayer->image()->setContainerSizeForRenderer(clientForBackgroundImage, fillTileSize, style()->effectiveZoom()); geometry.setTileSize(fillTileSize); EFillRepeat backgroundRepeatX = fillLayer->repeatX(); EFillRepeat backgroundRepeatY = fillLayer->repeatY(); RenderView* renderView = view(); + int availableWidth = positioningAreaSize.width() - geometry.tileSize().width(); + int availableHeight = positioningAreaSize.height() - geometry.tileSize().height(); + + LayoutUnit computedXPosition = minimumValueForLength(fillLayer->xPosition(), availableWidth, renderView, true); + if (backgroundRepeatX == RoundFill && positioningAreaSize.width() > 0 && fillTileSize.width() > 0) { + int nrTiles = ceil((double)positioningAreaSize.width() / fillTileSize.width()); + + if (fillLayer->size().size.height().isAuto() && backgroundRepeatY != RoundFill) + fillTileSize.setHeight(fillTileSize.height() * positioningAreaSize.width() / (nrTiles * fillTileSize.width())); + + fillTileSize.setWidth(positioningAreaSize.width() / nrTiles); + geometry.setTileSize(fillTileSize); + geometry.setPhaseX(geometry.tileSize().width() ? geometry.tileSize().width() - roundToInt(computedXPosition + left) % geometry.tileSize().width() : 0); + } + + LayoutUnit computedYPosition = minimumValueForLength(fillLayer->yPosition(), availableHeight, renderView, true); + if (backgroundRepeatY == RoundFill && positioningAreaSize.height() > 0 && fillTileSize.height() > 0) { + int nrTiles = ceil((double)positioningAreaSize.height() / fillTileSize.height()); + + if (fillLayer->size().size.width().isAuto() && backgroundRepeatX != RoundFill) + fillTileSize.setWidth(fillTileSize.width() * positioningAreaSize.height() / (nrTiles * fillTileSize.height())); + + fillTileSize.setHeight(positioningAreaSize.height() / nrTiles); + geometry.setTileSize(fillTileSize); + geometry.setPhaseY(geometry.tileSize().height() ? geometry.tileSize().height() - roundToInt(computedYPosition + top) % geometry.tileSize().height() : 0); + } - LayoutUnit xPosition = minimumValueForLength(fillLayer->xPosition(), positioningAreaSize.width() - geometry.tileSize().width(), renderView, true); if (backgroundRepeatX == RepeatFill) - geometry.setPhaseX(geometry.tileSize().width() ? geometry.tileSize().width() - roundToInt(xPosition + left) % geometry.tileSize().width() : 0); - else - geometry.setNoRepeatX(xPosition + left); + geometry.setPhaseX(geometry.tileSize().width() ? geometry.tileSize().width() - roundToInt(computedXPosition + left) % geometry.tileSize().width() : 0); + else if (backgroundRepeatX == NoRepeatFill) { + int xOffset = fillLayer->backgroundXOrigin() == RightEdge ? availableWidth - computedXPosition : computedXPosition; + geometry.setNoRepeatX(left + xOffset); + } - LayoutUnit yPosition = minimumValueForLength(fillLayer->yPosition(), positioningAreaSize.height() - geometry.tileSize().height(), renderView, true); if (backgroundRepeatY == RepeatFill) - geometry.setPhaseY(geometry.tileSize().height() ? geometry.tileSize().height() - roundToInt(yPosition + top) % geometry.tileSize().height() : 0); - else - geometry.setNoRepeatY(yPosition + top); + geometry.setPhaseY(geometry.tileSize().height() ? geometry.tileSize().height() - roundToInt(computedYPosition + top) % geometry.tileSize().height() : 0); + else if (backgroundRepeatY == NoRepeatFill) { + int yOffset = fillLayer->backgroundYOrigin() == BottomEdge ? availableHeight - computedYPosition : computedYPosition; + geometry.setNoRepeatY(top + yOffset); + } if (fixedAttachment) geometry.useFixedAttachment(snappedPaintRect.location()); @@ -1247,6 +1353,16 @@ void RenderBoxModelObject::calculateBackgroundImageGeometry(const FillLayer* fil geometry.setDestOrigin(geometry.destRect().location()); } +void RenderBoxModelObject::getGeometryForBackgroundImage(const RenderLayerModelObject* paintContainer, IntRect& destRect, IntPoint& phase, IntSize& tileSize) const +{ + const FillLayer* backgroundLayer = style()->backgroundLayers(); + BackgroundImageGeometry geometry; + calculateBackgroundImageGeometry(paintContainer, backgroundLayer, destRect, geometry); + phase = geometry.phase(); + tileSize = geometry.tileSize(); + destRect = geometry.destRect(); +} + static LayoutUnit computeBorderImageSide(Length borderSlice, LayoutUnit borderSide, LayoutUnit imageSide, LayoutUnit boxExtent, RenderView* renderView) { if (borderSlice.isRelative()) @@ -1795,13 +1911,17 @@ void RenderBoxModelObject::paintBorderSides(GraphicsContext* graphicsContext, co void RenderBoxModelObject::paintTranslucentBorderSides(GraphicsContext* graphicsContext, const RenderStyle* style, const RoundedRect& outerBorder, const RoundedRect& innerBorder, const IntPoint& innerBorderAdjustment, const BorderEdge edges[], BorderEdgeFlags edgesToDraw, BackgroundBleedAvoidance bleedAvoidance, bool includeLogicalLeftEdge, bool includeLogicalRightEdge, bool antialias) { + // willBeOverdrawn assumes that we draw in order: top, bottom, left, right. + // This is different from BoxSide enum order. + static BoxSide paintOrder[] = { BSTop, BSBottom, BSLeft, BSRight }; + while (edgesToDraw) { // Find undrawn edges sharing a color. Color commonColor; BorderEdgeFlags commonColorEdgeSet = 0; - for (int i = BSTop; i <= BSLeft; ++i) { - BoxSide currSide = static_cast<BoxSide>(i); + for (size_t i = 0; i < sizeof(paintOrder) / sizeof(paintOrder[0]); ++i) { + BoxSide currSide = paintOrder[i]; if (!includesEdge(edgesToDraw, currSide)) continue; @@ -1977,7 +2097,7 @@ void RenderBoxModelObject::paintBorder(const PaintInfo& info, const LayoutRect& if (clipToOuterBorder) { // Clip to the inner and outer radii rects. if (bleedAvoidance != BackgroundBleedUseTransparencyLayer) - graphicsContext->addRoundedRectClip(outerBorder); + graphicsContext->clipRoundedRect(outerBorder); // isRenderable() check avoids issue described in https://bugs.webkit.org/show_bug.cgi?id=38787 // The inside will be clipped out later (in clipBorderSideForComplexInnerPath) if (innerBorder.isRenderable()) @@ -2074,7 +2194,7 @@ void RenderBoxModelObject::drawBoxSideFromPath(GraphicsContext* graphicsContext, innerBorderTopWidth, innerBorderBottomWidth, innerBorderLeftWidth, innerBorderRightWidth, includeLogicalLeftEdge, includeLogicalRightEdge); - graphicsContext->addRoundedRectClip(innerClip); + graphicsContext->clipRoundedRect(innerClip); drawBoxSideFromPath(graphicsContext, borderRect, borderPath, edges, thickness, drawThickness, side, style, color, SOLID, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge); } @@ -2125,7 +2245,7 @@ void RenderBoxModelObject::drawBoxSideFromPath(GraphicsContext* graphicsContext, topWidth, bottomWidth, leftWidth, rightWidth, includeLogicalLeftEdge, includeLogicalRightEdge); - graphicsContext->addRoundedRectClip(clipRect); + graphicsContext->clipRoundedRect(clipRect); drawBoxSideFromPath(graphicsContext, borderRect, borderPath, edges, thickness, drawThickness, side, style, color, s2, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge); return; } @@ -2492,11 +2612,11 @@ bool RenderBoxModelObject::boxShadowShouldBeAppliedToBackground(BackgroundBleedA return true; } -static inline IntRect areaCastingShadowInHole(const IntRect& holeRect, int shadowBlur, int shadowSpread, const IntSize& shadowOffset) +static inline IntRect areaCastingShadowInHole(const IntRect& holeRect, int shadowExtent, int shadowSpread, const IntSize& shadowOffset) { IntRect bounds(holeRect); - bounds.inflate(shadowBlur); + bounds.inflate(shadowExtent); if (shadowSpread < 0) bounds.inflate(-shadowSpread); @@ -2525,10 +2645,11 @@ void RenderBoxModelObject::paintBoxShadow(const PaintInfo& info, const LayoutRec continue; IntSize shadowOffset(shadow->x(), shadow->y()); - int shadowBlur = shadow->blur(); + int shadowRadius = shadow->radius(); + int shadowPaintingExtent = shadow->paintingExtent(); int shadowSpread = shadow->spread(); - if (shadowOffset.isZero() && !shadowBlur && !shadowSpread) + if (shadowOffset.isZero() && !shadowRadius && !shadowSpread) continue; const Color& shadowColor = shadow->color(); @@ -2540,7 +2661,7 @@ void RenderBoxModelObject::paintBoxShadow(const PaintInfo& info, const LayoutRec continue; IntRect shadowRect(border.rect()); - shadowRect.inflate(shadowBlur + shadowSpread); + shadowRect.inflate(shadowPaintingExtent + shadowSpread); shadowRect.move(shadowOffset); GraphicsContextStateSaver stateSaver(*context); @@ -2548,14 +2669,14 @@ void RenderBoxModelObject::paintBoxShadow(const PaintInfo& info, const LayoutRec // Move the fill just outside the clip, adding 1 pixel separation so that the fill does not // bleed in (due to antialiasing) if the context is transformed. - IntSize extraOffset(paintRect.pixelSnappedWidth() + max(0, shadowOffset.width()) + shadowBlur + 2 * shadowSpread + 1, 0); + IntSize extraOffset(paintRect.pixelSnappedWidth() + max(0, shadowOffset.width()) + shadowPaintingExtent + 2 * shadowSpread + 1, 0); shadowOffset -= extraOffset; fillRect.move(extraOffset); if (shadow->isWebkitBoxShadow()) - context->setLegacyShadow(shadowOffset, shadowBlur, shadowColor, s->colorSpace()); + context->setLegacyShadow(shadowOffset, shadowRadius, shadowColor, s->colorSpace()); else - context->setShadow(shadowOffset, shadowBlur, shadowColor, s->colorSpace()); + context->setShadow(shadowOffset, shadowRadius, shadowColor, s->colorSpace()); if (hasBorderRadius) { RoundedRect rectToClipOut = border; @@ -2571,7 +2692,7 @@ void RenderBoxModelObject::paintBoxShadow(const PaintInfo& info, const LayoutRec context->clipOutRoundedRect(rectToClipOut); RoundedRect influenceRect(shadowRect, border.radii()); - influenceRect.expandRadii(2 * shadowBlur + shadowSpread); + influenceRect.expandRadii(2 * shadowPaintingExtent + shadowSpread); if (allCornersClippedOut(influenceRect, info.rect)) context->fillRect(fillRect.rect(), Color::black, s->colorSpace()); else { @@ -2614,23 +2735,23 @@ void RenderBoxModelObject::paintBoxShadow(const PaintInfo& info, const LayoutRec if (!includeLogicalLeftEdge) { if (isHorizontal) { - holeRect.move(-max(shadowOffset.width(), 0) - shadowBlur, 0); - holeRect.setWidth(holeRect.width() + max(shadowOffset.width(), 0) + shadowBlur); + holeRect.move(-max(shadowOffset.width(), 0) - shadowPaintingExtent, 0); + holeRect.setWidth(holeRect.width() + max(shadowOffset.width(), 0) + shadowPaintingExtent); } else { - holeRect.move(0, -max(shadowOffset.height(), 0) - shadowBlur); - holeRect.setHeight(holeRect.height() + max(shadowOffset.height(), 0) + shadowBlur); + holeRect.move(0, -max(shadowOffset.height(), 0) - shadowPaintingExtent); + holeRect.setHeight(holeRect.height() + max(shadowOffset.height(), 0) + shadowPaintingExtent); } } if (!includeLogicalRightEdge) { if (isHorizontal) - holeRect.setWidth(holeRect.width() - min(shadowOffset.width(), 0) + shadowBlur); + holeRect.setWidth(holeRect.width() - min(shadowOffset.width(), 0) + shadowPaintingExtent); else - holeRect.setHeight(holeRect.height() - min(shadowOffset.height(), 0) + shadowBlur); + holeRect.setHeight(holeRect.height() - min(shadowOffset.height(), 0) + shadowPaintingExtent); } Color fillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), 255); - IntRect outerRect = areaCastingShadowInHole(border.rect(), shadowBlur, shadowSpread, shadowOffset); + IntRect outerRect = areaCastingShadowInHole(border.rect(), shadowPaintingExtent, shadowSpread, shadowOffset); RoundedRect roundedHole(holeRect, border.radii()); GraphicsContextStateSaver stateSaver(*context); @@ -2642,14 +2763,14 @@ void RenderBoxModelObject::paintBoxShadow(const PaintInfo& info, const LayoutRec } else context->clip(border.rect()); - IntSize extraOffset(2 * paintRect.pixelSnappedWidth() + max(0, shadowOffset.width()) + shadowBlur - 2 * shadowSpread + 1, 0); + IntSize extraOffset(2 * paintRect.pixelSnappedWidth() + max(0, shadowOffset.width()) + shadowPaintingExtent - 2 * shadowSpread + 1, 0); context->translate(extraOffset.width(), extraOffset.height()); shadowOffset -= extraOffset; if (shadow->isWebkitBoxShadow()) - context->setLegacyShadow(shadowOffset, shadowBlur, shadowColor, s->colorSpace()); + context->setLegacyShadow(shadowOffset, shadowRadius, shadowColor, s->colorSpace()); else - context->setShadow(shadowOffset, shadowBlur, shadowColor, s->colorSpace()); + context->setShadow(shadowOffset, shadowRadius, shadowColor, s->colorSpace()); context->fillRectWithRoundedHole(outerRect, roundedHole, fillColor, s->colorSpace()); } @@ -2776,12 +2897,24 @@ void RenderBoxModelObject::mapAbsoluteToLocalPoint(MapCoordinatesFlags mode, Tra if (!o) return; + // The point inside a box that's inside a region has its coordinates relative to the region, + // not the FlowThread that is its container in the RenderObject tree. + if (o->isRenderFlowThread() && isRenderBlock()) { + // FIXME (CSSREGIONS): switch to Box instead of Block when we'll have range information + // for boxes as well, not just for blocks. + RenderRegion* startRegion; + RenderRegion* endRegion; + toRenderFlowThread(o)->getRegionRangeForBox(toRenderBlock(this), startRegion, endRegion); + if (startRegion) + o = startRegion; + } + o->mapAbsoluteToLocalPoint(mode, transformState); LayoutSize containerOffset = offsetFromContainer(o, LayoutPoint()); if (!style()->hasOutOfFlowPosition() && o->hasColumns()) { - RenderBlock* block = static_cast<RenderBlock*>(o); + RenderBlock* block = toRenderBlock(o); LayoutPoint point(roundedLayoutPoint(transformState.mappedPoint())); point -= containerOffset; block->adjustForColumnRect(containerOffset, point); @@ -2819,8 +2952,8 @@ void RenderBoxModelObject::moveChildrenTo(RenderBoxModelObject* toBoxModelObject // or when fullRemoveInsert is false. if (fullRemoveInsert && isRenderBlock()) { RenderBlock* block = toRenderBlock(this); - if (block->hasPositionedObjects()) - block->removePositionedObjects(0); + block->removePositionedObjects(0); + block->removeFloatingObjects(); } ASSERT(!beforeChild || toBoxModelObject == beforeChild->parent()); diff --git a/Source/WebCore/rendering/RenderBoxModelObject.h b/Source/WebCore/rendering/RenderBoxModelObject.h index eea0d6893..8a35a3323 100644 --- a/Source/WebCore/rendering/RenderBoxModelObject.h +++ b/Source/WebCore/rendering/RenderBoxModelObject.h @@ -26,7 +26,6 @@ #include "LayoutRect.h" #include "RenderLayerModelObject.h" -#include "ShadowData.h" namespace WebCore { @@ -45,6 +44,7 @@ enum BackgroundBleedAvoidance { enum ContentChangeType { ImageChanged, MaskImageChanged, + BackgroundImageChanged, CanvasChanged, CanvasPixelsChanged, VideoChanged, @@ -52,6 +52,7 @@ enum ContentChangeType { }; class KeyframeList; +class InlineFlowBox; class StickyPositionViewportConstraints; // This class is the base for all objects that adhere to the CSS box model as described @@ -59,7 +60,7 @@ class StickyPositionViewportConstraints; class RenderBoxModelObject : public RenderLayerModelObject { public: - RenderBoxModelObject(Node*); + RenderBoxModelObject(ContainerNode*); virtual ~RenderBoxModelObject(); LayoutSize relativePositionOffset() const; @@ -80,8 +81,8 @@ public: int pixelSnappedOffsetLeft() const { return roundToInt(offsetLeft()); } int pixelSnappedOffsetTop() const { return roundToInt(offsetTop()); } - int pixelSnappedOffsetWidth() const; - int pixelSnappedOffsetHeight() const; + virtual int pixelSnappedOffsetWidth() const; + virtual int pixelSnappedOffsetHeight() const; virtual void updateFromStyle() OVERRIDE; @@ -91,14 +92,14 @@ public: virtual IntRect borderBoundingBox() const = 0; // These return the CSS computed padding values. - LayoutUnit computedCSSPaddingTop() const; - LayoutUnit computedCSSPaddingBottom() const; - LayoutUnit computedCSSPaddingLeft() const; - LayoutUnit computedCSSPaddingRight() const; - LayoutUnit computedCSSPaddingBefore() const; - LayoutUnit computedCSSPaddingAfter() const; - LayoutUnit computedCSSPaddingStart() const; - LayoutUnit computedCSSPaddingEnd() const; + LayoutUnit computedCSSPaddingTop() const { return computedCSSPadding(style()->paddingTop()); } + LayoutUnit computedCSSPaddingBottom() const { return computedCSSPadding(style()->paddingBottom()); } + LayoutUnit computedCSSPaddingLeft() const { return computedCSSPadding(style()->paddingLeft()); } + LayoutUnit computedCSSPaddingRight() const { return computedCSSPadding(style()->paddingRight()); } + LayoutUnit computedCSSPaddingBefore() const { return computedCSSPadding(style()->paddingBefore()); } + LayoutUnit computedCSSPaddingAfter() const { return computedCSSPadding(style()->paddingAfter()); } + LayoutUnit computedCSSPaddingStart() const { return computedCSSPadding(style()->paddingStart()); } + LayoutUnit computedCSSPaddingEnd() const { return computedCSSPadding(style()->paddingEnd()); } // These functions are used during layout. Table cells and the MathML // code override them to include some extra intrinsic padding. @@ -120,16 +121,22 @@ public: virtual int borderStart() const { return style()->borderStartWidth(); } virtual int borderEnd() const { return style()->borderEndWidth(); } + LayoutUnit borderAndPaddingStart() const { return borderStart() + paddingStart(); } + LayoutUnit borderAndPaddingBefore() const { return borderBefore() + paddingBefore(); } + LayoutUnit borderAndPaddingAfter() const { return borderAfter() + paddingAfter(); } + LayoutUnit borderAndPaddingHeight() const { return borderTop() + borderBottom() + paddingTop() + paddingBottom(); } LayoutUnit borderAndPaddingWidth() const { return borderLeft() + borderRight() + paddingLeft() + paddingRight(); } - LayoutUnit borderAndPaddingLogicalHeight() const { return borderBefore() + borderAfter() + paddingBefore() + paddingAfter(); } + LayoutUnit borderAndPaddingLogicalHeight() const { return borderAndPaddingBefore() + borderAndPaddingAfter(); } LayoutUnit borderAndPaddingLogicalWidth() const { return borderStart() + borderEnd() + paddingStart() + paddingEnd(); } LayoutUnit borderAndPaddingLogicalLeft() const { return style()->isHorizontalWritingMode() ? borderLeft() + paddingLeft() : borderTop() + paddingTop(); } - LayoutUnit borderAndPaddingStart() const { return borderStart() + paddingStart(); } LayoutUnit borderLogicalLeft() const { return style()->isHorizontalWritingMode() ? borderLeft() : borderTop(); } LayoutUnit borderLogicalRight() const { return style()->isHorizontalWritingMode() ? borderRight() : borderBottom(); } + LayoutUnit paddingLogicalLeft() const { return style()->isHorizontalWritingMode() ? paddingLeft() : paddingTop(); } + LayoutUnit paddingLogicalRight() const { return style()->isHorizontalWritingMode() ? paddingRight() : paddingBottom(); } + virtual LayoutUnit marginTop() const = 0; virtual LayoutUnit marginBottom() const = 0; virtual LayoutUnit marginLeft() const = 0; @@ -140,6 +147,8 @@ public: virtual LayoutUnit marginEnd(const RenderStyle* otherStyle = 0) const = 0; LayoutUnit marginHeight() const { return marginTop() + marginBottom(); } LayoutUnit marginWidth() const { return marginLeft() + marginRight(); } + LayoutUnit marginLogicalHeight() const { return marginBefore() + marginAfter(); } + LayoutUnit marginLogicalWidth() const { return marginStart() + marginEnd(); } bool hasInlineDirectionBordersPaddingOrMargin() const { return hasInlineDirectionBordersOrPadding() || marginStart()|| marginEnd(); } bool hasInlineDirectionBordersOrPadding() const { return borderStart() || borderEnd() || paddingStart()|| paddingEnd(); } @@ -165,6 +174,10 @@ public: virtual void setSelectionState(SelectionState s); + bool canHaveBoxInfoInRegion() const { return !isFloating() && !isReplaced() && !isInline() && !hasColumns() && !isTableCell() && isBlockFlow() && !isRenderSVGBlock(); } + + + void getGeometryForBackgroundImage(const RenderLayerModelObject* paintContainer, IntRect& destRect, IntPoint& phase, IntSize& tileSize) const; #if USE(ACCELERATED_COMPOSITING) void contentChanged(ContentChangeType); bool hasAcceleratedCompositing() const; @@ -185,6 +198,9 @@ protected: class BackgroundImageGeometry { public: + BackgroundImageGeometry() + : m_hasNonLocalGeometry(false) + { } IntPoint destOrigin() const { return m_destOrigin; } void setDestOrigin(const IntPoint& destOrigin) { @@ -221,16 +237,21 @@ protected: void useFixedAttachment(const IntPoint& attachmentPoint); void clip(const IntRect&); + + void setHasNonLocalGeometry(bool hasNonLocalGeometry = true) { m_hasNonLocalGeometry = hasNonLocalGeometry; } + bool hasNonLocalGeometry() const { return m_hasNonLocalGeometry; } + private: IntRect m_destRect; IntPoint m_destOrigin; IntPoint m_phase; IntSize m_tileSize; + bool m_hasNonLocalGeometry; // Has background-attachment: fixed. Implies that we can't always cheaply compute destRect. }; LayoutPoint adjustedPositionRelativeToOffsetParent(const LayoutPoint&) const; - void calculateBackgroundImageGeometry(const FillLayer*, const LayoutRect& paintRect, BackgroundImageGeometry&); + void calculateBackgroundImageGeometry(const RenderLayerModelObject* paintContainer, const FillLayer*, const LayoutRect& paintRect, BackgroundImageGeometry&, RenderObject* = 0) const; void getBorderEdgeInfo(class BorderEdge[], const RenderStyle*, bool includeLogicalLeftEdge = true, bool includeLogicalRightEdge = true) const; bool borderObscuresBackgroundEdge(const FloatSize& contextScale) const; bool borderObscuresBackground() const; @@ -248,6 +269,8 @@ protected: static void clipRoundedInnerRect(GraphicsContext*, const LayoutRect&, const RoundedRect& clipRect); + bool hasAutoHeightOrContainingBlockWithAutoHeight() const; + public: // For RenderBlocks and RenderInlines with m_style->styleType() == FIRST_LETTER, this tracks their remaining text fragments RenderObject* firstLetterRemainingText() const; @@ -278,6 +301,7 @@ public: void moveChildrenTo(RenderBoxModelObject* toBoxModelObject, RenderObject* startChild, RenderObject* endChild, RenderObject* beforeChild, bool fullRemoveInsert = false); private: + LayoutUnit computedCSSPadding(Length) const; virtual bool isBoxModelObject() const { return true; } virtual LayoutRect frameRectForStickyPositioning() const = 0; @@ -289,7 +313,8 @@ private: RoundedRect getBackgroundRoundedRect(const LayoutRect&, InlineFlowBox*, LayoutUnit inlineBoxWidth, LayoutUnit inlineBoxHeight, bool includeLogicalLeftEdge, bool includeLogicalRightEdge) const; - + + bool fixedBackgroundPaintsInLocalCoordinates() const; void clipBorderSidePolygon(GraphicsContext*, const RoundedRect& outerBorder, const RoundedRect& innerBorder, BoxSide, bool firstEdgeMatches, bool secondEdgeMatches); @@ -309,13 +334,13 @@ private: inline RenderBoxModelObject* toRenderBoxModelObject(RenderObject* object) { - ASSERT(!object || object->isBoxModelObject()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isBoxModelObject()); return static_cast<RenderBoxModelObject*>(object); } inline const RenderBoxModelObject* toRenderBoxModelObject(const RenderObject* object) { - ASSERT(!object || object->isBoxModelObject()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isBoxModelObject()); return static_cast<const RenderBoxModelObject*>(object); } diff --git a/Source/WebCore/rendering/RenderButton.cpp b/Source/WebCore/rendering/RenderButton.cpp index 69cef2922..4f655fb43 100644 --- a/Source/WebCore/rendering/RenderButton.cpp +++ b/Source/WebCore/rendering/RenderButton.cpp @@ -33,8 +33,8 @@ namespace WebCore { using namespace HTMLNames; -RenderButton::RenderButton(Node* node) - : RenderDeprecatedFlexibleBox(node) +RenderButton::RenderButton(Element* element) + : RenderFlexibleBox(element) , m_buttonText(0) , m_inner(0) , m_default(false) @@ -52,7 +52,7 @@ void RenderButton::addChild(RenderObject* newChild, RenderObject* beforeChild) ASSERT(!firstChild()); m_inner = createAnonymousBlock(style()->display()); setupInnerStyle(m_inner->style()); - RenderDeprecatedFlexibleBox::addChild(m_inner); + RenderFlexibleBox::addChild(m_inner); } m_inner->addChild(newChild, beforeChild); @@ -65,7 +65,7 @@ void RenderButton::removeChild(RenderObject* oldChild) // violated. if (oldChild == m_inner || !m_inner || oldChild->parent() == this) { ASSERT(oldChild == m_inner || !m_inner); - RenderDeprecatedFlexibleBox::removeChild(oldChild); + RenderFlexibleBox::removeChild(oldChild); m_inner = 0; } else m_inner->removeChild(oldChild); @@ -75,10 +75,13 @@ void RenderButton::styleWillChange(StyleDifference diff, const RenderStyle* newS { if (m_inner) { // RenderBlock::setStyle is going to apply a new style to the inner block, which - // will have the initial box flex value, 0. The current value is 1, because we set + // will have the initial flex value, 0. The current value is 1, because we set // it right below. Here we change it back to 0 to avoid getting a spurious layout hint - // because of the difference. - m_inner->style()->setBoxFlex(0); + // because of the difference. Same goes for the other properties. + // FIXME: Make this hack unnecessary. + m_inner->style()->setFlexGrow(newStyle->initialFlexGrow()); + m_inner->style()->setMarginTop(newStyle->initialMargin()); + m_inner->style()->setMarginBottom(newStyle->initialMargin()); } RenderBlock::styleWillChange(diff, newStyle); } @@ -108,15 +111,19 @@ void RenderButton::setupInnerStyle(RenderStyle* innerStyle) ASSERT(innerStyle->refCount() == 1); // RenderBlock::createAnonymousBlock creates a new RenderStyle, so this is // safe to modify. - innerStyle->setBoxFlex(1.0f); - innerStyle->setBoxOrient(style()->boxOrient()); + innerStyle->setFlexGrow(1.0f); + // Use margin:auto instead of align-items:center to get safe centering, i.e. + // when the content overflows, treat it the same as align-items: flex-start. + innerStyle->setMarginTop(Length()); + innerStyle->setMarginBottom(Length()); + innerStyle->setFlexDirection(style()->flexDirection()); } void RenderButton::updateFromElement() { // If we're an input element, we may need to change our button text. - if (node()->hasTagName(inputTag)) { - HTMLInputElement* input = static_cast<HTMLInputElement*>(node()); + if (isHTMLInputElement(node())) { + HTMLInputElement* input = toHTMLInputElement(node()); String value = input->valueWithDefault(); setText(value); } @@ -150,15 +157,7 @@ bool RenderButton::canHaveGeneratedChildren() const // Input elements can't have generated children, but button elements can. We'll // write the code assuming any other button types that might emerge in the future // can also have children. - return !node()->hasTagName(inputTag); -} - -void RenderButton::updateBeforeAfterContent(PseudoId type) -{ - if (m_inner) - m_inner->children()->updateBeforeAfterContent(m_inner, type, this); - else - children()->updateBeforeAfterContent(this, type); + return !isHTMLInputElement(node()); } LayoutRect RenderButton::controlClipRect(const LayoutPoint& additionalOffset) const diff --git a/Source/WebCore/rendering/RenderButton.h b/Source/WebCore/rendering/RenderButton.h index 5be62b012..9bdc48751 100644 --- a/Source/WebCore/rendering/RenderButton.h +++ b/Source/WebCore/rendering/RenderButton.h @@ -21,7 +21,7 @@ #ifndef RenderButton_h #define RenderButton_h -#include "RenderDeprecatedFlexibleBox.h" +#include "RenderFlexibleBox.h" #include "Timer.h" #include <wtf/OwnPtr.h> @@ -32,9 +32,9 @@ class RenderTextFragment; // RenderButtons are just like normal flexboxes except that they will generate an anonymous block child. // For inputs, they will also generate an anonymous RenderText and keep its style and content up // to date as the button changes. -class RenderButton : public RenderDeprecatedFlexibleBox { +class RenderButton : public RenderFlexibleBox { public: - explicit RenderButton(Node*); + explicit RenderButton(Element*); virtual ~RenderButton(); virtual const char* renderName() const { return "RenderButton"; } @@ -50,8 +50,6 @@ public: void setupInnerStyle(RenderStyle*); virtual void updateFromElement(); - virtual void updateBeforeAfterContent(PseudoId); - virtual bool canHaveGeneratedChildren() const OVERRIDE; virtual bool hasControlClip() const { return true; } virtual LayoutRect controlClipRect(const LayoutPoint&) const; @@ -63,7 +61,7 @@ private: virtual void styleWillChange(StyleDifference, const RenderStyle* newStyle); virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); - virtual bool hasLineIfEmpty() const { return true; } + virtual bool hasLineIfEmpty() const { return node() && node()->toInputElement(); } virtual bool requiresForcedStyleRecalcPropagation() const { return true; } @@ -78,13 +76,13 @@ private: inline RenderButton* toRenderButton(RenderObject* object) { - ASSERT(!object || object->isRenderButton()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isRenderButton()); return static_cast<RenderButton*>(object); } inline const RenderButton* toRenderButton(const RenderObject* object) { - ASSERT(!object || object->isRenderButton()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isRenderButton()); return static_cast<const RenderButton*>(object); } diff --git a/Source/WebCore/rendering/RenderCombineText.cpp b/Source/WebCore/rendering/RenderCombineText.cpp index ce316b6b9..39070b49c 100644 --- a/Source/WebCore/rendering/RenderCombineText.cpp +++ b/Source/WebCore/rendering/RenderCombineText.cpp @@ -26,7 +26,7 @@ namespace WebCore { -const float textCombineMargin = 1.1f; // Allow em + 10% margin +const float textCombineMargin = 1.15f; // Allow em + 15% margin RenderCombineText::RenderCombineText(Node* node, PassRefPtr<StringImpl> string) : RenderText(node, string) diff --git a/Source/WebCore/rendering/RenderCombineText.h b/Source/WebCore/rendering/RenderCombineText.h index 26021c7f9..6ee9ff27f 100644 --- a/Source/WebCore/rendering/RenderCombineText.h +++ b/Source/WebCore/rendering/RenderCombineText.h @@ -51,13 +51,13 @@ private: inline RenderCombineText* toRenderCombineText(RenderObject* object) { - ASSERT(!object || object->isCombineText()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isCombineText()); return static_cast<RenderCombineText*>(object); } inline const RenderCombineText* toRenderCombineText(const RenderObject* object) { - ASSERT(!object || object->isCombineText()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isCombineText()); return static_cast<const RenderCombineText*>(object); } diff --git a/Source/WebCore/rendering/RenderCounter.cpp b/Source/WebCore/rendering/RenderCounter.cpp index 24c863304..d72199ecf 100644 --- a/Source/WebCore/rendering/RenderCounter.cpp +++ b/Source/WebCore/rendering/RenderCounter.cpp @@ -27,6 +27,7 @@ #include "Element.h" #include "HTMLNames.h" #include "HTMLOListElement.h" +#include "NodeTraversal.h" #include "RenderListItem.h" #include "RenderListMarker.h" #include "RenderStyle.h" @@ -52,111 +53,34 @@ static CounterMaps& counterMaps() return staticCounterMaps; } -static RenderObject* rendererOfAfterPseudoElement(RenderObject* renderer) -{ - RenderObject* lastContinuation = renderer; - while (RenderObject* continuation = lastContinuation->virtualContinuation()) - lastContinuation = continuation; - return lastContinuation->afterPseudoElementRenderer(); -} - // This function processes the renderer tree in the order of the DOM tree // including pseudo elements as defined in CSS 2.1. -// Anonymous renderers are skipped except for those representing pseudo elements. static RenderObject* previousInPreOrder(const RenderObject* object) { - Element* parent; - Element* sibling; - switch (object->style()->styleType()) { - case NOPSEUDO: - ASSERT(!object->isAnonymous()); - parent = toElement(object->node()); - sibling = parent->previousElementSibling(); - parent = parent->parentElement(); - break; - case BEFORE: - return object->generatingNode()->renderer(); // It is always the generating node's renderer - case AFTER: - parent = toElement(object->generatingNode()); - sibling = parent->lastElementChild(); - break; - default: - ASSERT_NOT_REACHED(); - return 0; - } - while (sibling) { - if (RenderObject* renderer = sibling->renderer()) { - if (RenderObject* after = rendererOfAfterPseudoElement(renderer)) - return after; - parent = sibling; - sibling = sibling->lastElementChild(); - if (!sibling) { - if (RenderObject* before = renderer->beforePseudoElementRenderer()) - return before; - return renderer; - } - } else - sibling = sibling->previousElementSibling(); - } - if (!parent) - return 0; - RenderObject* renderer = parent->renderer(); // Should never be null - if (RenderObject* before = renderer->beforePseudoElementRenderer()) - return before; - return renderer; + Element* self = toElement(object->node()); + Element* previous = ElementTraversal::previousIncludingPseudo(self); + while (previous && !previous->renderer()) + previous = ElementTraversal::previousIncludingPseudo(previous); + return previous ? previous->renderer() : 0; } // This function processes the renderer tree in the order of the DOM tree // including pseudo elements as defined in CSS 2.1. -// Anonymous renderers are skipped except for those representing pseudo elements. static RenderObject* previousSiblingOrParent(const RenderObject* object) { - Element* parent; - Element* sibling; - switch (object->style()->styleType()) { - case NOPSEUDO: - ASSERT(!object->isAnonymous()); - parent = toElement(object->node()); - sibling = parent->previousElementSibling(); - parent = parent->parentElement(); - break; - case BEFORE: - return object->generatingNode()->renderer(); // It is always the generating node's renderer - case AFTER: - parent = toElement(object->generatingNode()); - sibling = parent->lastElementChild(); - break; - default: - ASSERT_NOT_REACHED(); - return 0; - } - while (sibling) { - if (RenderObject* renderer = sibling->renderer()) // This skips invisible nodes - return renderer; - sibling = sibling->previousElementSibling(); - } - if (parent) { - RenderObject* renderer = parent->renderer(); - if (RenderObject* before = renderer->virtualChildren()->beforePseudoElementRenderer(renderer)) - return before; - return renderer; - } - return 0; + Element* self = toElement(object->node()); + Element* previous = ElementTraversal::pseudoAwarePreviousSibling(self); + while (previous && !previous->renderer()) + previous = ElementTraversal::pseudoAwarePreviousSibling(previous); + if (previous) + return previous->renderer(); + previous = self->parentElement(); + return previous ? previous->renderer() : 0; } -static Element* parentElement(RenderObject* object) +static inline Element* parentElement(RenderObject* object) { - switch (object->style()->styleType()) { - case NOPSEUDO: - ASSERT(!object->isAnonymous()); - return toElement(object->node())->parentElement(); - case BEFORE: - case AFTER: - return toElement(object->generatingNode()); - default: - ASSERT_NOT_REACHED(); - return 0; - } + return toElement(object->node())->parentElement(); } static inline bool areRenderersElementsSiblings(RenderObject* first, RenderObject* second) @@ -166,51 +90,13 @@ static inline bool areRenderersElementsSiblings(RenderObject* first, RenderObjec // This function processes the renderer tree in the order of the DOM tree // including pseudo elements as defined in CSS 2.1. -// Anonymous renderers are skipped except for those representing pseudo elements. static RenderObject* nextInPreOrder(const RenderObject* object, const Element* stayWithin, bool skipDescendants = false) { - Element* self; - Element* child; - RenderObject* result; - self = toElement(object->generatingNode()); - if (skipDescendants) - goto nextsibling; - switch (object->style()->styleType()) { - case NOPSEUDO: - ASSERT(!object->isAnonymous()); - result = object->beforePseudoElementRenderer(); - if (result) - return result; - break; - case BEFORE: - break; - case AFTER: - goto nextsibling; - default: - ASSERT_NOT_REACHED(); - return 0; - } - child = self->firstElementChild(); - while (true) { - while (child) { - result = child->renderer(); - if (result) - return result; - child = child->nextElementSibling(); - } - result = rendererOfAfterPseudoElement(self->renderer()); - if (result) - return result; -nextsibling: - if (self == stayWithin) - return 0; - child = self->nextElementSibling(); - self = self->parentElement(); - if (!self) { - ASSERT(!child); // We can only reach this if we are searching beyond the root element - return 0; // which cannot have siblings - } - } + Element* self = toElement(object->node()); + Element* next = skipDescendants ? ElementTraversal::nextIncludingPseudoSkippingChildren(self, stayWithin) : ElementTraversal::nextIncludingPseudo(self, stayWithin); + while (next && !next->renderer()) + next = skipDescendants ? ElementTraversal::nextIncludingPseudoSkippingChildren(next, stayWithin) : ElementTraversal::nextIncludingPseudo(next, stayWithin); + return next ? next->renderer() : 0; } static bool planCounter(RenderObject* object, const AtomicString& identifier, bool& isReset, int& value) @@ -412,7 +298,7 @@ static CounterNode* makeCounterNode(RenderObject* object, const AtomicString& id if (object->hasCounterNodeMap()) { if (CounterMap* nodeMap = counterMaps().get(object)) { - if (CounterNode* node = nodeMap->get(identifier).get()) + if (CounterNode* node = nodeMap->get(identifier)) return node; } } @@ -447,7 +333,7 @@ static CounterNode* makeCounterNode(RenderObject* object, const AtomicString& id skipDescendants = false; if (!currentRenderer->hasCounterNodeMap()) continue; - CounterNode* currentCounter = maps.get(currentRenderer)->get(identifier).get(); + CounterNode* currentCounter = maps.get(currentRenderer)->get(identifier); if (!currentCounter) continue; skipDescendants = true; @@ -501,7 +387,7 @@ PassRefPtr<StringImpl> RenderCounter::originalText() const while (true) { if (!beforeAfterContainer) return 0; - if (!beforeAfterContainer->isAnonymous()) + if (!beforeAfterContainer->isAnonymous() && !beforeAfterContainer->isPseudoElement()) return 0; // RenderCounters are restricted to before and after pseudo elements PseudoId containerStyle = beforeAfterContainer->style()->styleType(); if ((containerStyle == BEFORE) || (containerStyle == AFTER)) @@ -529,14 +415,24 @@ PassRefPtr<StringImpl> RenderCounter::originalText() const return text.impl(); } -void RenderCounter::updateText() +void RenderCounter::updateCounter() { computePreferredLogicalWidths(0); } void RenderCounter::computePreferredLogicalWidths(float lead) { +#ifndef NDEBUG + // FIXME: We shouldn't be modifying the tree in computePreferredLogicalWidths. + // Instead, we should properly hook the appropriate changes in the DOM and modify + // the render tree then. When that's done, we also won't need to override + // computePreferredLogicalWidths at all. + // https://bugs.webkit.org/show_bug.cgi?id=104829 + SetLayoutNeededForbiddenScope layoutForbiddenScope(this, false); +#endif + setTextInternal(originalText()); + RenderText::computePreferredLogicalWidths(lead); } @@ -731,7 +627,7 @@ void showCounterRendererTree(const WebCore::RenderObject* renderer, const char* fprintf(stderr, "%p N:%p P:%p PS:%p NS:%p C:%p\n", current, current->node(), current->parent(), current->previousSibling(), current->nextSibling(), current->hasCounterNodeMap() ? - counterName ? WebCore::counterMaps().get(current)->get(identifier).get() : (WebCore::CounterNode*)1 : (WebCore::CounterNode*)0); + counterName ? WebCore::counterMaps().get(current)->get(identifier) : (WebCore::CounterNode*)1 : (WebCore::CounterNode*)0); } fflush(stderr); } diff --git a/Source/WebCore/rendering/RenderCounter.h b/Source/WebCore/rendering/RenderCounter.h index 548828647..ed2fdf5cf 100644 --- a/Source/WebCore/rendering/RenderCounter.h +++ b/Source/WebCore/rendering/RenderCounter.h @@ -40,6 +40,8 @@ public: static void rendererRemovedFromTree(RenderObject*); static void rendererStyleChanged(RenderObject*, const RenderStyle* oldStyle, const RenderStyle* newStyle); + void updateCounter(); + protected: virtual void willBeDestroyed(); @@ -48,8 +50,7 @@ private: virtual bool isCounter() const; virtual PassRefPtr<StringImpl> originalText() const; - virtual void updateText() OVERRIDE; - virtual void computePreferredLogicalWidths(float leadWidth); + virtual void computePreferredLogicalWidths(float leadWidth) OVERRIDE; // Removes the reference to the CounterNode associated with this renderer. // This is used to cause a counter display update when the CounterNode tree changes. @@ -63,7 +64,7 @@ private: inline RenderCounter* toRenderCounter(RenderObject* object) { - ASSERT(!object || object->isCounter()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isCounter()); return static_cast<RenderCounter*>(object); } diff --git a/Source/WebCore/rendering/RenderDeprecatedFlexibleBox.cpp b/Source/WebCore/rendering/RenderDeprecatedFlexibleBox.cpp index 3323986c9..3106fc9a2 100644 --- a/Source/WebCore/rendering/RenderDeprecatedFlexibleBox.cpp +++ b/Source/WebCore/rendering/RenderDeprecatedFlexibleBox.cpp @@ -25,6 +25,7 @@ #include "config.h" #include "RenderDeprecatedFlexibleBox.h" +#include "FeatureObserver.h" #include "Font.h" #include "LayoutRepainter.h" #include "RenderLayer.h" @@ -119,17 +120,33 @@ private: int m_ordinalIteration; }; -RenderDeprecatedFlexibleBox::RenderDeprecatedFlexibleBox(Node* node) - : RenderBlock(node) +RenderDeprecatedFlexibleBox::RenderDeprecatedFlexibleBox(Element* element) + : RenderBlock(element) { setChildrenInline(false); // All of our children must be block-level m_stretchingChildren = false; + if (!isAnonymous()) { + const KURL& url = document()->url(); + if (url.protocolIs("chrome")) + FeatureObserver::observe(document(), FeatureObserver::DeprecatedFlexboxChrome); + else if (url.protocolIs("chrome-extension")) + FeatureObserver::observe(document(), FeatureObserver::DeprecatedFlexboxChromeExtension); + else + FeatureObserver::observe(document(), FeatureObserver::DeprecatedFlexboxWebContent); + } } RenderDeprecatedFlexibleBox::~RenderDeprecatedFlexibleBox() { } +RenderDeprecatedFlexibleBox* RenderDeprecatedFlexibleBox::createAnonymous(Document* document) +{ + RenderDeprecatedFlexibleBox* renderer = new (document->renderArena()) RenderDeprecatedFlexibleBox(0); + renderer->setDocumentForAnonymous(document); + return renderer; +} + static LayoutUnit marginWidthForChild(RenderBox* child) { // A margin basically has three types: fixed, percentage, and auto (variable). @@ -174,56 +191,47 @@ void RenderDeprecatedFlexibleBox::styleWillChange(StyleDifference diff, const Re RenderBlock::styleWillChange(diff, newStyle); } -void RenderDeprecatedFlexibleBox::calcHorizontalPrefWidths() +void RenderDeprecatedFlexibleBox::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const { - for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { - if (childDoesNotAffectWidthOrFlexing(child)) - continue; - - LayoutUnit margin = marginWidthForChild(child); - m_minPreferredLogicalWidth += child->minPreferredLogicalWidth() + margin; - m_maxPreferredLogicalWidth += child->maxPreferredLogicalWidth() + margin; - } -} + if (hasMultipleLines() || isVertical()) { + for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { + if (childDoesNotAffectWidthOrFlexing(child)) + continue; -void RenderDeprecatedFlexibleBox::calcVerticalPrefWidths() -{ - for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { - if (childDoesNotAffectWidthOrFlexing(child)) - continue; + LayoutUnit margin = marginWidthForChild(child); + LayoutUnit width = child->minPreferredLogicalWidth() + margin; + minLogicalWidth = max(width, minLogicalWidth); - LayoutUnit margin = marginWidthForChild(child); - LayoutUnit width = child->minPreferredLogicalWidth() + margin; - m_minPreferredLogicalWidth = max(width, m_minPreferredLogicalWidth); + width = child->maxPreferredLogicalWidth() + margin; + maxLogicalWidth = max(width, maxLogicalWidth); + } + } else { + for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { + if (childDoesNotAffectWidthOrFlexing(child)) + continue; - width = child->maxPreferredLogicalWidth() + margin; - m_maxPreferredLogicalWidth = max(width, m_maxPreferredLogicalWidth); + LayoutUnit margin = marginWidthForChild(child); + minLogicalWidth += child->minPreferredLogicalWidth() + margin; + maxLogicalWidth += child->maxPreferredLogicalWidth() + margin; + } } + + maxLogicalWidth = max(minLogicalWidth, maxLogicalWidth); + + LayoutUnit scrollbarWidth = instrinsicScrollbarLogicalWidth(); + maxLogicalWidth += scrollbarWidth; + minLogicalWidth += scrollbarWidth; } void RenderDeprecatedFlexibleBox::computePreferredLogicalWidths() { ASSERT(preferredLogicalWidthsDirty()); + m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = 0; if (style()->width().isFixed() && style()->width().value() > 0) m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = adjustContentBoxLogicalWidthForBoxSizing(style()->width().value()); - else { - m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = 0; - - if (hasMultipleLines() || isVertical()) - calcVerticalPrefWidths(); - else - calcHorizontalPrefWidths(); - - m_maxPreferredLogicalWidth = max(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth); - } - - if (hasOverflowClip() && style()->overflowY() == OSCROLL) { - layer()->setHasVerticalScrollbar(true); - LayoutUnit scrollbarWidth = verticalScrollbarWidth(); - m_maxPreferredLogicalWidth += scrollbarWidth; - m_minPreferredLogicalWidth += scrollbarWidth; - } + else + computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth); if (style()->minWidth().isFixed() && style()->minWidth().value() > 0) { m_maxPreferredLogicalWidth = max(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style()->minWidth().value())); @@ -242,6 +250,47 @@ void RenderDeprecatedFlexibleBox::computePreferredLogicalWidths() setPreferredLogicalWidthsDirty(false); } +// Use an inline capacity of 8, since flexbox containers usually have less than 8 children. +typedef Vector<LayoutRect, 8> ChildFrameRects; +typedef Vector<LayoutSize, 8> ChildLayoutDeltas; + +static void appendChildFrameRects(RenderDeprecatedFlexibleBox* box, ChildFrameRects& childFrameRects) +{ + FlexBoxIterator iterator(box); + for (RenderBox* child = iterator.first(); child; child = iterator.next()) { + if (!child->isOutOfFlowPositioned()) + childFrameRects.append(child->frameRect()); + } +} + +static void appendChildLayoutDeltas(RenderDeprecatedFlexibleBox* box, ChildLayoutDeltas& childLayoutDeltas) +{ + FlexBoxIterator iterator(box); + for (RenderBox* child = iterator.first(); child; child = iterator.next()) { + if (!child->isOutOfFlowPositioned()) + childLayoutDeltas.append(LayoutSize()); + } +} + +static void repaintChildrenDuringLayoutIfMoved(RenderDeprecatedFlexibleBox* box, const ChildFrameRects& oldChildRects) +{ + size_t childIndex = 0; + FlexBoxIterator iterator(box); + for (RenderBox* child = iterator.first(); child; child = iterator.next()) { + if (child->isOutOfFlowPositioned()) + continue; + + // If the child moved, we have to repaint it as well as any floating/positioned + // descendants. An exception is if we need a layout. In this case, we know we're going to + // repaint ourselves (and the child) anyway. + if (!box->selfNeedsLayout() && child->checkForRepaintDuringLayout()) + child->repaintDuringLayoutIfMoved(oldChildRects[childIndex]); + + ++childIndex; + } + ASSERT(childIndex == oldChildRects.size()); +} + void RenderDeprecatedFlexibleBox::layoutBlock(bool relayoutChildren, LayoutUnit) { ASSERT(needsLayout()); @@ -252,20 +301,18 @@ void RenderDeprecatedFlexibleBox::layoutBlock(bool relayoutChildren, LayoutUnit) LayoutRepainter repainter(*this, checkForRepaintDuringLayout()); LayoutStateMaintainer statePusher(view(), this, locationOffset(), hasTransform() || hasReflection() || style()->isFlippedBlocksWritingMode()); - if (inRenderFlowThread()) { - // Regions changing widths can force us to relayout our children. - if (logicalWidthChangedInRegions()) - relayoutChildren = true; - } - updateRegionsAndExclusionsLogicalSize(); + // Regions changing widths can force us to relayout our children. + RenderFlowThread* flowThread = flowThreadContainingBlock(); + if (logicalWidthChangedInRegions(flowThread)) + relayoutChildren = true; + if (updateRegionsAndShapesBeforeChildLayout(flowThread)) + relayoutChildren = true; LayoutSize previousSize = size(); updateLogicalWidth(); updateLogicalHeight(); - m_overflow.clear(); - if (previousSize != size() || (parent()->isDeprecatedFlexibleBox() && parent()->style()->boxOrient() == HORIZONTAL && parent()->style()->boxAlign() == BSTRETCH)) @@ -277,11 +324,23 @@ void RenderDeprecatedFlexibleBox::layoutBlock(bool relayoutChildren, LayoutUnit) initMaxMarginValues(); +#if !ASSERT_DISABLED + LayoutSize oldLayoutDelta = view()->layoutDelta(); +#endif + + ChildFrameRects oldChildRects; + appendChildFrameRects(this, oldChildRects); + + dirtyForLayoutFromPercentageHeightDescendants(); + if (isHorizontal()) layoutHorizontalBox(relayoutChildren); else layoutVerticalBox(relayoutChildren); + repaintChildrenDuringLayoutIfMoved(this, oldChildRects); + ASSERT(view()->layoutDeltaMatches(oldLayoutDelta)); + LayoutUnit oldClientAfterEdge = clientLogicalBottom(); updateLogicalHeight(); @@ -290,7 +349,7 @@ void RenderDeprecatedFlexibleBox::layoutBlock(bool relayoutChildren, LayoutUnit) layoutPositionedObjects(relayoutChildren || isRoot()); - computeRegionRangeForBlock(); + updateRegionsAndShapesAfterChildLayout(flowThread); if (!isFloatingOrOutOfFlowPositioned() && height() == 0) { // We are a block with no border and padding and a computed height @@ -321,8 +380,7 @@ void RenderDeprecatedFlexibleBox::layoutBlock(bool relayoutChildren, LayoutUnit) // Update our scrollbars if we're overflow:auto/scroll/hidden now that we know if // we overflow or not. - if (hasOverflowClip()) - layer()->updateScrollInfoAfterLayout(); + updateScrollInfoAfterLayout(); // Repaint with our new bounds if they are different from our old bounds. repainter.repaintAfterLayout(); @@ -353,6 +411,16 @@ static void gatherFlexChildrenInfo(FlexBoxIterator& iterator, bool relayoutChild } } +static void layoutChildIfNeededApplyingDelta(RenderBox* child, const LayoutSize& layoutDelta) +{ + if (!child->needsLayout()) + return; + + child->view()->addLayoutDelta(layoutDelta); + child->layoutIfNeeded(); + child->view()->addLayoutDelta(-layoutDelta); +} + void RenderDeprecatedFlexibleBox::layoutHorizontalBox(bool relayoutChildren) { LayoutUnit toAdd = borderBottom() + paddingBottom() + horizontalScrollbarHeight(); @@ -363,43 +431,52 @@ void RenderDeprecatedFlexibleBox::layoutHorizontalBox(bool relayoutChildren) LayoutUnit remainingSpace = 0; - FlexBoxIterator iterator(this); unsigned int highestFlexGroup = 0; unsigned int lowestFlexGroup = 0; - bool haveFlex = false, flexingChildren = false; + bool haveFlex = false, flexingChildren = false; gatherFlexChildrenInfo(iterator, relayoutChildren, highestFlexGroup, lowestFlexGroup, haveFlex); RenderBlock::startDelayUpdateScrollInfo(); + ChildLayoutDeltas childLayoutDeltas; + appendChildLayoutDeltas(this, childLayoutDeltas); + // We do 2 passes. The first pass is simply to lay everyone out at - // their preferred widths. The second pass handles flexing the children. + // their preferred widths. The subsequent passes handle flexing the children. + // The first pass skips flexible objects completely. do { // Reset our height. setHeight(yPos); xPos = borderLeft() + paddingLeft(); + size_t childIndex = 0; + // Our first pass is done without flexing. We simply lay the children // out within the box. We have to do a layout first in order to determine // our box's intrinsic height. LayoutUnit maxAscent = 0, maxDescent = 0; for (RenderBox* child = iterator.first(); child; child = iterator.next()) { - // make sure we relayout children if we need it. - if (relayoutChildren || (child->isReplaced() && (child->style()->width().isPercent() || child->style()->height().isPercent()))) + if (relayoutChildren) child->setChildNeedsLayout(true, MarkOnlyThis); if (child->isOutOfFlowPositioned()) continue; - + + LayoutSize& childLayoutDelta = childLayoutDeltas[childIndex++]; + // Compute the child's vertical margins. child->computeAndSetBlockDirectionMargins(this); if (!child->needsLayout()) child->markForPaginationRelayoutIfNeeded(); - + + // Apply the child's current layout delta. + layoutChildIfNeededApplyingDelta(child, childLayoutDelta); + // Now do the layout. - child->layoutIfNeeded(); + layoutChildIfNeededApplyingDelta(child, childLayoutDelta); // Update our height and overflow height. if (style()->boxAlign() == BBASELINE) { @@ -421,6 +498,7 @@ void RenderDeprecatedFlexibleBox::layoutHorizontalBox(bool relayoutChildren) else setHeight(max(height(), yPos + child->height() + child->marginHeight())); } + ASSERT(childIndex == childLayoutDeltas.size()); if (!iterator.first() && hasLineIfEmpty()) setHeight(height() + lineHeight(true, style()->isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes)); @@ -435,6 +513,7 @@ void RenderDeprecatedFlexibleBox::layoutHorizontalBox(bool relayoutChildren) heightSpecified = true; // Now that our height is actually known, we can place our boxes. + childIndex = 0; m_stretchingChildren = (style()->boxAlign() == BSTRETCH); for (RenderBox* child = iterator.first(); child; child = iterator.next()) { if (child->isOutOfFlowPositioned()) { @@ -449,14 +528,15 @@ void RenderDeprecatedFlexibleBox::layoutHorizontalBox(bool relayoutChildren) continue; } + LayoutSize& childLayoutDelta = childLayoutDeltas[childIndex++]; + if (child->style()->visibility() == COLLAPSE) { // visibility: collapsed children do not participate in our positioning. - // But we need to lay them down. - child->layoutIfNeeded(); + // But we need to lay them out. + layoutChildIfNeededApplyingDelta(child, childLayoutDelta); continue; } - // We need to see if this child's height has changed, since we make block elements // fill the height of a containing box by default. // Now do a layout. @@ -468,7 +548,7 @@ void RenderDeprecatedFlexibleBox::layoutHorizontalBox(bool relayoutChildren) if (!child->needsLayout()) child->markForPaginationRelayoutIfNeeded(); - child->layoutIfNeeded(); + layoutChildIfNeededApplyingDelta(child, childLayoutDelta); // We can place the child now, using our value of box-align. xPos += child->marginLeft(); @@ -493,10 +573,11 @@ void RenderDeprecatedFlexibleBox::layoutHorizontalBox(bool relayoutChildren) break; } - placeChild(child, LayoutPoint(xPos, childY)); + placeChild(child, LayoutPoint(xPos, childY), &childLayoutDelta); xPos += child->width() + child->marginRight(); } + ASSERT(childIndex == childLayoutDeltas.size()); remainingSpace = borderLeft() + paddingLeft() + contentWidth() - xPos; @@ -654,7 +735,7 @@ void RenderDeprecatedFlexibleBox::layoutVerticalBox(bool relayoutChildren) FlexBoxIterator iterator(this); unsigned int highestFlexGroup = 0; unsigned int lowestFlexGroup = 0; - bool haveFlex = false, flexingChildren = false; + bool haveFlex = false, flexingChildren = false; gatherFlexChildrenInfo(iterator, relayoutChildren, highestFlexGroup, lowestFlexGroup, haveFlex); // We confine the line clamp ugliness to vertical flexible boxes (thus keeping it out of @@ -665,6 +746,9 @@ void RenderDeprecatedFlexibleBox::layoutVerticalBox(bool relayoutChildren) RenderBlock::startDelayUpdateScrollInfo(); + ChildLayoutDeltas childLayoutDeltas; + appendChildLayoutDeltas(this, childLayoutDeltas); + // We do 2 passes. The first pass is simply to lay everyone out at // their preferred widths. The second pass handles flexing the children. // Our first pass is done without flexing. We simply lay the children @@ -673,9 +757,10 @@ void RenderDeprecatedFlexibleBox::layoutVerticalBox(bool relayoutChildren) setHeight(borderTop() + paddingTop()); LayoutUnit minHeight = height() + toAdd; + size_t childIndex = 0; for (RenderBox* child = iterator.first(); child; child = iterator.next()) { // Make sure we relayout children if we need it. - if (!haveLineClamp && (relayoutChildren || (child->isReplaced() && (child->style()->width().isPercent() || child->style()->height().isPercent())))) + if (!haveLineClamp && relayoutChildren) child->setChildNeedsLayout(true, MarkOnlyThis); if (child->isOutOfFlowPositioned()) { @@ -690,10 +775,12 @@ void RenderDeprecatedFlexibleBox::layoutVerticalBox(bool relayoutChildren) continue; } + LayoutSize& childLayoutDelta = childLayoutDeltas[childIndex++]; + if (child->style()->visibility() == COLLAPSE) { // visibility: collapsed children do not participate in our positioning. // But we need to lay them down. - child->layoutIfNeeded(); + layoutChildIfNeededApplyingDelta(child, childLayoutDelta); continue; } @@ -707,7 +794,7 @@ void RenderDeprecatedFlexibleBox::layoutVerticalBox(bool relayoutChildren) child->markForPaginationRelayoutIfNeeded(); // Now do a layout. - child->layoutIfNeeded(); + layoutChildIfNeededApplyingDelta(child, childLayoutDelta); // We can place the child now, using our value of box-align. LayoutUnit childX = borderLeft() + paddingLeft(); @@ -731,9 +818,10 @@ void RenderDeprecatedFlexibleBox::layoutVerticalBox(bool relayoutChildren) } // Place the child. - placeChild(child, LayoutPoint(childX, height())); + placeChild(child, LayoutPoint(childX, height()), &childLayoutDelta); setHeight(height() + child->height() + child->marginBottom()); } + ASSERT(childIndex == childLayoutDeltas.size()); yPos = height(); @@ -1010,18 +1098,12 @@ void RenderDeprecatedFlexibleBox::clearLineClamp() } } -void RenderDeprecatedFlexibleBox::placeChild(RenderBox* child, const LayoutPoint& location) +void RenderDeprecatedFlexibleBox::placeChild(RenderBox* child, const LayoutPoint& location, LayoutSize* childLayoutDelta) { - LayoutRect oldRect = child->frameRect(); - - // Place the child. + // Place the child and track the layout delta so we can apply it if we do another layout. + if (childLayoutDelta) + *childLayoutDelta += LayoutSize(child->x() - location.x(), child->y() - location.y()); child->setLocation(location); - - // If the child moved, we have to repaint it as well as any floating/positioned - // descendants. An exception is if we need a layout. In this case, we know we're going to - // repaint ourselves (and the child) anyway. - if (!selfNeedsLayout() && child->checkForRepaintDuringLayout()) - child->repaintDuringLayoutIfMoved(oldRect); } LayoutUnit RenderDeprecatedFlexibleBox::allowedChildFlex(RenderBox* child, bool expanding, unsigned int group) @@ -1083,12 +1165,15 @@ LayoutUnit RenderDeprecatedFlexibleBox::allowedChildFlex(RenderBox* child, bool return 0; } -const char *RenderDeprecatedFlexibleBox::renderName() const +const char* RenderDeprecatedFlexibleBox::renderName() const { if (isFloating()) return "RenderDeprecatedFlexibleBox (floating)"; if (isOutOfFlowPositioned()) return "RenderDeprecatedFlexibleBox (positioned)"; + // FIXME: Temporary hack while the new generated content system is being implemented. + if (isPseudoElement()) + return "RenderDeprecatedFlexibleBox (generated)"; if (isAnonymous()) return "RenderDeprecatedFlexibleBox (generated)"; if (isRelPositioned()) diff --git a/Source/WebCore/rendering/RenderDeprecatedFlexibleBox.h b/Source/WebCore/rendering/RenderDeprecatedFlexibleBox.h index 851fbe464..5f223cd4b 100644 --- a/Source/WebCore/rendering/RenderDeprecatedFlexibleBox.h +++ b/Source/WebCore/rendering/RenderDeprecatedFlexibleBox.h @@ -31,14 +31,12 @@ class FlexBoxIterator; class RenderDeprecatedFlexibleBox : public RenderBlock { public: - RenderDeprecatedFlexibleBox(Node*); + RenderDeprecatedFlexibleBox(Element*); virtual ~RenderDeprecatedFlexibleBox(); - virtual const char* renderName() const; + static RenderDeprecatedFlexibleBox* createAnonymous(Document*); - virtual void computePreferredLogicalWidths(); - void calcHorizontalPrefWidths(); - void calcVerticalPrefWidths(); + virtual const char* renderName() const; virtual void styleWillChange(StyleDifference, const RenderStyle* newStyle) OVERRIDE; @@ -51,9 +49,12 @@ public: virtual bool isStretchingChildren() const { return m_stretchingChildren; } virtual bool canCollapseAnonymousBlockChild() const OVERRIDE { return false; } - void placeChild(RenderBox* child, const LayoutPoint& location); + void placeChild(RenderBox* child, const LayoutPoint& location, LayoutSize* childLayoutDelta = 0); protected: + virtual void computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const OVERRIDE; + virtual void computePreferredLogicalWidths() OVERRIDE; + LayoutUnit allowedChildFlex(RenderBox* child, bool expanding, unsigned group); bool hasMultipleLines() const { return style()->boxLines() == MULTIPLE; } diff --git a/Source/WebCore/rendering/RenderDetailsMarker.cpp b/Source/WebCore/rendering/RenderDetailsMarker.cpp index 345c1389e..2ae332f3c 100644 --- a/Source/WebCore/rendering/RenderDetailsMarker.cpp +++ b/Source/WebCore/rendering/RenderDetailsMarker.cpp @@ -19,11 +19,12 @@ */ #include "config.h" +#if ENABLE(DETAILS_ELEMENT) #include "RenderDetailsMarker.h" -#if ENABLE(DETAILS_ELEMENT) || ENABLE(INPUT_MULTIPLE_FIELDS_UI) #include "Element.h" #include "GraphicsContext.h" +#include "HTMLInputElement.h" #include "HTMLNames.h" #include "PaintInfo.h" @@ -31,8 +32,8 @@ namespace WebCore { using namespace HTMLNames; -RenderDetailsMarker::RenderDetailsMarker(Node* node) - : RenderBlock(node) +RenderDetailsMarker::RenderDetailsMarker(Element* element) + : RenderBlock(element) { } @@ -144,7 +145,7 @@ bool RenderDetailsMarker::isOpen() const continue; if (renderer->node()->hasTagName(detailsTag)) return !toElement(renderer->node())->getAttribute(openAttr).isNull(); - if (renderer->node()->hasTagName(inputTag)) + if (isHTMLInputElement(renderer->node())) return true; } diff --git a/Source/WebCore/rendering/RenderDetailsMarker.h b/Source/WebCore/rendering/RenderDetailsMarker.h index f60b3cb48..db7d29d70 100644 --- a/Source/WebCore/rendering/RenderDetailsMarker.h +++ b/Source/WebCore/rendering/RenderDetailsMarker.h @@ -21,14 +21,14 @@ #ifndef RenderDetailsMarker_h #define RenderDetailsMarker_h -#if ENABLE(DETAILS_ELEMENT) || ENABLE(INPUT_MULTIPLE_FIELDS_UI) +#if ENABLE(DETAILS_ELEMENT) #include "RenderBlock.h" namespace WebCore { class RenderDetailsMarker : public RenderBlock { public: - RenderDetailsMarker(Node*); + RenderDetailsMarker(Element*); enum Orientation { Up, Down, Left, Right }; @@ -46,13 +46,13 @@ private: inline RenderDetailsMarker* toRenderDetailsMarker(RenderObject* object) { - ASSERT(!object || object->isDetailsMarker()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isDetailsMarker()); return static_cast<RenderDetailsMarker*>(object); } inline const RenderDetailsMarker* toRenderDetailsMarker(const RenderObject* object) { - ASSERT(!object || object->isDetailsMarker()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isDetailsMarker()); return static_cast<const RenderDetailsMarker*>(object); } diff --git a/Source/WebCore/rendering/RenderDialog.cpp b/Source/WebCore/rendering/RenderDialog.cpp index b2498ae4c..b8f3d1985 100644 --- a/Source/WebCore/rendering/RenderDialog.cpp +++ b/Source/WebCore/rendering/RenderDialog.cpp @@ -53,7 +53,7 @@ void RenderDialog::layout() FrameView* frameView = document()->view(); int scrollTop = frameView->scrollOffset().height(); FloatPoint absolutePoint(0, scrollTop); - int visibleHeight = frameView->visibleContentRect(true).height(); + int visibleHeight = frameView->visibleContentRect(ScrollableArea::IncludeScrollbars).height(); if (height() < visibleHeight) absolutePoint.move(0, (visibleHeight - height()) / 2); FloatPoint localPoint = containingBlock()->absoluteToLocal(absolutePoint); diff --git a/Source/WebCore/rendering/RenderDialog.h b/Source/WebCore/rendering/RenderDialog.h index ea669469e..a672c06b8 100644 --- a/Source/WebCore/rendering/RenderDialog.h +++ b/Source/WebCore/rendering/RenderDialog.h @@ -36,8 +36,8 @@ class HTMLDialogElement; class RenderDialog : public RenderBlock { public: - explicit RenderDialog(Node* node) - : RenderBlock(node) + explicit RenderDialog(Element* element) + : RenderBlock(element) { } virtual ~RenderDialog() { } diff --git a/Source/WebCore/rendering/RenderEmbeddedObject.cpp b/Source/WebCore/rendering/RenderEmbeddedObject.cpp index e964855d7..69e2fd495 100644 --- a/Source/WebCore/rendering/RenderEmbeddedObject.cpp +++ b/Source/WebCore/rendering/RenderEmbeddedObject.cpp @@ -24,10 +24,11 @@ #include "config.h" #include "RenderEmbeddedObject.h" +#include "CSSValueKeywords.h" #include "Chrome.h" #include "ChromeClient.h" #include "Cursor.h" -#include "CSSValueKeywords.h" +#include "EventHandler.h" #include "Font.h" #include "FontSelector.h" #include "Frame.h" @@ -38,6 +39,7 @@ #include "HTMLNames.h" #include "HTMLObjectElement.h" #include "HTMLParamElement.h" +#include "HTMLPlugInElement.h" #include "HitTestResult.h" #include "LocalizedStrings.h" #include "MIMETypeRegistry.h" @@ -45,13 +47,16 @@ #include "Page.h" #include "PaintInfo.h" #include "Path.h" +#include "PlatformMouseEvent.h" #include "PluginViewBase.h" +#include "RenderLayer.h" #include "RenderTheme.h" #include "RenderView.h" #include "RenderWidgetProtector.h" #include "Settings.h" #include "Text.h" #include "TextRun.h" +#include <wtf/StackStats.h> #if ENABLE(PLUGIN_PROXY_FOR_VIDEO) #include "HTMLMediaElement.h" @@ -60,29 +65,43 @@ namespace WebCore { using namespace HTMLNames; - + static const float replacementTextRoundedRectHeight = 18; static const float replacementTextRoundedRectLeftRightTextMargin = 6; -static const float replacementTextRoundedRectOpacity = 0.20f; -static const float replacementTextPressedRoundedRectOpacity = 0.65f; +static const float replacementTextRoundedRectBottomTextPadding = 1; +static const float replacementTextRoundedRectOpacity = 0.8f; static const float replacementTextRoundedRectRadius = 5; -static const float replacementTextTextOpacity = 0.55f; -static const float replacementTextPressedTextOpacity = 0.65f; +static const float replacementArrowLeftMargin = 5; +static const float replacementArrowPadding = 4; static const Color& replacementTextRoundedRectPressedColor() { - static const Color lightGray(205, 205, 205); - return lightGray; + static const Color pressed(205, 205, 205); + return pressed; } - + +static const Color& replacementTextRoundedRectColor() +{ + static const Color standard(221, 221, 221); + return standard; +} + +static const Color& replacementTextColor() +{ + static const Color standard(102, 102, 102); + return standard; +} + RenderEmbeddedObject::RenderEmbeddedObject(Element* element) : RenderPart(element) , m_hasFallbackContent(false) - , m_showsUnavailablePluginIndicator(false) + , m_isPluginUnavailable(false) + , m_isUnavailablePluginIndicatorHidden(false) , m_unavailablePluginIndicatorIsPressed(false) , m_mouseDownWasInUnavailablePluginIndicator(false) { - view()->frameView()->setIsVisuallyNonEmpty(); + // Actual size is not known yet, report the default intrinsic size. + view()->frameView()->incrementVisuallyNonEmptyPixelCount(roundedIntSize(intrinsicSize())); } RenderEmbeddedObject::~RenderEmbeddedObject() @@ -102,7 +121,7 @@ bool RenderEmbeddedObject::requiresLayer() const bool RenderEmbeddedObject::allowsAcceleratedCompositing() const { - return widget() && widget()->isPluginViewBase() && static_cast<PluginViewBase*>(widget())->platformLayer(); + return widget() && widget()->isPluginViewBase() && toPluginViewBase(widget())->platformLayer(); } #endif @@ -117,44 +136,94 @@ static String unavailablePluginReplacementText(RenderEmbeddedObject::PluginUnava return blockedPluginByContentSecurityPolicyText(); case RenderEmbeddedObject::InsecurePluginVersion: return insecurePluginVersionText(); - case RenderEmbeddedObject::PluginInactive: - return inactivePluginText(); } ASSERT_NOT_REACHED(); return String(); } -void RenderEmbeddedObject::setPluginUnavailabilityReason(PluginUnavailabilityReason pluginUnavailabilityReason) +static bool shouldUnavailablePluginMessageBeButton(Document* document, RenderEmbeddedObject::PluginUnavailabilityReason pluginUnavailabilityReason) { - ASSERT(!m_showsUnavailablePluginIndicator); - m_showsUnavailablePluginIndicator = true; - m_pluginUnavailabilityReason = pluginUnavailabilityReason; + Page* page = document->page(); + return page && page->chrome().client()->shouldUnavailablePluginMessageBeButton(pluginUnavailabilityReason); +} - m_unavailablePluginReplacementText = unavailablePluginReplacementText(pluginUnavailabilityReason); +void RenderEmbeddedObject::setPluginUnavailabilityReason(PluginUnavailabilityReason pluginUnavailabilityReason) +{ + setPluginUnavailabilityReasonWithDescription(pluginUnavailabilityReason, unavailablePluginReplacementText(pluginUnavailabilityReason)); } -bool RenderEmbeddedObject::showsUnavailablePluginIndicator() const +void RenderEmbeddedObject::setPluginUnavailabilityReasonWithDescription(PluginUnavailabilityReason pluginUnavailabilityReason, const String& description) { - return m_showsUnavailablePluginIndicator; + ASSERT(!m_isPluginUnavailable); + m_isPluginUnavailable = true; + m_pluginUnavailabilityReason = pluginUnavailabilityReason; + + if (description.isEmpty()) + m_unavailablePluginReplacementText = unavailablePluginReplacementText(pluginUnavailabilityReason); + else + m_unavailablePluginReplacementText = description; } void RenderEmbeddedObject::setUnavailablePluginIndicatorIsPressed(bool pressed) { if (m_unavailablePluginIndicatorIsPressed == pressed) return; - + m_unavailablePluginIndicatorIsPressed = pressed; repaint(); } +void RenderEmbeddedObject::paintSnapshotImage(PaintInfo& paintInfo, const LayoutPoint& paintOffset, Image* image) +{ + LayoutUnit cWidth = contentWidth(); + LayoutUnit cHeight = contentHeight(); + if (!cWidth || !cHeight) + return; + + GraphicsContext* context = paintInfo.context; + LayoutSize contentSize(cWidth, cHeight); + LayoutPoint contentLocation = location() + paintOffset; + contentLocation.move(borderLeft() + paddingLeft(), borderTop() + paddingTop()); + + LayoutRect rect(contentLocation, contentSize); + IntRect alignedRect = pixelSnappedIntRect(rect); + if (alignedRect.width() <= 0 || alignedRect.height() <= 0) + return; + + bool useLowQualityScaling = shouldPaintAtLowQuality(context, image, image, alignedRect.size()); + context->drawImage(image, style()->colorSpace(), alignedRect, CompositeSourceOver, shouldRespectImageOrientation(), useLowQualityScaling); +} + +void RenderEmbeddedObject::paintContents(PaintInfo& paintInfo, const LayoutPoint& paintOffset) +{ + Element* element = toElement(node()); + if (!element || !element->isPluginElement()) + return; + + HTMLPlugInElement* plugInElement = toHTMLPlugInElement(element); + + if (plugInElement->displayState() > HTMLPlugInElement::DisplayingSnapshot) { + RenderPart::paintContents(paintInfo, paintOffset); + if (!plugInElement->isRestartedPlugin()) + return; + } + + if (!plugInElement->isPlugInImageElement()) + return; + + Image* snapshot = toHTMLPlugInImageElement(plugInElement)->snapshotImage(); + if (snapshot) + paintSnapshotImage(paintInfo, paintOffset, snapshot); +} + void RenderEmbeddedObject::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { Page* page = 0; if (Frame* frame = this->frame()) page = frame->page(); - if (showsUnavailablePluginIndicator()) { + if (isPluginUnavailable()) { if (page && paintInfo.phase == PaintPhaseForeground) page->addRelevantUnpaintedObject(this, visualOverflowRect()); RenderReplaced::paint(paintInfo, paintOffset); @@ -174,39 +243,66 @@ void RenderEmbeddedObject::paintReplaced(PaintInfo& paintInfo, const LayoutPoint if (paintInfo.phase == PaintPhaseSelection) return; - + GraphicsContext* context = paintInfo.context; if (context->paintingDisabled()) return; - + FloatRect contentRect; Path path; FloatRect replacementTextRect; + FloatRect arrowRect; Font font; TextRun run(""); float textWidth; - if (!getReplacementTextGeometry(paintOffset, contentRect, path, replacementTextRect, font, run, textWidth)) + if (!getReplacementTextGeometry(paintOffset, contentRect, path, replacementTextRect, arrowRect, font, run, textWidth)) return; - + GraphicsContextStateSaver stateSaver(*context); context->clip(contentRect); - context->setAlpha(m_unavailablePluginIndicatorIsPressed ? replacementTextPressedRoundedRectOpacity : replacementTextRoundedRectOpacity); - context->setFillColor(m_unavailablePluginIndicatorIsPressed ? replacementTextRoundedRectPressedColor() : Color::white, style()->colorSpace()); + context->setAlpha(replacementTextRoundedRectOpacity); + context->setFillColor(m_unavailablePluginIndicatorIsPressed ? replacementTextRoundedRectPressedColor() : replacementTextRoundedRectColor(), style()->colorSpace()); context->fillPath(path); const FontMetrics& fontMetrics = font.fontMetrics(); float labelX = roundf(replacementTextRect.location().x() + (replacementTextRect.size().width() - textWidth) / 2); float labelY = roundf(replacementTextRect.location().y() + (replacementTextRect.size().height() - fontMetrics.height()) / 2 + fontMetrics.ascent()); - context->setAlpha(m_unavailablePluginIndicatorIsPressed ? replacementTextPressedTextOpacity : replacementTextTextOpacity); - context->setFillColor(Color::black, style()->colorSpace()); + context->setFillColor(replacementTextColor(), style()->colorSpace()); context->drawBidiText(font, run, FloatPoint(labelX, labelY)); } -bool RenderEmbeddedObject::getReplacementTextGeometry(const LayoutPoint& accumulatedOffset, FloatRect& contentRect, Path& path, FloatRect& replacementTextRect, Font& font, TextRun& run, float& textWidth) const +void RenderEmbeddedObject::setUnavailablePluginIndicatorIsHidden(bool hidden) +{ + m_isUnavailablePluginIndicatorHidden = hidden; + + repaint(); +} + +static void addReplacementArrowPath(Path& path, const FloatRect& insideRect) +{ + FloatRect rect(insideRect); + rect.inflate(-replacementArrowPadding); + + FloatPoint center = rect.center(); + FloatSize arrowEdge(rect.width() / 2, rect.height() / 3); + FloatSize arrowHorizontalEdge(arrowEdge.width(), 0); + FloatSize arrowVerticalEdge(0, arrowEdge.height()); + + path.moveTo(FloatPoint(floorf(center.x()), floorf(rect.y()))); + path.addLineTo(FloatPoint(floorf(center.x()), floorf(rect.y() + arrowEdge.height()))); + path.addLineTo(FloatPoint(ceilf(center.x() - arrowEdge.width()), floorf(rect.y() + arrowEdge.height()))); + path.addLineTo(FloatPoint(ceilf(center.x() - arrowEdge.width()), ceilf(rect.y() + arrowEdge.height() + arrowEdge.height()))); + path.addLineTo(FloatPoint(floorf(center.x()), ceilf(rect.y() + arrowEdge.height() + arrowEdge.height()))); + path.addLineTo(FloatPoint(floorf(center.x()), ceilf(rect.y() + arrowEdge.height() + arrowEdge.height() + arrowEdge.height()))); + path.addLineTo(FloatPoint(ceilf(center.x() + arrowEdge.width()), center.y())); + path.closeSubpath(); +} + +bool RenderEmbeddedObject::getReplacementTextGeometry(const LayoutPoint& accumulatedOffset, FloatRect& contentRect, Path& path, FloatRect& replacementTextRect, FloatRect& arrowRect, Font& font, TextRun& run, float& textWidth) const { contentRect = contentBoxRect(); contentRect.moveBy(roundedIntPoint(accumulatedOffset)); - + FontDescription fontDescription; RenderTheme::defaultTheme()->systemFont(CSSValueWebkitSmallControl, fontDescription); fontDescription.setWeight(FontWeightBold); @@ -221,25 +317,115 @@ bool RenderEmbeddedObject::getReplacementTextGeometry(const LayoutPoint& accumul run = TextRun(m_unavailablePluginReplacementText); textWidth = font.width(run); - + replacementTextRect.setSize(FloatSize(textWidth + replacementTextRoundedRectLeftRightTextMargin * 2, replacementTextRoundedRectHeight)); float x = (contentRect.size().width() / 2 - replacementTextRect.size().width() / 2) + contentRect.location().x(); float y = (contentRect.size().height() / 2 - replacementTextRect.size().height() / 2) + contentRect.location().y(); replacementTextRect.setLocation(FloatPoint(x, y)); - + + replacementTextRect.setHeight(replacementTextRect.height() + replacementTextRoundedRectBottomTextPadding); + path.addRoundedRect(replacementTextRect, FloatSize(replacementTextRoundedRectRadius, replacementTextRoundedRectRadius)); + if (shouldUnavailablePluginMessageBeButton(document(), m_pluginUnavailabilityReason)) { + arrowRect = path.boundingRect(); + arrowRect.setX(ceilf(arrowRect.maxX() + replacementArrowLeftMargin)); + arrowRect.setWidth(arrowRect.height()); + arrowRect.inflate(-0.5); + path.addEllipse(arrowRect); + addReplacementArrowPath(path, arrowRect); + } + return true; } +LayoutRect RenderEmbeddedObject::unavailablePluginIndicatorBounds(const LayoutPoint& accumulatedOffset) const +{ + FloatRect contentRect; + Path path; + FloatRect replacementTextRect; + FloatRect arrowRect; + Font font; + TextRun run("", 0); + float textWidth; + if (getReplacementTextGeometry(accumulatedOffset, contentRect, path, replacementTextRect, arrowRect, font, run, textWidth)) + return LayoutRect(path.boundingRect()); + + return LayoutRect(); +} + +bool RenderEmbeddedObject::isReplacementObscured() const +{ + // Return whether or not the replacement content for blocked plugins is accessible to the user. + + // Check the opacity of each layer containing the element or its ancestors. + float opacity = 1.0; + for (RenderLayer* layer = enclosingLayer(); layer; layer = layer->parent()) { + RenderLayerModelObject* renderer = layer->renderer(); + RenderStyle* style = renderer->style(); + opacity *= style->opacity(); + if (opacity < 0.1) + return true; + } + + // Calculate the absolute rect for the blocked plugin replacement text. + IntRect absoluteBoundingBox = absoluteBoundingBoxRect(); + LayoutPoint absoluteLocation(absoluteBoundingBox.location()); + LayoutRect rect = unavailablePluginIndicatorBounds(absoluteLocation); + if (rect.isEmpty()) + return true; + + RenderView* docRenderer = document()->renderView(); + ASSERT(docRenderer); + if (!docRenderer) + return true; + + HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::IgnoreClipping | HitTestRequest::DisallowShadowContent); + HitTestResult result; + HitTestLocation location; + + LayoutUnit x = rect.x(); + LayoutUnit y = rect.y(); + LayoutUnit width = rect.width(); + LayoutUnit height = rect.height(); + + // Hit test the center and near the corners of the replacement text to ensure + // it is visible and is not masked by other elements. + bool hit = false; + location = LayoutPoint(x + width / 2, y + height / 2); + hit = docRenderer->hitTest(request, location, result); + if (!hit || result.innerNode() != node()) + return true; + + location = LayoutPoint(x, y); + hit = docRenderer->hitTest(request, location, result); + if (!hit || result.innerNode() != node()) + return true; + + location = LayoutPoint(x + width, y); + hit = docRenderer->hitTest(request, location, result); + if (!hit || result.innerNode() != node()) + return true; + + location = LayoutPoint(x + width, y + height); + hit = docRenderer->hitTest(request, location, result); + if (!hit || result.innerNode() != node()) + return true; + + location = LayoutPoint(x, y + height); + hit = docRenderer->hitTest(request, location, result); + if (!hit || result.innerNode() != node()) + return true; + + return false; +} + void RenderEmbeddedObject::layout() { StackStats::LayoutCheckPoint layoutCheckPoint; ASSERT(needsLayout()); -#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) LayoutSize oldSize = contentBoxRect().size(); -#endif updateLogicalWidth(); updateLogicalHeight(); @@ -251,19 +437,42 @@ void RenderEmbeddedObject::layout() updateLayerTransform(); - if (!widget() && frameView()) + bool wasMissingWidget = false; + if (!widget() && frameView() && canHaveWidget()) { + wasMissingWidget = true; frameView()->addWidgetToUpdate(this); + } setNeedsLayout(false); -#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + LayoutSize newSize = contentBoxRect().size(); + + if (!wasMissingWidget && newSize.width() >= oldSize.width() && newSize.height() >= oldSize.height()) { + Element* element = toElement(node()); + if (element && element->isPluginElement() && toHTMLPlugInElement(element)->isPlugInImageElement()) { + HTMLPlugInImageElement* plugInImageElement = toHTMLPlugInImageElement(element); + if (plugInImageElement->displayState() > HTMLPlugInElement::DisplayingSnapshot && plugInImageElement->snapshotDecision() == HTMLPlugInImageElement::MaySnapshotWhenResized && document()->view()) { + plugInImageElement->setNeedsCheckForSizeChange(); + document()->view()->addWidgetToUpdate(this); + } + } + } + + if (!canHaveChildren()) + return; + // This code copied from RenderMedia::layout(). - RenderBox* controlsRenderer = toRenderBox(m_children.firstChild()); - if (!controlsRenderer) + RenderObject* child = m_children.firstChild(); + + if (!child) return; - - LayoutSize newSize = contentBoxRect().size(); - if (newSize == oldSize && !controlsRenderer->needsLayout()) + + RenderBox* childBox = toRenderBox(child); + + if (!childBox) + return; + + if (newSize == oldSize && !childBox->needsLayout()) return; // When calling layout() on a child node, a parent must either push a LayoutStateMaintainter, or @@ -271,26 +480,25 @@ void RenderEmbeddedObject::layout() // and this method will be called many times per second during playback, use a LayoutStateMaintainer: LayoutStateMaintainer statePusher(view(), this, locationOffset(), hasTransform() || hasReflection() || style()->isFlippedBlocksWritingMode()); - controlsRenderer->setLocation(LayoutPoint(borderLeft(), borderTop()) + LayoutSize(paddingLeft(), paddingTop())); - controlsRenderer->style()->setHeight(Length(newSize.height(), Fixed)); - controlsRenderer->style()->setWidth(Length(newSize.width(), Fixed)); - controlsRenderer->setNeedsLayout(true, MarkOnlyThis); - controlsRenderer->layout(); + childBox->setLocation(LayoutPoint(borderLeft(), borderTop()) + LayoutSize(paddingLeft(), paddingTop())); + childBox->style()->setHeight(Length(newSize.height(), Fixed)); + childBox->style()->setWidth(Length(newSize.width(), Fixed)); + childBox->setNeedsLayout(true, MarkOnlyThis); + childBox->layout(); setChildNeedsLayout(false); statePusher.pop(); -#endif } void RenderEmbeddedObject::viewCleared() { // This is required for <object> elements whose contents are rendered by WebCore (e.g. src="foo.html"). if (node() && widget() && widget()->isFrameView()) { - FrameView* view = static_cast<FrameView*>(widget()); + FrameView* view = toFrameView(widget()); int marginWidth = -1; int marginHeight = -1; if (node()->hasTagName(iframeTag)) { - HTMLIFrameElement* frame = static_cast<HTMLIFrameElement*>(node()); + HTMLIFrameElement* frame = toHTMLIFrameElement(node()); marginWidth = frame->marginWidth(); marginHeight = frame->marginHeight(); } @@ -309,7 +517,7 @@ bool RenderEmbeddedObject::nodeAtPoint(const HitTestRequest& request, HitTestRes if (!widget() || !widget()->isPluginViewBase()) return true; - PluginViewBase* view = static_cast<PluginViewBase*>(widget()); + PluginViewBase* view = toPluginViewBase(widget()); IntPoint roundedPoint = locationInContainer.roundedPoint(); if (Scrollbar* horizontalScrollbar = view->horizontalScrollbar()) { @@ -334,7 +542,7 @@ bool RenderEmbeddedObject::scroll(ScrollDirection direction, ScrollGranularity g if (!widget() || !widget()->isPluginViewBase()) return false; - return static_cast<PluginViewBase*>(widget())->scroll(direction, granularity); + return toPluginViewBase(widget())->scroll(direction, granularity); } bool RenderEmbeddedObject::logicalScroll(ScrollLogicalDirection direction, ScrollGranularity granularity, float multiplier, Node** stopNode) @@ -349,34 +557,29 @@ bool RenderEmbeddedObject::isInUnavailablePluginIndicator(const LayoutPoint& poi FloatRect contentRect; Path path; FloatRect replacementTextRect; + FloatRect arrowRect; Font font; TextRun run(""); float textWidth; - return getReplacementTextGeometry(IntPoint(), contentRect, path, replacementTextRect, font, run, textWidth) - && path.contains(point); + return getReplacementTextGeometry(IntPoint(), contentRect, path, replacementTextRect, arrowRect, font, run, textWidth) + && (path.contains(point) || arrowRect.contains(point)); } bool RenderEmbeddedObject::isInUnavailablePluginIndicator(MouseEvent* event) const { - return isInUnavailablePluginIndicator(roundedLayoutPoint(absoluteToLocal(event->absoluteLocation(), UseTransforms | SnapOffsetForTransforms))); -} - -static bool shouldUnavailablePluginMessageBeButton(Document* document, RenderEmbeddedObject::PluginUnavailabilityReason pluginUnavailabilityReason) -{ - Page* page = document->page(); - return page && page->chrome()->client()->shouldUnavailablePluginMessageBeButton(pluginUnavailabilityReason); + return isInUnavailablePluginIndicator(roundedLayoutPoint(absoluteToLocal(event->absoluteLocation(), UseTransforms))); } void RenderEmbeddedObject::handleUnavailablePluginIndicatorEvent(Event* event) { if (!shouldUnavailablePluginMessageBeButton(document(), m_pluginUnavailabilityReason)) return; - + if (!event->isMouseEvent()) return; - + MouseEvent* mouseEvent = static_cast<MouseEvent*>(event); - HTMLPlugInElement* element = static_cast<HTMLPlugInElement*>(node()); + HTMLPlugInElement* element = toHTMLPlugInElement(node()); if (event->type() == eventNames().mousedownEvent && static_cast<MouseEvent*>(event)->button() == LeftButton) { m_mouseDownWasInUnavailablePluginIndicator = isInUnavailablePluginIndicator(mouseEvent); if (m_mouseDownWasInUnavailablePluginIndicator) { @@ -387,7 +590,7 @@ void RenderEmbeddedObject::handleUnavailablePluginIndicatorEvent(Event* event) setUnavailablePluginIndicatorIsPressed(true); } event->setDefaultHandled(); - } + } if (event->type() == eventNames().mouseupEvent && static_cast<MouseEvent*>(event)->button() == LeftButton) { if (m_unavailablePluginIndicatorIsPressed) { if (Frame* frame = document()->frame()) { @@ -398,7 +601,7 @@ void RenderEmbeddedObject::handleUnavailablePluginIndicatorEvent(Event* event) } if (m_mouseDownWasInUnavailablePluginIndicator && isInUnavailablePluginIndicator(mouseEvent)) { if (Page* page = document()->page()) - page->chrome()->client()->unavailablePluginButtonClicked(element, m_pluginUnavailabilityReason); + page->chrome().client()->unavailablePluginButtonClicked(element, m_pluginUnavailabilityReason); } m_mouseDownWasInUnavailablePluginIndicator = false; event->setDefaultHandled(); @@ -418,4 +621,20 @@ CursorDirective RenderEmbeddedObject::getCursor(const LayoutPoint& point, Cursor return RenderPart::getCursor(point, cursor); } +bool RenderEmbeddedObject::canHaveChildren() const +{ +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + if (!node()) + return false; + + if (toElement(node())->isMediaElement()) + return true; +#endif + + if (isSnapshottedPlugIn()) + return true; + + return false; +} + } diff --git a/Source/WebCore/rendering/RenderEmbeddedObject.h b/Source/WebCore/rendering/RenderEmbeddedObject.h index ef09ebb4d..af3b901a8 100644 --- a/Source/WebCore/rendering/RenderEmbeddedObject.h +++ b/Source/WebCore/rendering/RenderEmbeddedObject.h @@ -42,10 +42,14 @@ public: PluginCrashed, PluginBlockedByContentSecurityPolicy, InsecurePluginVersion, - PluginInactive, }; void setPluginUnavailabilityReason(PluginUnavailabilityReason); - bool showsUnavailablePluginIndicator() const; + void setPluginUnavailabilityReasonWithDescription(PluginUnavailabilityReason, const String& description); + + bool isPluginUnavailable() const { return m_isPluginUnavailable; } + bool showsUnavailablePluginIndicator() const { return isPluginUnavailable() && !m_isUnavailablePluginIndicatorHidden; } + + void setUnavailablePluginIndicatorIsHidden(bool); // FIXME: This belongs on HTMLObjectElement. bool hasFallbackContent() const { return m_hasFallbackContent; } @@ -53,6 +57,8 @@ public: void handleUnavailablePluginIndicatorEvent(Event*); + bool isReplacementObscured() const; + #if USE(ACCELERATED_COMPOSITING) virtual bool allowsAcceleratedCompositing() const; #endif @@ -63,10 +69,8 @@ protected: virtual CursorDirective getCursor(const LayoutPoint&, Cursor&) const; -#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) const RenderObjectChildList* children() const { return &m_children; } RenderObjectChildList* children() { return &m_children; } -#endif protected: virtual void layout() OVERRIDE; @@ -75,6 +79,9 @@ private: virtual const char* renderName() const { return "RenderEmbeddedObject"; } virtual bool isEmbeddedObject() const { return true; } + void paintSnapshotImage(PaintInfo&, const LayoutPoint&, Image*); + virtual void paintContents(PaintInfo&, const LayoutPoint&) OVERRIDE; + #if USE(ACCELERATED_COMPOSITING) virtual bool requiresLayer() const; #endif @@ -89,29 +96,30 @@ private: void setUnavailablePluginIndicatorIsPressed(bool); bool isInUnavailablePluginIndicator(MouseEvent*) const; bool isInUnavailablePluginIndicator(const LayoutPoint&) const; - bool getReplacementTextGeometry(const LayoutPoint& accumulatedOffset, FloatRect& contentRect, Path&, FloatRect& replacementTextRect, Font&, TextRun&, float& textWidth) const; + bool getReplacementTextGeometry(const LayoutPoint& accumulatedOffset, FloatRect& contentRect, Path&, FloatRect& replacementTextRect, FloatRect& arrowRect, Font&, TextRun&, float& textWidth) const; + LayoutRect unavailablePluginIndicatorBounds(const LayoutPoint&) const; -#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) - virtual bool canHaveChildren() const { return node() && toElement(node())->isMediaElement(); } + virtual bool canHaveChildren() const; virtual RenderObjectChildList* virtualChildren() { return children(); } virtual const RenderObjectChildList* virtualChildren() const { return children(); } -#endif + + virtual bool canHaveWidget() const { return true; } bool m_hasFallbackContent; // FIXME: This belongs on HTMLObjectElement. - bool m_showsUnavailablePluginIndicator; + bool m_isPluginUnavailable; + bool m_isUnavailablePluginIndicatorHidden; PluginUnavailabilityReason m_pluginUnavailabilityReason; String m_unavailablePluginReplacementText; bool m_unavailablePluginIndicatorIsPressed; bool m_mouseDownWasInUnavailablePluginIndicator; -#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) RenderObjectChildList m_children; -#endif + String m_unavailabilityDescription; }; inline RenderEmbeddedObject* toRenderEmbeddedObject(RenderObject* object) { - ASSERT(!object || object->isEmbeddedObject()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isEmbeddedObject()); return static_cast<RenderEmbeddedObject*>(object); } diff --git a/Source/WebCore/rendering/RenderFieldset.cpp b/Source/WebCore/rendering/RenderFieldset.cpp index 1ebae53d5..3216999cc 100644 --- a/Source/WebCore/rendering/RenderFieldset.cpp +++ b/Source/WebCore/rendering/RenderFieldset.cpp @@ -36,7 +36,7 @@ namespace WebCore { using namespace HTMLNames; -RenderFieldset::RenderFieldset(Node* element) +RenderFieldset::RenderFieldset(Element* element) : RenderBlock(element) { } @@ -212,13 +212,4 @@ void RenderFieldset::paintMask(PaintInfo& paintInfo, const LayoutPoint& paintOff paintMaskImages(paintInfo, paintRect); } -bool RenderFieldset::stretchesToMinIntrinsicLogicalWidth() const -{ - // If width is explicitly specified then Fieldsets should not stretch - if (style()->width().isPercent()) - return false; - - return true; -} - } // namespace WebCore diff --git a/Source/WebCore/rendering/RenderFieldset.h b/Source/WebCore/rendering/RenderFieldset.h index 74ad92b6e..3481c87aa 100644 --- a/Source/WebCore/rendering/RenderFieldset.h +++ b/Source/WebCore/rendering/RenderFieldset.h @@ -30,7 +30,7 @@ namespace WebCore { class RenderFieldset : public RenderBlock { public: - explicit RenderFieldset(Node*); + explicit RenderFieldset(Element*); enum FindLegendOption { IgnoreFloatingOrOutOfFlow, IncludeFloatingOrOutOfFlow }; RenderBox* findLegend(FindLegendOption = IgnoreFloatingOrOutOfFlow) const; @@ -43,7 +43,6 @@ private: virtual void computePreferredLogicalWidths(); virtual bool avoidsFloats() const { return true; } - virtual bool stretchesToMinIntrinsicLogicalWidth() const OVERRIDE; virtual void paintBoxDecorations(PaintInfo&, const LayoutPoint&); virtual void paintMask(PaintInfo&, const LayoutPoint&); @@ -51,7 +50,7 @@ private: inline RenderFieldset* toRenderFieldset(RenderObject* object) { - ASSERT(!object || object->isFieldset()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isFieldset()); return static_cast<RenderFieldset*>(object); } diff --git a/Source/WebCore/rendering/RenderFileUploadControl.cpp b/Source/WebCore/rendering/RenderFileUploadControl.cpp index ce6f278b5..6113cef0c 100644 --- a/Source/WebCore/rendering/RenderFileUploadControl.cpp +++ b/Source/WebCore/rendering/RenderFileUploadControl.cpp @@ -68,18 +68,10 @@ bool RenderFileUploadControl::canBeReplacedWithInlineRunIn() const void RenderFileUploadControl::updateFromElement() { - HTMLInputElement* input = static_cast<HTMLInputElement*>(node()); + HTMLInputElement* input = toHTMLInputElement(node()); ASSERT(input->isFileUpload()); if (HTMLInputElement* button = uploadButton()) { - bool newDisabled = !theme()->isEnabled(this); - // We should avoid to call HTMLFormControlElement::setDisabled() as - // possible because setAttribute() in setDisabled() can cause style - // recalculation, and HTMLFormControlElement::recalcStyle() calls - // updateFromElement() eventually. - if (button->disabled() != newDisabled) - button->setDisabled(newDisabled); - bool newCanReceiveDroppedFilesState = input->canReceiveDroppedFiles(); if (m_canReceiveDroppedFiles != newCanReceiveDroppedFilesState) { m_canReceiveDroppedFiles = newCanReceiveDroppedFilesState; @@ -97,12 +89,12 @@ void RenderFileUploadControl::updateFromElement() static int nodeWidth(Node* node) { - return node ? node->renderBox()->pixelSnappedWidth() : 0; + return (node && node->renderBox()) ? node->renderBox()->pixelSnappedWidth() : 0; } int RenderFileUploadControl::maxFilenameWidth() const { - HTMLInputElement* input = static_cast<HTMLInputElement*>(node()); + HTMLInputElement* input = toHTMLInputElement(node()); return max(0, contentBoxRect().pixelSnappedWidth() - nodeWidth(uploadButton()) - afterButtonSpacing - (input->icon() ? iconWidth + iconFilenameSpacing : 0)); } @@ -135,7 +127,7 @@ void RenderFileUploadControl::paintObject(PaintInfo& paintInfo, const LayoutPoin if (!button) return; - HTMLInputElement* input = static_cast<HTMLInputElement*>(node()); + HTMLInputElement* input = toHTMLInputElement(node()); LayoutUnit buttonWidth = nodeWidth(button); LayoutUnit buttonAndIconWidth = buttonWidth + afterButtonSpacing + (input->icon() ? iconWidth + iconFilenameSpacing : 0); @@ -144,10 +136,14 @@ void RenderFileUploadControl::paintObject(PaintInfo& paintInfo, const LayoutPoin textX = contentLeft + buttonAndIconWidth; else textX = contentLeft + contentWidth() - buttonAndIconWidth - font.width(textRun); + + LayoutUnit textY = 0; // We want to match the button's baseline - RenderButton* buttonRenderer = toRenderButton(button->renderer()); // FIXME: Make this work with transforms. - LayoutUnit textY = paintOffset.y() + buttonRenderer->baselinePosition(AlphabeticBaseline, true, HorizontalLine, PositionOnContainingLine); + if (RenderButton* buttonRenderer = toRenderButton(button->renderer())) + textY = paintOffset.y() + borderTop() + paddingTop() + buttonRenderer->baselinePosition(AlphabeticBaseline, true, HorizontalLine, PositionOnContainingLine); + else + textY = baselinePosition(AlphabeticBaseline, true, HorizontalLine, PositionOnContainingLine); paintInfo.context->setFillColor(style()->visitedDependentColor(CSSPropertyColor), style()->colorSpace()); @@ -172,6 +168,28 @@ void RenderFileUploadControl::paintObject(PaintInfo& paintInfo, const LayoutPoin RenderBlock::paintObject(paintInfo, paintOffset); } +void RenderFileUploadControl::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const +{ + // Figure out how big the filename space needs to be for a given number of characters + // (using "0" as the nominal character). + const UChar character = '0'; + const String characterAsString = String(&character, 1); + const Font& font = style()->font(); + // FIXME: Remove the need for this const_cast by making constructTextRun take a const RenderObject*. + RenderFileUploadControl* renderer = const_cast<RenderFileUploadControl*>(this); + float minDefaultLabelWidth = defaultWidthNumChars * font.width(constructTextRun(renderer, font, characterAsString, style(), TextRun::AllowTrailingExpansion)); + + const String label = theme()->fileListDefaultLabel(node()->toInputElement()->multiple()); + float defaultLabelWidth = font.width(constructTextRun(renderer, font, label, style(), TextRun::AllowTrailingExpansion)); + if (HTMLInputElement* button = uploadButton()) + if (RenderObject* buttonRenderer = button->renderer()) + defaultLabelWidth += buttonRenderer->maxPreferredLogicalWidth() + afterButtonSpacing; + maxLogicalWidth = static_cast<int>(ceilf(max(minDefaultLabelWidth, defaultLabelWidth))); + + if (!style()->width().isPercent()) + minLogicalWidth = maxLogicalWidth; +} + void RenderFileUploadControl::computePreferredLogicalWidths() { ASSERT(preferredLogicalWidthsDirty()); @@ -179,38 +197,19 @@ void RenderFileUploadControl::computePreferredLogicalWidths() m_minPreferredLogicalWidth = 0; m_maxPreferredLogicalWidth = 0; - RenderStyle* style = this->style(); - ASSERT(style); - - const Font& font = style->font(); - if (style->width().isFixed() && style->width().value() > 0) - m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = adjustContentBoxLogicalWidthForBoxSizing(style->width().value()); - else { - // Figure out how big the filename space needs to be for a given number of characters - // (using "0" as the nominal character). - const UChar character = '0'; - const String characterAsString = String(&character, 1); - float minDefaultLabelWidth = defaultWidthNumChars * font.width(constructTextRun(this, font, characterAsString, style, TextRun::AllowTrailingExpansion)); - - const String label = theme()->fileListDefaultLabel(node()->toInputElement()->multiple()); - float defaultLabelWidth = font.width(constructTextRun(this, font, label, style, TextRun::AllowTrailingExpansion)); - if (HTMLInputElement* button = uploadButton()) - if (RenderObject* buttonRenderer = button->renderer()) - defaultLabelWidth += buttonRenderer->maxPreferredLogicalWidth() + afterButtonSpacing; - m_maxPreferredLogicalWidth = static_cast<int>(ceilf(max(minDefaultLabelWidth, defaultLabelWidth))); - } - - if (style->minWidth().isFixed() && style->minWidth().value() > 0) { - m_maxPreferredLogicalWidth = max(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style->minWidth().value())); - m_minPreferredLogicalWidth = max(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style->minWidth().value())); - } else if (style->width().isPercent() || (style->width().isAuto() && style->height().isPercent())) - m_minPreferredLogicalWidth = 0; + if (style()->width().isFixed() && style()->width().value() > 0) + m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = adjustContentBoxLogicalWidthForBoxSizing(style()->width().value()); else - m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth; + computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth); + + if (style()->minWidth().isFixed() && style()->minWidth().value() > 0) { + m_maxPreferredLogicalWidth = max(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style()->minWidth().value())); + m_minPreferredLogicalWidth = max(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style()->minWidth().value())); + } - if (style->maxWidth().isFixed()) { - m_maxPreferredLogicalWidth = min(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style->maxWidth().value())); - m_minPreferredLogicalWidth = min(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style->maxWidth().value())); + if (style()->maxWidth().isFixed()) { + m_maxPreferredLogicalWidth = min(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style()->maxWidth().value())); + m_minPreferredLogicalWidth = min(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style()->maxWidth().value())); } int toAdd = borderAndPaddingWidth(); @@ -227,12 +226,12 @@ VisiblePosition RenderFileUploadControl::positionForPoint(const LayoutPoint&) HTMLInputElement* RenderFileUploadControl::uploadButton() const { - HTMLInputElement* input = static_cast<HTMLInputElement*>(node()); + HTMLInputElement* input = toHTMLInputElement(node()); ASSERT(input->shadow()); - Node* buttonNode = input->shadow()->oldestShadowRoot()->firstChild(); - return buttonNode && buttonNode->isHTMLElement() && buttonNode->hasTagName(inputTag) ? static_cast<HTMLInputElement*>(buttonNode) : 0; + Node* buttonNode = input->shadow()->shadowRoot()->firstChild(); + return buttonNode && buttonNode->isHTMLElement() && isHTMLInputElement(buttonNode) ? toHTMLInputElement(buttonNode) : 0; } String RenderFileUploadControl::buttonValue() @@ -245,7 +244,7 @@ String RenderFileUploadControl::buttonValue() String RenderFileUploadControl::fileTextValue() const { - HTMLInputElement* input = static_cast<HTMLInputElement*>(node()); + HTMLInputElement* input = toHTMLInputElement(node()); ASSERT(input->files()); return theme()->fileListNameForWidth(input->files(), style()->font(), maxFilenameWidth(), input->multiple()); } diff --git a/Source/WebCore/rendering/RenderFileUploadControl.h b/Source/WebCore/rendering/RenderFileUploadControl.h index 2ce4ca6f2..34d160422 100644 --- a/Source/WebCore/rendering/RenderFileUploadControl.h +++ b/Source/WebCore/rendering/RenderFileUploadControl.h @@ -46,6 +46,7 @@ private: virtual bool canBeReplacedWithInlineRunIn() const OVERRIDE; virtual void updateFromElement(); + virtual void computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const OVERRIDE; virtual void computePreferredLogicalWidths(); virtual void paintObject(PaintInfo&, const LayoutPoint&); @@ -62,13 +63,13 @@ private: inline RenderFileUploadControl* toRenderFileUploadControl(RenderObject* object) { - ASSERT(!object || object->isFileUploadControl()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isFileUploadControl()); return static_cast<RenderFileUploadControl*>(object); } inline const RenderFileUploadControl* toRenderFileUploadControl(const RenderObject* object) { - ASSERT(!object || object->isFileUploadControl()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isFileUploadControl()); return static_cast<const RenderFileUploadControl*>(object); } diff --git a/Source/WebCore/rendering/RenderFlexibleBox.cpp b/Source/WebCore/rendering/RenderFlexibleBox.cpp index e11d865f1..c95e37e2d 100644 --- a/Source/WebCore/rendering/RenderFlexibleBox.cpp +++ b/Source/WebCore/rendering/RenderFlexibleBox.cpp @@ -49,59 +49,52 @@ struct RenderFlexibleBox::OrderHashTraits : WTF::GenericHashTraits<int> { static bool isDeletedValue(int value) { return value == std::numeric_limits<int>::min() + 1; } }; -class RenderFlexibleBox::OrderIterator { -public: - OrderIterator(RenderFlexibleBox* flexibleBox, const OrderHashSet& orderValues) - : m_flexibleBox(flexibleBox) - , m_currentChild(0) - , m_orderValuesIterator(0) - { - copyToVector(orderValues, m_orderValues); - std::sort(m_orderValues.begin(), m_orderValues.end()); - first(); - } +RenderFlexibleBox::OrderIterator::OrderIterator(const RenderFlexibleBox* flexibleBox) + : m_flexibleBox(flexibleBox) + , m_currentChild(0) + , m_orderValuesIterator(0) +{ +} - RenderBox* currentChild() { return m_currentChild; } +void RenderFlexibleBox::OrderIterator::setOrderValues(const OrderHashSet& orderValues) +{ + reset(); + copyToVector(orderValues, m_orderValues); + std::sort(m_orderValues.begin(), m_orderValues.end()); +} - RenderBox* first() - { - reset(); - return next(); - } +RenderBox* RenderFlexibleBox::OrderIterator::first() +{ + reset(); + return next(); +} - RenderBox* next() - { - do { - if (!m_currentChild) { +RenderBox* RenderFlexibleBox::OrderIterator::next() +{ + do { + if (!m_currentChild) { + if (m_orderValuesIterator == m_orderValues.end()) + return 0; + if (m_orderValuesIterator) { + ++m_orderValuesIterator; if (m_orderValuesIterator == m_orderValues.end()) return 0; - if (m_orderValuesIterator) { - ++m_orderValuesIterator; - if (m_orderValuesIterator == m_orderValues.end()) - return 0; - } else - m_orderValuesIterator = m_orderValues.begin(); - - m_currentChild = m_flexibleBox->firstChildBox(); } else - m_currentChild = m_currentChild->nextSiblingBox(); - } while (!m_currentChild || m_currentChild->style()->order() != *m_orderValuesIterator); + m_orderValuesIterator = m_orderValues.begin(); - return m_currentChild; - } + m_currentChild = m_flexibleBox->firstChildBox(); + } else + m_currentChild = m_currentChild->nextSiblingBox(); + } while (!m_currentChild || m_currentChild->style()->order() != *m_orderValuesIterator); - void reset() - { - m_currentChild = 0; - m_orderValuesIterator = 0; - } + return m_currentChild; +} -private: - RenderFlexibleBox* m_flexibleBox; - RenderBox* m_currentChild; - Vector<int> m_orderValues; - Vector<int>::const_iterator m_orderValuesIterator; -}; +void RenderFlexibleBox::OrderIterator::reset() +{ + m_currentChild = 0; + m_orderValuesIterator = 0; +} struct RenderFlexibleBox::LineContext { LineContext(LayoutUnit crossAxisOffset, LayoutUnit crossAxisExtent, size_t numberOfChildren, LayoutUnit maxAscent) @@ -130,8 +123,9 @@ struct RenderFlexibleBox::Violation { }; -RenderFlexibleBox::RenderFlexibleBox(Node* node) - : RenderBlock(node) +RenderFlexibleBox::RenderFlexibleBox(Element* element) + : RenderBlock(element) + , m_orderIterator(this) , m_numberOfInFlowChildrenOnFirstLine(-1) { setChildrenInline(false); // All of our children must be block-level. @@ -141,6 +135,13 @@ RenderFlexibleBox::~RenderFlexibleBox() { } +RenderFlexibleBox* RenderFlexibleBox::createAnonymous(Document* document) +{ + RenderFlexibleBox* renderer = new (document->renderArena()) RenderFlexibleBox(0); + renderer->setDocumentForAnonymous(document); + return renderer; +} + const char* RenderFlexibleBox::renderName() const { return "RenderFlexibleBox"; @@ -161,60 +162,57 @@ static LayoutUnit marginLogicalWidthForChild(RenderBox* child, RenderStyle* pare return margin; } +void RenderFlexibleBox::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const +{ + // FIXME: We're ignoring flex-basis here and we shouldn't. We can't start honoring it though until + // the flex shorthand stops setting it to 0. + // See https://bugs.webkit.org/show_bug.cgi?id=116117, + for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { + if (child->isOutOfFlowPositioned()) + continue; + + LayoutUnit margin = marginLogicalWidthForChild(child, style()); + bool hasOrthogonalWritingMode = child->isHorizontalWritingMode() != isHorizontalWritingMode(); + LayoutUnit minPreferredLogicalWidth = hasOrthogonalWritingMode ? child->logicalHeight() : child->minPreferredLogicalWidth(); + LayoutUnit maxPreferredLogicalWidth = hasOrthogonalWritingMode ? child->logicalHeight() : child->maxPreferredLogicalWidth(); + minPreferredLogicalWidth += margin; + maxPreferredLogicalWidth += margin; + if (!isColumnFlow()) { + maxLogicalWidth += maxPreferredLogicalWidth; + if (isMultiline()) { + // For multiline, the min preferred width is if you put a break between each item. + minLogicalWidth = std::max(minLogicalWidth, minPreferredLogicalWidth); + } else + minLogicalWidth += minPreferredLogicalWidth; + } else { + minLogicalWidth = std::max(minPreferredLogicalWidth, minLogicalWidth); + if (isMultiline()) { + // For multiline, the max preferred width is if you never break between items. + maxLogicalWidth += maxPreferredLogicalWidth; + } else + maxLogicalWidth = std::max(maxPreferredLogicalWidth, maxLogicalWidth); + } + } + + maxLogicalWidth = std::max(minLogicalWidth, maxLogicalWidth); + + LayoutUnit scrollbarWidth = instrinsicScrollbarLogicalWidth(); + maxLogicalWidth += scrollbarWidth; + minLogicalWidth += scrollbarWidth; +} + void RenderFlexibleBox::computePreferredLogicalWidths() { ASSERT(preferredLogicalWidthsDirty()); + m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = 0; + RenderStyle* styleToUse = style(); // FIXME: This should probably be checking for isSpecified since you should be able to use percentage, calc or viewport relative values for width. if (styleToUse->logicalWidth().isFixed() && styleToUse->logicalWidth().value() > 0) m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalWidth().value()); - else { - m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = 0; - - for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { - if (child->isOutOfFlowPositioned()) - continue; - - LayoutUnit margin = marginLogicalWidthForChild(child, style()); - bool hasOrthogonalWritingMode = child->isHorizontalWritingMode() != isHorizontalWritingMode(); - LayoutUnit minPreferredLogicalWidth = hasOrthogonalWritingMode ? child->logicalHeight() : child->minPreferredLogicalWidth(); - LayoutUnit maxPreferredLogicalWidth = hasOrthogonalWritingMode ? child->logicalHeight() : child->maxPreferredLogicalWidth(); - minPreferredLogicalWidth += margin; - maxPreferredLogicalWidth += margin; - if (!isColumnFlow()) { - m_maxPreferredLogicalWidth += maxPreferredLogicalWidth; - if (isMultiline()) { - // For multiline, the min preferred width is if you put a break between each item. - m_minPreferredLogicalWidth = std::max(m_minPreferredLogicalWidth, minPreferredLogicalWidth); - } else - m_minPreferredLogicalWidth += minPreferredLogicalWidth; - } else { - m_minPreferredLogicalWidth = std::max(minPreferredLogicalWidth, m_minPreferredLogicalWidth); - if (isMultiline()) { - // For multiline, the max preferred width is if you put a break between each item. - m_maxPreferredLogicalWidth += maxPreferredLogicalWidth; - } else - m_maxPreferredLogicalWidth = std::max(maxPreferredLogicalWidth, m_maxPreferredLogicalWidth); - } - } - - m_maxPreferredLogicalWidth = std::max(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth); - } - - LayoutUnit scrollbarWidth = 0; - if (hasOverflowClip()) { - if (isHorizontalWritingMode() && styleToUse->overflowY() == OSCROLL) { - ASSERT(layer()->hasVerticalScrollbar()); - scrollbarWidth = verticalScrollbarWidth(); - } else if (!isHorizontalWritingMode() && styleToUse->overflowX() == OSCROLL) { - ASSERT(layer()->hasHorizontalScrollbar()); - scrollbarWidth = horizontalScrollbarHeight(); - } - } - - m_maxPreferredLogicalWidth += scrollbarWidth; - m_minPreferredLogicalWidth += scrollbarWidth; + else + computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth); // FIXME: This should probably be checking for isSpecified since you should be able to use percentage, calc or viewport relative values for min-width. if (styleToUse->logicalMinWidth().isFixed() && styleToUse->logicalMinWidth().value() > 0) { @@ -252,13 +250,11 @@ int RenderFlexibleBox::baselinePosition(FontBaseline, bool, LineDirectionMode di int RenderFlexibleBox::firstLineBoxBaseline() const { - ASSERT(m_orderIterator); - if (isWritingModeRoot() || m_numberOfInFlowChildrenOnFirstLine <= 0) return -1; RenderBox* baselineChild = 0; int childNumber = 0; - for (RenderBox* child = m_orderIterator->first(); child; child = m_orderIterator->next()) { + for (RenderBox* child = m_orderIterator.first(); child; child = m_orderIterator.next()) { if (child->isOutOfFlowPositioned()) continue; if (alignmentForChild(child) == AlignBaseline && !hasAutoMarginsInCrossAxis(child)) { @@ -302,6 +298,29 @@ int RenderFlexibleBox::inlineBlockBaseline(LineDirectionMode direction) const return synthesizedBaselineFromContentBox(this, direction) + marginAscent; } +static EAlignItems resolveAlignment(const RenderStyle* parentStyle, const RenderStyle* childStyle) +{ + EAlignItems align = childStyle->alignSelf(); + if (align == AlignAuto) + align = parentStyle->alignItems(); + return align; +} + +void RenderFlexibleBox::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) +{ + RenderBlock::styleDidChange(diff, oldStyle); + + if (oldStyle && oldStyle->alignItems() == AlignStretch && diff == StyleDifferenceLayout) { + // Flex items that were previously stretching need to be relayed out so we can compute new available cross axis space. + // This is only necessary for stretching since other alignment values don't change the size of the box. + for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { + EAlignItems previousAlignment = resolveAlignment(oldStyle, child->style()); + if (previousAlignment == AlignStretch && previousAlignment != resolveAlignment(style(), child->style())) + child->setChildNeedsLayout(true, MarkOnlyThis); + } + } +} + void RenderFlexibleBox::layoutBlock(bool relayoutChildren, LayoutUnit) { ASSERT(needsLayout()); @@ -310,47 +329,52 @@ void RenderFlexibleBox::layoutBlock(bool relayoutChildren, LayoutUnit) return; LayoutRepainter repainter(*this, checkForRepaintDuringLayout()); - LayoutStateMaintainer statePusher(view(), this, locationOffset(), hasTransform() || hasReflection() || style()->isFlippedBlocksWritingMode()); - if (inRenderFlowThread()) { - // Regions changing widths can force us to relayout our children. - if (logicalWidthChangedInRegions()) - relayoutChildren = true; - } - updateRegionsAndExclusionsLogicalSize(); + if (updateLogicalWidthAndColumnWidth()) + relayoutChildren = true; - LayoutSize previousSize = size(); + LayoutUnit previousHeight = logicalHeight(); + setLogicalHeight(borderAndPaddingLogicalHeight() + scrollbarLogicalHeight()); + + LayoutStateMaintainer statePusher(view(), this, locationOffset(), hasTransform() || hasReflection() || style()->isFlippedBlocksWritingMode()); - setLogicalHeight(0); - updateLogicalWidth(); + // Regions changing widths can force us to relayout our children. + RenderFlowThread* flowThread = flowThreadContainingBlock(); + if (logicalWidthChangedInRegions(flowThread)) + relayoutChildren = true; + if (updateRegionsAndShapesBeforeChildLayout(flowThread)) + relayoutChildren = true; m_numberOfInFlowChildrenOnFirstLine = -1; - m_overflow.clear(); RenderBlock::startDelayUpdateScrollInfo(); - WTF::Vector<LineContext> lineContexts; + dirtyForLayoutFromPercentageHeightDescendants(); + + Vector<LineContext> lineContexts; OrderHashSet orderValues; - computeMainAxisPreferredSizes(relayoutChildren, orderValues); - m_orderIterator = adoptPtr(new OrderIterator(this, orderValues)); - layoutFlexItems(*m_orderIterator, lineContexts); + computeMainAxisPreferredSizes(orderValues); + m_orderIterator.setOrderValues(orderValues); + + ChildFrameRects oldChildRects; + appendChildFrameRects(oldChildRects); + layoutFlexItems(relayoutChildren, lineContexts); - LayoutUnit oldClientAfterEdge = clientLogicalBottom(); updateLogicalHeight(); - repositionLogicalHeightDependentFlexItems(*m_orderIterator, lineContexts, oldClientAfterEdge); + repositionLogicalHeightDependentFlexItems(lineContexts); RenderBlock::finishDelayUpdateScrollInfo(); - if (size() != previousSize) + if (logicalHeight() != previousHeight) relayoutChildren = true; layoutPositionedObjects(relayoutChildren || isRoot()); - computeRegionRangeForBlock(); - clearChildOverrideSizes(); + updateRegionsAndShapesAfterChildLayout(flowThread); + repaintChildrenDuringLayoutIfMoved(oldChildRects); // FIXME: css3/flexbox/repaint-rtl-column.html seems to repaint more overflow than it needs to. - computeOverflow(oldClientAfterEdge); + computeOverflow(clientLogicalBottomAfterRepositioning()); statePusher.pop(); updateLayerTransform(); @@ -364,41 +388,67 @@ void RenderFlexibleBox::layoutBlock(bool relayoutChildren, LayoutUnit) setNeedsLayout(false); } -void RenderFlexibleBox::paintChildren(PaintInfo& paintInfo, const LayoutPoint& paintOffset, PaintInfo& paintInfoForChild, bool usePrintRect) +void RenderFlexibleBox::appendChildFrameRects(ChildFrameRects& childFrameRects) { - ASSERT(m_orderIterator); + for (RenderBox* child = m_orderIterator.first(); child; child = m_orderIterator.next()) { + if (!child->isOutOfFlowPositioned()) + childFrameRects.append(child->frameRect()); + } +} - for (RenderBox* child = m_orderIterator->first(); child; child = m_orderIterator->next()) { +void RenderFlexibleBox::repaintChildrenDuringLayoutIfMoved(const ChildFrameRects& oldChildRects) +{ + size_t childIndex = 0; + for (RenderBox* child = m_orderIterator.first(); child; child = m_orderIterator.next()) { + if (child->isOutOfFlowPositioned()) + continue; + + // If the child moved, we have to repaint it as well as any floating/positioned + // descendants. An exception is if we need a layout. In this case, we know we're going to + // repaint ourselves (and the child) anyway. + if (!selfNeedsLayout() && child->checkForRepaintDuringLayout()) + child->repaintDuringLayoutIfMoved(oldChildRects[childIndex]); + ++childIndex; + } + ASSERT(childIndex == oldChildRects.size()); +} + +void RenderFlexibleBox::paintChildren(PaintInfo& paintInfo, const LayoutPoint& paintOffset, PaintInfo& paintInfoForChild, bool usePrintRect) +{ + for (RenderBox* child = m_orderIterator.first(); child; child = m_orderIterator.next()) { if (!paintChild(child, paintInfo, paintOffset, paintInfoForChild, usePrintRect)) return; } } -void RenderFlexibleBox::repositionLogicalHeightDependentFlexItems(OrderIterator& iterator, WTF::Vector<LineContext>& lineContexts, LayoutUnit& oldClientAfterEdge) +void RenderFlexibleBox::repositionLogicalHeightDependentFlexItems(Vector<LineContext>& lineContexts) { LayoutUnit crossAxisStartEdge = lineContexts.isEmpty() ? LayoutUnit() : lineContexts[0].crossAxisOffset; - alignFlexLines(iterator, lineContexts); + alignFlexLines(lineContexts); // If we have a single line flexbox, the line height is all the available space. // For flex-direction: row, this means we need to use the height, so we do this after calling updateLogicalHeight. if (!isMultiline() && lineContexts.size() == 1) lineContexts[0].crossAxisExtent = crossAxisContentExtent(); - alignChildren(iterator, lineContexts); + alignChildren(lineContexts); - if (style()->flexWrap() == FlexWrapReverse) { - if (isHorizontalFlow()) - oldClientAfterEdge = clientLogicalBottom(); - flipForWrapReverse(iterator, lineContexts, crossAxisStartEdge); - } + if (style()->flexWrap() == FlexWrapReverse) + flipForWrapReverse(lineContexts, crossAxisStartEdge); // direction:rtl + flex-direction:column means the cross-axis direction is flipped. - flipForRightToLeftColumn(iterator); + flipForRightToLeftColumn(); } -void RenderFlexibleBox::clearChildOverrideSizes() +LayoutUnit RenderFlexibleBox::clientLogicalBottomAfterRepositioning() { - for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) - child->clearOverrideSize(); + LayoutUnit maxChildLogicalBottom = 0; + for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { + if (child->isOutOfFlowPositioned()) + continue; + LayoutUnit childLogicalBottom = logicalTopForChild(child) + logicalHeightForChild(child) + marginAfterForChild(child); + maxChildLogicalBottom = std::max(maxChildLogicalBottom, childLogicalBottom); + } + return std::max(clientLogicalBottom(), maxChildLogicalBottom); } bool RenderFlexibleBox::hasOrthogonalFlow(RenderBox* child) const @@ -476,8 +526,14 @@ LayoutUnit RenderFlexibleBox::mainAxisContentExtent(LayoutUnit contentLogicalHei { if (isColumnFlow()) { LogicalExtentComputedValues computedValues; - computeLogicalHeight(contentLogicalHeight, logicalTop(), computedValues); - return std::max(LayoutUnit(0), computedValues.m_extent - borderAndPaddingLogicalHeight() - scrollbarLogicalHeight()); + LayoutUnit borderPaddingAndScrollbar = borderAndPaddingLogicalHeight() + scrollbarLogicalHeight(); + if (contentLogicalHeight > LayoutUnit::max() - borderPaddingAndScrollbar) + contentLogicalHeight -= borderPaddingAndScrollbar; + LayoutUnit borderBoxLogicalHeight = contentLogicalHeight + borderPaddingAndScrollbar; + computeLogicalHeight(borderBoxLogicalHeight, logicalTop(), computedValues); + if (computedValues.m_extent == LayoutUnit::max()) + return computedValues.m_extent; + return std::max(LayoutUnit(0), computedValues.m_extent - borderPaddingAndScrollbar); } return contentLogicalWidth(); } @@ -486,9 +542,14 @@ LayoutUnit RenderFlexibleBox::computeMainAxisExtentForChild(RenderBox* child, Si { // FIXME: This is wrong for orthogonal flows. It should use the flexbox's writing-mode, not the child's in order // to figure out the logical height/width. + // FIXME: This is wrong if the height is set to an intrinsic keyword value. computeContentLogicalHeight will return -1. + // Instead, we need to layout the child an get the appropriate height value. + // https://bugs.webkit.org/show_bug.cgi?id=113610 if (isColumnFlow()) - return child->computeContentLogicalHeight(sizeType, size); - return child->adjustContentBoxLogicalWidthForBoxSizing(valueForLength(size, contentLogicalWidth(), view())); + return child->computeContentLogicalHeight(size); + // FIXME: Figure out how this should work for regions and pass in the appropriate values. + RenderRegion* region = 0; + return child->computeLogicalWidthInRegionUsing(sizeType, size, contentLogicalWidth(), this, region) - child->borderAndPaddingLogicalWidth(); } WritingMode RenderFlexibleBox::transformedWritingMode() const @@ -664,21 +725,10 @@ LayoutPoint RenderFlexibleBox::flowAwareLocationForChild(RenderBox* child) const void RenderFlexibleBox::setFlowAwareLocationForChild(RenderBox* child, const LayoutPoint& location) { - LayoutRect oldFrameRect = child->frameRect(); - if (isHorizontalFlow()) child->setLocation(location); else child->setLocation(location.transposedPoint()); - - // If the child moved, we have to repaint it as well as any floating/positioned - // descendants. An exception is if we need a layout. In this case, we know we're going to - // repaint ourselves (and the child) anyway. - // FIXME: In some cases, we might overpaint as we move a child multiple times. We could reduce - // overpainting by keeping track of the original position of a child and running this check on - // the final position. - if (!selfNeedsLayout() && child->checkForRepaintDuringLayout()) - child->repaintDuringLayoutIfMoved(oldFrameRect); } LayoutUnit RenderFlexibleBox::mainAxisBorderAndPaddingExtentForChild(RenderBox* child) const @@ -691,10 +741,19 @@ LayoutUnit RenderFlexibleBox::mainAxisScrollbarExtentForChild(RenderBox* child) return isHorizontalFlow() ? child->verticalScrollbarWidth() : child->horizontalScrollbarHeight(); } -LayoutUnit RenderFlexibleBox::preferredMainAxisContentExtentForChild(RenderBox* child) +LayoutUnit RenderFlexibleBox::preferredMainAxisContentExtentForChild(RenderBox* child, bool hasInfiniteLineLength) { + bool hasOverrideSize = child->hasOverrideWidth() || child->hasOverrideHeight(); + if (hasOverrideSize) + child->clearOverrideSize(); + Length flexBasis = flexBasisForChild(child); - if (flexBasis.isAuto()) { + if (flexBasis.isAuto() || (flexBasis.isFixed() && !flexBasis.value() && hasInfiniteLineLength)) { + if (hasOrthogonalFlow(child)) { + if (hasOverrideSize) + child->setChildNeedsLayout(true, MarkOnlyThis); + child->layoutIfNeeded(); + } LayoutUnit mainAxisExtent = hasOrthogonalFlow(child) ? child->logicalHeight() : child->maxPreferredLogicalWidth(); ASSERT(mainAxisExtent - mainAxisBorderAndPaddingExtentForChild(child) >= 0); return mainAxisExtent - mainAxisBorderAndPaddingExtentForChild(child); @@ -702,7 +761,7 @@ LayoutUnit RenderFlexibleBox::preferredMainAxisContentExtentForChild(RenderBox* return std::max(LayoutUnit(0), computeMainAxisExtentForChild(child, MainOrPreferredSize, flexBasis)); } -void RenderFlexibleBox::layoutFlexItems(OrderIterator& iterator, WTF::Vector<LineContext>& lineContexts) +void RenderFlexibleBox::layoutFlexItems(bool relayoutChildren, Vector<LineContext>& lineContexts) { OrderedFlexItemList orderedChildren; LayoutUnit preferredMainAxisExtent; @@ -710,18 +769,31 @@ void RenderFlexibleBox::layoutFlexItems(OrderIterator& iterator, WTF::Vector<Lin double totalWeightedFlexShrink; LayoutUnit minMaxAppliedMainAxisExtent; + m_orderIterator.first(); LayoutUnit crossAxisOffset = flowAwareBorderBefore() + flowAwarePaddingBefore(); - while (computeNextFlexLine(iterator, orderedChildren, preferredMainAxisExtent, totalFlexGrow, totalWeightedFlexShrink, minMaxAppliedMainAxisExtent)) { + bool hasInfiniteLineLength = false; + while (computeNextFlexLine(orderedChildren, preferredMainAxisExtent, totalFlexGrow, totalWeightedFlexShrink, minMaxAppliedMainAxisExtent, hasInfiniteLineLength)) { LayoutUnit availableFreeSpace = mainAxisContentExtent(preferredMainAxisExtent) - preferredMainAxisExtent; FlexSign flexSign = (minMaxAppliedMainAxisExtent < preferredMainAxisExtent + availableFreeSpace) ? PositiveFlexibility : NegativeFlexibility; InflexibleFlexItemSize inflexibleItems; - WTF::Vector<LayoutUnit> childSizes; - while (!resolveFlexibleLengths(flexSign, orderedChildren, availableFreeSpace, totalFlexGrow, totalWeightedFlexShrink, inflexibleItems, childSizes)) { + Vector<LayoutUnit> childSizes; + while (!resolveFlexibleLengths(flexSign, orderedChildren, availableFreeSpace, totalFlexGrow, totalWeightedFlexShrink, inflexibleItems, childSizes, hasInfiniteLineLength)) { ASSERT(totalFlexGrow >= 0 && totalWeightedFlexShrink >= 0); ASSERT(inflexibleItems.size() > 0); } - layoutAndPlaceChildren(crossAxisOffset, orderedChildren, childSizes, availableFreeSpace, lineContexts); + layoutAndPlaceChildren(crossAxisOffset, orderedChildren, childSizes, availableFreeSpace, relayoutChildren, lineContexts); + } + if (hasLineIfEmpty()) { + // Even if computeNextFlexLine returns true, the flexbox might not have + // a line because all our children might be out of flow positioned. + // Instead of just checking if we have a line, make sure the flexbox + // has at least a line's worth of height to cover this case. + LayoutUnit minHeight = borderAndPaddingLogicalHeight() + + lineHeight(true, isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes) + + scrollbarLogicalHeight(); + if (height() < minHeight) + setLogicalHeight(minHeight); } } @@ -758,6 +830,8 @@ LayoutUnit RenderFlexibleBox::autoMarginOffsetInMainAxis(const OrderedFlexItemLi void RenderFlexibleBox::updateAutoMarginsInMainAxis(RenderBox* child, LayoutUnit autoMarginOffset) { + ASSERT(autoMarginOffset >= 0); + if (isHorizontalFlow()) { if (child->style()->marginLeft().isAuto()) child->setMarginLeft(autoMarginOffset); @@ -788,6 +862,7 @@ LayoutUnit RenderFlexibleBox::availableAlignmentSpaceForChild(LayoutUnit lineCro bool RenderFlexibleBox::updateAutoMarginsInCrossAxis(RenderBox* child, LayoutUnit availableAlignmentSpace) { ASSERT(!child->isOutOfFlowPositioned()); + ASSERT(availableAlignmentSpace >= 0); bool isHorizontal = isHorizontalFlow(); Length start = isHorizontal ? child->style()->marginTop() : child->style()->marginLeft(); @@ -837,7 +912,7 @@ LayoutUnit RenderFlexibleBox::computeChildMarginValue(Length margin, RenderView* return minimumValueForLength(margin, availableSize, view); } -void RenderFlexibleBox::computeMainAxisPreferredSizes(bool relayoutChildren, OrderHashSet& orderValues) +void RenderFlexibleBox::computeMainAxisPreferredSizes(OrderHashSet& orderValues) { RenderView* renderView = view(); for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { @@ -846,14 +921,6 @@ void RenderFlexibleBox::computeMainAxisPreferredSizes(bool relayoutChildren, Ord if (child->isOutOfFlowPositioned()) continue; - // Only need to layout here if we will need to get the logicalHeight of the child in computeNextFlexLine. - Length childMainAxisMin = isHorizontalFlow() ? child->style()->minWidth() : child->style()->minHeight(); - if (hasOrthogonalFlow(child) && (flexBasisForChild(child).isAuto() || childMainAxisMin.isAuto())) { - if (!relayoutChildren) - child->setChildNeedsLayout(true, MarkOnlyThis); - child->layoutIfNeeded(); - } - // Before running the flex algorithm, 'auto' has a margin of 0. // Also, if we're not auto sizing, we don't do a layout that computes the start/end margins. if (isHorizontalFlow()) { @@ -868,9 +935,8 @@ void RenderFlexibleBox::computeMainAxisPreferredSizes(bool relayoutChildren, Ord LayoutUnit RenderFlexibleBox::adjustChildSizeForMinAndMax(RenderBox* child, LayoutUnit childSize) { - // FIXME: Support intrinsic min/max lengths. Length max = isHorizontalFlow() ? child->style()->maxWidth() : child->style()->maxHeight(); - if (max.isSpecified()) { + if (max.isSpecifiedOrIntrinsic()) { LayoutUnit maxExtent = computeMainAxisExtentForChild(child, MaxSize, max); if (maxExtent != -1 && childSize > maxExtent) childSize = maxExtent; @@ -878,35 +944,33 @@ LayoutUnit RenderFlexibleBox::adjustChildSizeForMinAndMax(RenderBox* child, Layo Length min = isHorizontalFlow() ? child->style()->minWidth() : child->style()->minHeight(); LayoutUnit minExtent = 0; - if (min.isSpecified()) + if (min.isSpecifiedOrIntrinsic()) minExtent = computeMainAxisExtentForChild(child, MinSize, min); - else if (min.isAuto()) { - minExtent = hasOrthogonalFlow(child) ? child->logicalHeight() : child->minPreferredLogicalWidth(); - minExtent -= mainAxisBorderAndPaddingExtentForChild(child); - } return std::max(childSize, minExtent); } -bool RenderFlexibleBox::computeNextFlexLine(OrderIterator& iterator, OrderedFlexItemList& orderedChildren, LayoutUnit& preferredMainAxisExtent, double& totalFlexGrow, double& totalWeightedFlexShrink, LayoutUnit& minMaxAppliedMainAxisExtent) +bool RenderFlexibleBox::computeNextFlexLine(OrderedFlexItemList& orderedChildren, LayoutUnit& preferredMainAxisExtent, double& totalFlexGrow, double& totalWeightedFlexShrink, LayoutUnit& minMaxAppliedMainAxisExtent, bool& hasInfiniteLineLength) { orderedChildren.clear(); preferredMainAxisExtent = 0; totalFlexGrow = totalWeightedFlexShrink = 0; minMaxAppliedMainAxisExtent = 0; - if (!iterator.currentChild()) + if (!m_orderIterator.currentChild()) return false; LayoutUnit lineBreakLength = mainAxisContentExtent(LayoutUnit::max()); + hasInfiniteLineLength = lineBreakLength == LayoutUnit::max(); + bool lineHasInFlowItem = false; - for (RenderBox* child = iterator.currentChild(); child; child = iterator.next()) { + for (RenderBox* child = m_orderIterator.currentChild(); child; child = m_orderIterator.next()) { if (child->isOutOfFlowPositioned()) { orderedChildren.append(child); continue; } - LayoutUnit childMainAxisExtent = preferredMainAxisContentExtentForChild(child); + LayoutUnit childMainAxisExtent = preferredMainAxisContentExtentForChild(child, hasInfiniteLineLength); LayoutUnit childMainAxisMarginBoxExtent = mainAxisBorderAndPaddingExtentForChild(child) + childMainAxisExtent; childMainAxisMarginBoxExtent += isHorizontalFlow() ? child->marginWidth() : child->marginHeight(); @@ -924,12 +988,12 @@ bool RenderFlexibleBox::computeNextFlexLine(OrderIterator& iterator, OrderedFlex return true; } -void RenderFlexibleBox::freezeViolations(const WTF::Vector<Violation>& violations, LayoutUnit& availableFreeSpace, double& totalFlexGrow, double& totalWeightedFlexShrink, InflexibleFlexItemSize& inflexibleItems) +void RenderFlexibleBox::freezeViolations(const Vector<Violation>& violations, LayoutUnit& availableFreeSpace, double& totalFlexGrow, double& totalWeightedFlexShrink, InflexibleFlexItemSize& inflexibleItems, bool hasInfiniteLineLength) { for (size_t i = 0; i < violations.size(); ++i) { RenderBox* child = violations[i].child; LayoutUnit childSize = violations[i].childSize; - LayoutUnit preferredChildSize = preferredMainAxisContentExtentForChild(child); + LayoutUnit preferredChildSize = preferredMainAxisContentExtentForChild(child, hasInfiniteLineLength); availableFreeSpace -= childSize - preferredChildSize; totalFlexGrow -= child->style()->flexGrow(); totalWeightedFlexShrink -= child->style()->flexShrink() * preferredChildSize; @@ -938,13 +1002,13 @@ void RenderFlexibleBox::freezeViolations(const WTF::Vector<Violation>& violation } // Returns true if we successfully ran the algorithm and sized the flex items. -bool RenderFlexibleBox::resolveFlexibleLengths(FlexSign flexSign, const OrderedFlexItemList& children, LayoutUnit& availableFreeSpace, double& totalFlexGrow, double& totalWeightedFlexShrink, InflexibleFlexItemSize& inflexibleItems, WTF::Vector<LayoutUnit>& childSizes) +bool RenderFlexibleBox::resolveFlexibleLengths(FlexSign flexSign, const OrderedFlexItemList& children, LayoutUnit& availableFreeSpace, double& totalFlexGrow, double& totalWeightedFlexShrink, InflexibleFlexItemSize& inflexibleItems, Vector<LayoutUnit>& childSizes, bool hasInfiniteLineLength) { childSizes.clear(); LayoutUnit totalViolation = 0; LayoutUnit usedFreeSpace = 0; - WTF::Vector<Violation> minViolations; - WTF::Vector<Violation> maxViolations; + Vector<Violation> minViolations; + Vector<Violation> maxViolations; for (size_t i = 0; i < children.size(); ++i) { RenderBox* child = children[i]; if (child->isOutOfFlowPositioned()) { @@ -955,12 +1019,15 @@ bool RenderFlexibleBox::resolveFlexibleLengths(FlexSign flexSign, const OrderedF if (inflexibleItems.contains(child)) childSizes.append(inflexibleItems.get(child)); else { - LayoutUnit preferredChildSize = preferredMainAxisContentExtentForChild(child); + LayoutUnit preferredChildSize = preferredMainAxisContentExtentForChild(child, hasInfiniteLineLength); LayoutUnit childSize = preferredChildSize; - if (availableFreeSpace > 0 && totalFlexGrow > 0 && flexSign == PositiveFlexibility && isfinite(totalFlexGrow)) - childSize += roundedLayoutUnit(availableFreeSpace * child->style()->flexGrow() / totalFlexGrow); - else if (availableFreeSpace < 0 && totalWeightedFlexShrink > 0 && flexSign == NegativeFlexibility && isfinite(totalWeightedFlexShrink)) - childSize += roundedLayoutUnit(availableFreeSpace * child->style()->flexShrink() * preferredChildSize / totalWeightedFlexShrink); + double extraSpace = 0; + if (availableFreeSpace > 0 && totalFlexGrow > 0 && flexSign == PositiveFlexibility && std::isfinite(totalFlexGrow)) + extraSpace = availableFreeSpace * child->style()->flexGrow() / totalFlexGrow; + else if (availableFreeSpace < 0 && totalWeightedFlexShrink > 0 && flexSign == NegativeFlexibility && std::isfinite(totalWeightedFlexShrink)) + extraSpace = availableFreeSpace * child->style()->flexShrink() * preferredChildSize / totalWeightedFlexShrink; + if (std::isfinite(extraSpace)) + childSize += roundedLayoutUnit(extraSpace); LayoutUnit adjustedChildSize = adjustChildSizeForMinAndMax(child, childSize); childSizes.append(adjustedChildSize); @@ -976,7 +1043,7 @@ bool RenderFlexibleBox::resolveFlexibleLengths(FlexSign flexSign, const OrderedF } if (totalViolation) - freezeViolations(totalViolation < 0 ? maxViolations : minViolations, availableFreeSpace, totalFlexGrow, totalWeightedFlexShrink, inflexibleItems); + freezeViolations(totalViolation < 0 ? maxViolations : minViolations, availableFreeSpace, totalFlexGrow, totalWeightedFlexShrink, inflexibleItems, hasInfiniteLineLength); else availableFreeSpace -= usedFreeSpace; @@ -1037,9 +1104,7 @@ void RenderFlexibleBox::prepareChildForPositionedLayout(RenderBox* child, Layout EAlignItems RenderFlexibleBox::alignmentForChild(RenderBox* child) const { - EAlignItems align = child->style()->alignSelf(); - if (align == AlignAuto) - align = style()->alignItems(); + EAlignItems align = resolveAlignment(style(), child->style()); if (align == AlignBaseline && hasOrthogonalFlow(child)) align = AlignFlexStart; @@ -1065,7 +1130,22 @@ size_t RenderFlexibleBox::numberOfInFlowPositionedChildren(const OrderedFlexItem return count; } -void RenderFlexibleBox::layoutAndPlaceChildren(LayoutUnit& crossAxisOffset, const OrderedFlexItemList& children, const WTF::Vector<LayoutUnit>& childSizes, LayoutUnit availableFreeSpace, WTF::Vector<LineContext>& lineContexts) +bool RenderFlexibleBox::needToStretchChild(RenderBox* child) +{ + if (alignmentForChild(child) != AlignStretch) + return false; + + Length crossAxisLength = isHorizontalFlow() ? child->style()->height() : child->style()->width(); + return crossAxisLength.isAuto(); +} + +void RenderFlexibleBox::resetAutoMarginsAndLogicalTopInCrossAxis(RenderBox* child) +{ + if (hasAutoMarginsInCrossAxis(child)) + child->updateLogicalHeight(); +} + +void RenderFlexibleBox::layoutAndPlaceChildren(LayoutUnit& crossAxisOffset, const OrderedFlexItemList& children, const Vector<LayoutUnit>& childSizes, LayoutUnit availableFreeSpace, bool relayoutChildren, Vector<LineContext>& lineContexts) { ASSERT(childSizes.size() == children.size()); @@ -1087,10 +1167,17 @@ void RenderFlexibleBox::layoutAndPlaceChildren(LayoutUnit& crossAxisOffset, cons prepareChildForPositionedLayout(child, mainAxisOffset, crossAxisOffset, FlipForRowReverse); continue; } + LayoutUnit childPreferredSize = childSizes[i] + mainAxisBorderAndPaddingExtentForChild(child); setLogicalOverrideSize(child, childPreferredSize); // FIXME: Can avoid laying out here in some cases. See https://webkit.org/b/87905. - child->setChildNeedsLayout(true, MarkOnlyThis); + if (needToStretchChild(child) || childPreferredSize != mainAxisExtentForChild(child)) + child->setChildNeedsLayout(true, MarkOnlyThis); + else { + // To avoid double applying margin changes in updateAutoMarginsInCrossAxis, we reset the margins here. + resetAutoMarginsAndLogicalTopInCrossAxis(child); + } + updateBlockChildDirtyBitsBeforeLayout(relayoutChildren, child); child->layoutIfNeeded(); updateAutoMarginsInMainAxis(child, autoMarginOffset); @@ -1106,7 +1193,7 @@ void RenderFlexibleBox::layoutAndPlaceChildren(LayoutUnit& crossAxisOffset, cons childCrossAxisMarginBoxExtent = maxAscent + maxDescent; } else childCrossAxisMarginBoxExtent = crossAxisExtentForChild(child) + crossAxisMarginExtentForChild(child); - if (!isColumnFlow() && style()->logicalHeight().isAuto()) + if (!isColumnFlow()) setLogicalHeight(std::max(logicalHeight(), crossAxisOffset + flowAwareBorderAfter() + flowAwarePaddingAfter() + childCrossAxisMarginBoxExtent + crossAxisScrollbarExtent())); maxChildCrossAxisExtent = std::max(maxChildCrossAxisExtent, childCrossAxisMarginBoxExtent); @@ -1196,7 +1283,7 @@ static LayoutUnit alignContentSpaceBetweenChildren(LayoutUnit availableFreeSpace return 0; } -void RenderFlexibleBox::alignFlexLines(OrderIterator& iterator, WTF::Vector<LineContext>& lineContexts) +void RenderFlexibleBox::alignFlexLines(Vector<LineContext>& lineContexts) { if (!isMultiline() || style()->alignContent() == AlignContentFlexStart) return; @@ -1205,11 +1292,11 @@ void RenderFlexibleBox::alignFlexLines(OrderIterator& iterator, WTF::Vector<Line for (size_t i = 0; i < lineContexts.size(); ++i) availableCrossAxisSpace -= lineContexts[i].crossAxisExtent; - RenderBox* child = iterator.first(); + RenderBox* child = m_orderIterator.first(); LayoutUnit lineOffset = initialAlignContentOffset(availableCrossAxisSpace, style()->alignContent(), lineContexts.size()); for (unsigned lineNumber = 0; lineNumber < lineContexts.size(); ++lineNumber) { lineContexts[lineNumber].crossAxisOffset += lineOffset; - for (size_t childNumber = 0; childNumber < lineContexts[lineNumber].numberOfChildren; ++childNumber, child = iterator.next()) + for (size_t childNumber = 0; childNumber < lineContexts[lineNumber].numberOfChildren; ++childNumber, child = m_orderIterator.next()) adjustAlignmentForChild(child, lineOffset); if (style()->alignContent() == AlignContentStretch && availableCrossAxisSpace > 0) @@ -1234,18 +1321,18 @@ void RenderFlexibleBox::adjustAlignmentForChild(RenderBox* child, LayoutUnit del setFlowAwareLocationForChild(child, flowAwareLocationForChild(child) + LayoutSize(0, delta)); } -void RenderFlexibleBox::alignChildren(OrderIterator& iterator, const WTF::Vector<LineContext>& lineContexts) +void RenderFlexibleBox::alignChildren(const Vector<LineContext>& lineContexts) { // Keep track of the space between the baseline edge and the after edge of the box for each line. - WTF::Vector<LayoutUnit> minMarginAfterBaselines; + Vector<LayoutUnit> minMarginAfterBaselines; - RenderBox* child = iterator.first(); + RenderBox* child = m_orderIterator.first(); for (size_t lineNumber = 0; lineNumber < lineContexts.size(); ++lineNumber) { LayoutUnit minMarginAfterBaseline = LayoutUnit::max(); LayoutUnit lineCrossAxisExtent = lineContexts[lineNumber].crossAxisExtent; LayoutUnit maxAscent = lineContexts[lineNumber].maxAscent; - for (size_t childNumber = 0; childNumber < lineContexts[lineNumber].numberOfChildren; ++childNumber, child = iterator.next()) { + for (size_t childNumber = 0; childNumber < lineContexts[lineNumber].numberOfChildren; ++childNumber, child = m_orderIterator.next()) { ASSERT(child); if (child->isOutOfFlowPositioned()) { if (style()->flexWrap() == FlexWrapReverse) @@ -1253,7 +1340,7 @@ void RenderFlexibleBox::alignChildren(OrderIterator& iterator, const WTF::Vector continue; } - if (updateAutoMarginsInCrossAxis(child, availableAlignmentSpaceForChild(lineCrossAxisExtent, child))) + if (updateAutoMarginsInCrossAxis(child, std::max(LayoutUnit(0), availableAlignmentSpaceForChild(lineCrossAxisExtent, child)))) continue; switch (alignmentForChild(child)) { @@ -1296,10 +1383,10 @@ void RenderFlexibleBox::alignChildren(OrderIterator& iterator, const WTF::Vector // wrap-reverse flips the cross axis start and end. For baseline alignment, this means we // need to align the after edge of baseline elements with the after edge of the flex line. - child = iterator.first(); + child = m_orderIterator.first(); for (size_t lineNumber = 0; lineNumber < lineContexts.size(); ++lineNumber) { LayoutUnit minMarginAfterBaseline = minMarginAfterBaselines[lineNumber]; - for (size_t childNumber = 0; childNumber < lineContexts[lineNumber].numberOfChildren; ++childNumber, child = iterator.next()) { + for (size_t childNumber = 0; childNumber < lineContexts[lineNumber].numberOfChildren; ++childNumber, child = m_orderIterator.next()) { ASSERT(child); if (alignmentForChild(child) == AlignBaseline && !hasAutoMarginsInCrossAxis(child) && minMarginAfterBaseline) adjustAlignmentForChild(child, minMarginAfterBaseline); @@ -1320,7 +1407,7 @@ void RenderFlexibleBox::applyStretchAlignmentToChild(RenderBox* child, LayoutUni child->setOverrideLogicalContentHeight(desiredLogicalHeight - child->borderAndPaddingLogicalHeight()); child->setLogicalHeight(0); child->setChildNeedsLayout(true, MarkOnlyThis); - child->layoutIfNeeded(); + child->layout(); } } } else if (isColumnFlow() && child->style()->logicalWidth().isAuto()) { @@ -1332,19 +1419,19 @@ void RenderFlexibleBox::applyStretchAlignmentToChild(RenderBox* child, LayoutUni if (childWidth != child->logicalWidth()) { child->setOverrideLogicalContentWidth(childWidth - child->borderAndPaddingLogicalWidth()); child->setChildNeedsLayout(true, MarkOnlyThis); - child->layoutIfNeeded(); + child->layout(); } } } } -void RenderFlexibleBox::flipForRightToLeftColumn(OrderIterator& iterator) +void RenderFlexibleBox::flipForRightToLeftColumn() { if (style()->isLeftToRightDirection() || !isColumnFlow()) return; LayoutUnit crossExtent = crossAxisExtent(); - for (RenderBox* child = iterator.first(); child; child = iterator.next()) { + for (RenderBox* child = m_orderIterator.first(); child; child = m_orderIterator.next()) { if (child->isOutOfFlowPositioned()) continue; LayoutPoint location = flowAwareLocationForChild(child); @@ -1353,12 +1440,12 @@ void RenderFlexibleBox::flipForRightToLeftColumn(OrderIterator& iterator) } } -void RenderFlexibleBox::flipForWrapReverse(OrderIterator& iterator, const WTF::Vector<LineContext>& lineContexts, LayoutUnit crossAxisStartEdge) +void RenderFlexibleBox::flipForWrapReverse(const Vector<LineContext>& lineContexts, LayoutUnit crossAxisStartEdge) { LayoutUnit contentExtent = crossAxisContentExtent(); - RenderBox* child = iterator.first(); + RenderBox* child = m_orderIterator.first(); for (size_t lineNumber = 0; lineNumber < lineContexts.size(); ++lineNumber) { - for (size_t childNumber = 0; childNumber < lineContexts[lineNumber].numberOfChildren; ++childNumber, child = iterator.next()) { + for (size_t childNumber = 0; childNumber < lineContexts[lineNumber].numberOfChildren; ++childNumber, child = m_orderIterator.next()) { ASSERT(child); LayoutUnit lineCrossAxisExtent = lineContexts[lineNumber].crossAxisExtent; LayoutUnit originalOffset = lineContexts[lineNumber].crossAxisOffset - crossAxisStartEdge; diff --git a/Source/WebCore/rendering/RenderFlexibleBox.h b/Source/WebCore/rendering/RenderFlexibleBox.h index 98eafca7c..ad7707585 100644 --- a/Source/WebCore/rendering/RenderFlexibleBox.h +++ b/Source/WebCore/rendering/RenderFlexibleBox.h @@ -32,21 +32,21 @@ #define RenderFlexibleBox_h #include "RenderBlock.h" -#include <wtf/OwnPtr.h> namespace WebCore { class RenderFlexibleBox : public RenderBlock { public: - RenderFlexibleBox(Node*); + RenderFlexibleBox(Element*); virtual ~RenderFlexibleBox(); + static RenderFlexibleBox* createAnonymous(Document*); + virtual const char* renderName() const OVERRIDE; virtual bool isFlexibleBox() const OVERRIDE { return true; } virtual bool avoidsFloats() const OVERRIDE { return true; } virtual bool canCollapseAnonymousBlockChild() const OVERRIDE { return false; } - virtual void computePreferredLogicalWidths() OVERRIDE; virtual void layoutBlock(bool relayoutChildren, LayoutUnit pageLogicalHeight = 0) OVERRIDE; virtual int baselinePosition(FontBaseline, bool firstLine, LineDirectionMode, LinePositionMode = PositionOnContainingLine) const OVERRIDE; @@ -57,6 +57,12 @@ public: bool isHorizontalFlow() const; +protected: + virtual void computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const OVERRIDE; + virtual void computePreferredLogicalWidths() OVERRIDE; + + virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); + private: enum FlexSign { PositiveFlexibility, @@ -71,13 +77,32 @@ private: struct OrderHashTraits; typedef HashSet<int, DefaultHash<int>::Hash, OrderHashTraits> OrderHashSet; - class OrderIterator; - typedef WTF::HashMap<const RenderBox*, LayoutUnit> InflexibleFlexItemSize; - typedef WTF::Vector<RenderBox*> OrderedFlexItemList; + class OrderIterator { + WTF_MAKE_NONCOPYABLE(OrderIterator); + public: + OrderIterator(const RenderFlexibleBox*); + void setOrderValues(const OrderHashSet&); + RenderBox* currentChild() const { return m_currentChild; } + RenderBox* first(); + RenderBox* next(); + void reset(); + + private: + const RenderFlexibleBox* m_flexibleBox; + RenderBox* m_currentChild; + Vector<int> m_orderValues; + Vector<int>::const_iterator m_orderValuesIterator; + }; + + typedef HashMap<const RenderBox*, LayoutUnit> InflexibleFlexItemSize; + typedef Vector<RenderBox*> OrderedFlexItemList; struct LineContext; struct Violation; + // Use an inline capacity of 8, since flexbox containers usually have less than 8 children. + typedef Vector<LayoutRect, 8> ChildFrameRects; + bool hasOrthogonalFlow(RenderBox* child) const; bool isColumnFlow() const; bool isLeftToRightFlow() const; @@ -113,42 +138,61 @@ private: EAlignItems alignmentForChild(RenderBox* child) const; LayoutUnit mainAxisBorderAndPaddingExtentForChild(RenderBox* child) const; LayoutUnit mainAxisScrollbarExtentForChild(RenderBox* child) const; - LayoutUnit preferredMainAxisContentExtentForChild(RenderBox* child); + LayoutUnit preferredMainAxisContentExtentForChild(RenderBox* child, bool hasInfiniteLineLength); - void layoutFlexItems(OrderIterator&, WTF::Vector<LineContext>&); + void layoutFlexItems(bool relayoutChildren, Vector<LineContext>&); LayoutUnit autoMarginOffsetInMainAxis(const OrderedFlexItemList&, LayoutUnit& availableFreeSpace); void updateAutoMarginsInMainAxis(RenderBox* child, LayoutUnit autoMarginOffset); bool hasAutoMarginsInCrossAxis(RenderBox* child) const; bool updateAutoMarginsInCrossAxis(RenderBox* child, LayoutUnit availableAlignmentSpace); - void repositionLogicalHeightDependentFlexItems(OrderIterator&, WTF::Vector<LineContext>&, LayoutUnit& oldClientAfterEdge); - void clearChildOverrideSizes(); + void repositionLogicalHeightDependentFlexItems(Vector<LineContext>&); + LayoutUnit clientLogicalBottomAfterRepositioning(); + void appendChildFrameRects(ChildFrameRects&); + void repaintChildrenDuringLayoutIfMoved(const ChildFrameRects&); LayoutUnit availableAlignmentSpaceForChild(LayoutUnit lineCrossAxisExtent, RenderBox*); LayoutUnit marginBoxAscentForChild(RenderBox*); LayoutUnit computeChildMarginValue(Length margin, RenderView*); - void computeMainAxisPreferredSizes(bool relayoutChildren, OrderHashSet&); + void computeMainAxisPreferredSizes(OrderHashSet&); LayoutUnit adjustChildSizeForMinAndMax(RenderBox*, LayoutUnit childSize); - bool computeNextFlexLine(OrderIterator&, OrderedFlexItemList& orderedChildren, LayoutUnit& preferredMainAxisExtent, double& totalFlexGrow, double& totalWeightedFlexShrink, LayoutUnit& minMaxAppliedMainAxisExtent); + bool computeNextFlexLine(OrderedFlexItemList& orderedChildren, LayoutUnit& preferredMainAxisExtent, double& totalFlexGrow, double& totalWeightedFlexShrink, LayoutUnit& minMaxAppliedMainAxisExtent, bool& hasInfiniteLineLength); - bool resolveFlexibleLengths(FlexSign, const OrderedFlexItemList&, LayoutUnit& availableFreeSpace, double& totalFlexGrow, double& totalWeightedFlexShrink, InflexibleFlexItemSize&, WTF::Vector<LayoutUnit>& childSizes); - void freezeViolations(const WTF::Vector<Violation>&, LayoutUnit& availableFreeSpace, double& totalFlexGrow, double& totalWeightedFlexShrink, InflexibleFlexItemSize&); + bool resolveFlexibleLengths(FlexSign, const OrderedFlexItemList&, LayoutUnit& availableFreeSpace, double& totalFlexGrow, double& totalWeightedFlexShrink, InflexibleFlexItemSize&, Vector<LayoutUnit>& childSizes, bool hasInfiniteLineLength); + void freezeViolations(const Vector<Violation>&, LayoutUnit& availableFreeSpace, double& totalFlexGrow, double& totalWeightedFlexShrink, InflexibleFlexItemSize&, bool hasInfiniteLineLength); + void resetAutoMarginsAndLogicalTopInCrossAxis(RenderBox*); + bool needToStretchChild(RenderBox*); void setLogicalOverrideSize(RenderBox* child, LayoutUnit childPreferredSize); void prepareChildForPositionedLayout(RenderBox* child, LayoutUnit mainAxisOffset, LayoutUnit crossAxisOffset, PositionedLayoutMode); size_t numberOfInFlowPositionedChildren(const OrderedFlexItemList&) const; - void layoutAndPlaceChildren(LayoutUnit& crossAxisOffset, const OrderedFlexItemList&, const WTF::Vector<LayoutUnit>& childSizes, LayoutUnit availableFreeSpace, WTF::Vector<LineContext>&); + void layoutAndPlaceChildren(LayoutUnit& crossAxisOffset, const OrderedFlexItemList&, const Vector<LayoutUnit>& childSizes, LayoutUnit availableFreeSpace, bool relayoutChildren, Vector<LineContext>&); void layoutColumnReverse(const OrderedFlexItemList&, LayoutUnit crossAxisOffset, LayoutUnit availableFreeSpace); - void alignFlexLines(OrderIterator&, WTF::Vector<LineContext>&); - void alignChildren(OrderIterator&, const WTF::Vector<LineContext>&); + void alignFlexLines(Vector<LineContext>&); + void alignChildren(const Vector<LineContext>&); void applyStretchAlignmentToChild(RenderBox*, LayoutUnit lineCrossAxisExtent); - void flipForRightToLeftColumn(OrderIterator&); - void flipForWrapReverse(OrderIterator&, const WTF::Vector<LineContext>&, LayoutUnit crossAxisStartEdge); + void flipForRightToLeftColumn(); + void flipForWrapReverse(const Vector<LineContext>&, LayoutUnit crossAxisStartEdge); - OwnPtr<OrderIterator> m_orderIterator; + mutable OrderIterator m_orderIterator; int m_numberOfInFlowChildrenOnFirstLine; }; +inline RenderFlexibleBox* toRenderFlexibleBox(RenderObject* object) +{ + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isFlexibleBox()); + return static_cast<RenderFlexibleBox*>(object); +} + +inline const RenderFlexibleBox* toRenderFlexibleBox(const RenderObject* object) +{ + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isFlexibleBox()); + return static_cast<const RenderFlexibleBox*>(object); +} + +// This will catch anyone doing an unnecessary cast. +void toRenderFlexibleBox(const RenderFlexibleBox*); + } // namespace WebCore #endif // RenderFlexibleBox_h diff --git a/Source/WebCore/rendering/RenderFlowThread.cpp b/Source/WebCore/rendering/RenderFlowThread.cpp index 4aa170a2c..f898d7257 100644 --- a/Source/WebCore/rendering/RenderFlowThread.cpp +++ b/Source/WebCore/rendering/RenderFlowThread.cpp @@ -35,29 +35,34 @@ #include "HitTestRequest.h" #include "HitTestResult.h" #include "Node.h" +#include "PODIntervalTree.h" #include "PaintInfo.h" #include "RenderBoxRegionInfo.h" +#include "RenderInline.h" #include "RenderLayer.h" #include "RenderRegion.h" #include "RenderView.h" #include "TransformState.h" #include "WebKitNamedFlow.h" +#include <wtf/StackStats.h> namespace WebCore { -RenderFlowThread::RenderFlowThread(Node* node) - : RenderBlock(node) +RenderFlowThread::RenderFlowThread() + : RenderBlock(0) + , m_previousRegionCount(0) + , m_autoLogicalHeightRegionsCount(0) , m_regionsInvalidated(false) , m_regionsHaveUniformLogicalWidth(true) , m_regionsHaveUniformLogicalHeight(true) - , m_overset(true) , m_hasRegionsWithStyling(false) , m_dispatchRegionLayoutUpdateEvent(false) - , m_pageLogicalHeightChanged(false) + , m_dispatchRegionOversetChangeEvent(false) + , m_pageLogicalSizeChanged(false) + , m_inConstrainedLayoutPhase(false) + , m_needsTwoPhasesLayout(false) { - ASSERT(node->document()->cssRegionsEnabled()); - setIsAnonymous(false); - setInRenderFlowThread(); + setFlowThreadState(InsideOutOfFlowThread); } PassRefPtr<RenderStyle> RenderFlowThread::createFlowThreadStyle(RenderStyle* parentStyle) @@ -96,17 +101,27 @@ void RenderFlowThread::addRegionToThread(RenderRegion* renderRegion) ASSERT(renderRegion); m_regionList.add(renderRegion); renderRegion->setIsValid(true); - invalidateRegions(); - checkRegionsWithStyling(); } void RenderFlowThread::removeRegionFromThread(RenderRegion* renderRegion) { ASSERT(renderRegion); - m_regionRangeMap.clear(); m_regionList.remove(renderRegion); - invalidateRegions(); - checkRegionsWithStyling(); +} + +void RenderFlowThread::invalidateRegions() +{ + if (m_regionsInvalidated) { + ASSERT(selfNeedsLayout()); + return; + } + + m_regionRangeMap.clear(); + m_breakBeforeToRegionMap.clear(); + m_breakAfterToRegionMap.clear(); + setNeedsLayout(true); + + m_regionsInvalidated = true; } class CurrentRenderFlowThreadDisabler { @@ -130,29 +145,33 @@ private: RenderFlowThread* m_renderFlowThread; }; -void RenderFlowThread::layout() +void RenderFlowThread::validateRegions() { - StackStats::LayoutCheckPoint layoutCheckPoint; - - m_pageLogicalHeightChanged = m_regionsInvalidated && everHadLayout(); if (m_regionsInvalidated) { m_regionsInvalidated = false; m_regionsHaveUniformLogicalWidth = true; m_regionsHaveUniformLogicalHeight = true; - m_regionRangeMap.clear(); - m_breakBeforeToRegionMap.clear(); - m_breakAfterToRegionMap.clear(); - LayoutUnit previousRegionLogicalWidth = 0; - LayoutUnit previousRegionLogicalHeight = 0; - bool firstRegionVisited = false; if (hasRegions()) { + LayoutUnit previousRegionLogicalWidth = 0; + LayoutUnit previousRegionLogicalHeight = 0; + bool firstRegionVisited = false; + for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { RenderRegion* region = *iter; - ASSERT(!region->needsLayout()); - + ASSERT(!region->needsLayout() || region->isRenderRegionSet()); + region->deleteAllRenderBoxRegionInfo(); + // In the normal layout phase we need to initialize the computedAutoHeight for auto-height regions. + // See initializeRegionsComputedAutoHeight for the explanation. + // Also, if we have auto-height regions we can't assume m_regionsHaveUniformLogicalHeight to be true in the first phase + // because the auto-height regions don't have their height computed yet. + if (!inConstrainedLayoutPhase() && region->hasAutoLogicalHeight()) { + region->setComputedAutoHeight(region->maxPageLogicalHeight()); + m_regionsHaveUniformLogicalHeight = false; + } + LayoutUnit regionLogicalWidth = region->pageLogicalWidth(); LayoutUnit regionLogicalHeight = region->pageLogicalHeight(); @@ -167,30 +186,51 @@ void RenderFlowThread::layout() previousRegionLogicalWidth = regionLogicalWidth; } - - updateLogicalWidth(); // Called to get the maximum logical width for the region. - updateRegionsFlowThreadPortionRect(); } } + updateLogicalWidth(); // Called to get the maximum logical width for the region. + updateRegionsFlowThreadPortionRect(); +} + +void RenderFlowThread::layout() +{ + StackStats::LayoutCheckPoint layoutCheckPoint; + + m_pageLogicalSizeChanged = m_regionsInvalidated && everHadLayout(); + + // In case this is the second pass of the normal phase we need to update the auto-height regions to their initial value. + // If the region chain was invalidated this will happen anyway. + if (!m_regionsInvalidated && !inConstrainedLayoutPhase()) + initializeRegionsComputedAutoHeight(); + + validateRegions(); + + // This is the first phase of the layout and because we have auto-height regions we'll need a second + // pass to update the flow with the computed auto-height regions. + m_needsTwoPhasesLayout = !inConstrainedLayoutPhase() && hasAutoLogicalHeightRegions(); + CurrentRenderFlowThreadMaintainer currentFlowThreadSetter(this); RenderBlock::layout(); - m_pageLogicalHeightChanged = false; + m_pageLogicalSizeChanged = false; if (lastRegion()) lastRegion()->expandToEncompassFlowThreadContentsIfNeeded(); if (shouldDispatchRegionLayoutUpdateEvent()) dispatchRegionLayoutUpdateEvent(); + + if (shouldDispatchRegionOversetChangeEvent()) + dispatchRegionOversetChangeEvent(); } void RenderFlowThread::updateLogicalWidth() { - LayoutUnit logicalWidth = 0; + LayoutUnit logicalWidth = initialLogicalWidth(); for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { RenderRegion* region = *iter; - ASSERT(!region->needsLayout()); + ASSERT(!region->needsLayout() || region->isRenderRegionSet()); logicalWidth = max(region->pageLogicalWidth(), logicalWidth); } setLogicalWidth(logicalWidth); @@ -211,62 +251,90 @@ void RenderFlowThread::computeLogicalHeight(LayoutUnit, LayoutUnit logicalTop, L computedValues.m_position = logicalTop; computedValues.m_extent = 0; + const LayoutUnit maxFlowSize = RenderFlowThread::maxLogicalHeight(); for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { RenderRegion* region = *iter; - ASSERT(!region->needsLayout()); + ASSERT(!region->needsLayout() || region->isRenderRegionSet()); - if (region->needsOverrideLogicalContentHeightComputation()) { - // If we have an auto logical height region for which we did not compute a height yet, - // then we cannot compute and update the height of this flow. - return; - } + LayoutUnit distanceToMaxSize = maxFlowSize - computedValues.m_extent; + computedValues.m_extent += std::min(distanceToMaxSize, region->logicalHeightOfAllFlowThreadContent()); - computedValues.m_extent += region->logicalHeightOfAllFlowThreadContent(); + // If we reached the maximum size there's no point in going further. + if (computedValues.m_extent == maxFlowSize) + return; } } -void RenderFlowThread::paintFlowThreadPortionInRegion(PaintInfo& paintInfo, RenderRegion* region, LayoutRect flowThreadPortionRect, LayoutRect flowThreadPortionOverflowRect, const LayoutPoint& paintOffset) const +LayoutRect RenderFlowThread::computeRegionClippingRect(const LayoutPoint& offset, const LayoutRect& flowThreadPortionRect, const LayoutRect& flowThreadPortionOverflowRect) const +{ + LayoutRect regionClippingRect(offset + (flowThreadPortionOverflowRect.location() - flowThreadPortionRect.location()), flowThreadPortionOverflowRect.size()); + if (style()->isFlippedBlocksWritingMode()) + regionClippingRect.move(flowThreadPortionRect.size() - flowThreadPortionOverflowRect.size()); + return regionClippingRect; +} + +void RenderFlowThread::paintFlowThreadPortionInRegion(PaintInfo& paintInfo, RenderRegion* region, const LayoutRect& flowThreadPortionRect, const LayoutRect& flowThreadPortionOverflowRect, const LayoutPoint& paintOffset) const { GraphicsContext* context = paintInfo.context; if (!context) return; - // Adjust the clipping rect for the region. - // paintOffset contains the offset where the painting should occur - // adjusted with the region padding and border. - LayoutRect regionClippingRect(paintOffset + (flowThreadPortionOverflowRect.location() - flowThreadPortionRect.location()), flowThreadPortionOverflowRect.size()); + // RenderFlowThread should start painting its content in a position that is offset + // from the region rect's current position. The amount of offset is equal to the location of + // the flow thread portion in the flow thread's local coordinates. + // Note that we have to pixel snap the location at which we're going to paint, since this is necessary + // to minimize the amount of incorrect snapping that would otherwise occur. + // If we tried to paint by applying a non-integral translation, then all the + // layout code that attempted to pixel snap would be incorrect. + IntPoint adjustedPaintOffset; + LayoutPoint portionLocation; + if (style()->isFlippedBlocksWritingMode()) { + LayoutRect flippedFlowThreadPortionRect(flowThreadPortionRect); + flipForWritingMode(flippedFlowThreadPortionRect); + portionLocation = flippedFlowThreadPortionRect.location(); + } else + portionLocation = flowThreadPortionRect.location(); + adjustedPaintOffset = roundedIntPoint(paintOffset - portionLocation); + + // The clipping rect for the region is set up by assuming the flowThreadPortionRect is going to paint offset from adjustedPaintOffset. + // Remember that we pixel snapped and moved the paintOffset and stored the snapped result in adjustedPaintOffset. Now we add back in + // the flowThreadPortionRect's location to get the spot where we expect the portion to actually paint. This can be non-integral and + // that's ok. We then pixel snap the resulting clipping rect to account for snapping that will occur when the flow thread paints. + IntRect regionClippingRect = pixelSnappedIntRect(computeRegionClippingRect(adjustedPaintOffset + portionLocation, flowThreadPortionRect, flowThreadPortionOverflowRect)); PaintInfo info(paintInfo); - info.rect.intersect(pixelSnappedIntRect(regionClippingRect)); + info.rect.intersect(regionClippingRect); if (!info.rect.isEmpty()) { context->save(); context->clip(regionClippingRect); - // RenderFlowThread should start painting its content in a position that is offset - // from the region rect's current position. The amount of offset is equal to the location of - // the flow thread portion in the flow thread's local coordinates. - IntPoint renderFlowThreadOffset; - if (style()->isFlippedBlocksWritingMode()) { - LayoutRect flippedFlowThreadPortionRect(flowThreadPortionRect); - flipForWritingMode(flippedFlowThreadPortionRect); - renderFlowThreadOffset = roundedIntPoint(paintOffset - flippedFlowThreadPortionRect.location()); - } else - renderFlowThreadOffset = roundedIntPoint(paintOffset - flowThreadPortionRect.location()); - - context->translate(renderFlowThreadOffset.x(), renderFlowThreadOffset.y()); - info.rect.moveBy(-renderFlowThreadOffset); + context->translate(adjustedPaintOffset.x(), adjustedPaintOffset.y()); + info.rect.moveBy(-adjustedPaintOffset); - layer()->paint(context, info.rect, 0, 0, region, RenderLayer::PaintLayerTemporaryClipRects); + PaintBehavior paintBehavior = 0; + if (info.phase == PaintPhaseTextClip) + paintBehavior |= PaintBehaviorForceBlackText; + else if (info.phase == PaintPhaseSelection) + paintBehavior |= PaintBehaviorSelectionOnly; + + layer()->paint(context, info.rect, paintBehavior, 0, region, RenderLayer::PaintLayerTemporaryClipRects); context->restore(); } } -bool RenderFlowThread::hitTestFlowThreadPortionInRegion(RenderRegion* region, LayoutRect flowThreadPortionRect, LayoutRect flowThreadPortionOverflowRect, const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset) const +bool RenderFlowThread::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction) { - LayoutRect regionClippingRect(accumulatedOffset + (flowThreadPortionOverflowRect.location() - flowThreadPortionRect.location()), flowThreadPortionOverflowRect.size()); + if (hitTestAction == HitTestBlockBackground) + return false; + return RenderBlock::nodeAtPoint(request, result, locationInContainer, accumulatedOffset, hitTestAction); +} + +bool RenderFlowThread::hitTestFlowThreadPortionInRegion(RenderRegion* region, const LayoutRect& flowThreadPortionRect, const LayoutRect& flowThreadPortionOverflowRect, const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset) const +{ + LayoutRect regionClippingRect = computeRegionClippingRect(accumulatedOffset, flowThreadPortionRect, flowThreadPortionOverflowRect); if (!regionClippingRect.contains(locationInContainer.point())) return false; @@ -279,7 +347,7 @@ bool RenderFlowThread::hitTestFlowThreadPortionInRegion(RenderRegion* region, La renderFlowThreadOffset = accumulatedOffset - flowThreadPortionRect.location(); // Always ignore clipping, since the RenderFlowThread has nothing to do with the bounds of the FrameView. - HitTestRequest newRequest(request.type() | HitTestRequest::IgnoreClipping); + HitTestRequest newRequest(request.type() | HitTestRequest::IgnoreClipping | HitTestRequest::DisallowShadowContent); // Make a new temporary HitTestLocation in the new region. HitTestLocation newHitTestLocation(locationInContainer, -renderFlowThreadOffset, region); @@ -318,76 +386,134 @@ void RenderFlowThread::repaintRectangleInRegions(const LayoutRect& repaintRect, } } -RenderRegion* RenderFlowThread::regionAtBlockOffset(LayoutUnit offset, bool extendLastRegion) const +RenderRegion* RenderFlowThread::regionAtBlockOffset(LayoutUnit offset, bool extendLastRegion, RegionAutoGenerationPolicy autoGenerationPolicy) { ASSERT(!m_regionsInvalidated); - // If no region matches the position and extendLastRegion is true, it will return - // the last valid region. It is similar to auto extending the size of the last region. - RenderRegion* lastValidRegion = 0; - - LayoutUnit accumulatedLogicalHeight = 0; - - // FIXME: The regions are always in order, optimize this search. - for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { - RenderRegion* region = *iter; + if (autoGenerationPolicy == AllowRegionAutoGeneration) + autoGenerateRegionsToBlockOffset(offset); - if (offset <= 0) - return region; + if (offset <= 0) + return m_regionList.isEmpty() ? 0 : m_regionList.first(); - if (extendLastRegion || region->isRenderRegionSet()) - lastValidRegion = region; + RegionSearchAdapter adapter(offset); + m_regionIntervalTree.allOverlapsWithAdapter<RegionSearchAdapter>(adapter); - // If we did not compute the region's height, we should consider this region - // tall enough to accomodate all content. - if (region->needsOverrideLogicalContentHeightComputation()) - return region; + // If no region was found, the offset is in the flow thread overflow. + // The last region will contain the offset if extendLastRegion is set or if the last region is a set. + if (!adapter.result() && !m_regionList.isEmpty() && (extendLastRegion || m_regionList.last()->isRenderRegionSet())) + return m_regionList.last(); - if (region->hasOverrideHeight() && view()->normalLayoutPhase()) { - accumulatedLogicalHeight += region->overrideLogicalContentHeight(); - if (offset < accumulatedLogicalHeight) - return region; - continue; + return adapter.result(); +} + +LayoutPoint RenderFlowThread::adjustedPositionRelativeToOffsetParent(const RenderBoxModelObject& boxModelObject, const LayoutPoint& startPoint) +{ + LayoutPoint referencePoint = startPoint; + + // FIXME: This needs to be adapted for different writing modes inside the flow thread. + RenderRegion* startRegion = regionAtBlockOffset(referencePoint.y()); + if (startRegion) { + // Take into account the offset coordinates of the region. + RenderBoxModelObject* currObject = startRegion; + RenderBoxModelObject* currOffsetParent; + while ((currOffsetParent = currObject->offsetParent())) { + referencePoint.move(currObject->offsetLeft(), currObject->offsetTop()); + + // Since we're looking for the offset relative to the body, we must also + // take into consideration the borders of the region's offsetParent. + if (currOffsetParent->isBox() && !currOffsetParent->isBody()) + referencePoint.move(toRenderBox(currOffsetParent)->borderLeft(), toRenderBox(currOffsetParent)->borderTop()); + + currObject = currOffsetParent; } - - LayoutRect regionRect = region->flowThreadPortionRect(); - accumulatedLogicalHeight += isHorizontalWritingMode() ? regionRect.height() : regionRect.width(); - if (offset < accumulatedLogicalHeight) - return region; + + // We need to check if any of this box's containing blocks start in a different region + // and if so, drop the object's top position (which was computed relative to its containing block + // and is no longer valid) and recompute it using the region in which it flows as reference. + bool wasComputedRelativeToOtherRegion = false; + const RenderBlock* objContainingBlock = boxModelObject.containingBlock(); + while (objContainingBlock && !objContainingBlock->isRenderNamedFlowThread()) { + // Check if this object is in a different region. + RenderRegion* parentStartRegion = 0; + RenderRegion* parentEndRegion = 0; + getRegionRangeForBox(objContainingBlock, parentStartRegion, parentEndRegion); + if (parentStartRegion && parentStartRegion != startRegion) { + wasComputedRelativeToOtherRegion = true; + break; + } + objContainingBlock = objContainingBlock->containingBlock(); + } + + if (wasComputedRelativeToOtherRegion) { + if (boxModelObject.isBox()) { + // Use borderBoxRectInRegion to account for variations such as percentage margins. + LayoutRect borderBoxRect = toRenderBox(&boxModelObject)->borderBoxRectInRegion(startRegion, RenderBox::DoNotCacheRenderBoxRegionInfo); + referencePoint.move(borderBoxRect.location().x(), 0); + } + + // Get the logical top coordinate of the current object. + LayoutUnit top = 0; + if (boxModelObject.isRenderBlock()) + top = toRenderBlock(&boxModelObject)->offsetFromLogicalTopOfFirstPage(); + else { + if (boxModelObject.containingBlock()) + top = boxModelObject.containingBlock()->offsetFromLogicalTopOfFirstPage(); + + if (boxModelObject.isBox()) + top += toRenderBox(&boxModelObject)->topLeftLocation().y(); + else if (boxModelObject.isRenderInline()) + top -= toRenderInline(&boxModelObject)->borderTop(); + } + + // Get the logical top of the region this object starts in + // and compute the object's top, relative to the region's top. + LayoutUnit regionLogicalTop = startRegion->pageLogicalTopForOffset(top); + LayoutUnit topRelativeToRegion = top - regionLogicalTop; + referencePoint.setY(startRegion->offsetTop() + topRelativeToRegion); + + // Since the top has been overriden, check if the + // relative/sticky positioning must be reconsidered. + if (boxModelObject.isRelPositioned()) + referencePoint.move(0, boxModelObject.relativePositionOffset().height()); + else if (boxModelObject.isStickyPositioned()) + referencePoint.move(0, boxModelObject.stickyPositionOffset().height()); + } + + // Since we're looking for the offset relative to the body, we must also + // take into consideration the borders of the region. + referencePoint.move(startRegion->borderLeft(), startRegion->borderTop()); } - - return lastValidRegion; + + return referencePoint; } -LayoutUnit RenderFlowThread::pageLogicalTopForOffset(LayoutUnit offset) const +LayoutUnit RenderFlowThread::pageLogicalTopForOffset(LayoutUnit offset) { RenderRegion* region = regionAtBlockOffset(offset); return region ? region->pageLogicalTopForOffset(offset) : LayoutUnit(); } -LayoutUnit RenderFlowThread::pageLogicalWidthForOffset(LayoutUnit offset) const +LayoutUnit RenderFlowThread::pageLogicalWidthForOffset(LayoutUnit offset) { RenderRegion* region = regionAtBlockOffset(offset, true); return region ? region->pageLogicalWidth() : contentLogicalWidth(); } -LayoutUnit RenderFlowThread::pageLogicalHeightForOffset(LayoutUnit offset) const +LayoutUnit RenderFlowThread::pageLogicalHeightForOffset(LayoutUnit offset) { RenderRegion* region = regionAtBlockOffset(offset); if (!region) return 0; - if (region->needsOverrideLogicalContentHeightComputation()) - return LayoutUnit::max() / 2; + return region->pageLogicalHeight(); } -LayoutUnit RenderFlowThread::pageRemainingLogicalHeightForOffset(LayoutUnit offset, PageBoundaryRule pageBoundaryRule) const +LayoutUnit RenderFlowThread::pageRemainingLogicalHeightForOffset(LayoutUnit offset, PageBoundaryRule pageBoundaryRule) { RenderRegion* region = regionAtBlockOffset(offset); if (!region) return 0; - if (region->needsOverrideLogicalContentHeightComputation()) - return LayoutUnit::max() / 2; LayoutUnit pageLogicalTop = region->pageLogicalTopForOffset(offset); LayoutUnit pageLogicalHeight = region->pageLogicalHeight(); @@ -414,7 +540,7 @@ RenderRegion* RenderFlowThread::mapFromFlowToRegion(TransformState& transformSta // Note: Using the center in order to avoid rounding errors. LayoutPoint center = boxRect.center(); - RenderRegion* renderRegion = regionAtBlockOffset(isHorizontalWritingMode() ? center.y() : center.x(), true); + RenderRegion* renderRegion = const_cast<RenderFlowThread*>(this)->regionAtBlockOffset(isHorizontalWritingMode() ? center.y() : center.x(), true, DisallowRegionAutoGeneration); if (!renderRegion) return 0; @@ -431,6 +557,12 @@ void RenderFlowThread::removeRenderBoxRegionInfo(RenderBox* box) if (!hasRegions()) return; + // If the region chain was invalidated the next layout will clear the box information from all the regions. + if (m_regionsInvalidated) { + ASSERT(selfNeedsLayout()); + return; + } + RenderRegion* startRegion; RenderRegion* endRegion; getRegionRangeForBox(box, startRegion, endRegion); @@ -453,25 +585,33 @@ void RenderFlowThread::removeRenderBoxRegionInfo(RenderBox* box) m_regionRangeMap.remove(box); } -bool RenderFlowThread::logicalWidthChangedInRegions(const RenderBlock* block, LayoutUnit offsetFromLogicalTopOfFirstPage) +bool RenderFlowThread::logicalWidthChangedInRegionsForBlock(const RenderBlock* block) { - if (!hasRegions() || block == this) // Not necessary, since if any region changes, we do a full pagination relayout anyway. + if (!hasRegions()) return false; RenderRegion* startRegion; RenderRegion* endRegion; getRegionRangeForBox(block, startRegion, endRegion); + // When the region chain is invalidated the box information is discarded so we must assume the width has changed. + if (m_pageLogicalSizeChanged && !startRegion) + return true; + + // Not necessary for the flow thread, since we already computed the correct info for it. + if (block == this) + return false; + for (RenderRegionList::iterator iter = m_regionList.find(startRegion); iter != m_regionList.end(); ++iter) { RenderRegion* region = *iter; - ASSERT(!region->needsLayout()); + ASSERT(!region->needsLayout() || region->isRenderRegionSet()); OwnPtr<RenderBoxRegionInfo> oldInfo = region->takeRenderBoxRegionInfo(block); if (!oldInfo) continue; LayoutUnit oldLogicalWidth = oldInfo->logicalWidth(); - RenderBoxRegionInfo* newInfo = block->renderBoxRegionInfo(region, offsetFromLogicalTopOfFirstPage); + RenderBoxRegionInfo* newInfo = block->renderBoxRegionInfo(region); if (!newInfo || newInfo->logicalWidth() != oldLogicalWidth) return true; @@ -551,6 +691,8 @@ void RenderFlowThread::setRegionRangeForBox(const RenderBox* box, LayoutUnit off if (!hasRegions()) return; + ASSERT(box->logicalHeight() >= 0); + // FIXME: Not right for differing writing-modes. RenderRegion* startRegion = regionAtBlockOffset(offsetFromLogicalTopOfFirstPage, true); RenderRegion* endRegion = regionAtBlockOffset(offsetFromLogicalTopOfFirstPage + box->logicalHeight(), true); @@ -598,47 +740,11 @@ void RenderFlowThread::getRegionRangeForBox(const RenderBox* box, RenderRegion*& ASSERT(m_regionList.contains(startRegion) && m_regionList.contains(endRegion)); } -void RenderFlowThread::computeOverflowStateForRegions(LayoutUnit oldClientAfterEdge) +void RenderFlowThread::applyBreakAfterContent(LayoutUnit clientHeight) { - LayoutUnit height = oldClientAfterEdge; - // Simulate a region break at height. If it points inside an auto logical height region, - // then it may determine the region override logical content height. - addForcedRegionBreak(height, this, false); - - // FIXME: the visual overflow of middle region (if it is the last one to contain any content in a render flow thread) - // might not be taken into account because the render flow thread height is greater that that regions height + its visual overflow - // because of how computeLogicalHeight is implemented for RenderFlowThread (as a sum of all regions height). - // This means that the middle region will be marked as fit (even if it has visual overflow flowing into the next region) - if (hasRenderOverflow() - && ( (isHorizontalWritingMode() && visualOverflowRect().maxY() > clientBoxRect().maxY()) - || (!isHorizontalWritingMode() && visualOverflowRect().maxX() > clientBoxRect().maxX()))) - height = isHorizontalWritingMode() ? visualOverflowRect().maxY() : visualOverflowRect().maxX(); - - RenderRegion* lastReg = lastRegion(); - for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { - RenderRegion* region = *iter; - LayoutUnit flowMin = height - (isHorizontalWritingMode() ? region->flowThreadPortionRect().y() : region->flowThreadPortionRect().x()); - LayoutUnit flowMax = height - (isHorizontalWritingMode() ? region->flowThreadPortionRect().maxY() : region->flowThreadPortionRect().maxX()); - RenderRegion::RegionState previousState = region->regionState(); - RenderRegion::RegionState state = RenderRegion::RegionFit; - if (flowMin <= 0) - state = RenderRegion::RegionEmpty; - if (flowMax > 0 && region == lastReg) - state = RenderRegion::RegionOverset; - region->setRegionState(state); - // determine whether the NamedFlow object should dispatch a regionLayoutUpdate event - // FIXME: currently it cannot determine whether a region whose regionOverset state remained either "fit" or "overset" has actually - // changed, so it just assumes that the NamedFlow should dispatch the event - if (previousState != state - || state == RenderRegion::RegionFit - || state == RenderRegion::RegionOverset) - setDispatchRegionLayoutUpdateEvent(true); - } - - // With the regions overflow state computed we can also set the overset flag for the named flow. - // If there are no valid regions in the chain, overset is true. - m_overset = lastReg ? lastReg->regionState() == RenderRegion::RegionOverset : true; + // then it may determine the region computed autoheight. + addForcedRegionBreak(clientHeight, this, false); } bool RenderFlowThread::regionInRange(const RenderRegion* targetRegion, const RenderRegion* startRegion, const RenderRegion* endRegion) const @@ -675,9 +781,8 @@ bool RenderFlowThread::objectInFlowRegion(const RenderObject* object, const Rend ASSERT(object); ASSERT(region); - if (!object->inRenderFlowThread()) - return false; - if (object->enclosingRenderFlowThread() != this) + RenderFlowThread* flowThread = object->flowThreadContainingBlock(); + if (flowThread != this) return false; if (!m_regionList.contains(const_cast<RenderRegion*>(region))) return false; @@ -717,7 +822,7 @@ bool RenderFlowThread::objectInFlowRegion(const RenderObject* object, const Rend } #ifndef NDEBUG -unsigned RenderFlowThread::autoLogicalHeightRegionsCount() const +bool RenderFlowThread::isAutoLogicalHeightRegionsCountConsistent() const { unsigned autoLogicalHeightRegions = 0; for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { @@ -726,45 +831,30 @@ unsigned RenderFlowThread::autoLogicalHeightRegionsCount() const autoLogicalHeightRegions++; } - return autoLogicalHeightRegions; + return autoLogicalHeightRegions == m_autoLogicalHeightRegionsCount; } #endif -void RenderFlowThread::resetRegionsOverrideLogicalContentHeight() +// During the normal layout phase of the named flow the regions are initialized with a height equal to their max-height. +// This way unforced breaks are automatically placed when a region is full and the content height/position correctly estimated. +// Also, the region where a forced break falls is exactly the region found at the forced break offset inside the flow content. +void RenderFlowThread::initializeRegionsComputedAutoHeight(RenderRegion* startRegion) { - ASSERT(view()->layoutState()); - ASSERT(view()->normalLayoutPhase()); - - // We need to reset the override logical content height for regions with auto logical height - // only if the flow thread content needs layout. - if (!needsLayout()) + ASSERT(!inConstrainedLayoutPhase()); + if (!hasAutoLogicalHeightRegions()) return; - // FIXME: optimize this to iterate the region chain only if the flow thread has auto logical height - // region. - - for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { - RenderRegion* region = *iter; - if (!region->hasAutoLogicalHeight()) - continue; - - region->clearOverrideLogicalContentHeight(); - // FIXME: We need to find a way to avoid marking all the regions ancestors for layout - // as we are already inside layout. - region->setNeedsLayout(true); + RenderRegionList::iterator regionIter = startRegion ? m_regionList.find(startRegion) : m_regionList.begin(); + for (; regionIter != m_regionList.end(); ++regionIter) { + RenderRegion* region = *regionIter; + if (region->hasAutoLogicalHeight()) + region->setComputedAutoHeight(region->maxPageLogicalHeight()); } - // Make sure we don't skip any region breaks when we do the layout again. - // Using m_regionsInvalidated to force all the RenderFlowThread children do the layout again. - m_regionsInvalidated = true; } void RenderFlowThread::markAutoLogicalHeightRegionsForLayout() { - ASSERT(view()->layoutState()); - ASSERT(view()->constrainedFlowThreadsLayoutPhase()); - - // FIXME: optimize this to iterate the region chain only if the flow thread has auto logical height - // region. + ASSERT(hasAutoLogicalHeightRegions()); for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { RenderRegion* region = *iter; @@ -775,46 +865,40 @@ void RenderFlowThread::markAutoLogicalHeightRegionsForLayout() // as we are already inside layout. region->setNeedsLayout(true); } - - m_regionsInvalidated = true; - setNeedsLayout(true); } -void RenderFlowThread::updateRegionsFlowThreadPortionRect() +void RenderFlowThread::updateRegionsFlowThreadPortionRect(const RenderRegion* lastRegionWithContent) { + ASSERT(!lastRegionWithContent || (!inConstrainedLayoutPhase() && hasAutoLogicalHeightRegions())); LayoutUnit logicalHeight = 0; + bool emptyRegionsSegment = false; + // FIXME: Optimize not to clear the interval all the time. This implies manually managing the tree nodes lifecycle. + m_regionIntervalTree.clear(); + m_regionIntervalTree.initIfNeeded(); for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { RenderRegion* region = *iter; + // If we find an empty auto-height region, clear the computedAutoHeight value. + if (emptyRegionsSegment && region->hasAutoLogicalHeight()) + region->clearComputedAutoHeight(); + LayoutUnit regionLogicalWidth = region->pageLogicalWidth(); - LayoutUnit regionLogicalHeight = region->logicalHeightOfAllFlowThreadContent(); + LayoutUnit regionLogicalHeight = std::min<LayoutUnit>(RenderFlowThread::maxLogicalHeight() - logicalHeight, region->logicalHeightOfAllFlowThreadContent()); LayoutRect regionRect(style()->direction() == LTR ? LayoutUnit() : logicalWidth() - regionLogicalWidth, logicalHeight, regionLogicalWidth, regionLogicalHeight); - // When a flow thread has more than one auto logical height region, - // we have to take into account the override logical content height value, - // if computed for an auto logical height region, and use it to set the height - // for the region rect. This way, the regions in the chain following the auto - // logical height region, will be able to fragment the right part of their - // associated flow thread content (and compute their overrideComputedLogicalHeight properly). - if (region->hasOverrideHeight() && view()->normalLayoutPhase()) { - regionLogicalHeight = region->overrideLogicalContentHeight(); - regionRect.setHeight(regionLogicalHeight); - } - region->setFlowThreadPortionRect(isHorizontalWritingMode() ? regionRect : regionRect.transposedRect()); + + m_regionIntervalTree.add(RegionIntervalTree::createInterval(logicalHeight, logicalHeight + regionLogicalHeight, region)); + logicalHeight += regionLogicalHeight; - } -} -void RenderFlowThread::clearOverrideLogicalContentHeightInRegions(RenderRegion* startRegion) -{ - RenderRegionList::iterator regionIter = startRegion ? m_regionList.find(startRegion) : m_regionList.begin(); - for (; regionIter != m_regionList.end(); ++regionIter) { - RenderRegion* region = *regionIter; - if (region->hasAutoLogicalHeight()) - region->clearOverrideLogicalContentHeight(); + // Once we find the last region with content the next regions are considered empty. + if (lastRegionWithContent == region) + emptyRegionsSegment = true; } + + ASSERT(!lastRegionWithContent || emptyRegionsSegment); } // Even if we require the break to occur at offsetBreakInFlowThread, because regions may have min/max-height values, @@ -824,9 +908,9 @@ bool RenderFlowThread::addForcedRegionBreak(LayoutUnit offsetBreakInFlowThread, { // We take breaks into account for height computation for auto logical height regions // only in the layout phase in which we lay out the flows threads unconstrained - // and we use the content breaks to determine the overrideContentLogicalHeight for + // and we use the content breaks to determine the computed auto height for // auto logical height regions. - if (view()->constrainedFlowThreadsLayoutPhase()) + if (inConstrainedLayoutPhase()) return false; // Breaks can come before or after some objects. We need to track these objects, so that if we get @@ -838,7 +922,7 @@ bool RenderFlowThread::addForcedRegionBreak(LayoutUnit offsetBreakInFlowThread, RenderRegionList::iterator regionIter = m_regionList.find(iter->value); ASSERT(regionIter != m_regionList.end()); ASSERT((*regionIter)->hasAutoLogicalHeight()); - clearOverrideLogicalContentHeightInRegions(*regionIter); + initializeRegionsComputedAutoHeight(*regionIter); // We need to update the regions flow thread portion rect because we are going to process // a break on these regions. @@ -846,63 +930,224 @@ bool RenderFlowThread::addForcedRegionBreak(LayoutUnit offsetBreakInFlowThread, } // Simulate a region break at offsetBreakInFlowThread. If it points inside an auto logical height region, - // then it determines the region override logical content height. + // then it determines the region computed auto height. RenderRegion* region = regionAtBlockOffset(offsetBreakInFlowThread); if (!region) return false; - // We want to distribute the offsetBreakInFlowThread content among the regions starting with the found region. - bool overrideLogicalContentHeightComputed = false; + bool lastBreakAfterContent = breakChild == this; + bool hasComputedAutoHeight = false; LayoutUnit currentRegionOffsetInFlowThread = isHorizontalWritingMode() ? region->flowThreadPortionRect().y() : region->flowThreadPortionRect().x(); LayoutUnit offsetBreakInCurrentRegion = offsetBreakInFlowThread - currentRegionOffsetInFlowThread; - RenderRegionList::iterator regionIter = m_regionList.find(region); - ASSERT(regionIter != m_regionList.end()); - for (; regionIter != m_regionList.end(); ++regionIter) { - RenderRegion* region = *regionIter; - if (region->needsOverrideLogicalContentHeightComputation()) { - mapToUse.set(breakChild, region); + if (region->hasAutoLogicalHeight()) { + // A forced break can appear only in an auto-height region that didn't have a forced break before. + // This ASSERT is a good-enough heuristic to verify the above condition. + ASSERT(region->maxPageLogicalHeight() == region->computedAutoHeight()); - overrideLogicalContentHeightComputed = true; + mapToUse.set(breakChild, region); - // Compute the region height pretending that the offsetBreakInCurrentRegion is the logicalHeight for the auto-height region. - LayoutUnit regionOverrideLogicalContentHeight = region->computeReplacedLogicalHeightRespectingMinMaxHeight(offsetBreakInCurrentRegion); - region->setOverrideLogicalContentHeight(regionOverrideLogicalContentHeight); + hasComputedAutoHeight = true; - offsetBreakInCurrentRegion -= regionOverrideLogicalContentHeight; - currentRegionOffsetInFlowThread += regionOverrideLogicalContentHeight; - } else - currentRegionOffsetInFlowThread += isHorizontalWritingMode() ? region->flowThreadPortionRect().height() : region->flowThreadPortionRect().width(); + // Compute the region height pretending that the offsetBreakInCurrentRegion is the logicalHeight for the auto-height region. + LayoutUnit regionComputedAutoHeight = region->constrainContentBoxLogicalHeightByMinMax(offsetBreakInCurrentRegion); - // If the current offset if greater than the break offset, bail out and skip the current region. - if (currentRegionOffsetInFlowThread >= offsetBreakInFlowThread) { - ++regionIter; - break; - } - } + // The new height of this region needs to be smaller than the initial value, the max height. A forced break is the only way to change the initial + // height of an auto-height region besides content ending. + ASSERT(regionComputedAutoHeight <= region->maxPageLogicalHeight()); - // The remaining auto logical height regions in the chain that were unable to receive content - // and set their overrideLogicalContentHeight should have their associated values cleared. - if (regionIter != m_regionList.end()) - clearOverrideLogicalContentHeightInRegions(*regionIter); + region->setComputedAutoHeight(regionComputedAutoHeight); + + currentRegionOffsetInFlowThread += regionComputedAutoHeight; + } else + currentRegionOffsetInFlowThread += isHorizontalWritingMode() ? region->flowThreadPortionRect().height() : region->flowThreadPortionRect().width(); - if (overrideLogicalContentHeightComputed) + // If the break was found inside an auto-height region its size changed so we need to recompute the flow thread portion rectangles. + // Also, if this is the last break after the content we need to clear the computedAutoHeight value on the last empty regions. + if (hasAutoLogicalHeightRegions() && lastBreakAfterContent) + updateRegionsFlowThreadPortionRect(region); + else if (hasComputedAutoHeight) updateRegionsFlowThreadPortionRect(); if (offsetBreakAdjustment) *offsetBreakAdjustment = max<LayoutUnit>(0, currentRegionOffsetInFlowThread - offsetBreakInFlowThread); - return overrideLogicalContentHeightComputed; + return hasComputedAutoHeight; +} + +void RenderFlowThread::incrementAutoLogicalHeightRegions() +{ + if (!m_autoLogicalHeightRegionsCount) + view()->flowThreadController()->incrementFlowThreadsWithAutoLogicalHeightRegions(); + ++m_autoLogicalHeightRegionsCount; +} + +void RenderFlowThread::decrementAutoLogicalHeightRegions() +{ + ASSERT(m_autoLogicalHeightRegionsCount > 0); + --m_autoLogicalHeightRegionsCount; + if (!m_autoLogicalHeightRegionsCount) + view()->flowThreadController()->decrementFlowThreadsWithAutoLogicalHeightRegions(); +} + +void RenderFlowThread::collectLayerFragments(LayerFragments& layerFragments, const LayoutRect& layerBoundingBox, const LayoutRect& dirtyRect) +{ + ASSERT(!m_regionsInvalidated); + + for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { + RenderRegion* region = *iter; + region->collectLayerFragments(layerFragments, layerBoundingBox, dirtyRect); + } +} + +LayoutRect RenderFlowThread::fragmentsBoundingBox(const LayoutRect& layerBoundingBox) +{ + ASSERT(!m_regionsInvalidated); + + LayoutRect result; + for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { + RenderRegion* region = *iter; + LayerFragments fragments; + region->collectLayerFragments(fragments, layerBoundingBox, PaintInfo::infiniteRect()); + for (size_t i = 0; i < fragments.size(); ++i) { + const LayerFragment& fragment = fragments.at(i); + LayoutRect fragmentRect(layerBoundingBox); + fragmentRect.intersect(fragment.paginationClip); + fragmentRect.moveBy(fragment.paginationOffset); + result.unite(fragmentRect); + } + } + + return result; +} + +bool RenderFlowThread::hasCachedOffsetFromLogicalTopOfFirstRegion(const RenderBox* box) const +{ + return m_boxesToOffsetMap.contains(box); +} + +LayoutUnit RenderFlowThread::cachedOffsetFromLogicalTopOfFirstRegion(const RenderBox* box) const +{ + return m_boxesToOffsetMap.get(box); +} + +void RenderFlowThread::setOffsetFromLogicalTopOfFirstRegion(const RenderBox* box, LayoutUnit offset) +{ + m_boxesToOffsetMap.set(box, offset); +} + +void RenderFlowThread::clearOffsetFromLogicalTopOfFirstRegion(const RenderBox* box) +{ + ASSERT(m_boxesToOffsetMap.contains(box)); + m_boxesToOffsetMap.remove(box); +} + +const RenderBox* RenderFlowThread::currentActiveRenderBox() const +{ + if (m_activeObjectsStack.isEmpty()) + return 0; + + const RenderObject* currentObject = m_activeObjectsStack.last(); + return currentObject->isBox() ? toRenderBox(currentObject) : 0; +} + +void RenderFlowThread::pushFlowThreadLayoutState(const RenderObject* object) +{ + if (const RenderBox* currentBoxDescendant = currentActiveRenderBox()) { + LayoutState* layoutState = currentBoxDescendant->view()->layoutState(); + if (layoutState && layoutState->isPaginated()) { + ASSERT(layoutState->m_renderer == currentBoxDescendant); + LayoutSize offsetDelta = layoutState->m_layoutOffset - layoutState->m_pageOffset; + setOffsetFromLogicalTopOfFirstRegion(currentBoxDescendant, currentBoxDescendant->isHorizontalWritingMode() ? offsetDelta.height() : offsetDelta.width()); + } + } + + m_activeObjectsStack.add(object); +} + +void RenderFlowThread::popFlowThreadLayoutState() +{ + m_activeObjectsStack.removeLast(); + + if (const RenderBox* currentBoxDescendant = currentActiveRenderBox()) { + LayoutState* layoutState = currentBoxDescendant->view()->layoutState(); + if (layoutState && layoutState->isPaginated()) + clearOffsetFromLogicalTopOfFirstRegion(currentBoxDescendant); + } +} + +LayoutUnit RenderFlowThread::offsetFromLogicalTopOfFirstRegion(const RenderBlock* currentBlock) const +{ + // First check if we cached the offset for the block if it's an ancestor containing block of the box + // being currently laid out. + if (hasCachedOffsetFromLogicalTopOfFirstRegion(currentBlock)) + return cachedOffsetFromLogicalTopOfFirstRegion(currentBlock); + + // If it's the current box being laid out, use the layout state. + const RenderBox* currentBoxDescendant = currentActiveRenderBox(); + if (currentBlock == currentBoxDescendant) { + LayoutState* layoutState = view()->layoutState(); + ASSERT(layoutState->m_renderer == currentBlock); + ASSERT(layoutState && layoutState->isPaginated()); + LayoutSize offsetDelta = layoutState->m_layoutOffset - layoutState->m_pageOffset; + return currentBoxDescendant->isHorizontalWritingMode() ? offsetDelta.height() : offsetDelta.width(); + } + + // As a last resort, take the slow path. + LayoutRect blockRect(0, 0, currentBlock->width(), currentBlock->height()); + while (currentBlock && !currentBlock->isRenderFlowThread()) { + RenderBlock* containerBlock = currentBlock->containingBlock(); + ASSERT(containerBlock); + if (!containerBlock) + return 0; + LayoutPoint currentBlockLocation = currentBlock->location(); + + if (containerBlock->style()->writingMode() != currentBlock->style()->writingMode()) { + // We have to put the block rect in container coordinates + // and we have to take into account both the container and current block flipping modes + if (containerBlock->style()->isFlippedBlocksWritingMode()) { + if (containerBlock->isHorizontalWritingMode()) + blockRect.setY(currentBlock->height() - blockRect.maxY()); + else + blockRect.setX(currentBlock->width() - blockRect.maxX()); + } + currentBlock->flipForWritingMode(blockRect); + } + blockRect.moveBy(currentBlockLocation); + currentBlock = containerBlock; + } + + return currentBlock->isHorizontalWritingMode() ? blockRect.y() : blockRect.x(); +} + +void RenderFlowThread::RegionSearchAdapter::collectIfNeeded(const RegionInterval& interval) +{ + if (m_result) + return; + if (interval.low() <= m_offset && interval.high() > m_offset) + m_result = interval.data(); +} + +void RenderFlowThread::mapLocalToContainer(const RenderLayerModelObject* repaintContainer, TransformState& transformState, MapCoordinatesFlags mode, bool* wasFixed) const +{ + if (this == repaintContainer) + return; + + if (RenderRegion* region = mapFromFlowToRegion(transformState)) + // FIXME: The cast below is probably not the best solution, we may need to find a better way. + static_cast<const RenderObject*>(region)->mapLocalToContainer(region->containerForRepaint(), transformState, mode, wasFixed); } CurrentRenderFlowThreadMaintainer::CurrentRenderFlowThreadMaintainer(RenderFlowThread* renderFlowThread) : m_renderFlowThread(renderFlowThread) + , m_previousRenderFlowThread(0) { if (!m_renderFlowThread) return; RenderView* view = m_renderFlowThread->view(); - ASSERT(!view->flowThreadController()->currentRenderFlowThread()); + m_previousRenderFlowThread = view->flowThreadController()->currentRenderFlowThread(); + ASSERT(!m_previousRenderFlowThread || !renderFlowThread->isRenderNamedFlowThread()); view->flowThreadController()->setCurrentRenderFlowThread(m_renderFlowThread); } @@ -912,7 +1157,7 @@ CurrentRenderFlowThreadMaintainer::~CurrentRenderFlowThreadMaintainer() return; RenderView* view = m_renderFlowThread->view(); ASSERT(view->flowThreadController()->currentRenderFlowThread() == m_renderFlowThread); - view->flowThreadController()->setCurrentRenderFlowThread(0); + view->flowThreadController()->setCurrentRenderFlowThread(m_previousRenderFlowThread); } diff --git a/Source/WebCore/rendering/RenderFlowThread.h b/Source/WebCore/rendering/RenderFlowThread.h index c39874c64..ffd42bc16 100644 --- a/Source/WebCore/rendering/RenderFlowThread.h +++ b/Source/WebCore/rendering/RenderFlowThread.h @@ -35,10 +35,11 @@ #include <wtf/HashCountedSet.h> #include <wtf/ListHashSet.h> #include <wtf/PassRefPtr.h> -#include <wtf/UnusedParam.h> namespace WebCore { +struct LayerFragment; +typedef Vector<LayerFragment, 1> LayerFragments; class RenderFlowThread; class RenderStyle; class RenderRegion; @@ -53,7 +54,7 @@ typedef ListHashSet<RenderRegion*> RenderRegionList; class RenderFlowThread: public RenderBlock { public: - RenderFlowThread(Node*); + RenderFlowThread(); virtual ~RenderFlowThread() { }; virtual bool isRenderFlowThread() const { return true; } @@ -76,15 +77,17 @@ public: virtual void updateLogicalWidth() OVERRIDE; virtual void computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues&) const OVERRIDE; - void paintFlowThreadPortionInRegion(PaintInfo&, RenderRegion*, LayoutRect flowThreadPortionRect, LayoutRect flowThreadPortionOverflowRect, const LayoutPoint&) const; - bool hitTestFlowThreadPortionInRegion(RenderRegion*, LayoutRect flowThreadPortionRect, LayoutRect flowThreadPortionOverflowRect, const HitTestRequest&, HitTestResult&, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset) const; + void paintFlowThreadPortionInRegion(PaintInfo&, RenderRegion*, const LayoutRect& flowThreadPortionRect, const LayoutRect& flowThreadPortionOverflowRect, const LayoutPoint&) const; + bool hitTestFlowThreadPortionInRegion(RenderRegion*, const LayoutRect& flowThreadPortionRect, const LayoutRect& flowThreadPortionOverflowRect, const HitTestRequest&, HitTestResult&, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset) const; + virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction) OVERRIDE; bool hasRegions() const { return m_regionList.size(); } // Check if the content is flown into at least a region with region styling rules. bool hasRegionsWithStyling() const { return m_hasRegionsWithStyling; } void checkRegionsWithStyling(); - void invalidateRegions() { m_regionsInvalidated = true; setNeedsLayout(true); } + void validateRegions(); + void invalidateRegions(); bool hasValidRegionInfo() const { return !m_regionsInvalidated && !m_regionList.isEmpty(); } static PassRefPtr<RenderStyle> createFlowThreadStyle(RenderStyle* parentStyle); @@ -92,12 +95,22 @@ public: void styleDidChange(StyleDifference, const RenderStyle* oldStyle); void repaintRectangleInRegions(const LayoutRect&, bool immediate) const; + + LayoutPoint adjustedPositionRelativeToOffsetParent(const RenderBoxModelObject&, const LayoutPoint&); + + LayoutUnit pageLogicalTopForOffset(LayoutUnit); + LayoutUnit pageLogicalWidthForOffset(LayoutUnit); + LayoutUnit pageLogicalHeightForOffset(LayoutUnit); + LayoutUnit pageRemainingLogicalHeightForOffset(LayoutUnit, PageBoundaryRule = IncludePageBoundary); + + virtual void setPageBreak(LayoutUnit /*offset*/, LayoutUnit /*spaceShortage*/) { } + virtual void updateMinimumPageHeight(LayoutUnit /*offset*/, LayoutUnit /*minHeight*/) { } - LayoutUnit pageLogicalTopForOffset(LayoutUnit) const; - LayoutUnit pageLogicalWidthForOffset(LayoutUnit) const; - LayoutUnit pageLogicalHeightForOffset(LayoutUnit) const; - LayoutUnit pageRemainingLogicalHeightForOffset(LayoutUnit, PageBoundaryRule = IncludePageBoundary) const; - RenderRegion* regionAtBlockOffset(LayoutUnit, bool extendLastRegion = false) const; + enum RegionAutoGenerationPolicy { + AllowRegionAutoGeneration, + DisallowRegionAutoGeneration, + }; + RenderRegion* regionAtBlockOffset(LayoutUnit, bool extendLastRegion = false, RegionAutoGenerationPolicy = AllowRegionAutoGeneration); bool regionsHaveUniformLogicalWidth() const { return m_regionsHaveUniformLogicalWidth; } bool regionsHaveUniformLogicalHeight() const { return m_regionsHaveUniformLogicalHeight; } @@ -105,7 +118,7 @@ public: RenderRegion* mapFromFlowToRegion(TransformState&) const; void removeRenderBoxRegionInfo(RenderBox*); - bool logicalWidthChangedInRegions(const RenderBlock*, LayoutUnit offsetFromLogicalTopOfFirstPage); + bool logicalWidthChangedInRegionsForBlock(const RenderBlock*); LayoutUnit contentLogicalWidthOfFirstRegion() const; LayoutUnit contentLogicalHeightOfFirstRegion() const; @@ -114,47 +127,88 @@ public: RenderRegion* firstRegion() const; RenderRegion* lastRegion() const; + bool previousRegionCountChanged() const { return m_previousRegionCount != m_regionList.size(); }; + void updatePreviousRegionCount() { m_previousRegionCount = m_regionList.size(); }; + void setRegionRangeForBox(const RenderBox*, LayoutUnit offsetFromLogicalTopOfFirstPage); void getRegionRangeForBox(const RenderBox*, RenderRegion*& startRegion, RenderRegion*& endRegion) const; void clearRenderObjectCustomStyle(const RenderObject*, const RenderRegion* oldStartRegion = 0, const RenderRegion* oldEndRegion = 0, const RenderRegion* newStartRegion = 0, const RenderRegion* newEndRegion = 0); - - void computeOverflowStateForRegions(LayoutUnit oldClientAfterEdge); - - bool overset() const { return m_overset; } // Check if the object is in region and the region is part of this flow thread. bool objectInFlowRegion(const RenderObject*, const RenderRegion*) const; - void resetRegionsOverrideLogicalContentHeight(); void markAutoLogicalHeightRegionsForLayout(); bool addForcedRegionBreak(LayoutUnit, RenderObject* breakChild, bool isBefore, LayoutUnit* offsetBreakAdjustment = 0); + void applyBreakAfterContent(LayoutUnit); + + bool pageLogicalSizeChanged() const { return m_pageLogicalSizeChanged; } - bool pageLogicalHeightChanged() const { return m_pageLogicalHeightChanged; } + bool hasAutoLogicalHeightRegions() const { ASSERT(isAutoLogicalHeightRegionsCountConsistent()); return m_autoLogicalHeightRegionsCount; } + void incrementAutoLogicalHeightRegions(); + void decrementAutoLogicalHeightRegions(); #ifndef NDEBUG - unsigned autoLogicalHeightRegionsCount() const; + bool isAutoLogicalHeightRegionsCountConsistent() const; #endif + void collectLayerFragments(LayerFragments&, const LayoutRect& layerBoundingBox, const LayoutRect& dirtyRect); + LayoutRect fragmentsBoundingBox(const LayoutRect& layerBoundingBox); + + void setInConstrainedLayoutPhase(bool value) { m_inConstrainedLayoutPhase = value; } + bool inConstrainedLayoutPhase() const { return m_inConstrainedLayoutPhase; } + + bool needsTwoPhasesLayout() const { return m_needsTwoPhasesLayout; } + void clearNeedsTwoPhasesLayout() { m_needsTwoPhasesLayout = false; } + + void pushFlowThreadLayoutState(const RenderObject*); + void popFlowThreadLayoutState(); + LayoutUnit offsetFromLogicalTopOfFirstRegion(const RenderBlock*) const; + + // Used to estimate the maximum height of the flow thread. + static LayoutUnit maxLogicalHeight() { return LayoutUnit::max() / 2; } + protected: virtual const char* renderName() const = 0; - void updateRegionsFlowThreadPortionRect(); + // Overridden by columns/pages to set up an initial logical width of the page width even when + // no regions have been generated yet. + virtual LayoutUnit initialLogicalWidth() const { return 0; }; + + virtual void mapLocalToContainer(const RenderLayerModelObject* repaintContainer, TransformState&, MapCoordinatesFlags = ApplyContainerFlip, bool* wasFixed = 0) const OVERRIDE; + + void updateRegionsFlowThreadPortionRect(const RenderRegion* = 0); bool shouldRepaint(const LayoutRect&) const; bool regionInRange(const RenderRegion* targetRegion, const RenderRegion* startRegion, const RenderRegion* endRegion) const; - + + LayoutRect computeRegionClippingRect(const LayoutPoint&, const LayoutRect&, const LayoutRect&) const; + void setDispatchRegionLayoutUpdateEvent(bool value) { m_dispatchRegionLayoutUpdateEvent = value; } bool shouldDispatchRegionLayoutUpdateEvent() { return m_dispatchRegionLayoutUpdateEvent; } + void setDispatchRegionOversetChangeEvent(bool value) { m_dispatchRegionOversetChangeEvent = value; } + bool shouldDispatchRegionOversetChangeEvent() const { return m_dispatchRegionOversetChangeEvent; } + // Override if the flow thread implementation supports dispatching events when the flow layout is updated (e.g. for named flows) virtual void dispatchRegionLayoutUpdateEvent() { m_dispatchRegionLayoutUpdateEvent = false; } + virtual void dispatchRegionOversetChangeEvent() { m_dispatchRegionOversetChangeEvent = false; } + + void initializeRegionsComputedAutoHeight(RenderRegion* = 0); + + virtual void autoGenerateRegionsToBlockOffset(LayoutUnit) { }; - void clearOverrideLogicalContentHeightInRegions(RenderRegion* startRegion = 0); + inline bool hasCachedOffsetFromLogicalTopOfFirstRegion(const RenderBox*) const; + inline LayoutUnit cachedOffsetFromLogicalTopOfFirstRegion(const RenderBox*) const; + inline void setOffsetFromLogicalTopOfFirstRegion(const RenderBox*, LayoutUnit); + inline void clearOffsetFromLogicalTopOfFirstRegion(const RenderBox*); + + inline const RenderBox* currentActiveRenderBox() const; RenderRegionList m_regionList; + unsigned short m_previousRegionCount; class RenderRegionRange { public: @@ -182,6 +236,28 @@ protected: RenderRegion* m_endRegion; }; + typedef PODInterval<LayoutUnit, RenderRegion*> RegionInterval; + typedef PODIntervalTree<LayoutUnit, RenderRegion*> RegionIntervalTree; + + class RegionSearchAdapter { + public: + RegionSearchAdapter(LayoutUnit offset) + : m_offset(offset) + , m_result(0) + { + } + + const LayoutUnit& lowValue() const { return m_offset; } + const LayoutUnit& highValue() const { return m_offset; } + void collectIfNeeded(const RegionInterval&); + + RenderRegion* result() const { return m_result; } + + private: + LayoutUnit m_offset; + RenderRegion* m_result; + }; + // A maps from RenderBox typedef HashMap<const RenderBox*, RenderRegionRange> RenderRegionRangeMap; RenderRegionRangeMap m_regionRangeMap; @@ -190,24 +266,35 @@ protected: RenderObjectToRegionMap m_breakBeforeToRegionMap; RenderObjectToRegionMap m_breakAfterToRegionMap; + typedef ListHashSet<const RenderObject*> RenderObjectStack; + RenderObjectStack m_activeObjectsStack; + typedef HashMap<const RenderBox*, LayoutUnit> RenderBoxToOffsetMap; + RenderBoxToOffsetMap m_boxesToOffsetMap; + + unsigned m_autoLogicalHeightRegionsCount; + + RegionIntervalTree m_regionIntervalTree; + bool m_regionsInvalidated : 1; bool m_regionsHaveUniformLogicalWidth : 1; bool m_regionsHaveUniformLogicalHeight : 1; - bool m_overset : 1; bool m_hasRegionsWithStyling : 1; bool m_dispatchRegionLayoutUpdateEvent : 1; - bool m_pageLogicalHeightChanged : 1; + bool m_dispatchRegionOversetChangeEvent : 1; + bool m_pageLogicalSizeChanged : 1; + bool m_inConstrainedLayoutPhase : 1; + bool m_needsTwoPhasesLayout : 1; }; inline RenderFlowThread* toRenderFlowThread(RenderObject* object) { - ASSERT(!object || object->isRenderFlowThread()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isRenderFlowThread()); return static_cast<RenderFlowThread*>(object); } inline const RenderFlowThread* toRenderFlowThread(const RenderObject* object) { - ASSERT(!object || object->isRenderFlowThread()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isRenderFlowThread()); return static_cast<const RenderFlowThread*>(object); } @@ -221,8 +308,20 @@ public: ~CurrentRenderFlowThreadMaintainer(); private: RenderFlowThread* m_renderFlowThread; + RenderFlowThread* m_previousRenderFlowThread; +}; + +// These structures are used by PODIntervalTree for debugging. +#ifndef NDEBUG +template <> struct ValueToString<LayoutUnit> { + static String string(const LayoutUnit value) { return String::number(value.toFloat()); } }; +template <> struct ValueToString<RenderRegion*> { + static String string(const RenderRegion* value) { return String::format("%p", value); } +}; +#endif + } // namespace WebCore #endif // RenderFlowThread_h diff --git a/Source/WebCore/rendering/RenderFrame.cpp b/Source/WebCore/rendering/RenderFrame.cpp index f4c3cb6b6..9bd7c1654 100644 --- a/Source/WebCore/rendering/RenderFrame.cpp +++ b/Source/WebCore/rendering/RenderFrame.cpp @@ -54,7 +54,7 @@ void RenderFrame::viewCleared() if (!element || !widget() || !widget()->isFrameView()) return; - FrameView* view = static_cast<FrameView*>(widget()); + FrameView* view = toFrameView(widget()); int marginWidth = element->marginWidth(); int marginHeight = element->marginHeight(); diff --git a/Source/WebCore/rendering/RenderFrame.h b/Source/WebCore/rendering/RenderFrame.h index 6f48e2679..9eadb9393 100644 --- a/Source/WebCore/rendering/RenderFrame.h +++ b/Source/WebCore/rendering/RenderFrame.h @@ -47,7 +47,7 @@ private: inline RenderFrame* toRenderFrame(RenderObject* object) { - ASSERT(!object || object->isFrame()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isFrame()); return static_cast<RenderFrame*>(object); } diff --git a/Source/WebCore/rendering/RenderFrameBase.cpp b/Source/WebCore/rendering/RenderFrameBase.cpp index aa82870d4..94c09002c 100644 --- a/Source/WebCore/rendering/RenderFrameBase.cpp +++ b/Source/WebCore/rendering/RenderFrameBase.cpp @@ -54,8 +54,8 @@ inline bool shouldExpandFrame(LayoutUnit width, LayoutUnit height, bool hasFixed void RenderFrameBase::layoutWithFlattening(bool hasFixedWidth, bool hasFixedHeight) { - FrameView* childFrameView = static_cast<FrameView*>(widget()); - RenderView* childRoot = childFrameView ? static_cast<RenderView*>(childFrameView->frame()->contentRenderer()) : 0; + FrameView* childFrameView = toFrameView(widget()); + RenderView* childRoot = childFrameView ? childFrameView->frame()->contentRenderer() : 0; if (!childRoot || !shouldExpandFrame(width(), height(), hasFixedWidth, hasFixedHeight)) { updateWidgetPosition(); @@ -67,15 +67,12 @@ void RenderFrameBase::layoutWithFlattening(bool hasFixedWidth, bool hasFixedHeig // need to update to calculate min/max correctly updateWidgetPosition(); - if (childRoot->preferredLogicalWidthsDirty()) - childRoot->computePreferredLogicalWidths(); // if scrollbars are off, and the width or height are fixed // we obey them and do not expand. With frame flattening // no subframe much ever become scrollable. - HTMLFrameElementBase* element = static_cast<HTMLFrameElementBase*>(node()); - bool isScrollable = element->scrollingMode() != ScrollbarAlwaysOff; + bool isScrollable = toHTMLFrameElementBase(node())->scrollingMode() != ScrollbarAlwaysOff; // consider iframe inset border int hBorder = borderLeft() + borderRight(); diff --git a/Source/WebCore/rendering/RenderFrameSet.cpp b/Source/WebCore/rendering/RenderFrameSet.cpp index e4e7e24bb..308204547 100644 --- a/Source/WebCore/rendering/RenderFrameSet.cpp +++ b/Source/WebCore/rendering/RenderFrameSet.cpp @@ -40,6 +40,7 @@ #include "RenderLayer.h" #include "RenderView.h" #include "Settings.h" +#include <wtf/StackStats.h> namespace WebCore { @@ -157,24 +158,6 @@ void RenderFrameSet::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) } } -bool RenderFrameSet::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, - const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction action) -{ - if (action != HitTestForeground) - return false; - - bool inside = RenderBox::nodeAtPoint(request, result, locationInContainer, accumulatedOffset, action) - || m_isResizing; - - if (inside && frameSet()->noResize() - && !request.readOnly() && !result.innerNode() && !request.touchMove()) { - result.setInnerNode(node()); - result.setInnerNonSharedNode(node()); - } - - return inside || m_isChildResizing; -} - void RenderFrameSet::GridAxis::resize(int size) { m_sizes.resize(size); @@ -420,10 +403,10 @@ void RenderFrameSet::computeEdgeInfo() if (!child) return; - int rows = frameSet()->totalRows(); - int cols = frameSet()->totalCols(); - for (int r = 0; r < rows; ++r) { - for (int c = 0; c < cols; ++c) { + size_t rows = m_rows.m_sizes.size(); + size_t cols = m_cols.m_sizes.size(); + for (size_t r = 0; r < rows; ++r) { + for (size_t c = 0; c < cols; ++c) { FrameEdgeInfo edgeInfo; if (child->isFrameSet()) edgeInfo = toRenderFrameSet(child)->edgeInfo(); @@ -464,8 +447,11 @@ void RenderFrameSet::layout() bool doFullRepaint = selfNeedsLayout() && checkForRepaintDuringLayout(); LayoutRect oldBounds; - if (doFullRepaint) - oldBounds = absoluteClippedOverflowRect(); + RenderLayerModelObject* repaintContainer = 0; + if (doFullRepaint) { + repaintContainer = containerForRepaint(); + oldBounds = clippedOverflowRectForRepaint(repaintContainer); + } if (!parent()->isFrameSet() && !document()->printing()) { setWidth(view()->viewWidth()); @@ -493,19 +479,15 @@ void RenderFrameSet::layout() computeEdgeInfo(); + updateLayerTransform(); + if (doFullRepaint) { - view()->repaintViewRectangle(oldBounds); - LayoutRect newBounds = absoluteClippedOverflowRect(); + repaintUsingContainer(repaintContainer, pixelSnappedIntRect(oldBounds)); + LayoutRect newBounds = clippedOverflowRectForRepaint(repaintContainer); if (newBounds != oldBounds) - view()->repaintViewRectangle(newBounds); + repaintUsingContainer(repaintContainer, pixelSnappedIntRect(newBounds)); } - // If this FrameSet has a transform matrix then we need to recompute it - // because the transform origin is a function the size of the RenderFrameSet - // which may not be computed until it is attached to the render tree. - if (layer() && hasTransform()) - layer()->updateTransform(); - setNeedsLayout(false); } @@ -700,7 +682,7 @@ bool RenderFrameSet::userResize(MouseEvent* evt) if (needsLayout()) return false; if (evt->type() == eventNames().mousedownEvent && evt->button() == LeftButton) { - FloatPoint localPos = absoluteToLocal(evt->absoluteLocation(), UseTransforms | SnapOffsetForTransforms); + FloatPoint localPos = absoluteToLocal(evt->absoluteLocation(), UseTransforms); startResizing(m_cols, localPos.x()); startResizing(m_rows, localPos.y()); if (m_cols.m_splitBeingResized != noSplit || m_rows.m_splitBeingResized != noSplit) { @@ -710,7 +692,7 @@ bool RenderFrameSet::userResize(MouseEvent* evt) } } else { if (evt->type() == eventNames().mousemoveEvent || (evt->type() == eventNames().mouseupEvent && evt->button() == LeftButton)) { - FloatPoint localPos = absoluteToLocal(evt->absoluteLocation(), UseTransforms | SnapOffsetForTransforms); + FloatPoint localPos = absoluteToLocal(evt->absoluteLocation(), UseTransforms); continueResizing(m_cols, localPos.x()); continueResizing(m_rows, localPos.y()); if (evt->type() == eventNames().mouseupEvent && evt->button() == LeftButton) { diff --git a/Source/WebCore/rendering/RenderFrameSet.h b/Source/WebCore/rendering/RenderFrameSet.h index edab2d98c..0cfce5290 100644 --- a/Source/WebCore/rendering/RenderFrameSet.h +++ b/Source/WebCore/rendering/RenderFrameSet.h @@ -84,6 +84,7 @@ private: public: GridAxis(); void resize(int); + Vector<int> m_sizes; Vector<int> m_deltas; Vector<bool> m_preventResize; @@ -99,7 +100,6 @@ private: virtual bool isFrameSet() const { return true; } virtual void layout(); - virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction) OVERRIDE; virtual void paint(PaintInfo&, const LayoutPoint&); virtual bool isChildAllowed(RenderObject*, RenderStyle*) const; virtual CursorDirective getCursor(const LayoutPoint&, Cursor&) const; @@ -137,7 +137,7 @@ private: inline RenderFrameSet* toRenderFrameSet(RenderObject* object) { - ASSERT(!object || object->isFrameSet()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isFrameSet()); return static_cast<RenderFrameSet*>(object); } diff --git a/Source/WebCore/rendering/RenderFullScreen.cpp b/Source/WebCore/rendering/RenderFullScreen.cpp index 2f1c311b1..af11971c1 100644 --- a/Source/WebCore/rendering/RenderFullScreen.cpp +++ b/Source/WebCore/rendering/RenderFullScreen.cpp @@ -39,9 +39,10 @@ using namespace WebCore; class RenderFullScreenPlaceholder : public RenderBlock { public: RenderFullScreenPlaceholder(RenderFullScreen* owner) - : RenderBlock(owner->document()) + : RenderBlock(0) , m_owner(owner) - { + { + setDocumentForAnonymous(owner->document()); } private: virtual bool isRenderFullScreenPlaceholder() const { return true; } @@ -55,13 +56,20 @@ void RenderFullScreenPlaceholder::willBeDestroyed() RenderBlock::willBeDestroyed(); } -RenderFullScreen::RenderFullScreen(Node* node) - : RenderDeprecatedFlexibleBox(node) +RenderFullScreen::RenderFullScreen() + : RenderFlexibleBox(0) , m_placeholder(0) -{ +{ setReplaced(false); } +RenderFullScreen* RenderFullScreen::createAnonymous(Document* document) +{ + RenderFullScreen* renderer = new (document->renderArena()) RenderFullScreen(); + renderer->setDocumentForAnonymous(document); + return renderer; +} + void RenderFullScreen::willBeDestroyed() { if (m_placeholder) { @@ -76,7 +84,7 @@ void RenderFullScreen::willBeDestroyed() if (document() && document()->fullScreenRenderer() == this) document()->fullScreenRendererDestroyed(); - RenderDeprecatedFlexibleBox::willBeDestroyed(); + RenderFlexibleBox::willBeDestroyed(); } static PassRefPtr<RenderStyle> createFullScreenStyle() @@ -89,10 +97,10 @@ static PassRefPtr<RenderStyle> createFullScreenStyle() fullscreenStyle->setFontDescription(FontDescription()); fullscreenStyle->font().update(0); - fullscreenStyle->setDisplay(BOX); - fullscreenStyle->setBoxPack(Center); - fullscreenStyle->setBoxAlign(BCENTER); - fullscreenStyle->setBoxOrient(VERTICAL); + fullscreenStyle->setDisplay(FLEX); + fullscreenStyle->setJustifyContent(JustifyCenter); + fullscreenStyle->setAlignItems(AlignCenter); + fullscreenStyle->setFlexDirection(FlowColumn); fullscreenStyle->setPosition(FixedPosition); fullscreenStyle->setWidth(Length(100.0, Percent)); @@ -107,7 +115,7 @@ static PassRefPtr<RenderStyle> createFullScreenStyle() RenderObject* RenderFullScreen::wrapRenderer(RenderObject* object, RenderObject* parent, Document* document) { - RenderFullScreen* fullscreenRenderer = new (document->renderArena()) RenderFullScreen(document); + RenderFullScreen* fullscreenRenderer = RenderFullScreen::createAnonymous(document); fullscreenRenderer->setStyle(createFullScreenStyle()); if (parent && !parent->isChildAllowed(fullscreenRenderer, fullscreenRenderer->style())) { fullscreenRenderer->destroy(); @@ -144,6 +152,11 @@ void RenderFullScreen::unwrapRenderer() if (parent()) { RenderObject* child; while ((child = firstChild())) { + // We have to clear the override size, because as a flexbox, we + // may have set one on the child, and we don't want to leave that + // lying around on the child. + if (child->isBox()) + toRenderBox(child)->clearOverrideSize(); child->remove(); parent()->addChild(child, this); parent()->setNeedsLayoutAndPrefWidthsRecalc(); diff --git a/Source/WebCore/rendering/RenderFullScreen.h b/Source/WebCore/rendering/RenderFullScreen.h index a54ac1561..976ebcefc 100644 --- a/Source/WebCore/rendering/RenderFullScreen.h +++ b/Source/WebCore/rendering/RenderFullScreen.h @@ -27,14 +27,15 @@ #if ENABLE(FULLSCREEN_API) -#include "RenderDeprecatedFlexibleBox.h" +#include "RenderFlexibleBox.h" #include "StyleInheritedData.h" namespace WebCore { -class RenderFullScreen : public RenderDeprecatedFlexibleBox { +class RenderFullScreen : public RenderFlexibleBox { public: - RenderFullScreen(Node*); + static RenderFullScreen* createAnonymous(Document*); + virtual bool isRenderFullScreen() const { return true; } virtual const char* renderName() const { return "RenderFullScreen"; } @@ -47,15 +48,16 @@ public: void unwrapRenderer(); private: + RenderFullScreen(); virtual void willBeDestroyed(); - + protected: RenderBlock* m_placeholder; }; inline RenderFullScreen* toRenderFullScreen(RenderObject* object) { - ASSERT(object->isRenderFullScreen()); + ASSERT_WITH_SECURITY_IMPLICATION(object->isRenderFullScreen()); return static_cast<RenderFullScreen*>(object); } diff --git a/Source/WebCore/rendering/RenderGeometryMap.cpp b/Source/WebCore/rendering/RenderGeometryMap.cpp index 74434895f..4513ef6a1 100644 --- a/Source/WebCore/rendering/RenderGeometryMap.cpp +++ b/Source/WebCore/rendering/RenderGeometryMap.cpp @@ -51,6 +51,7 @@ void RenderGeometryMap::mapToContainer(TransformState& transformState, const Ren // If the mapping includes something like columns, we have to go via renderers. if (hasNonUniformStep()) { m_mapping.last().m_renderer->mapLocalToContainer(container, transformState, ApplyContainerFlip | m_mapCoordinatesFlags); + transformState.flatten(); return; } @@ -104,7 +105,7 @@ FloatPoint RenderGeometryMap::mapToContainer(const FloatPoint& p, const RenderLa FloatPoint result; if (!hasFixedPositionStep() && !hasTransformStep() && !hasNonUniformStep() && (!container || (m_mapping.size() && container == m_mapping[0].m_renderer))) - result = p + m_accumulatedOffset; + result = p + roundedIntSize(m_accumulatedOffset); else { TransformState transformState(TransformState::ApplyTransformDirection, p); mapToContainer(transformState, container); diff --git a/Source/WebCore/rendering/RenderGeometryMap.h b/Source/WebCore/rendering/RenderGeometryMap.h index bab106f1d..00c41f7cc 100644 --- a/Source/WebCore/rendering/RenderGeometryMap.h +++ b/Source/WebCore/rendering/RenderGeometryMap.h @@ -74,7 +74,7 @@ struct RenderGeometryMapStep { class RenderGeometryMap { WTF_MAKE_NONCOPYABLE(RenderGeometryMap); public: - RenderGeometryMap(MapCoordinatesFlags = UseTransforms | SnapOffsetForTransforms); + RenderGeometryMap(MapCoordinatesFlags = UseTransforms); ~RenderGeometryMap(); MapCoordinatesFlags mapCoordinatesFlags() const { return m_mapCoordinatesFlags; } diff --git a/Source/WebCore/rendering/RenderGrid.cpp b/Source/WebCore/rendering/RenderGrid.cpp index e7a351c86..a65941552 100644 --- a/Source/WebCore/rendering/RenderGrid.cpp +++ b/Source/WebCore/rendering/RenderGrid.cpp @@ -33,18 +33,101 @@ namespace WebCore { -class RenderGrid::GridTrack { +static const int infinity = intMaxForLayoutUnit; + +class GridTrack { public: GridTrack() : m_usedBreadth(0) + , m_maxBreadth(0) + { + } + + void growUsedBreadth(LayoutUnit growth) + { + ASSERT(growth >= 0); + m_usedBreadth += growth; + } + LayoutUnit usedBreadth() const { return m_usedBreadth; } + + void growMaxBreadth(LayoutUnit growth) { + if (m_maxBreadth == infinity) + m_maxBreadth = m_usedBreadth + growth; + else + m_maxBreadth += growth; + } + LayoutUnit maxBreadthIfNotInfinite() const + { + return (m_maxBreadth == infinity) ? m_usedBreadth : m_maxBreadth; } LayoutUnit m_usedBreadth; + LayoutUnit m_maxBreadth; +}; + +class RenderGrid::GridIterator { + WTF_MAKE_NONCOPYABLE(GridIterator); +public: + // |direction| is the direction that is fixed to |fixedTrackIndex| so e.g + // GridIterator(m_grid, ForColumns, 1) will walk over the rows of the 2nd column. + GridIterator(const Vector<Vector<Vector<RenderBox*, 1> > >& grid, TrackSizingDirection direction, size_t fixedTrackIndex) + : m_grid(grid) + , m_direction(direction) + , m_rowIndex((direction == ForColumns) ? 0 : fixedTrackIndex) + , m_columnIndex((direction == ForColumns) ? fixedTrackIndex : 0) + , m_childIndex(0) + { + ASSERT(m_rowIndex < m_grid.size()); + ASSERT(m_columnIndex < m_grid[0].size()); + } + + RenderBox* nextGridItem() + { + if (!m_grid.size()) + return 0; + + size_t& varyingTrackIndex = (m_direction == ForColumns) ? m_rowIndex : m_columnIndex; + const size_t endOfVaryingTrackIndex = (m_direction == ForColumns) ? m_grid.size() : m_grid[0].size(); + for (; varyingTrackIndex < endOfVaryingTrackIndex; ++varyingTrackIndex) { + const Vector<RenderBox*>& children = m_grid[m_rowIndex][m_columnIndex]; + if (m_childIndex < children.size()) + return children[m_childIndex++]; + + m_childIndex = 0; + } + return 0; + } + + PassOwnPtr<GridCoordinate> nextEmptyGridArea() + { + if (m_grid.isEmpty()) + return nullptr; + + size_t& varyingTrackIndex = (m_direction == ForColumns) ? m_rowIndex : m_columnIndex; + const size_t endOfVaryingTrackIndex = (m_direction == ForColumns) ? m_grid.size() : m_grid[0].size(); + for (; varyingTrackIndex < endOfVaryingTrackIndex; ++varyingTrackIndex) { + const Vector<RenderBox*>& children = m_grid[m_rowIndex][m_columnIndex]; + if (children.isEmpty()) { + OwnPtr<GridCoordinate> result = adoptPtr(new GridCoordinate(GridSpan(m_rowIndex, m_rowIndex), GridSpan(m_columnIndex, m_columnIndex))); + // Advance the iterator to avoid an infinite loop where we would return the same grid area over and over. + ++varyingTrackIndex; + return result.release(); + } + } + return nullptr; + } + +private: + const Vector<Vector<Vector<RenderBox*, 1> > >& m_grid; + TrackSizingDirection m_direction; + size_t m_rowIndex; + size_t m_columnIndex; + size_t m_childIndex; }; -RenderGrid::RenderGrid(Node* node) - : RenderBlock(node) +RenderGrid::RenderGrid(Element* element) + : RenderBlock(element) { // All of our children must be block level. setChildrenInline(false); @@ -66,20 +149,18 @@ void RenderGrid::layoutBlock(bool relayoutChildren, LayoutUnit) LayoutRepainter repainter(*this, checkForRepaintDuringLayout()); LayoutStateMaintainer statePusher(view(), this, locationOffset(), hasTransform() || hasReflection() || style()->isFlippedBlocksWritingMode()); - if (inRenderFlowThread()) { - // Regions changing widths can force us to relayout our children. - if (logicalWidthChangedInRegions()) - relayoutChildren = true; - } - updateRegionsAndExclusionsLogicalSize(); + // Regions changing widths can force us to relayout our children. + RenderFlowThread* flowThread = flowThreadContainingBlock(); + if (logicalWidthChangedInRegions(flowThread)) + relayoutChildren = true; + if (updateRegionsAndShapesBeforeChildLayout(flowThread)) + relayoutChildren = true; LayoutSize previousSize = size(); setLogicalHeight(0); updateLogicalWidth(); - m_overflow.clear(); - layoutGridItems(); LayoutUnit oldClientAfterEdge = clientLogicalBottom(); @@ -90,7 +171,7 @@ void RenderGrid::layoutBlock(bool relayoutChildren, LayoutUnit) layoutPositionedObjects(relayoutChildren || isRoot()); - computeRegionRangeForBlock(); + updateRegionsAndShapesAfterChildLayout(flowThread); computeOverflow(oldClientAfterEdge); statePusher.pop(); @@ -99,14 +180,35 @@ void RenderGrid::layoutBlock(bool relayoutChildren, LayoutUnit) // Update our scroll information if we're overflow:auto/scroll/hidden now that we know if // we overflow or not. - if (hasOverflowClip()) - layer()->updateScrollInfoAfterLayout(); + updateScrollInfoAfterLayout(); repainter.repaintAfterLayout(); setNeedsLayout(false); } +void RenderGrid::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const +{ + const_cast<RenderGrid*>(this)->placeItemsOnGrid(); + + // FIXME: This is an inefficient way to fill our sizes as it will try every grid areas, when we would + // only want to account for fixed grid tracks and grid items. Also this will be incorrect if we have spanning + // grid items. + for (size_t i = 0; i < gridColumnCount(); ++i) { + const GridTrackSize& trackSize = gridTrackSize(ForColumns, i); + LayoutUnit minTrackBreadth = computePreferredTrackWidth(trackSize.minTrackBreadth(), i); + LayoutUnit maxTrackBreadth = computePreferredTrackWidth(trackSize.maxTrackBreadth(), i); + maxTrackBreadth = std::max(maxTrackBreadth, minTrackBreadth); + + minLogicalWidth += minTrackBreadth; + maxLogicalWidth += maxTrackBreadth; + + // FIXME: This should add in the scrollbarWidth (e.g. see RenderFlexibleBox). + } + + const_cast<RenderGrid*>(this)->clearGrid(); +} + void RenderGrid::computePreferredLogicalWidths() { ASSERT(preferredLogicalWidthsDirty()); @@ -114,64 +216,471 @@ void RenderGrid::computePreferredLogicalWidths() m_minPreferredLogicalWidth = 0; m_maxPreferredLogicalWidth = 0; - // FIXME: We don't take our own logical width into account. + // FIXME: We don't take our own logical width into account. Once we do, we need to make sure + // we apply (and test the interaction with) min-width / max-width. - const Vector<GridTrackSize>& trackStyles = style()->gridColumns(); + computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth); - for (size_t i = 0; i < trackStyles.size(); ++i) { - Length trackLength = trackStyles[i].length(); - if (!trackLength.isFixed()) { - notImplemented(); - continue; + LayoutUnit borderAndPaddingInInlineDirection = borderAndPaddingLogicalWidth(); + m_minPreferredLogicalWidth += borderAndPaddingInInlineDirection; + m_maxPreferredLogicalWidth += borderAndPaddingInInlineDirection; + + setPreferredLogicalWidthsDirty(false); +} + +LayoutUnit RenderGrid::computePreferredTrackWidth(const Length& length, size_t trackIndex) const +{ + if (length.isFixed()) { + // Grid areas don't have borders, margins or paddings so we don't need to account for them. + return length.intValue(); + } + + if (length.isMinContent()) { + LayoutUnit minContentSize = 0; + GridIterator iterator(m_grid, ForColumns, trackIndex); + while (RenderBox* gridItem = iterator.nextGridItem()) { + // FIXME: We should include the child's fixed margins like RenderFlexibleBox. + minContentSize = std::max(minContentSize, gridItem->minPreferredLogicalWidth()); } + return minContentSize; + } - m_minPreferredLogicalWidth += trackLength.intValue(); - m_maxPreferredLogicalWidth += trackLength.intValue(); + if (length.isMaxContent()) { + LayoutUnit maxContentSize = 0; + GridIterator iterator(m_grid, ForColumns, trackIndex); + while (RenderBox* gridItem = iterator.nextGridItem()) { + // FIXME: We should include the child's fixed margins like RenderFlexibleBox. + maxContentSize = std::max(maxContentSize, gridItem->maxPreferredLogicalWidth()); + } + return maxContentSize; } - // FIXME: We should account for min / max logical width. + // FIXME: css3-sizing mentions that we should resolve "definite sizes" + // (including <percentage> and calc()) but we don't do it elsewhere. + return 0; +} - // FIXME: Include borders and paddings in inline direction. +void RenderGrid::computedUsedBreadthOfGridTracks(TrackSizingDirection direction, Vector<GridTrack>& columnTracks, Vector<GridTrack>& rowTracks) +{ + LayoutUnit availableLogicalSpace = (direction == ForColumns) ? availableLogicalWidth() : availableLogicalHeight(IncludeMarginBorderPadding); + Vector<GridTrack>& tracks = (direction == ForColumns) ? columnTracks : rowTracks; + for (size_t i = 0; i < tracks.size(); ++i) { + GridTrack& track = tracks[i]; + const GridTrackSize& trackSize = gridTrackSize(direction, i); + const Length& minTrackBreadth = trackSize.minTrackBreadth(); + const Length& maxTrackBreadth = trackSize.maxTrackBreadth(); + + track.m_usedBreadth = computeUsedBreadthOfMinLength(direction, minTrackBreadth); + track.m_maxBreadth = computeUsedBreadthOfMaxLength(direction, maxTrackBreadth); + + track.m_maxBreadth = std::max(track.m_maxBreadth, track.m_usedBreadth); + } - setPreferredLogicalWidthsDirty(false); + // FIXME: We shouldn't call resolveContentBasedTrackSizingFunctions if we have no min-content / max-content tracks. + resolveContentBasedTrackSizingFunctions(direction, columnTracks, rowTracks, availableLogicalSpace); + + if (availableLogicalSpace <= 0) + return; + + const size_t tracksSize = tracks.size(); + Vector<GridTrack*> tracksForDistribution(tracksSize); + for (size_t i = 0; i < tracksSize; ++i) + tracksForDistribution[i] = tracks.data() + i; + + distributeSpaceToTracks(tracksForDistribution, 0, &GridTrack::usedBreadth, &GridTrack::growUsedBreadth, availableLogicalSpace); +} + +LayoutUnit RenderGrid::computeUsedBreadthOfMinLength(TrackSizingDirection direction, const Length& trackLength) const +{ + if (trackLength.isFixed() || trackLength.isPercent() || trackLength.isViewportPercentage()) + return computeUsedBreadthOfSpecifiedLength(direction, trackLength); + + ASSERT(trackLength.isMinContent() || trackLength.isMaxContent() || trackLength.isAuto()); + return 0; +} + +LayoutUnit RenderGrid::computeUsedBreadthOfMaxLength(TrackSizingDirection direction, const Length& trackLength) const +{ + if (trackLength.isFixed() || trackLength.isPercent() || trackLength.isViewportPercentage()) { + LayoutUnit computedBreadth = computeUsedBreadthOfSpecifiedLength(direction, trackLength); + // FIXME: We should ASSERT that computedBreadth cannot return infinity but it's currently + // possible. See https://bugs.webkit.org/show_bug.cgi?id=107053 + return computedBreadth; + } + + ASSERT(trackLength.isMinContent() || trackLength.isMaxContent() || trackLength.isAuto()); + return infinity; } -void RenderGrid::computedUsedBreadthOfGridTracks(TrackSizingDirection direction, Vector<GridTrack>& tracks) +LayoutUnit RenderGrid::computeUsedBreadthOfSpecifiedLength(TrackSizingDirection direction, const Length& trackLength) const +{ + // FIXME: We still need to support calc() here (https://webkit.org/b/103761). + ASSERT(trackLength.isFixed() || trackLength.isPercent() || trackLength.isViewportPercentage()); + return valueForLength(trackLength, direction == ForColumns ? logicalWidth() : computeContentLogicalHeight(style()->logicalHeight()), view()); +} + +const GridTrackSize& RenderGrid::gridTrackSize(TrackSizingDirection direction, size_t i) const +{ + const Vector<GridTrackSize>& trackStyles = (direction == ForColumns) ? style()->gridColumns() : style()->gridRows(); + if (i >= trackStyles.size()) + return (direction == ForColumns) ? style()->gridAutoColumns() : style()->gridAutoRows(); + + return trackStyles[i]; +} + +static size_t estimatedGridSizeForPosition(const GridPosition& position) +{ + if (position.isAuto()) + return 1; + + return std::max(position.integerPosition(), 1); +} + +size_t RenderGrid::maximumIndexInDirection(TrackSizingDirection direction) const { const Vector<GridTrackSize>& trackStyles = (direction == ForColumns) ? style()->gridColumns() : style()->gridRows(); - for (size_t i = 0; i < trackStyles.size(); ++i) { - GridTrack track; - if (trackStyles[i].length().isFixed()) - track.m_usedBreadth = trackStyles[i].length().getFloatValue(); - else - notImplemented(); - tracks.append(track); + size_t maximumIndex = std::max<size_t>(1, trackStyles.size()); + + for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { + // This function bypasses the cache (cachedGridCoordinate()) as it is used to build it. + // Also we can't call resolveGridPositionsFromStyle here as it assumes that the grid is build and we are in + // the middle of building it. However we should be able to share more code with the previous logic (FIXME). + const GridPosition& initialPosition = (direction == ForColumns) ? child->style()->gridItemStart() : child->style()->gridItemBefore(); + const GridPosition& finalPosition = (direction == ForColumns) ? child->style()->gridItemEnd() : child->style()->gridItemAfter(); + + size_t estimatedSizeForInitialPosition = estimatedGridSizeForPosition(initialPosition); + size_t estimatedSizeForFinalPosition = estimatedGridSizeForPosition(finalPosition); + ASSERT(estimatedSizeForInitialPosition); + ASSERT(estimatedSizeForFinalPosition); + + maximumIndex = std::max(maximumIndex, estimatedSizeForInitialPosition); + maximumIndex = std::max(maximumIndex, estimatedSizeForFinalPosition); + } + + return maximumIndex; +} + +LayoutUnit RenderGrid::logicalContentHeightForChild(RenderBox* child, Vector<GridTrack>& columnTracks) +{ + // FIXME: We shouldn't force a layout every time this function is called but + // 1) Return computeLogicalHeight's value if it's available. Unfortunately computeLogicalHeight + // doesn't return if the logical height is available so would need to be changed. + // 2) Relayout if the column track's used breadth changed OR the logical height is unavailable. + if (!child->needsLayout()) + child->setNeedsLayout(true, MarkOnlyThis); + + child->setOverrideContainingBlockContentLogicalWidth(gridAreaBreadthForChild(child, ForColumns, columnTracks)); + // If |child| has a percentage logical height, we shouldn't let it override its intrinsic height, which is + // what we are interested in here. Thus we need to set the override logical height to -1 (no possible resolution). + child->setOverrideContainingBlockContentLogicalHeight(-1); + child->layout(); + return child->logicalHeight(); +} + +LayoutUnit RenderGrid::minContentForChild(RenderBox* child, TrackSizingDirection direction, Vector<GridTrack>& columnTracks) +{ + bool hasOrthogonalWritingMode = child->isHorizontalWritingMode() != isHorizontalWritingMode(); + // FIXME: Properly support orthogonal writing mode. + if (hasOrthogonalWritingMode) + return 0; + + if (direction == ForColumns) { + // FIXME: It's unclear if we should return the intrinsic width or the preferred width. + // See http://lists.w3.org/Archives/Public/www-style/2013Jan/0245.html + return child->minPreferredLogicalWidth(); } + + return logicalContentHeightForChild(child, columnTracks); +} + +LayoutUnit RenderGrid::maxContentForChild(RenderBox* child, TrackSizingDirection direction, Vector<GridTrack>& columnTracks) +{ + bool hasOrthogonalWritingMode = child->isHorizontalWritingMode() != isHorizontalWritingMode(); + // FIXME: Properly support orthogonal writing mode. + if (hasOrthogonalWritingMode) + return LayoutUnit(); + + if (direction == ForColumns) { + // FIXME: It's unclear if we should return the intrinsic width or the preferred width. + // See http://lists.w3.org/Archives/Public/www-style/2013Jan/0245.html + return child->maxPreferredLogicalWidth(); + } + + return logicalContentHeightForChild(child, columnTracks); +} + +void RenderGrid::resolveContentBasedTrackSizingFunctions(TrackSizingDirection direction, Vector<GridTrack>& columnTracks, Vector<GridTrack>& rowTracks, LayoutUnit& availableLogicalSpace) +{ + // FIXME: Split the grid tracks once we support fractions (step 1 of the algorithm). + + Vector<GridTrack>& tracks = (direction == ForColumns) ? columnTracks : rowTracks; + + // FIXME: Per step 2 of the specification, we should order the grid items by increasing span. + for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { + resolveContentBasedTrackSizingFunctionsForItems(direction, columnTracks, rowTracks, child, &GridTrackSize::hasMinOrMaxContentMinTrackBreadth, &RenderGrid::minContentForChild, &GridTrack::usedBreadth, &GridTrack::growUsedBreadth); + resolveContentBasedTrackSizingFunctionsForItems(direction, columnTracks, rowTracks, child, &GridTrackSize::hasMaxContentMinTrackBreadth, &RenderGrid::maxContentForChild, &GridTrack::usedBreadth, &GridTrack::growUsedBreadth); + resolveContentBasedTrackSizingFunctionsForItems(direction, columnTracks, rowTracks, child, &GridTrackSize::hasMinOrMaxContentMaxTrackBreadth, &RenderGrid::minContentForChild, &GridTrack::maxBreadthIfNotInfinite, &GridTrack::growMaxBreadth); + resolveContentBasedTrackSizingFunctionsForItems(direction, columnTracks, rowTracks, child, &GridTrackSize::hasMaxContentMaxTrackBreadth, &RenderGrid::maxContentForChild, &GridTrack::maxBreadthIfNotInfinite, &GridTrack::growMaxBreadth); + } + + for (size_t i = 0; i < tracks.size(); ++i) { + GridTrack& track = tracks[i]; + if (track.m_maxBreadth == infinity) + track.m_maxBreadth = track.m_usedBreadth; + + availableLogicalSpace -= track.m_usedBreadth; + } +} + +void RenderGrid::resolveContentBasedTrackSizingFunctionsForItems(TrackSizingDirection direction, Vector<GridTrack>& columnTracks, Vector<GridTrack>& rowTracks, RenderBox* gridItem, FilterFunction filterFunction, SizingFunction sizingFunction, AccumulatorGetter trackGetter, AccumulatorGrowFunction trackGrowthFunction) +{ + const GridCoordinate coordinate = cachedGridCoordinate(gridItem); + const size_t initialTrackIndex = (direction == ForColumns) ? coordinate.columns.initialPositionIndex : coordinate.rows.initialPositionIndex; + const size_t finalTrackIndex = (direction == ForColumns) ? coordinate.columns.finalPositionIndex : coordinate.rows.finalPositionIndex; + + Vector<GridTrack*> tracks; + for (size_t trackIndex = initialTrackIndex; trackIndex <= finalTrackIndex; ++trackIndex) { + const GridTrackSize& trackSize = gridTrackSize(direction, trackIndex); + if (!(trackSize.*filterFunction)()) + continue; + + GridTrack& track = (direction == ForColumns) ? columnTracks[trackIndex] : rowTracks[trackIndex]; + tracks.append(&track); + } + + LayoutUnit additionalBreadthSpace = (this->*sizingFunction)(gridItem, direction, columnTracks); + for (size_t trackIndexForSpace = initialTrackIndex; trackIndexForSpace <= finalTrackIndex; ++trackIndexForSpace) { + GridTrack& track = (direction == ForColumns) ? columnTracks[trackIndexForSpace] : rowTracks[trackIndexForSpace]; + additionalBreadthSpace -= (track.*trackGetter)(); + } + + // FIXME: We should pass different values for |tracksForGrowthAboveMaxBreadth|. + distributeSpaceToTracks(tracks, &tracks, trackGetter, trackGrowthFunction, additionalBreadthSpace); +} + +static bool sortByGridTrackGrowthPotential(const GridTrack* track1, const GridTrack* track2) +{ + return (track1->m_maxBreadth - track1->m_usedBreadth) < (track2->m_maxBreadth - track2->m_usedBreadth); +} + +void RenderGrid::distributeSpaceToTracks(Vector<GridTrack*>& tracks, Vector<GridTrack*>* tracksForGrowthAboveMaxBreadth, AccumulatorGetter trackGetter, AccumulatorGrowFunction trackGrowthFunction, LayoutUnit& availableLogicalSpace) +{ + std::sort(tracks.begin(), tracks.end(), sortByGridTrackGrowthPotential); + + size_t tracksSize = tracks.size(); + Vector<LayoutUnit> updatedTrackBreadths(tracksSize); + + for (size_t i = 0; i < tracksSize; ++i) { + GridTrack& track = *tracks[i]; + LayoutUnit availableLogicalSpaceShare = availableLogicalSpace / (tracksSize - i); + LayoutUnit trackBreadth = (tracks[i]->*trackGetter)(); + LayoutUnit growthShare = std::min(availableLogicalSpaceShare, track.m_maxBreadth - trackBreadth); + updatedTrackBreadths[i] = trackBreadth + growthShare; + availableLogicalSpace -= growthShare; + } + + if (availableLogicalSpace > 0 && tracksForGrowthAboveMaxBreadth) { + tracksSize = tracksForGrowthAboveMaxBreadth->size(); + for (size_t i = 0; i < tracksSize; ++i) { + LayoutUnit growthShare = availableLogicalSpace / (tracksSize - i); + updatedTrackBreadths[i] += growthShare; + availableLogicalSpace -= growthShare; + } + } + + for (size_t i = 0; i < tracksSize; ++i) { + LayoutUnit growth = updatedTrackBreadths[i] - (tracks[i]->*trackGetter)(); + if (growth >= 0) + (tracks[i]->*trackGrowthFunction)(growth); + } +} + +#ifndef NDEBUG +bool RenderGrid::tracksAreWiderThanMinTrackBreadth(TrackSizingDirection direction, const Vector<GridTrack>& tracks) +{ + for (size_t i = 0; i < tracks.size(); ++i) { + const GridTrackSize& trackSize = gridTrackSize(direction, i); + const Length& minTrackBreadth = trackSize.minTrackBreadth(); + if (computeUsedBreadthOfMinLength(direction, minTrackBreadth) > tracks[i].m_usedBreadth) + return false; + } + return true; +} +#endif + +void RenderGrid::growGrid(TrackSizingDirection direction) +{ + if (direction == ForColumns) { + const size_t oldColumnSize = m_grid[0].size(); + for (size_t row = 0; row < m_grid.size(); ++row) + m_grid[row].grow(oldColumnSize + 1); + } else { + const size_t oldRowSize = m_grid.size(); + m_grid.grow(oldRowSize + 1); + m_grid[oldRowSize].grow(m_grid[0].size()); + } +} + +void RenderGrid::insertItemIntoGrid(RenderBox* child, const GridCoordinate& coordinate) +{ + m_grid[coordinate.rows.initialPositionIndex][coordinate.columns.initialPositionIndex].append(child); + m_gridItemCoordinate.set(child, coordinate); +} + +void RenderGrid::insertItemIntoGrid(RenderBox* child, size_t rowTrack, size_t columnTrack) +{ + const GridSpan& rowSpan = resolveGridPositionsFromAutoPlacementPosition(child, ForRows, rowTrack); + const GridSpan& columnSpan = resolveGridPositionsFromAutoPlacementPosition(child, ForColumns, columnTrack); + insertItemIntoGrid(child, GridCoordinate(rowSpan, columnSpan)); +} + +void RenderGrid::placeItemsOnGrid() +{ + ASSERT(!gridWasPopulated()); + ASSERT(m_gridItemCoordinate.isEmpty()); + + m_grid.grow(maximumIndexInDirection(ForRows)); + size_t maximumColumnIndex = maximumIndexInDirection(ForColumns); + for (size_t i = 0; i < m_grid.size(); ++i) + m_grid[i].grow(maximumColumnIndex); + + Vector<RenderBox*> autoMajorAxisAutoGridItems; + Vector<RenderBox*> specifiedMajorAxisAutoGridItems; + GridAutoFlow autoFlow = style()->gridAutoFlow(); + for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { + // FIXME: We never re-resolve positions if the grid is grown during auto-placement which may lead auto / <integer> + // positions to not match the author's intent. The specification is unclear on what should be done in this case. + OwnPtr<GridSpan> rowPositions = resolveGridPositionsFromStyle(child, ForRows); + OwnPtr<GridSpan> columnPositions = resolveGridPositionsFromStyle(child, ForColumns); + if (!rowPositions || !columnPositions) { + GridSpan* majorAxisPositions = (autoPlacementMajorAxisDirection() == ForColumns) ? columnPositions.get() : rowPositions.get(); + if (!majorAxisPositions) + autoMajorAxisAutoGridItems.append(child); + else + specifiedMajorAxisAutoGridItems.append(child); + continue; + } + insertItemIntoGrid(child, GridCoordinate(*rowPositions, *columnPositions)); + } + + ASSERT(gridRowCount() >= style()->gridRows().size()); + ASSERT(gridColumnCount() >= style()->gridColumns().size()); + + if (autoFlow == AutoFlowNone) { + // If we did collect some grid items, they won't be placed thus never laid out. + ASSERT(!autoMajorAxisAutoGridItems.size()); + ASSERT(!specifiedMajorAxisAutoGridItems.size()); + return; + } + + placeSpecifiedMajorAxisItemsOnGrid(specifiedMajorAxisAutoGridItems); + placeAutoMajorAxisItemsOnGrid(autoMajorAxisAutoGridItems); +} + +void RenderGrid::placeSpecifiedMajorAxisItemsOnGrid(Vector<RenderBox*> autoGridItems) +{ + for (size_t i = 0; i < autoGridItems.size(); ++i) { + OwnPtr<GridSpan> majorAxisPositions = resolveGridPositionsFromStyle(autoGridItems[i], autoPlacementMajorAxisDirection()); + GridIterator iterator(m_grid, autoPlacementMajorAxisDirection(), majorAxisPositions->initialPositionIndex); + if (OwnPtr<GridCoordinate> emptyGridArea = iterator.nextEmptyGridArea()) { + insertItemIntoGrid(autoGridItems[i], emptyGridArea->rows.initialPositionIndex, emptyGridArea->columns.initialPositionIndex); + continue; + } + + growGrid(autoPlacementMinorAxisDirection()); + OwnPtr<GridCoordinate> emptyGridArea = iterator.nextEmptyGridArea(); + ASSERT(emptyGridArea); + insertItemIntoGrid(autoGridItems[i], emptyGridArea->rows.initialPositionIndex, emptyGridArea->columns.initialPositionIndex); + } +} + +void RenderGrid::placeAutoMajorAxisItemsOnGrid(Vector<RenderBox*> autoGridItems) +{ + for (size_t i = 0; i < autoGridItems.size(); ++i) + placeAutoMajorAxisItemOnGrid(autoGridItems[i]); +} + +void RenderGrid::placeAutoMajorAxisItemOnGrid(RenderBox* gridItem) +{ + OwnPtr<GridSpan> minorAxisPositions = resolveGridPositionsFromStyle(gridItem, autoPlacementMinorAxisDirection()); + ASSERT(!resolveGridPositionsFromStyle(gridItem, autoPlacementMajorAxisDirection())); + size_t minorAxisIndex = 0; + if (minorAxisPositions) { + minorAxisIndex = minorAxisPositions->initialPositionIndex; + GridIterator iterator(m_grid, autoPlacementMinorAxisDirection(), minorAxisIndex); + if (OwnPtr<GridCoordinate> emptyGridArea = iterator.nextEmptyGridArea()) { + insertItemIntoGrid(gridItem, emptyGridArea->rows.initialPositionIndex, emptyGridArea->columns.initialPositionIndex); + return; + } + } else { + const size_t endOfMajorAxis = (autoPlacementMajorAxisDirection() == ForColumns) ? gridColumnCount() : gridRowCount(); + for (size_t majorAxisIndex = 0; majorAxisIndex < endOfMajorAxis; ++majorAxisIndex) { + GridIterator iterator(m_grid, autoPlacementMajorAxisDirection(), majorAxisIndex); + if (OwnPtr<GridCoordinate> emptyGridArea = iterator.nextEmptyGridArea()) { + insertItemIntoGrid(gridItem, emptyGridArea->rows.initialPositionIndex, emptyGridArea->columns.initialPositionIndex); + return; + } + } + } + + // We didn't find an empty grid area so we need to create an extra major axis line and insert our gridItem in it. + const size_t columnIndex = (autoPlacementMajorAxisDirection() == ForColumns) ? m_grid[0].size() : minorAxisIndex; + const size_t rowIndex = (autoPlacementMajorAxisDirection() == ForColumns) ? minorAxisIndex : m_grid.size(); + growGrid(autoPlacementMajorAxisDirection()); + insertItemIntoGrid(gridItem, rowIndex, columnIndex); +} + +RenderGrid::TrackSizingDirection RenderGrid::autoPlacementMajorAxisDirection() const +{ + GridAutoFlow flow = style()->gridAutoFlow(); + ASSERT(flow != AutoFlowNone); + return (flow == AutoFlowColumn) ? ForColumns : ForRows; +} + +RenderGrid::TrackSizingDirection RenderGrid::autoPlacementMinorAxisDirection() const +{ + GridAutoFlow flow = style()->gridAutoFlow(); + ASSERT(flow != AutoFlowNone); + return (flow == AutoFlowColumn) ? ForRows : ForColumns; +} + +void RenderGrid::clearGrid() +{ + m_grid.clear(); + m_gridItemCoordinate.clear(); } void RenderGrid::layoutGridItems() { - Vector<GridTrack> columnTracks, rowTracks; - computedUsedBreadthOfGridTracks(ForColumns, columnTracks); - // FIXME: The logical width of Grid Columns from the prior step is used in - // the formatting of Grid items in content-sized Grid Rows to determine - // their required height. We will probably need to pass columns through. - computedUsedBreadthOfGridTracks(ForRows, rowTracks); + placeItemsOnGrid(); + + Vector<GridTrack> columnTracks(gridColumnCount()); + Vector<GridTrack> rowTracks(gridRowCount()); + computedUsedBreadthOfGridTracks(ForColumns, columnTracks, rowTracks); + ASSERT(tracksAreWiderThanMinTrackBreadth(ForColumns, columnTracks)); + computedUsedBreadthOfGridTracks(ForRows, columnTracks, rowTracks); + ASSERT(tracksAreWiderThanMinTrackBreadth(ForRows, rowTracks)); for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { LayoutPoint childPosition = findChildLogicalPosition(child, columnTracks, rowTracks); - size_t columnTrack = resolveGridPosition(child->style()->gridItemColumn()); - size_t rowTrack = resolveGridPosition(child->style()->gridItemRow()); + // Because the grid area cannot be styled, we don't need to adjust + // the grid breadth to account for 'box-sizing'. + LayoutUnit oldOverrideContainingBlockContentLogicalWidth = child->hasOverrideContainingBlockLogicalWidth() ? child->overrideContainingBlockContentLogicalWidth() : LayoutUnit(); + LayoutUnit oldOverrideContainingBlockContentLogicalHeight = child->hasOverrideContainingBlockLogicalHeight() ? child->overrideContainingBlockContentLogicalHeight() : LayoutUnit(); - // FIXME: Properly support implicit rows and columns (bug 103573). - if (columnTrack < columnTracks.size() && rowTrack < rowTracks.size()) { - // Because the grid area cannot be styled, we don't need to adjust - // the grid breadth to account for 'box-sizing'. - child->setOverrideContainingBlockContentLogicalWidth(columnTracks[columnTrack].m_usedBreadth); - child->setOverrideContainingBlockContentLogicalHeight(rowTracks[rowTrack].m_usedBreadth); - } + // FIXME: For children in a content sized track, we clear the overrideContainingBlockContentLogicalHeight + // in minContentForChild / maxContentForChild which means that we will always relayout the child. + LayoutUnit overrideContainingBlockContentLogicalWidth = gridAreaBreadthForChild(child, ForColumns, columnTracks); + LayoutUnit overrideContainingBlockContentLogicalHeight = gridAreaBreadthForChild(child, ForRows, rowTracks); + if (oldOverrideContainingBlockContentLogicalWidth != overrideContainingBlockContentLogicalWidth || oldOverrideContainingBlockContentLogicalHeight != overrideContainingBlockContentLogicalHeight) + child->setNeedsLayout(true, MarkOnlyThis); + + child->setOverrideContainingBlockContentLogicalWidth(overrideContainingBlockContentLogicalWidth); + child->setOverrideContainingBlockContentLogicalHeight(overrideContainingBlockContentLogicalHeight); + + LayoutRect oldChildRect = child->frameRect(); // FIXME: Grid items should stretch to fill their cells. Once we // implement grid-{column,row}-align, we can also shrink to fit. For @@ -180,43 +689,114 @@ void RenderGrid::layoutGridItems() // FIXME: Handle border & padding on the grid element. child->setLogicalLocation(childPosition); + + // If the child moved, we have to repaint it as well as any floating/positioned + // descendants. An exception is if we need a layout. In this case, we know we're going to + // repaint ourselves (and the child) anyway. + if (!selfNeedsLayout() && child->checkForRepaintDuringLayout()) + child->repaintDuringLayoutIfMoved(oldChildRect); } - // FIXME: Handle border & padding on the grid element. for (size_t i = 0; i < rowTracks.size(); ++i) setLogicalHeight(logicalHeight() + rowTracks[i].m_usedBreadth); + + // FIXME: We should handle min / max logical height. + + setLogicalHeight(logicalHeight() + borderAndPaddingLogicalHeight()); + clearGrid(); +} + +RenderGrid::GridCoordinate RenderGrid::cachedGridCoordinate(const RenderBox* gridItem) const +{ + ASSERT(m_gridItemCoordinate.contains(gridItem)); + return m_gridItemCoordinate.get(gridItem); +} + +RenderGrid::GridSpan RenderGrid::resolveGridPositionsFromAutoPlacementPosition(const RenderBox*, TrackSizingDirection, size_t initialPosition) const +{ + // FIXME: We don't support spanning with auto positions yet. Once we do, this is wrong. Also we should make + // sure the grid can accomodate the new item as we only grow 1 position in a given direction. + return GridSpan(initialPosition, initialPosition); } -size_t RenderGrid::resolveGridPosition(const GridPosition& position) const +PassOwnPtr<RenderGrid::GridSpan> RenderGrid::resolveGridPositionsFromStyle(const RenderBox* gridItem, TrackSizingDirection direction) const { + ASSERT(gridWasPopulated()); + + const GridPosition& initialPosition = (direction == ForColumns) ? gridItem->style()->gridItemStart() : gridItem->style()->gridItemBefore(); + const GridPositionSide initialPositionSide = (direction == ForColumns) ? StartSide : BeforeSide; + const GridPosition& finalPosition = (direction == ForColumns) ? gridItem->style()->gridItemEnd() : gridItem->style()->gridItemAfter(); + const GridPositionSide finalPositionSide = (direction == ForColumns) ? EndSide : AfterSide; + + if (initialPosition.isAuto() && finalPosition.isAuto()) { + if (style()->gridAutoFlow() == AutoFlowNone) + return adoptPtr(new GridSpan(0, 0)); + + // We can't get our grid positions without running the auto placement algorithm. + return nullptr; + } + + if (initialPosition.isAuto()) { + // Infer the position from the final position ('auto / 1' case). + const size_t finalResolvedPosition = resolveGridPositionFromStyle(finalPosition, finalPositionSide); + return adoptPtr(new GridSpan(finalResolvedPosition, finalResolvedPosition)); + } + + if (finalPosition.isAuto()) { + // Infer our position from the initial position ('1 / auto' case). + const size_t initialResolvedPosition = resolveGridPositionFromStyle(initialPosition, initialPositionSide); + return adoptPtr(new GridSpan(initialResolvedPosition, initialResolvedPosition)); + } + + return adoptPtr(new GridSpan(resolveGridPositionFromStyle(initialPosition, initialPositionSide), resolveGridPositionFromStyle(finalPosition, finalPositionSide))); +} + +size_t RenderGrid::resolveGridPositionFromStyle(const GridPosition& position, GridPositionSide side) const +{ + ASSERT(gridWasPopulated()); + // FIXME: Handle other values for grid-{row,column} like ranges or line names. switch (position.type()) { - case IntegerPosition: + case IntegerPosition: { // FIXME: What does a non-positive integer mean for a column/row? - if (!position.isPositive()) - return 0; + size_t resolvedPosition = position.isPositive() ? position.integerPosition() - 1 : 0; + + if (side == StartSide || side == BeforeSide) + return resolvedPosition; - return position.integerPosition() - 1; + const size_t endOfTrack = (side == EndSide) ? gridColumnCount() - 1 : gridRowCount() - 1; + ASSERT(endOfTrack >= resolvedPosition); + return endOfTrack - resolvedPosition; + } case AutoPosition: - // FIXME: We should follow 'grid-auto-flow' for resolution. - // Until then, we use the 'grid-auto-flow: none' behavior (which is the default) - // and resolve 'auto' as the first row / column. + // 'auto' depends on the opposite position for resolution (e.g. grid-row: auto / 1). + ASSERT_NOT_REACHED(); return 0; } ASSERT_NOT_REACHED(); return 0; } +LayoutUnit RenderGrid::gridAreaBreadthForChild(const RenderBox* child, TrackSizingDirection direction, const Vector<GridTrack>& tracks) const +{ + const GridCoordinate& coordinate = cachedGridCoordinate(child); + const GridSpan& span = (direction == ForColumns) ? coordinate.columns : coordinate.rows; + LayoutUnit gridAreaBreadth = 0; + for (size_t trackIndex = span.initialPositionIndex; trackIndex <= span.finalPositionIndex; ++trackIndex) + gridAreaBreadth += tracks[trackIndex].m_usedBreadth; + return gridAreaBreadth; +} + LayoutPoint RenderGrid::findChildLogicalPosition(RenderBox* child, const Vector<GridTrack>& columnTracks, const Vector<GridTrack>& rowTracks) { - size_t columnTrack = resolveGridPosition(child->style()->gridItemColumn()); - size_t rowTrack = resolveGridPosition(child->style()->gridItemRow()); + const GridCoordinate& coordinate = cachedGridCoordinate(child); - LayoutPoint offset; + // The grid items should be inside the grid container's border box, that's why they need to be shifted. + LayoutPoint offset(borderAndPaddingStart(), borderAndPaddingBefore()); // FIXME: |columnTrack| and |rowTrack| should be smaller than our column / row count. - for (size_t i = 0; i < columnTrack && i < columnTracks.size(); ++i) + for (size_t i = 0; i < coordinate.columns.initialPositionIndex && i < columnTracks.size(); ++i) offset.setX(offset.x() + columnTracks[i].m_usedBreadth); - for (size_t i = 0; i < rowTrack && i < rowTracks.size(); ++i) + for (size_t i = 0; i < coordinate.rows.initialPositionIndex && i < rowTracks.size(); ++i) offset.setY(offset.y() + rowTracks[i].m_usedBreadth); // FIXME: Handle margins on the grid item. diff --git a/Source/WebCore/rendering/RenderGrid.h b/Source/WebCore/rendering/RenderGrid.h index 340c64851..eb2b2aaf2 100644 --- a/Source/WebCore/rendering/RenderGrid.h +++ b/Source/WebCore/rendering/RenderGrid.h @@ -30,27 +30,124 @@ namespace WebCore { +class GridTrack; + class RenderGrid : public RenderBlock { public: - RenderGrid(Node*); + RenderGrid(Element*); virtual ~RenderGrid(); virtual const char* renderName() const OVERRIDE; virtual void layoutBlock(bool relayoutChildren, LayoutUnit pageLogicalHeight = 0) OVERRIDE; - virtual void computePreferredLogicalWidths() OVERRIDE; virtual bool avoidsFloats() const OVERRIDE { return true; } virtual bool canCollapseAnonymousBlockChild() const OVERRIDE { return false; } private: - class GridTrack; + virtual bool isRenderGrid() const OVERRIDE { return true; } + virtual void computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const OVERRIDE; + virtual void computePreferredLogicalWidths() OVERRIDE; + + LayoutUnit computePreferredTrackWidth(const Length&, size_t) const; + + struct GridSpan { + GridSpan(size_t initialPosition, size_t finalPosition) + : initialPositionIndex(initialPosition) + , finalPositionIndex(finalPosition) + { + ASSERT(initialPositionIndex <= finalPositionIndex); + } + + size_t initialPositionIndex; + size_t finalPositionIndex; + }; + + struct GridCoordinate { + // HashMap requires a default constuctor. + GridCoordinate() + : columns(0, 0) + , rows(0, 0) + { + } + + GridCoordinate(const GridSpan& r, const GridSpan& c) + : columns(c) + , rows(r) + { + } + + GridSpan columns; + GridSpan rows; + }; + + class GridIterator; enum TrackSizingDirection { ForColumns, ForRows }; - void computedUsedBreadthOfGridTracks(TrackSizingDirection, Vector<GridTrack>&); + void computedUsedBreadthOfGridTracks(TrackSizingDirection, Vector<GridTrack>& columnTracks, Vector<GridTrack>& rowTracks); + LayoutUnit computeUsedBreadthOfMinLength(TrackSizingDirection, const Length&) const; + LayoutUnit computeUsedBreadthOfMaxLength(TrackSizingDirection, const Length&) const; + LayoutUnit computeUsedBreadthOfSpecifiedLength(TrackSizingDirection, const Length&) const; + void resolveContentBasedTrackSizingFunctions(TrackSizingDirection, Vector<GridTrack>& columnTracks, Vector<GridTrack>& rowTracks, LayoutUnit& availableLogicalSpace); + + void growGrid(TrackSizingDirection); + void insertItemIntoGrid(RenderBox*, size_t rowTrack, size_t columnTrack); + void insertItemIntoGrid(RenderBox*, const GridCoordinate&); + void placeItemsOnGrid(); + void placeSpecifiedMajorAxisItemsOnGrid(Vector<RenderBox*>); + void placeAutoMajorAxisItemsOnGrid(Vector<RenderBox*>); + void placeAutoMajorAxisItemOnGrid(RenderBox*); + TrackSizingDirection autoPlacementMajorAxisDirection() const; + TrackSizingDirection autoPlacementMinorAxisDirection() const; + void layoutGridItems(); + void clearGrid(); + + typedef LayoutUnit (RenderGrid::* SizingFunction)(RenderBox*, TrackSizingDirection, Vector<GridTrack>&); + typedef LayoutUnit (GridTrack::* AccumulatorGetter)() const; + typedef void (GridTrack::* AccumulatorGrowFunction)(LayoutUnit); + typedef bool (GridTrackSize::* FilterFunction)() const; + void resolveContentBasedTrackSizingFunctionsForItems(TrackSizingDirection, Vector<GridTrack>& columnTracks, Vector<GridTrack>& rowTracks, RenderBox*, FilterFunction, SizingFunction, AccumulatorGetter, AccumulatorGrowFunction); + void distributeSpaceToTracks(Vector<GridTrack*>&, Vector<GridTrack*>* tracksForGrowthAboveMaxBreadth, AccumulatorGetter, AccumulatorGrowFunction, LayoutUnit& availableLogicalSpace); + const GridTrackSize& gridTrackSize(TrackSizingDirection, size_t) const; + size_t maximumIndexInDirection(TrackSizingDirection) const; + + LayoutUnit logicalContentHeightForChild(RenderBox*, Vector<GridTrack>&); + LayoutUnit minContentForChild(RenderBox*, TrackSizingDirection, Vector<GridTrack>& columnTracks); + LayoutUnit maxContentForChild(RenderBox*, TrackSizingDirection, Vector<GridTrack>& columnTracks); LayoutPoint findChildLogicalPosition(RenderBox*, const Vector<GridTrack>& columnTracks, const Vector<GridTrack>& rowTracks); - size_t resolveGridPosition(const GridPosition&) const; + GridCoordinate cachedGridCoordinate(const RenderBox*) const; + + GridSpan resolveGridPositionsFromAutoPlacementPosition(const RenderBox*, TrackSizingDirection, size_t) const; + PassOwnPtr<GridSpan> resolveGridPositionsFromStyle(const RenderBox*, TrackSizingDirection) const; + enum GridPositionSide { + StartSide, + EndSide, + BeforeSide, + AfterSide + }; + size_t resolveGridPositionFromStyle(const GridPosition&, GridPositionSide) const; + + LayoutUnit gridAreaBreadthForChild(const RenderBox* child, TrackSizingDirection, const Vector<GridTrack>&) const; + +#ifndef NDEBUG + bool tracksAreWiderThanMinTrackBreadth(TrackSizingDirection, const Vector<GridTrack>&); + bool gridWasPopulated() const { return !m_grid.isEmpty() && !m_grid[0].isEmpty(); } +#endif + + size_t gridColumnCount() const + { + ASSERT(gridWasPopulated()); + return m_grid[0].size(); + } + size_t gridRowCount() const + { + ASSERT(gridWasPopulated()); + return m_grid.size(); + } + + Vector<Vector<Vector<RenderBox*, 1> > > m_grid; + HashMap<const RenderBox*, GridCoordinate> m_gridItemCoordinate; }; } // namespace WebCore diff --git a/Source/WebCore/rendering/RenderHTMLCanvas.cpp b/Source/WebCore/rendering/RenderHTMLCanvas.cpp index bfa1c4fbf..e68927033 100644 --- a/Source/WebCore/rendering/RenderHTMLCanvas.cpp +++ b/Source/WebCore/rendering/RenderHTMLCanvas.cpp @@ -44,7 +44,8 @@ using namespace HTMLNames; RenderHTMLCanvas::RenderHTMLCanvas(HTMLCanvasElement* element) : RenderReplaced(element, element->size()) { - view()->frameView()->setIsVisuallyNonEmpty(); + // Actual size is not known yet, report the default intrinsic size. + view()->frameView()->incrementVisuallyNonEmptyPixelCount(roundedIntSize(intrinsicSize())); } bool RenderHTMLCanvas::requiresLayer() const @@ -68,7 +69,7 @@ void RenderHTMLCanvas::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& pa } } - bool useLowQualityScale = style()->imageRendering() == ImageRenderingOptimizeContrast; + bool useLowQualityScale = style()->imageRendering() == ImageRenderingCrispEdges || style()->imageRendering() == ImageRenderingOptimizeSpeed; static_cast<HTMLCanvasElement*>(node())->paint(paintInfo.context, rect, useLowQualityScale); } diff --git a/Source/WebCore/rendering/RenderHTMLCanvas.h b/Source/WebCore/rendering/RenderHTMLCanvas.h index 5bed27f4f..f4e07cc63 100644 --- a/Source/WebCore/rendering/RenderHTMLCanvas.h +++ b/Source/WebCore/rendering/RenderHTMLCanvas.h @@ -49,7 +49,7 @@ private: inline RenderHTMLCanvas* toRenderHTMLCanvas(RenderObject* object) { - ASSERT(!object || !strcmp(object->renderName(), "RenderHTMLCanvas")); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isCanvas()); return static_cast<RenderHTMLCanvas*>(object); } diff --git a/Source/WebCore/rendering/RenderIFrame.cpp b/Source/WebCore/rendering/RenderIFrame.cpp index 2fcd58a77..2262a3fc9 100644 --- a/Source/WebCore/rendering/RenderIFrame.cpp +++ b/Source/WebCore/rendering/RenderIFrame.cpp @@ -33,6 +33,7 @@ #include "Page.h" #include "RenderView.h" #include "Settings.h" +#include <wtf/StackStats.h> namespace WebCore { @@ -63,7 +64,7 @@ LayoutUnit RenderIFrame::minPreferredLogicalWidth() const if (!childRoot) return 0; - return childRoot->minPreferredLogicalWidth(); + return childRoot->minPreferredLogicalWidth() + borderAndPaddingLogicalWidth(); } LayoutUnit RenderIFrame::maxPreferredLogicalWidth() const @@ -75,20 +76,25 @@ LayoutUnit RenderIFrame::maxPreferredLogicalWidth() const if (!childRoot) return 0; - return childRoot->maxPreferredLogicalWidth(); + return childRoot->maxPreferredLogicalWidth() + borderAndPaddingLogicalWidth(); } bool RenderIFrame::isSeamless() const { - return node() && node()->hasTagName(iframeTag) && static_cast<HTMLIFrameElement*>(node())->shouldDisplaySeamlessly(); + return node() && node()->hasTagName(iframeTag) && toHTMLIFrameElement(node())->shouldDisplaySeamlessly(); +} + +bool RenderIFrame::requiresLayer() const +{ + return RenderFrameBase::requiresLayer() || style()->resize() != RESIZE_NONE; } RenderView* RenderIFrame::contentRootRenderer() const { // FIXME: Is this always a valid cast? What about plugins? ASSERT(!widget() || widget()->isFrameView()); - FrameView* childFrameView = static_cast<FrameView*>(widget()); - return childFrameView ? static_cast<RenderView*>(childFrameView->frame()->contentRenderer()) : 0; + FrameView* childFrameView = toFrameView(widget()); + return childFrameView ? childFrameView->frame()->contentRenderer() : 0; } bool RenderIFrame::flattenFrame() const @@ -96,7 +102,7 @@ bool RenderIFrame::flattenFrame() const if (!node() || !node()->hasTagName(iframeTag)) return false; - HTMLIFrameElement* element = static_cast<HTMLIFrameElement*>(node()); + HTMLIFrameElement* element = toHTMLIFrameElement(node()); Frame* frame = element->document()->frame(); if (isSeamless()) @@ -130,17 +136,17 @@ void RenderIFrame::layoutSeamlessly() updateWidgetPosition(); // Tell the Widget about our new width/height (it will also layout the child document). // Laying out our kids is normally responsible for adjusting our height, so we set it here. - // Replaced elements do not respect padding, so we just add border to the child's height. - // FIXME: It's possible that seamless iframes (since they act like divs) *should* respect padding. - FrameView* childFrameView = static_cast<FrameView*>(widget()); + // Replaced elements normally do not respect padding, but seamless elements should: we'll add + // both padding and border to the child's logical height here. + FrameView* childFrameView = toFrameView(widget()); if (childFrameView) // Widget should never be null during layout(), but just in case. - setLogicalHeight(childFrameView->contentsHeight() + borderTop() + borderBottom()); + setLogicalHeight(childFrameView->contentsHeight() + borderTop() + borderBottom() + paddingTop() + paddingBottom()); updateLogicalHeight(); updateWidgetPosition(); // Notify the Widget of our final height. // Assert that the child document did a complete layout. - RenderView* childRoot = childFrameView ? static_cast<RenderView*>(childFrameView->frame()->contentRenderer()) : 0; + RenderView* childRoot = childFrameView ? childFrameView->frame()->contentRenderer() : 0; ASSERT(!childFrameView || !childFrameView->layoutPending()); ASSERT_UNUSED(childRoot, !childRoot || !childRoot->needsLayout()); } diff --git a/Source/WebCore/rendering/RenderIFrame.h b/Source/WebCore/rendering/RenderIFrame.h index 74163eb79..aac06560f 100644 --- a/Source/WebCore/rendering/RenderIFrame.h +++ b/Source/WebCore/rendering/RenderIFrame.h @@ -52,6 +52,8 @@ private: virtual const char* renderName() const OVERRIDE { return "RenderPartObject"; } // Lying for now to avoid breaking tests + virtual bool requiresLayer() const OVERRIDE; + void layoutSeamlessly(); RenderView* contentRootRenderer() const; @@ -59,13 +61,13 @@ private: inline RenderIFrame* toRenderIFrame(RenderObject* object) { - ASSERT(!object || object->isRenderIFrame()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isRenderIFrame()); return static_cast<RenderIFrame*>(object); } inline const RenderIFrame* toRenderIFrame(const RenderObject* object) { - ASSERT(!object || object->isRenderIFrame()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isRenderIFrame()); return static_cast<const RenderIFrame*>(object); } diff --git a/Source/WebCore/rendering/RenderImage.cpp b/Source/WebCore/rendering/RenderImage.cpp index c3bcf3a1a..698894bb9 100644 --- a/Source/WebCore/rendering/RenderImage.cpp +++ b/Source/WebCore/rendering/RenderImage.cpp @@ -29,6 +29,7 @@ #include "RenderImage.h" #include "BitmapImage.h" +#include "CachedImage.h" #include "Font.h" #include "FontCache.h" #include "Frame.h" @@ -44,7 +45,7 @@ #include "PaintInfo.h" #include "RenderView.h" #include "SVGImage.h" -#include <wtf/UnusedParam.h> +#include <wtf/StackStats.h> using namespace std; @@ -52,8 +53,8 @@ namespace WebCore { using namespace HTMLNames; -RenderImage::RenderImage(Node* node) - : RenderReplaced(node, IntSize()) +RenderImage::RenderImage(Element* element) + : RenderReplaced(element, IntSize()) , m_needsToSetSizeForAltText(false) , m_didIncrementVisuallyNonEmptyPixelCount(false) , m_isGeneratedContent(false) @@ -61,6 +62,13 @@ RenderImage::RenderImage(Node* node) updateAltText(); } +RenderImage* RenderImage::createAnonymous(Document* document) +{ + RenderImage* image = new (document->renderArena()) RenderImage(0); + image->setDocumentForAnonymous(document); + return image; +} + RenderImage::~RenderImage() { ASSERT(m_imageResource); @@ -119,8 +127,8 @@ bool RenderImage::setImageSizeForAltText(CachedImage* newImage /* = 0 */) FontCachePurgePreventer fontCachePurgePreventer; const Font& font = style()->font(); - IntSize textSize(min(font.width(RenderBlock::constructTextRun(this, font, m_altText, style())), maxAltTextWidth), min(font.fontMetrics().height(), maxAltTextHeight)); - imageSize = imageSize.expandedTo(textSize); + IntSize paddedTextSize(paddingWidth + min(ceilf(font.width(RenderBlock::constructTextRun(this, font, m_altText, style()))), maxAltTextWidth), paddingHeight + min(font.fontMetrics().height(), maxAltTextHeight)); + imageSize = imageSize.expandedTo(paddedTextSize); } if (imageSize == intrinsicSize()) @@ -157,7 +165,7 @@ void RenderImage::imageChanged(WrappedImagePtr newImage, const IntRect* rect) if (hasBoxDecorations() || hasMask()) RenderReplaced::imageChanged(newImage, rect); - + if (!m_imageResource) return; @@ -222,13 +230,27 @@ void RenderImage::imageDimensionsChanged(bool imageSizeChanged, const IntRect* r if (intrinsicSizeChanged) { if (!preferredLogicalWidthsDirty()) setPreferredLogicalWidthsDirty(true); - LogicalExtentComputedValues computedValues; - computeLogicalWidthInRegion(computedValues); - LayoutUnit newWidth = computedValues.m_extent; - computeLogicalHeight(height(), 0, computedValues); - LayoutUnit newHeight = computedValues.m_extent; - if (imageSizeChanged || width() != newWidth || height() != newHeight) { + bool hasOverrideSize = hasOverrideHeight() || hasOverrideWidth(); + if (!hasOverrideSize && !imageSizeChanged) { + LogicalExtentComputedValues computedValues; + computeLogicalWidthInRegion(computedValues); + LayoutUnit newWidth = computedValues.m_extent; + computeLogicalHeight(height(), 0, computedValues); + LayoutUnit newHeight = computedValues.m_extent; + + imageSizeChanged = width() != newWidth || height() != newHeight; + } + + // FIXME: We only need to recompute the containing block's preferred size + // if the containing block's size depends on the image's size (i.e., the container uses shrink-to-fit sizing). + // There's no easy way to detect that shrink-to-fit is needed, always force a layout. + bool containingBlockNeedsToRecomputePreferredSize = + style()->logicalWidth().isPercent() + || style()->logicalMaxWidth().isPercent() + || style()->logicalMinWidth().isPercent(); + + if (imageSizeChanged || hasOverrideSize || containingBlockNeedsToRecomputePreferredSize) { shouldRepaint = false; if (!selfNeedsLayout()) setNeedsLayout(true); @@ -263,6 +285,8 @@ void RenderImage::notifyFinished(CachedResource* newImage) if (documentBeingDestroyed()) return; + invalidateBackgroundObscurationStatus(); + #if USE(ACCELERATED_COMPOSITING) if (newImage == m_imageResource->cachedImage()) { // tell any potential compositing layers @@ -297,6 +321,8 @@ void RenderImage::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOf page->addRelevantUnpaintedObject(this, visualOverflowRect()); if (cWidth > 2 && cHeight > 2) { + const int borderWidth = 1; + // Draw an outline rect where the image should be. context->setStrokeStyle(SolidStroke); context->setStrokeColor(Color::lightGray, style()->colorSpace()); @@ -307,8 +333,8 @@ void RenderImage::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOf LayoutSize imageOffset; // When calculating the usable dimensions, exclude the pixels of // the ouline rect so the error image/alt text doesn't draw on it. - LayoutUnit usableWidth = cWidth - 2; - LayoutUnit usableHeight = cHeight - 2; + LayoutUnit usableWidth = cWidth - 2 * borderWidth; + LayoutUnit usableHeight = cHeight - 2 * borderWidth; RefPtr<Image> image = m_imageResource->image(); @@ -326,7 +352,7 @@ void RenderImage::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOf LayoutUnit centerY = (usableHeight - imageSize.height()) / 2; if (centerY < 0) centerY = 0; - imageOffset = LayoutSize(leftBorder + leftPad + centerX + 1, topBorder + topPad + centerY + 1); + imageOffset = LayoutSize(leftBorder + leftPad + centerX + borderWidth, topBorder + topPad + centerY + borderWidth); context->drawImage(image.get(), style()->colorSpace(), pixelSnappedIntRect(LayoutRect(paintOffset + imageOffset, imageSize)), CompositeSourceOver, shouldRespectImageOrientation()); errorPictureDrawn = true; } @@ -338,7 +364,7 @@ void RenderImage::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOf const FontMetrics& fontMetrics = font.fontMetrics(); LayoutUnit ascent = fontMetrics.ascent(); LayoutPoint altTextOffset = paintOffset; - altTextOffset.move(leftBorder + leftPad, topBorder + topPad + ascent); + altTextOffset.move(leftBorder + leftPad + (paddingWidth / 2) - borderWidth, topBorder + topPad + ascent + (paddingHeight / 2) - borderWidth); // Only draw the alt text if it'll fit within the content box, // and only if it fits above the error image. @@ -347,7 +373,7 @@ void RenderImage::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOf if (errorPictureDrawn) { if (usableWidth >= textWidth && fontMetrics.height() <= imageOffset.height()) context->drawText(font, textRun, altTextOffset); - } else if (usableWidth >= textWidth && cHeight >= fontMetrics.height()) + } else if (usableWidth >= textWidth && usableHeight >= fontMetrics.height()) context->drawText(font, textRun, altTextOffset); } } @@ -398,11 +424,11 @@ void RenderImage::paintAreaElementFocusRing(PaintInfo& paintInfo) if (paintInfo.context->paintingDisabled() && !paintInfo.context->updatingControlTints()) return; - Node* focusedNode = document->focusedNode(); - if (!focusedNode || !focusedNode->hasTagName(areaTag)) + Element* focusedElement = document->focusedElement(); + if (!focusedElement || !isHTMLAreaElement(focusedElement)) return; - HTMLAreaElement* areaElement = static_cast<HTMLAreaElement*>(focusedNode); + HTMLAreaElement* areaElement = toHTMLAreaElement(focusedElement); if (areaElement->imageElement() != node()) return; @@ -445,7 +471,7 @@ void RenderImage::paintIntoRect(GraphicsContext* context, const LayoutRect& rect if (!img || img->isNull()) return; - HTMLImageElement* imageElt = hostImageElement(); + HTMLImageElement* imageElt = (node() && isHTMLImageElement(node())) ? toHTMLImageElement(node()) : 0; CompositeOperator compositeOperator = imageElt ? imageElt->compositeOperator() : CompositeSourceOver; Image* image = m_imageResource->image().get(); bool useLowQualityScaling = shouldPaintAtLowQuality(context, image, image, alignedRect.size()); @@ -457,33 +483,38 @@ bool RenderImage::boxShadowShouldBeAppliedToBackground(BackgroundBleedAvoidance if (!RenderBoxModelObject::boxShadowShouldBeAppliedToBackground(bleedAvoidance)) return false; - return !backgroundIsObscured(); + return !const_cast<RenderImage*>(this)->backgroundIsKnownToBeObscured(); } -bool RenderImage::backgroundIsObscured() const +bool RenderImage::foregroundIsKnownToBeOpaqueInRect(const LayoutRect& localRect, unsigned maxDepthToTest) const { + UNUSED_PARAM(maxDepthToTest); if (!m_imageResource->hasImage() || m_imageResource->errorOccurred()) return false; - if (m_imageResource->cachedImage() && !m_imageResource->cachedImage()->isLoaded()) return false; - + if (!contentBoxRect().contains(localRect)) + return false; EFillBox backgroundClip = style()->backgroundClip(); - // Background paints under borders. if (backgroundClip == BorderFillBox && style()->hasBorder() && !borderObscuresBackground()) return false; - // Background shows in padding area. if ((backgroundClip == BorderFillBox || backgroundClip == PaddingFillBox) && style()->hasPadding()) return false; + // Check for image with alpha. + return m_imageResource->cachedImage() && m_imageResource->cachedImage()->currentFrameKnownToBeOpaque(this); +} - // Check for bitmap image with alpha. - Image* image = m_imageResource->image().get(); - if (!image || !image->isBitmapImage() || image->currentFrameHasAlpha()) +bool RenderImage::computeBackgroundIsKnownToBeObscured() +{ + if (!hasBackground()) return false; - - return true; + + LayoutRect paintedExtent; + if (!getBackgroundPaintedExtent(paintedExtent)) + return false; + return foregroundIsKnownToBeOpaqueInRect(paintedExtent, 0); } LayoutUnit RenderImage::minimumReplacedHeight() const @@ -493,7 +524,7 @@ LayoutUnit RenderImage::minimumReplacedHeight() const HTMLMapElement* RenderImage::imageMap() const { - HTMLImageElement* i = hostImageElement(); + HTMLImageElement* i = node() && isHTMLImageElement(node()) ? toHTMLImageElement(node()) : 0; return i ? i->treeScope()->getImageMap(i->fastGetAttribute(usemapAttr)) : 0; } @@ -526,10 +557,10 @@ void RenderImage::updateAltText() if (!node()) return; - if (node()->hasTagName(inputTag)) - m_altText = static_cast<HTMLInputElement*>(node())->altText(); - else if (HTMLImageElement* image = hostImageElement()) - m_altText = image->altText(); + if (isHTMLInputElement(node())) + m_altText = toHTMLInputElement(node())->altText(); + else if (isHTMLImageElement(node())) + m_altText = toHTMLImageElement(node())->altText(); } void RenderImage::layout() @@ -553,7 +584,7 @@ void RenderImage::computeIntrinsicRatioInformation(FloatSize& intrinsicSize, dou if (containingBlock->isBox()) { RenderBox* box = toRenderBox(containingBlock); intrinsicSize.setWidth(box->availableLogicalWidth()); - intrinsicSize.setHeight(box->availableLogicalHeight()); + intrinsicSize.setHeight(box->availableLogicalHeight(IncludeMarginBorderPadding)); } } // Don't compute an intrinsic ratio to preserve historical WebKit behavior if we're painting alt text and/or a broken image. @@ -563,24 +594,6 @@ void RenderImage::computeIntrinsicRatioInformation(FloatSize& intrinsicSize, dou } } -HTMLImageElement* RenderImage::hostImageElement() const -{ - if (!node()) - return 0; - - if (isHTMLImageElement(node())) - return toHTMLImageElement(node()); - - if (node()->hasTagName(webkitInnerImageTag)) { - if (Node* ancestor = node()->shadowAncestorNode()) { - if (ancestor->hasTagName(imgTag)) - return toHTMLImageElement(ancestor); - } - } - - return 0; -} - bool RenderImage::needsPreferredWidthsRecalculation() const { if (RenderReplaced::needsPreferredWidthsRecalculation()) diff --git a/Source/WebCore/rendering/RenderImage.h b/Source/WebCore/rendering/RenderImage.h index e734fdd4d..afd8e908c 100644 --- a/Source/WebCore/rendering/RenderImage.h +++ b/Source/WebCore/rendering/RenderImage.h @@ -31,14 +31,15 @@ namespace WebCore { class HTMLAreaElement; -class HTMLImageElement; class HTMLMapElement; class RenderImage : public RenderReplaced { public: - RenderImage(Node*); + RenderImage(Element*); virtual ~RenderImage(); + static RenderImage* createAnonymous(Document*); + void setImageResource(PassOwnPtr<RenderImageResource>); RenderImageResource* imageResource() { return m_imageResource.get(); } @@ -58,12 +59,13 @@ public: bool isGeneratedContent() const { return m_isGeneratedContent; } -protected: - HTMLImageElement* hostImageElement() const; + String altText() const { return m_altText; } +protected: virtual bool needsPreferredWidthsRecalculation() const; virtual RenderBox* embeddedContentBox() const; virtual void computeIntrinsicRatioInformation(FloatSize& intrinsicSize, double& intrinsicRatio, bool& isPercentageIntrinsicSize) const; + virtual bool foregroundIsKnownToBeOpaqueInRect(const LayoutRect& localRect, unsigned maxDepthToTest) const OVERRIDE; virtual void styleDidChange(StyleDifference, const RenderStyle*); @@ -87,7 +89,7 @@ private: virtual void paintReplaced(PaintInfo&, const LayoutPoint&); - virtual bool backgroundIsObscured() const; + virtual bool computeBackgroundIsKnownToBeObscured() OVERRIDE; virtual LayoutUnit minimumReplacedHeight() const OVERRIDE; @@ -114,13 +116,13 @@ private: inline RenderImage* toRenderImage(RenderObject* object) { - ASSERT(!object || object->isRenderImage()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isRenderImage()); return static_cast<RenderImage*>(object); } inline const RenderImage* toRenderImage(const RenderObject* object) { - ASSERT(!object || object->isRenderImage()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isRenderImage()); return static_cast<const RenderImage*>(object); } diff --git a/Source/WebCore/rendering/RenderImageResource.cpp b/Source/WebCore/rendering/RenderImageResource.cpp index 0c3a79e45..a0b46d358 100644 --- a/Source/WebCore/rendering/RenderImageResource.cpp +++ b/Source/WebCore/rendering/RenderImageResource.cpp @@ -28,6 +28,7 @@ #include "config.h" #include "RenderImageResource.h" +#include "CachedImage.h" #include "Image.h" #include "RenderImageResourceStyleImage.h" #include "RenderObject.h" @@ -36,7 +37,6 @@ namespace WebCore { RenderImageResource::RenderImageResource() : m_renderer(0) - , m_cachedImage(0) { } @@ -89,6 +89,16 @@ void RenderImageResource::resetAnimation() m_renderer->repaint(); } +PassRefPtr<Image> RenderImageResource::image(int, int) const +{ + return m_cachedImage ? m_cachedImage->imageForRenderer(m_renderer) : nullImage(); +} + +bool RenderImageResource::errorOccurred() const +{ + return m_cachedImage && m_cachedImage->errorOccurred(); +} + void RenderImageResource::setContainerSizeForRenderer(const IntSize& imageContainerSize) { ASSERT(m_renderer); @@ -101,4 +111,24 @@ Image* RenderImageResource::nullImage() return Image::nullImage(); } +bool RenderImageResource::usesImageContainerSize() const +{ + return m_cachedImage ? m_cachedImage->usesImageContainerSize() : false; +} + +bool RenderImageResource::imageHasRelativeWidth() const +{ + return m_cachedImage ? m_cachedImage->imageHasRelativeWidth() : false; +} + +bool RenderImageResource::imageHasRelativeHeight() const +{ + return m_cachedImage ? m_cachedImage->imageHasRelativeHeight() : false; +} + +LayoutSize RenderImageResource::imageSize(float multiplier) const +{ + return m_cachedImage ? m_cachedImage->imageSizeForRenderer(m_renderer, multiplier) : LayoutSize(); +} + } // namespace WebCore diff --git a/Source/WebCore/rendering/RenderImageResource.h b/Source/WebCore/rendering/RenderImageResource.h index 4bc6e973a..28dbd31a8 100644 --- a/Source/WebCore/rendering/RenderImageResource.h +++ b/Source/WebCore/rendering/RenderImageResource.h @@ -26,7 +26,6 @@ #ifndef RenderImageResource_h #define RenderImageResource_h -#include "CachedImage.h" #include "CachedResourceHandle.h" #include "Image.h" #include "LayoutSize.h" @@ -55,15 +54,15 @@ public: void resetAnimation(); - virtual PassRefPtr<Image> image(int /* width */ = 0, int /* height */ = 0) const { return m_cachedImage ? m_cachedImage->imageForRenderer(m_renderer) : nullImage(); } - virtual bool errorOccurred() const { return m_cachedImage && m_cachedImage->errorOccurred(); } + virtual PassRefPtr<Image> image(int width = 0, int height = 0) const; + virtual bool errorOccurred() const; virtual void setContainerSizeForRenderer(const IntSize&); - virtual bool usesImageContainerSize() const { return m_cachedImage ? m_cachedImage->usesImageContainerSize() : false; } - virtual bool imageHasRelativeWidth() const { return m_cachedImage ? m_cachedImage->imageHasRelativeWidth() : false; } - virtual bool imageHasRelativeHeight() const { return m_cachedImage ? m_cachedImage->imageHasRelativeHeight() : false; } + virtual bool usesImageContainerSize() const; + virtual bool imageHasRelativeWidth() const; + virtual bool imageHasRelativeHeight() const; - virtual LayoutSize imageSize(float multiplier) const { return m_cachedImage ? m_cachedImage->imageSizeForRenderer(m_renderer, multiplier) : LayoutSize(); } + virtual LayoutSize imageSize(float multiplier) const; virtual WrappedImagePtr imagePtr() const { return m_cachedImage.get(); } diff --git a/Source/WebCore/rendering/RenderImageResourceStyleImage.cpp b/Source/WebCore/rendering/RenderImageResourceStyleImage.cpp index ddb13447e..51cd80963 100644 --- a/Source/WebCore/rendering/RenderImageResourceStyleImage.cpp +++ b/Source/WebCore/rendering/RenderImageResourceStyleImage.cpp @@ -28,6 +28,7 @@ #include "config.h" #include "RenderImageResourceStyleImage.h" +#include "CachedImage.h" #include "RenderObject.h" #include "StyleCachedImage.h" diff --git a/Source/WebCore/rendering/RenderInline.cpp b/Source/WebCore/rendering/RenderInline.cpp index db1b185f6..5e6d643a7 100644 --- a/Source/WebCore/rendering/RenderInline.cpp +++ b/Source/WebCore/rendering/RenderInline.cpp @@ -32,6 +32,7 @@ #include "RenderArena.h" #include "RenderBlock.h" #include "RenderFlowThread.h" +#include "RenderFullScreen.h" #include "RenderGeometryMap.h" #include "RenderLayer.h" #include "RenderTheme.h" @@ -40,8 +41,6 @@ #include "TransformState.h" #include "VisiblePosition.h" -#include <wtf/TemporaryChange.h> - #if ENABLE(DASHBOARD_SUPPORT) || ENABLE(DRAGGABLE_REGION) #include "Frame.h" #endif @@ -50,13 +49,20 @@ using namespace std; namespace WebCore { -RenderInline::RenderInline(Node* node) - : RenderBoxModelObject(node) +RenderInline::RenderInline(Element* element) + : RenderBoxModelObject(element) , m_alwaysCreateLineBoxes(false) { setChildrenInline(true); } +RenderInline* RenderInline::createAnonymous(Document* document) +{ + RenderInline* renderer = new (document->renderArena()) RenderInline(0); + renderer->setDocumentForAnonymous(document); + return renderer; +} + void RenderInline::willBeDestroyed() { #if !ASSERT_DISABLED @@ -169,18 +175,11 @@ void RenderInline::styleDidChange(StyleDifference diff, const RenderStyle* oldSt // need to pass its style on to anyone else. RenderStyle* newStyle = style(); RenderInline* continuation = inlineElementContinuation(); - { - TemporaryChange<bool> enableAfter(RenderObjectChildList::s_enableUpdateBeforeAfterContent, false); - RenderInline* nextInlineElementCont = 0; - for (RenderInline* currCont = continuation; currCont; currCont = nextInlineElementCont) { - nextInlineElementCont = currCont->inlineElementContinuation(); - // We need to update :after content for the last continuation in the chain. - RenderObjectChildList::s_enableUpdateBeforeAfterContent = !nextInlineElementCont; - RenderBoxModelObject* nextCont = currCont->continuation(); - currCont->setContinuation(0); - currCont->setStyle(newStyle); - currCont->setContinuation(nextCont); - } + for (RenderInline* currCont = continuation; currCont; currCont = currCont->inlineElementContinuation()) { + RenderBoxModelObject* nextCont = currCont->continuation(); + currCont->setContinuation(0); + currCont->setStyle(newStyle); + currCont->setContinuation(nextCont); } // If an inline's in-flow positioning has changed then any descendant blocks will need to change their in-flow positioning accordingly. @@ -201,12 +200,6 @@ void RenderInline::styleDidChange(StyleDifference diff, const RenderStyle* oldSt } m_alwaysCreateLineBoxes = alwaysCreateLineBoxes; } - - // Update pseudos for :before and :after now. - if (!isAnonymous() && document()->styleSheetCollection()->usesBeforeAfterRules()) { - children()->updateBeforeAfterContent(this, BEFORE); - children()->updateBeforeAfterContent(this, AFTER); - } } void RenderInline::updateAlwaysCreateLineBoxes(bool fullLayout) @@ -219,13 +212,14 @@ void RenderInline::updateAlwaysCreateLineBoxes(bool fullLayout) RenderStyle* parentStyle = parent()->style(); RenderInline* parentRenderInline = parent()->isRenderInline() ? toRenderInline(parent()) : 0; bool checkFonts = document()->inNoQuirksMode(); + RenderFlowThread* flowThread = flowThreadContainingBlock(); bool alwaysCreateLineBoxes = (parentRenderInline && parentRenderInline->alwaysCreateLineBoxes()) || (parentRenderInline && parentStyle->verticalAlign() != BASELINE) || style()->verticalAlign() != BASELINE || style()->textEmphasisMark() != TextEmphasisMarkNone || (checkFonts && (!parentStyle->font().fontMetrics().hasIdenticalAscentDescentAndLineGap(style()->font().fontMetrics()) || parentStyle->lineHeight() != style()->lineHeight())) - || (inRenderFlowThread() && enclosingRenderFlowThread()->hasRegionsWithStyling()); + || (flowThread && flowThread->hasRegionsWithStyling()); if (!alwaysCreateLineBoxes && checkFonts && document()->styleSheetCollection()->usesFirstLineRules()) { // Have to check the first line style as well. @@ -324,21 +318,11 @@ void RenderInline::addChildIgnoringContinuation(RenderObject* newChild, RenderOb if (RenderObject* positionedAncestor = inFlowPositionedInlineAncestor(this)) newStyle->setPosition(positionedAncestor->style()->position()); - RenderBlock* newBox = new (renderArena()) RenderBlock(document() /* anonymous box */); + RenderBlock* newBox = RenderBlock::createAnonymous(document()); newBox->setStyle(newStyle.release()); RenderBoxModelObject* oldContinuation = continuation(); setContinuation(newBox); - // Someone may have put a <p> inside a <q>, causing a split. When this happens, the :after content - // has to move into the inline continuation. Call updateBeforeAfterContent to ensure that our :after - // content gets properly destroyed. - bool isLastChild = (beforeChild == lastChild()); - if (document()->styleSheetCollection()->usesBeforeAfterRules()) - children()->updateBeforeAfterContent(this, AFTER); - if (isLastChild && beforeChild != lastChild()) - beforeChild = 0; // We destroyed the last child, so now we need to update our insertion - // point to be 0. It's just a straight append now. - splitFlow(beforeChild, newBox, newChild, oldContinuation); return; } @@ -352,7 +336,7 @@ RenderInline* RenderInline::clone() const { RenderInline* cloneInline = new (renderArena()) RenderInline(node()); cloneInline->setStyle(style()); - cloneInline->setInRenderFlowThread(inRenderFlowThread()); + cloneInline->setFlowThreadState(flowThreadState()); return cloneInline; } @@ -364,6 +348,17 @@ void RenderInline::splitInlines(RenderBlock* fromBlock, RenderBlock* toBlock, RenderInline* cloneInline = clone(); cloneInline->setContinuation(oldCont); +#if ENABLE(FULLSCREEN_API) + // If we're splitting the inline containing the fullscreened element, + // |beforeChild| may be the renderer for the fullscreened element. However, + // that renderer is wrapped in a RenderFullScreen, so |this| is not its + // parent. Since the splitting logic expects |this| to be the parent, set + // |beforeChild| to be the RenderFullScreen. + const Element* fullScreenElement = document()->webkitCurrentFullScreenElement(); + if (fullScreenElement && beforeChild && beforeChild->node() == fullScreenElement) + beforeChild = document()->fullScreenRenderer(); +#endif + // Now take all of the children from beforeChild to the end and remove // them from |this| and place them in the clone. RenderObject* o = beforeChild; @@ -405,12 +400,6 @@ void RenderInline::splitInlines(RenderBlock* fromBlock, RenderBlock* toBlock, inlineCurr->setContinuation(cloneInline); cloneInline->setContinuation(oldCont); - // Someone may have indirectly caused a <q> to split. When this happens, the :after content - // has to move into the inline continuation. Call updateBeforeAfterContent to ensure that the inline's :after - // content gets properly destroyed. - if (document()->styleSheetCollection()->usesBeforeAfterRules()) - inlineCurr->children()->updateBeforeAfterContent(inlineCurr, AFTER); - // Now we need to take all of the children starting from the first child // *after* currChild and append them all to the clone. o = currChild->nextSibling(); @@ -762,9 +751,12 @@ const char* RenderInline::renderName() const return "RenderInline (relative positioned)"; if (isStickyPositioned()) return "RenderInline (sticky positioned)"; + // FIXME: Temporary hack while the new generated content system is being implemented. + if (isPseudoElement()) + return "RenderInline (generated)"; if (isAnonymous()) return "RenderInline (generated)"; - if (isRunIn()) + if (style() && isRunIn()) return "RenderInline (run-in)"; return "RenderInline"; } @@ -811,7 +803,7 @@ bool RenderInline::hitTestCulledInline(const HitTestRequest& request, HitTestRes // We can not use addNodeToRectBasedTestResult to determine if we fully enclose the hit-test area // because it can only handle rectangular targets. result.addNodeToRectBasedTestResult(node(), request, locationInContainer); - return regionResult.contains(enclosingIntRect(tmpLocation.boundingBox())); + return regionResult.contains(tmpLocation.boundingBox()); } return false; } @@ -1149,7 +1141,9 @@ LayoutSize RenderInline::offsetFromContainer(RenderObject* container, const Layo offset -= toRenderBox(container)->scrolledContentOffset(); if (offsetDependsOnPoint) - *offsetDependsOnPoint = container->hasColumns() || (container->isBox() && container->style()->isFlippedBlocksWritingMode()); + *offsetDependsOnPoint = container->hasColumns() + || (container->isBox() && container->style()->isFlippedBlocksWritingMode()) + || container->isRenderFlowThread(); return offset; } @@ -1184,8 +1178,6 @@ void RenderInline::mapLocalToContainer(const RenderLayerModelObject* repaintCont } LayoutSize containerOffset = offsetFromContainer(o, roundedLayoutPoint(transformState.mappedPoint())); - if (mode & SnapOffsetForTransforms) - containerOffset = roundedIntSize(containerOffset); bool preserve3D = mode & UseTransforms && (o->style()->preserves3D() || style()->preserves3D()); if (mode & UseTransforms && shouldUseTransformFromContainer(o)) { @@ -1224,8 +1216,6 @@ const RenderObject* RenderInline::pushMappingToContainer(const RenderLayerModelO bool offsetDependsOnPoint = false; LayoutSize containerOffset = offsetFromContainer(container, LayoutPoint(), &offsetDependsOnPoint); - if (geometryMap.mapCoordinatesFlags() & SnapOffsetForTransforms) - containerOffset = roundedIntSize(containerOffset); bool preserve3D = container->style()->preserves3D() || style()->preserves3D(); if (shouldUseTransformFromContainer(container)) { @@ -1400,7 +1390,7 @@ void RenderInline::imageChanged(WrappedImagePtr, const IntRect*) repaint(); } -void RenderInline::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint& additionalOffset) +void RenderInline::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint& additionalOffset, const RenderLayerModelObject* paintContainer) { AbsoluteRectsGeneratorContext context(rects, additionalOffset); generateLineBoxRects(context); @@ -1410,22 +1400,22 @@ void RenderInline::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint& FloatPoint pos(additionalOffset); // FIXME: This doesn't work correctly with transforms. if (curr->hasLayer()) - pos = curr->localToAbsolute(); + pos = curr->localToContainerPoint(FloatPoint(), paintContainer); else if (curr->isBox()) pos.move(toRenderBox(curr)->locationOffset()); - curr->addFocusRingRects(rects, flooredIntPoint(pos)); + curr->addFocusRingRects(rects, flooredIntPoint(pos), paintContainer); } } if (continuation()) { if (continuation()->isInline()) - continuation()->addFocusRingRects(rects, flooredLayoutPoint(additionalOffset + continuation()->containingBlock()->location() - containingBlock()->location())); + continuation()->addFocusRingRects(rects, flooredLayoutPoint(additionalOffset + continuation()->containingBlock()->location() - containingBlock()->location()), paintContainer); else - continuation()->addFocusRingRects(rects, flooredLayoutPoint(additionalOffset + toRenderBox(continuation())->location() - containingBlock()->location())); + continuation()->addFocusRingRects(rects, flooredLayoutPoint(additionalOffset + toRenderBox(continuation())->location() - containingBlock()->location()), paintContainer); } } -void RenderInline::paintOutline(GraphicsContext* graphicsContext, const LayoutPoint& paintOffset) +void RenderInline::paintOutline(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { if (!hasOutline()) return; @@ -1434,10 +1424,11 @@ void RenderInline::paintOutline(GraphicsContext* graphicsContext, const LayoutPo if (styleToUse->outlineStyleIsAuto() || hasOutlineAnnotation()) { if (!theme()->supportsFocusRing(styleToUse)) { // Only paint the focus ring by hand if the theme isn't able to draw the focus ring. - paintFocusRing(graphicsContext, paintOffset, styleToUse); + paintFocusRing(paintInfo, paintOffset, styleToUse); } } + GraphicsContext* graphicsContext = paintInfo.context; if (graphicsContext->paintingDisabled()) return; diff --git a/Source/WebCore/rendering/RenderInline.h b/Source/WebCore/rendering/RenderInline.h index 845904b65..6407de412 100644 --- a/Source/WebCore/rendering/RenderInline.h +++ b/Source/WebCore/rendering/RenderInline.h @@ -33,13 +33,17 @@ class Position; class RenderInline : public RenderBoxModelObject { public: - explicit RenderInline(Node*); + explicit RenderInline(Element*); + + static RenderInline* createAnonymous(Document*); RenderObject* firstChild() const { ASSERT(children() == virtualChildren()); return children()->firstChild(); } RenderObject* lastChild() const { ASSERT(children() == virtualChildren()); return children()->lastChild(); } virtual void addChild(RenderObject* newChild, RenderObject* beforeChild = 0); + Element* node() const { return toElement(RenderBoxModelObject::node()); } + virtual LayoutUnit marginLeft() const; virtual LayoutUnit marginRight() const; virtual LayoutUnit marginTop() const; @@ -77,8 +81,8 @@ public: LayoutSize offsetForInFlowPositionedInline(const RenderBox* child) const; - virtual void addFocusRingRects(Vector<IntRect>&, const LayoutPoint&); - void paintOutline(GraphicsContext*, const LayoutPoint&); + virtual void addFocusRingRects(Vector<IntRect>&, const LayoutPoint& additionalOffset, const RenderLayerModelObject* paintContainer = 0) OVERRIDE; + void paintOutline(PaintInfo&, const LayoutPoint&); using RenderBoxModelObject::continuation; using RenderBoxModelObject::setContinuation; @@ -140,7 +144,7 @@ private: virtual LayoutRect rectWithOutlineForRepaint(const RenderLayerModelObject* repaintContainer, LayoutUnit outlineWidth) const OVERRIDE; virtual void computeRectForRepaint(const RenderLayerModelObject* repaintContainer, LayoutRect&, bool fixed) const OVERRIDE; - virtual void mapLocalToContainer(const RenderLayerModelObject* repaintContainer, TransformState&, MapCoordinatesFlags = ApplyContainerFlip | SnapOffsetForTransforms, bool* wasFixed = 0) const OVERRIDE; + virtual void mapLocalToContainer(const RenderLayerModelObject* repaintContainer, TransformState&, MapCoordinatesFlags = ApplyContainerFlip, bool* wasFixed = 0) const OVERRIDE; virtual const RenderObject* pushMappingToContainer(const RenderLayerModelObject* ancestorToStopAt, RenderGeometryMap&) const OVERRIDE; virtual VisiblePosition positionForPoint(const LayoutPoint&); @@ -186,13 +190,13 @@ private: inline RenderInline* toRenderInline(RenderObject* object) { - ASSERT(!object || object->isRenderInline()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isRenderInline()); return static_cast<RenderInline*>(object); } inline const RenderInline* toRenderInline(const RenderObject* object) { - ASSERT(!object || object->isRenderInline()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isRenderInline()); return static_cast<const RenderInline*>(object); } diff --git a/Source/WebCore/rendering/RenderInputSpeech.cpp b/Source/WebCore/rendering/RenderInputSpeech.cpp index 8d42d3be8..65ee27503 100644 --- a/Source/WebCore/rendering/RenderInputSpeech.cpp +++ b/Source/WebCore/rendering/RenderInputSpeech.cpp @@ -29,9 +29,8 @@ */ #include "config.h" -#include "RenderInputSpeech.h" - #if ENABLE(INPUT_SPEECH) +#include "RenderInputSpeech.h" #include "GraphicsContext.h" #include "HTMLNames.h" diff --git a/Source/WebCore/rendering/RenderLayer.cpp b/Source/WebCore/rendering/RenderLayer.cpp index c8437895f..c378f18e6 100644 --- a/Source/WebCore/rendering/RenderLayer.cpp +++ b/Source/WebCore/rendering/RenderLayer.cpp @@ -44,22 +44,21 @@ #include "config.h" #include "RenderLayer.h" +#include "AnimationController.h" #include "ColumnInfo.h" #include "CSSPropertyNames.h" #include "Chrome.h" #include "Document.h" #include "DocumentEventQueue.h" #include "EventHandler.h" -#if ENABLE(CSS_FILTERS) -#include "FEColorMatrix.h" -#include "FEMerge.h" -#include "FilterEffectRenderer.h" -#endif +#include "FeatureObserver.h" #include "FloatConversion.h" #include "FloatPoint3D.h" #include "FloatRect.h" #include "FocusController.h" #include "Frame.h" +#include "FrameLoader.h" +#include "FrameLoaderClient.h" #include "FrameSelection.h" #include "FrameTree.h" #include "FrameView.h" @@ -68,6 +67,7 @@ #include "HTMLFrameElement.h" #include "HTMLFrameOwnerElement.h" #include "HTMLNames.h" +#include "HistogramSupport.h" #include "HitTestingTransformState.h" #include "HitTestRequest.h" #include "HitTestResult.h" @@ -93,6 +93,7 @@ #include "ScrollbarTheme.h" #include "ScrollingCoordinator.h" #include "Settings.h" +#include "ShadowRoot.h" #include "SourceGraphic.h" #include "StylePropertySet.h" #include "StyleResolver.h" @@ -100,9 +101,15 @@ #include "TransformationMatrix.h" #include "TranslateTransformOperation.h" #include <wtf/StdLibExtras.h> -#include <wtf/UnusedParam.h> #include <wtf/text/CString.h> +#if ENABLE(CSS_FILTERS) +#include "FEColorMatrix.h" +#include "FEMerge.h" +#include "FilterEffectRenderer.h" +#include "RenderLayerFilterInfo.h" +#endif + #if USE(ACCELERATED_COMPOSITING) #include "RenderLayerBacking.h" #include "RenderLayerCompositor.h" @@ -119,10 +126,6 @@ #include "ValidatedCustomFilterOperation.h" #endif -#if PLATFORM(BLACKBERRY) -#define DISABLE_ROUNDED_CORNER_CLIPPING -#endif - #define MIN_INTERSECT_FOR_REVEAL 32 using namespace std; @@ -134,17 +137,32 @@ using namespace HTMLNames; const int MinimumWidthWhileResizing = 100; const int MinimumHeightWhileResizing = 40; -bool ClipRect::intersects(const HitTestLocation& hitTestLocation) +bool ClipRect::intersects(const HitTestLocation& hitTestLocation) const { return hitTestLocation.intersects(m_rect); } +void makeMatrixRenderable(TransformationMatrix& matrix, bool has3DRendering) +{ +#if !ENABLE(3D_RENDERING) + UNUSED_PARAM(has3DRendering); + matrix.makeAffine(); +#else + if (!has3DRendering) + matrix.makeAffine(); +#endif +} + RenderLayer::RenderLayer(RenderLayerModelObject* renderer) : m_inResizeMode(false) , m_scrollDimensionsDirty(true) , m_normalFlowListDirty(true) , m_hasSelfPaintingLayerDescendant(false) , m_hasSelfPaintingLayerDescendantDirty(false) + , m_hasOutOfFlowPositionedDescendant(false) + , m_hasOutOfFlowPositionedDescendantDirty(true) + , m_needsCompositedScrolling(false) + , m_descendantsAreContiguousInStackingOrder(false) , m_isRootLayer(renderer->isRenderView()) , m_usedTransparency(false) , m_paintingInsideReflection(false) @@ -160,6 +178,7 @@ RenderLayer::RenderLayer(RenderLayerModelObject* renderer) #if USE(ACCELERATED_COMPOSITING) , m_hasCompositingDescendant(false) , m_indirectCompositingReason(NoIndirectCompositingReason) + , m_viewportConstrainedNotCompositedReason(NoNotCompositedReason) #endif , m_containsDirtyOverlayScrollbars(false) , m_updatingMarqueePosition(false) @@ -183,13 +202,14 @@ RenderLayer::RenderLayer(RenderLayerModelObject* renderer) , m_reflection(0) , m_scrollCorner(0) , m_resizer(0) + , m_enclosingPaginationLayer(0) { m_isNormalFlowOnly = shouldBeNormalFlowOnly(); m_isSelfPaintingLayer = shouldBeSelfPaintingLayer(); - // Non-stacking contexts should have empty z-order lists. As this is already the case, + // Non-stacking containers should have empty z-order lists. As this is already the case, // there is no need to dirty / recompute these lists. - m_zOrderListsDirty = isStackingContext(); + m_zOrderListsDirty = isStackingContainer(); ScrollableArea::setConstrainsScrollingToContentEdge(false); @@ -230,6 +250,11 @@ RenderLayer::~RenderLayer() destroyScrollbar(HorizontalScrollbar); destroyScrollbar(VerticalScrollbar); + if (renderer()->frame() && renderer()->frame()->page()) { + if (ScrollingCoordinator* scrollingCoordinator = renderer()->frame()->page()->scrollingCoordinator()) + scrollingCoordinator->willDestroyScrollableArea(this); + } + if (m_reflection) removeReflection(); @@ -250,6 +275,38 @@ RenderLayer::~RenderLayer() m_resizer->destroy(); } +String RenderLayer::name() const +{ + StringBuilder name; + name.append(renderer()->renderName()); + + if (Element* element = renderer()->node() && renderer()->node()->isElementNode() ? toElement(renderer()->node()) : 0) { + name.append(' '); + name.append(element->tagName()); + + if (element->hasID()) { + name.appendLiteral(" id=\'"); + name.append(element->getIdAttribute()); + name.append('\''); + } + + if (element->hasClass()) { + name.appendLiteral(" class=\'"); + for (size_t i = 0; i < element->classNames().size(); ++i) { + if (i > 0) + name.append(' '); + name.append(element->classNames()[i]); + } + name.append('\''); + } + } + + if (isReflection()) + name.appendLiteral(" (reflection)"); + + return name.toString(); +} + #if USE(ACCELERATED_COMPOSITING) RenderLayerCompositor* RenderLayer::compositor() const { @@ -303,6 +360,28 @@ bool RenderLayer::requiresFullLayerImageForFilters() const FilterEffectRenderer* filter = filterRenderer(); return filter ? filter->hasFilterThatMovesPixels() : false; } + +FilterEffectRenderer* RenderLayer::filterRenderer() const +{ + RenderLayerFilterInfo* filterInfo = this->filterInfo(); + return filterInfo ? filterInfo->renderer() : 0; +} + +RenderLayerFilterInfo* RenderLayer::filterInfo() const +{ + return hasFilterInfo() ? RenderLayerFilterInfo::filterInfoForRenderLayer(this) : 0; +} + +RenderLayerFilterInfo* RenderLayer::ensureFilterInfo() +{ + return RenderLayerFilterInfo::createFilterInfoForRenderLayerIfNeeded(this); +} + +void RenderLayer::removeFilterInfoIfNeeded() +{ + if (hasFilterInfo()) + RenderLayerFilterInfo::removeFilterInfoForRenderLayer(this); +} #endif LayoutPoint RenderLayer::computeOffsetFromRoot(bool& hasLayerOffset) const @@ -355,15 +434,17 @@ void RenderLayer::updateLayerPositions(RenderGeometryMap* geometryMap, UpdateLay // as canUseConvertToLayerCoords may be true for an ancestor layer. convertToLayerCoords(root(), offsetFromRoot); } - positionOverflowControls(toSize(roundedIntPoint(offsetFromRoot))); + positionOverflowControls(toIntSize(roundedIntPoint(offsetFromRoot))); } updateDescendantDependentFlags(); if (flags & UpdatePagination) updatePagination(); - else + else { m_isPaginated = false; + m_enclosingPaginationLayer = 0; + } if (m_hasVisibleContent) { RenderView* view = renderer()->view(); @@ -406,6 +487,11 @@ void RenderLayer::updateLayerPositions(RenderGeometryMap* geometryMap, UpdateLay flags &= ~IsCompositingUpdateRoot; #endif + if (useRegionBasedColumns() && renderer()->isInFlowRenderFlowThread()) { + updatePagination(); + flags |= UpdatePagination; + } + if (renderer()->hasColumns()) flags |= UpdatePagination; @@ -413,8 +499,14 @@ void RenderLayer::updateLayerPositions(RenderGeometryMap* geometryMap, UpdateLay child->updateLayerPositions(geometryMap, flags); #if USE(ACCELERATED_COMPOSITING) - if ((flags & UpdateCompositingLayers) && isComposited()) - backing()->updateAfterLayout(RenderLayerBacking::CompositingChildren, isUpdateRoot); + if ((flags & UpdateCompositingLayers) && isComposited()) { + RenderLayerBacking::UpdateAfterLayoutFlags updateFlags = RenderLayerBacking::CompositingChildrenOnly; + if (flags & NeedsFullRepaintInBacking) + updateFlags |= RenderLayerBacking::NeedsFullRepaint; + if (isUpdateRoot) + updateFlags |= RenderLayerBacking::IsUpdateRoot; + backing()->updateAfterLayout(updateFlags); + } #endif // With all our children positioned, now update our marquee if we need to. @@ -467,6 +559,170 @@ void RenderLayer::dirtyAncestorChainHasSelfPaintingLayerDescendantStatus() } } +bool RenderLayer::acceleratedCompositingForOverflowScrollEnabled() const +{ + return renderer()->frame() + && renderer()->frame()->page() + && renderer()->frame()->page()->settings()->acceleratedCompositingForOverflowScrollEnabled(); +} + +// If we are a stacking container, then this function will determine if our +// descendants for a contiguous block in stacking order. This is required in +// order for an element to be safely promoted to a stacking container. It is safe +// to become a stacking container if this change would not alter the stacking +// order of layers on the page. That can only happen if a non-descendant appear +// between us and our descendants in stacking order. Here's an example: +// +// this +// / | \. +// A B C +// /\ | /\. +// 0 -8 D 2 7 +// | +// 5 +// +// I've labeled our normal flow descendants A, B, C, and D, our stacking +// container descendants with their z indices, and us with 'this' (we're a +// stacking container and our zIndex doesn't matter here). These nodes appear in +// three lists: posZOrder, negZOrder, and normal flow (keep in mind that normal +// flow layers don't overlap). So if we arrange these lists in order we get our +// stacking order: +// +// [-8], [A-D], [0, 2, 5, 7]--> pos z-order. +// | | +// Neg z-order. <-+ +--> Normal flow descendants. +// +// We can then assign new, 'stacking' order indices to these elements as follows: +// +// [-8], [A-D], [0, 2, 5, 7] +// 'Stacking' indices: -1 0 1 2 3 4 +// +// Note that the normal flow descendants can share an index because they don't +// stack/overlap. Now our problem becomes very simple: a layer can safely become +// a stacking container if the stacking-order indices of it and its descendants +// appear in a contiguous block in the list of stacking indices. This problem +// can be solved very efficiently by calculating the min/max stacking indices in +// the subtree, and the number stacking container descendants. Once we have this +// information, we know that the subtree's indices form a contiguous block if: +// +// maxStackIndex - minStackIndex == numSCDescendants +// +// So for node A in the example above we would have: +// maxStackIndex = 1 +// minStackIndex = -1 +// numSCDecendants = 2 +// +// and so, +// maxStackIndex - minStackIndex == numSCDescendants +// ===> 1 - (-1) == 2 +// ===> 2 == 2 +// +// Since this is true, A can safely become a stacking container. +// Now, for node C we have: +// +// maxStackIndex = 4 +// minStackIndex = 0 <-- because C has stacking index 0. +// numSCDecendants = 2 +// +// and so, +// maxStackIndex - minStackIndex == numSCDescendants +// ===> 4 - 0 == 2 +// ===> 4 == 2 +// +// Since this is false, C cannot be safely promoted to a stacking container. This +// happened because of the elements with z-index 5 and 0. Now if 5 had been a +// child of C rather than D, and A had no child with Z index 0, we would have had: +// +// maxStackIndex = 3 +// minStackIndex = 0 <-- because C has stacking index 0. +// numSCDecendants = 3 +// +// and so, +// maxStackIndex - minStackIndex == numSCDescendants +// ===> 3 - 0 == 3 +// ===> 3 == 3 +// +// And we would conclude that C could be promoted. +void RenderLayer::updateDescendantsAreContiguousInStackingOrder() +{ + if (!isStackingContext() || !acceleratedCompositingForOverflowScrollEnabled()) + return; + + ASSERT(!m_normalFlowListDirty); + ASSERT(!m_zOrderListsDirty); + + OwnPtr<Vector<RenderLayer*> > posZOrderList; + OwnPtr<Vector<RenderLayer*> > negZOrderList; + rebuildZOrderLists(StopAtStackingContexts, posZOrderList, negZOrderList); + + // Create a reverse lookup. + HashMap<const RenderLayer*, int> lookup; + + if (negZOrderList) { + int stackingOrderIndex = -1; + size_t listSize = negZOrderList->size(); + for (size_t i = 0; i < listSize; ++i) { + RenderLayer* currentLayer = negZOrderList->at(listSize - i - 1); + if (!currentLayer->isStackingContext()) + continue; + lookup.set(currentLayer, stackingOrderIndex--); + } + } + + if (posZOrderList) { + size_t listSize = posZOrderList->size(); + int stackingOrderIndex = 1; + for (size_t i = 0; i < listSize; ++i) { + RenderLayer* currentLayer = posZOrderList->at(i); + if (!currentLayer->isStackingContext()) + continue; + lookup.set(currentLayer, stackingOrderIndex++); + } + } + + int minIndex = 0; + int maxIndex = 0; + int count = 0; + bool firstIteration = true; + updateDescendantsAreContiguousInStackingOrderRecursive(lookup, minIndex, maxIndex, count, firstIteration); +} + +void RenderLayer::updateDescendantsAreContiguousInStackingOrderRecursive(const HashMap<const RenderLayer*, int>& lookup, int& minIndex, int& maxIndex, int& count, bool firstIteration) +{ + if (isStackingContext() && !firstIteration) { + if (lookup.contains(this)) { + minIndex = std::min(minIndex, lookup.get(this)); + maxIndex = std::max(maxIndex, lookup.get(this)); + count++; + } + return; + } + + for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) { + int childMinIndex = 0; + int childMaxIndex = 0; + int childCount = 0; + child->updateDescendantsAreContiguousInStackingOrderRecursive(lookup, childMinIndex, childMaxIndex, childCount, false); + if (childCount) { + count += childCount; + minIndex = std::min(minIndex, childMinIndex); + maxIndex = std::max(maxIndex, childMaxIndex); + } + } + + if (!isStackingContext()) { + bool newValue = maxIndex - minIndex == count; +#if USE(ACCELERATED_COMPOSITING) + bool didUpdate = newValue != m_descendantsAreContiguousInStackingOrder; +#endif + m_descendantsAreContiguousInStackingOrder = newValue; +#if USE(ACCELERATED_COMPOSITING) + if (didUpdate) + updateNeedsCompositedScrolling(); +#endif + } +} + void RenderLayer::computeRepaintRects(const RenderLayerModelObject* repaintContainer, const RenderGeometryMap* geometryMap) { ASSERT(!m_visibleContentStatusDirty); @@ -572,6 +828,22 @@ void RenderLayer::updateLayerPositionsAfterScroll(RenderGeometryMap* geometryMap geometryMap->popMappingsToAncestor(parent()); } +#if USE(ACCELERATED_COMPOSITING) +void RenderLayer::positionNewlyCreatedOverflowControls() +{ + if (!backing()->hasUnpositionedOverflowControlsLayers()) + return; + + RenderGeometryMap geometryMap(UseTransforms); + RenderView* view = renderer()->view(); + if (this != view->layer() && parent()) + geometryMap.pushMappingsToAncestor(parent(), 0); + + LayoutPoint offsetFromRoot = LayoutPoint(geometryMap.absolutePoint(FloatPoint())); + positionOverflowControls(toIntSize(roundedIntPoint(offsetFromRoot))); +} +#endif + #if ENABLE(CSS_COMPOSITING) void RenderLayer::updateBlendMode() { @@ -657,6 +929,18 @@ TransformationMatrix RenderLayer::renderableTransform(PaintBehavior paintBehavio return *m_transform; } +RenderLayer* RenderLayer::enclosingOverflowClipLayer(IncludeSelfOrNot includeSelf) const +{ + const RenderLayer* layer = (includeSelf == IncludeSelf) ? this : parent(); + while (layer) { + if (layer->renderer()->hasOverflowClip()) + return const_cast<RenderLayer*>(layer); + + layer = layer->parent(); + } + return 0; +} + static bool checkContainingBlockChainForPagination(RenderLayerModelObject* renderer, RenderBox* ancestorColumnsRenderer) { RenderView* view = renderer->view(); @@ -679,30 +963,88 @@ static bool checkContainingBlockChainForPagination(RenderLayerModelObject* rende return true; } +bool RenderLayer::useRegionBasedColumns() const +{ + const Settings* settings = renderer()->document()->settings(); + return settings && settings->regionBasedColumnsEnabled(); +} + void RenderLayer::updatePagination() { m_isPaginated = false; + m_enclosingPaginationLayer = 0; + if (isComposited() || !parent()) return; // FIXME: We will have to deal with paginated compositing layers someday. // FIXME: For now the RenderView can't be paginated. Eventually printing will move to a model where it is though. - + + // The main difference between the paginated booleans for the old column code and the new column code + // is that each paginated layer has to paint on its own with the new code. There is no + // recurring into child layers. This means that the m_isPaginated bits for the new column code can't just be set on + // "roots" that get split and paint all their descendants. Instead each layer has to be checked individually and + // genuinely know if it is going to have to split itself up when painting only its contents (and not any other descendant + // layers). We track an enclosingPaginationLayer instead of using a simple bit, since we want to be able to get back + // to that layer easily. + bool regionBasedColumnsUsed = useRegionBasedColumns(); + if (regionBasedColumnsUsed && renderer()->isInFlowRenderFlowThread()) { + m_enclosingPaginationLayer = this; + return; + } + if (isNormalFlowOnly()) { - m_isPaginated = parent()->renderer()->hasColumns(); + if (regionBasedColumnsUsed) { + // Content inside a transform is not considered to be paginated, since we simply + // paint the transform multiple times in each column, so we don't have to use + // fragments for the transformed content. + m_enclosingPaginationLayer = parent()->enclosingPaginationLayer(); + if (m_enclosingPaginationLayer && m_enclosingPaginationLayer->hasTransform()) + m_enclosingPaginationLayer = 0; + } else + m_isPaginated = parent()->renderer()->hasColumns(); return; } - // If we're not normal flow, then we need to look for a multi-column object between us and our stacking context. - RenderLayer* ancestorStackingContext = stackingContext(); + // For the new columns code, we want to walk up our containing block chain looking for an enclosing layer. Once + // we find one, then we just check its pagination status. + if (regionBasedColumnsUsed) { + RenderView* view = renderer()->view(); + RenderBlock* containingBlock; + for (containingBlock = renderer()->containingBlock(); + containingBlock && containingBlock != view; + containingBlock = containingBlock->containingBlock()) { + if (containingBlock->hasLayer()) { + // Content inside a transform is not considered to be paginated, since we simply + // paint the transform multiple times in each column, so we don't have to use + // fragments for the transformed content. + m_enclosingPaginationLayer = containingBlock->layer()->enclosingPaginationLayer(); + if (m_enclosingPaginationLayer && m_enclosingPaginationLayer->hasTransform()) + m_enclosingPaginationLayer = 0; + return; + } + } + return; + } + + // If we're not normal flow, then we need to look for a multi-column object between us and our stacking container. + RenderLayer* ancestorStackingContainer = stackingContainer(); for (RenderLayer* curr = parent(); curr; curr = curr->parent()) { if (curr->renderer()->hasColumns()) { m_isPaginated = checkContainingBlockChainForPagination(renderer(), curr->renderBox()); return; } - if (curr == ancestorStackingContext) + if (curr == ancestorStackingContainer) return; } } +bool RenderLayer::canBeStackingContainer() const +{ + if (isStackingContext() || !stackingContainer()) + return true; + + return m_descendantsAreContiguousInStackingOrder; +} + void RenderLayer::setHasVisibleContent() { if (m_hasVisibleContent && !m_visibleContentStatusDirty) { @@ -715,9 +1057,9 @@ void RenderLayer::setHasVisibleContent() computeRepaintRects(renderer()->containerForRepaint()); if (!isNormalFlowOnly()) { // We don't collect invisible layers in z-order lists if we are not in compositing mode. - // As we became visible, we need to dirty our stacking contexts ancestors to be properly + // As we became visible, we need to dirty our stacking containers ancestors to be properly // collected. FIXME: When compositing, we could skip this dirtying phase. - for (RenderLayer* sc = stackingContext(); sc; sc = sc->stackingContext()) { + for (RenderLayer* sc = stackingContainer(); sc; sc = sc->stackingContainer()) { sc->dirtyZOrderLists(); if (sc->hasVisibleContent()) break; @@ -756,25 +1098,51 @@ void RenderLayer::setAncestorChainHasVisibleDescendant() } } -void RenderLayer::updateDescendantDependentFlags() +void RenderLayer::updateDescendantDependentFlags(HashSet<const RenderObject*>* outOfFlowDescendantContainingBlocks) { - if (m_visibleDescendantStatusDirty || m_hasSelfPaintingLayerDescendantDirty) { + if (m_visibleDescendantStatusDirty || m_hasSelfPaintingLayerDescendantDirty || m_hasOutOfFlowPositionedDescendantDirty) { m_hasVisibleDescendant = false; m_hasSelfPaintingLayerDescendant = false; + m_hasOutOfFlowPositionedDescendant = false; + + HashSet<const RenderObject*> childOutOfFlowDescendantContainingBlocks; for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) { - child->updateDescendantDependentFlags(); + childOutOfFlowDescendantContainingBlocks.clear(); + child->updateDescendantDependentFlags(&childOutOfFlowDescendantContainingBlocks); + + bool childIsOutOfFlowPositioned = child->renderer() && child->renderer()->isOutOfFlowPositioned(); + if (childIsOutOfFlowPositioned) + childOutOfFlowDescendantContainingBlocks.add(child->renderer()->containingBlock()); + + if (outOfFlowDescendantContainingBlocks) { + HashSet<const RenderObject*>::const_iterator it = childOutOfFlowDescendantContainingBlocks.begin(); + for (; it != childOutOfFlowDescendantContainingBlocks.end(); ++it) + outOfFlowDescendantContainingBlocks->add(*it); + } bool hasVisibleDescendant = child->m_hasVisibleContent || child->m_hasVisibleDescendant; bool hasSelfPaintingLayerDescendant = child->isSelfPaintingLayer() || child->hasSelfPaintingLayerDescendant(); + bool hasOutOfFlowPositionedDescendant = !childOutOfFlowDescendantContainingBlocks.isEmpty(); m_hasVisibleDescendant |= hasVisibleDescendant; m_hasSelfPaintingLayerDescendant |= hasSelfPaintingLayerDescendant; + m_hasOutOfFlowPositionedDescendant |= hasOutOfFlowPositionedDescendant; - if (m_hasVisibleDescendant && m_hasSelfPaintingLayerDescendant) + if (m_hasVisibleDescendant && m_hasSelfPaintingLayerDescendant && m_hasOutOfFlowPositionedDescendant) break; } + + if (outOfFlowDescendantContainingBlocks && renderer()) + outOfFlowDescendantContainingBlocks->remove(renderer()); + m_visibleDescendantStatusDirty = false; m_hasSelfPaintingLayerDescendantDirty = false; + +#if USE(ACCELERATED_COMPOSITING) + if (m_hasOutOfFlowPositionedDescendantDirty) + updateNeedsCompositedScrolling(); +#endif + m_hasOutOfFlowPositionedDescendantDirty = false; } if (m_visibleContentStatusDirty) { @@ -810,15 +1178,15 @@ void RenderLayer::updateDescendantDependentFlags() void RenderLayer::dirty3DTransformedDescendantStatus() { - RenderLayer* curr = stackingContext(); + RenderLayer* curr = stackingContainer(); if (curr) curr->m_3DTransformedDescendantStatusDirty = true; // This propagates up through preserve-3d hierarchies to the enclosing flattening layer. - // Note that preserves3D() creates stacking context, so we can just run up the stacking contexts. + // Note that preserves3D() creates stacking context, so we can just run up the stacking containers. while (curr && curr->preserves3D()) { curr->m_3DTransformedDescendantStatusDirty = true; - curr = curr->stackingContext(); + curr = curr->stackingContainer(); } } @@ -893,28 +1261,20 @@ bool RenderLayer::updateLayerPosition() RenderLayer* positionedParent = enclosingPositionedAncestor(); // For positioned layers, we subtract out the enclosing positioned layer's scroll offset. - LayoutSize offset = positionedParent->scrolledContentOffset(); - localPoint -= offset; + if (positionedParent->renderer()->hasOverflowClip()) { + LayoutSize offset = positionedParent->scrolledContentOffset(); + localPoint -= offset; + } if (renderer()->isOutOfFlowPositioned() && positionedParent->renderer()->isInFlowPositioned() && positionedParent->renderer()->isRenderInline()) { LayoutSize offset = toRenderInline(positionedParent->renderer())->offsetForInFlowPositionedInline(toRenderBox(renderer())); localPoint += offset; } } else if (parent()) { - if (isComposited()) { - // FIXME: Composited layers ignore pagination, so about the best we can do is make sure they're offset into the appropriate column. - // They won't split across columns properly. - LayoutSize columnOffset; - if (!parent()->renderer()->hasColumns() && parent()->renderer()->isRoot() && renderer()->view()->hasColumns()) - renderer()->view()->adjustForColumns(columnOffset, localPoint); - else - parent()->renderer()->adjustForColumns(columnOffset, localPoint); - - localPoint += columnOffset; + if (parent()->renderer()->hasOverflowClip()) { + IntSize scrollOffset = parent()->scrolledContentOffset(); + localPoint -= scrollOffset; } - - IntSize scrollOffset = parent()->scrolledContentOffset(); - localPoint -= scrollOffset; } bool positionOrOffsetChanged = false; @@ -977,11 +1337,13 @@ FloatPoint RenderLayer::perspectiveOrigin() const floatValueForLength(style->perspectiveOriginY(), borderBox.height())); } -RenderLayer* RenderLayer::stackingContext() const +RenderLayer* RenderLayer::stackingContainer() const { RenderLayer* layer = parent(); - while (layer && !layer->isRootLayer() && !layer->renderer()->isRoot() && layer->renderer()->style()->hasAutoZIndex()) + while (layer && !layer->isStackingContainer()) layer = layer->parent(); + + ASSERT(!layer || layer->isStackingContainer()); return layer; } @@ -1020,6 +1382,14 @@ IntRect RenderLayer::scrollableAreaBoundingBox() const return renderer()->absoluteBoundingBoxRect(); } +bool RenderLayer::scrollbarAnimationsAreSuppressed() const +{ + RenderView* view = renderer()->view(); + if (!view) + return false; + return view->frameView()->scrollbarAnimationsAreSuppressed(); +} + RenderLayer* RenderLayer::enclosingTransformedAncestor() const { RenderLayer* curr = parent(); @@ -1031,7 +1401,7 @@ RenderLayer* RenderLayer::enclosingTransformedAncestor() const static inline const RenderLayer* compositingContainer(const RenderLayer* layer) { - return layer->isNormalFlowOnly() ? layer->parent() : layer->stackingContext(); + return layer->isNormalFlowOnly() ? layer->parent() : layer->stackingContainer(); } inline bool RenderLayer::shouldRepaintAfterLayout() const @@ -1050,9 +1420,9 @@ inline bool RenderLayer::shouldRepaintAfterLayout() const } #if USE(ACCELERATED_COMPOSITING) -RenderLayer* RenderLayer::enclosingCompositingLayer(bool includeSelf) const +RenderLayer* RenderLayer::enclosingCompositingLayer(IncludeSelfOrNot includeSelf) const { - if (includeSelf && isComposited()) + if (includeSelf == IncludeSelf && isComposited()) return const_cast<RenderLayer*>(this); for (const RenderLayer* curr = compositingContainer(this); curr; curr = compositingContainer(curr)) { @@ -1063,9 +1433,9 @@ RenderLayer* RenderLayer::enclosingCompositingLayer(bool includeSelf) const return 0; } -RenderLayer* RenderLayer::enclosingCompositingLayerForRepaint(bool includeSelf) const +RenderLayer* RenderLayer::enclosingCompositingLayerForRepaint(IncludeSelfOrNot includeSelf) const { - if (includeSelf && isComposited() && !backing()->paintsIntoCompositedAncestor()) + if (includeSelf == IncludeSelf && isComposited() && !backing()->paintsIntoCompositedAncestor()) return const_cast<RenderLayer*>(this); for (const RenderLayer* curr = compositingContainer(this); curr; curr = compositingContainer(curr)) { @@ -1078,9 +1448,9 @@ RenderLayer* RenderLayer::enclosingCompositingLayerForRepaint(bool includeSelf) #endif #if ENABLE(CSS_FILTERS) -RenderLayer* RenderLayer::enclosingFilterLayer(bool includeSelf) const +RenderLayer* RenderLayer::enclosingFilterLayer(IncludeSelfOrNot includeSelf) const { - const RenderLayer* curr = includeSelf ? this : parent(); + const RenderLayer* curr = (includeSelf == IncludeSelf) ? this : parent(); for (; curr; curr = curr->parent()) { if (curr->requiresFullLayerImageForFilters()) return const_cast<RenderLayer*>(curr); @@ -1104,18 +1474,7 @@ void RenderLayer::setFilterBackendNeedsRepaintingInRect(const LayoutRect& rect, return; LayoutRect rectForRepaint = rect; - -#if ENABLE(CSS_FILTERS) - if (renderer()->style()->hasFilterOutsets()) { - int topOutset; - int rightOutset; - int bottomOutset; - int leftOutset; - renderer()->style()->getFilterOutsets(topOutset, rightOutset, bottomOutset, leftOutset); - rectForRepaint.move(-leftOutset, -topOutset); - rectForRepaint.expand(leftOutset + rightOutset, topOutset + bottomOutset); - } -#endif + renderer()->style()->filterOutsets().expandRect(rectForRepaint); RenderLayerFilterInfo* filterInfo = this->filterInfo(); ASSERT(filterInfo); @@ -1202,7 +1561,7 @@ RenderLayer* RenderLayer::clippingRootForPainting() const LayoutPoint RenderLayer::absoluteToContents(const LayoutPoint& absolutePoint) const { // We don't use convertToLayerCoords because it doesn't know about transforms - return roundedLayoutPoint(renderer()->absoluteToLocal(absolutePoint, UseTransforms | SnapOffsetForTransforms)); + return roundedLayoutPoint(renderer()->absoluteToLocal(absolutePoint, UseTransforms)); } bool RenderLayer::cannotBlitToWindow() const @@ -1237,18 +1596,29 @@ RenderLayer* RenderLayer::transparentPaintingAncestor() return 0; } -static LayoutRect transparencyClipBox(const RenderLayer*, const RenderLayer* rootLayer, PaintBehavior); +enum TransparencyClipBoxBehavior { + PaintingTransparencyClipBox, + HitTestingTransparencyClipBox +}; + +enum TransparencyClipBoxMode { + DescendantsOfTransparencyClipBox, + RootOfTransparencyClipBox +}; -static void expandClipRectForDescendantsAndReflection(LayoutRect& clipRect, const RenderLayer* layer, const RenderLayer* rootLayer, PaintBehavior paintBehavior) +static LayoutRect transparencyClipBox(const RenderLayer*, const RenderLayer* rootLayer, TransparencyClipBoxBehavior, TransparencyClipBoxMode, PaintBehavior = 0); + +static void expandClipRectForDescendantsAndReflection(LayoutRect& clipRect, const RenderLayer* layer, const RenderLayer* rootLayer, + TransparencyClipBoxBehavior transparencyBehavior, PaintBehavior paintBehavior) { // 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 (!layer->renderer()->hasMask()) { // 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. + // a stacking container. This means we can just walk the layer tree directly. for (RenderLayer* curr = layer->firstChild(); curr; curr = curr->nextSibling()) { if (!layer->reflection() || layer->reflectionLayer() != curr) - clipRect.unite(transparencyClipBox(curr, rootLayer, paintBehavior)); + clipRect.unite(transparencyClipBox(curr, rootLayer, transparencyBehavior, DescendantsOfTransparencyClipBox, paintBehavior)); } } @@ -1265,35 +1635,60 @@ static void expandClipRectForDescendantsAndReflection(LayoutRect& clipRect, cons } } -static LayoutRect transparencyClipBox(const RenderLayer* layer, const RenderLayer* rootLayer, PaintBehavior paintBehavior) +static LayoutRect transparencyClipBox(const RenderLayer* layer, const RenderLayer* rootLayer, TransparencyClipBoxBehavior transparencyBehavior, + TransparencyClipBoxMode transparencyMode, PaintBehavior paintBehavior) { // 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. - if (rootLayer != layer && layer->paintsWithTransform(paintBehavior)) { + if (rootLayer != layer && ((transparencyBehavior == PaintingTransparencyClipBox && layer->paintsWithTransform(paintBehavior)) + || (transparencyBehavior == HitTestingTransparencyClipBox && layer->hasTransform()))) { // 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. + const RenderLayer* paginationLayer = transparencyMode == DescendantsOfTransparencyClipBox ? layer->enclosingPaginationLayer() : 0; + const RenderLayer* rootLayerForTransform = paginationLayer ? paginationLayer : rootLayer; LayoutPoint delta; - layer->convertToLayerCoords(rootLayer, delta); + layer->convertToLayerCoords(rootLayerForTransform, delta); TransformationMatrix transform; transform.translate(delta.x(), delta.y()); transform = transform * *layer->transform(); + // We don't use fragment boxes when collecting a transformed layer's bounding box, since it always + // paints unfragmented. LayoutRect clipRect = layer->boundingBox(layer); - expandClipRectForDescendantsAndReflection(clipRect, layer, layer, paintBehavior); - return transform.mapRect(clipRect); + expandClipRectForDescendantsAndReflection(clipRect, layer, layer, transparencyBehavior, paintBehavior); +#if ENABLE(CSS_FILTERS) + layer->renderer()->style()->filterOutsets().expandRect(clipRect); +#endif + LayoutRect result = transform.mapRect(clipRect); + if (!paginationLayer) + return result; + + // We have to break up the transformed extent across our columns. + // Split our box up into the actual fragment boxes that render in the columns/pages and unite those together to + // get our true bounding box. + RenderFlowThread* enclosingFlowThread = toRenderFlowThread(paginationLayer->renderer()); + result = enclosingFlowThread->fragmentsBoundingBox(result); + + LayoutPoint rootLayerDelta; + paginationLayer->convertToLayerCoords(rootLayer, rootLayerDelta); + result.moveBy(rootLayerDelta); + return result; } - LayoutRect clipRect = layer->boundingBox(rootLayer); - expandClipRectForDescendantsAndReflection(clipRect, layer, rootLayer, paintBehavior); + LayoutRect clipRect = layer->boundingBox(rootLayer, RenderLayer::UseFragmentBoxes); + expandClipRectForDescendantsAndReflection(clipRect, layer, rootLayer, transparencyBehavior, paintBehavior); +#if ENABLE(CSS_FILTERS) + layer->renderer()->style()->filterOutsets().expandRect(clipRect); +#endif return clipRect; } LayoutRect RenderLayer::paintingExtent(const RenderLayer* rootLayer, const LayoutRect& paintDirtyRect, PaintBehavior paintBehavior) { - return intersection(transparencyClipBox(this, rootLayer, paintBehavior), paintDirtyRect); + return intersection(transparencyClipBox(this, rootLayer, PaintingTransparencyClipBox, RootOfTransparencyClipBox, paintBehavior), paintDirtyRect); } void RenderLayer::beginTransparencyLayers(GraphicsContext* context, const RenderLayer* rootLayer, const LayoutRect& paintDirtyRect, PaintBehavior paintBehavior) @@ -1360,10 +1755,10 @@ void RenderLayer::addChild(RenderLayer* child, RenderLayer* beforeChild) dirtyNormalFlowList(); if (!child->isNormalFlowOnly() || 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 + // Dirty the z-order list in which we are contained. The stackingContainer() 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->dirtyStackingContainerZOrderLists(); } child->updateDescendantDependentFlags(); @@ -1373,6 +1768,9 @@ void RenderLayer::addChild(RenderLayer* child, RenderLayer* beforeChild) if (child->isSelfPaintingLayer() || child->hasSelfPaintingLayerDescendant()) setAncestorChainHasSelfPaintingLayerDescendant(); + if (child->renderer() && (child->renderer()->isOutOfFlowPositioned() || child->hasOutOfFlowPositionedDescendant())) + setAncestorChainHasOutOfFlowPositionedDescendant(child->renderer()->containingBlock()); + #if USE(ACCELERATED_COMPOSITING) compositor()->layerWasAdded(this, child); #endif @@ -1401,10 +1799,13 @@ RenderLayer* RenderLayer::removeChild(RenderLayer* oldChild) if (!oldChild->isNormalFlowOnly() || 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(); + // from the main layer tree, so we need to null-check the |stackingContainer| value. + oldChild->dirtyStackingContainerZOrderLists(); } + if ((oldChild->renderer() && oldChild->renderer()->isOutOfFlowPositioned()) || oldChild->hasOutOfFlowPositionedDescendant()) + dirtyAncestorChainHasOutOfFlowPositionedDescendantStatus(); + oldChild->setPreviousSibling(0); oldChild->setNextSibling(0); oldChild->setParent(0); @@ -1479,34 +1880,39 @@ void RenderLayer::insertOnlyThisLayer() clearClipRectsIncludingDescendants(); } -void RenderLayer::convertToPixelSnappedLayerCoords(const RenderLayer* ancestorLayer, IntPoint& roundedLocation) const +void RenderLayer::convertToPixelSnappedLayerCoords(const RenderLayer* ancestorLayer, IntPoint& roundedLocation, ColumnOffsetAdjustment adjustForColumns) const { LayoutPoint location = roundedLocation; - convertToLayerCoords(ancestorLayer, location); + convertToLayerCoords(ancestorLayer, location, adjustForColumns); roundedLocation = roundedIntPoint(location); } -void RenderLayer::convertToPixelSnappedLayerCoords(const RenderLayer* ancestorLayer, IntRect& roundedRect) const +void RenderLayer::convertToPixelSnappedLayerCoords(const RenderLayer* ancestorLayer, IntRect& roundedRect, ColumnOffsetAdjustment adjustForColumns) const { LayoutRect rect = roundedRect; - convertToLayerCoords(ancestorLayer, rect); + convertToLayerCoords(ancestorLayer, rect, adjustForColumns); roundedRect = pixelSnappedIntRect(rect); } // Returns the layer reached on the walk up towards the ancestor. -static inline const RenderLayer* accumulateOffsetTowardsAncestor(const RenderLayer* layer, const RenderLayer* ancestorLayer, LayoutPoint& location) +static inline const RenderLayer* accumulateOffsetTowardsAncestor(const RenderLayer* layer, const RenderLayer* ancestorLayer, LayoutPoint& location, RenderLayer::ColumnOffsetAdjustment adjustForColumns) { ASSERT(ancestorLayer != layer); const RenderLayerModelObject* renderer = layer->renderer(); EPosition position = renderer->style()->position(); + // FIXME: Special casing RenderFlowThread so much for fixed positioning here is not great. + RenderFlowThread* fixedFlowThreadContainer = position == FixedPosition ? renderer->flowThreadContainingBlock() : 0; + if (fixedFlowThreadContainer && !fixedFlowThreadContainer->isOutOfFlowPositioned()) + fixedFlowThreadContainer = 0; + // FIXME: Positioning of out-of-flow(fixed, absolute) elements collected in a RenderFlowThread // may need to be revisited in a future patch. // If the fixed renderer is inside a RenderFlowThread, we should not compute location using localToAbsolute, // since localToAbsolute maps the coordinates from named flow to regions coordinates and regions can be // positioned in a completely different place in the viewport (RenderView). - if (position == FixedPosition && !renderer->inRenderFlowThread() && (!ancestorLayer || ancestorLayer == renderer->view()->layer())) { + if (position == FixedPosition && !fixedFlowThreadContainer && (!ancestorLayer || ancestorLayer == renderer->view()->layer())) { // If the fixed layer's container is the root, just add in the offset of the view. We can obtain this by calling // localToAbsolute() on the RenderView. FloatPoint absPos = renderer->localToAbsolute(FloatPoint(), IsFixed); @@ -1517,7 +1923,7 @@ static inline const RenderLayer* accumulateOffsetTowardsAncestor(const RenderLay // For the fixed positioned elements inside a render flow thread, we should also skip the code path below // Otherwise, for the case of ancestorLayer == rootLayer and fixed positioned element child of a transformed // element in render flow thread, we will hit the fixed positioned container before hitting the ancestor layer. - if (position == FixedPosition && !renderer->inRenderFlowThread()) { + if (position == FixedPosition && !fixedFlowThreadContainer) { // For a fixed layers, we need to walk up to the root to see if there's a fixed position container // (e.g. a transformed layer). It's an error to call convertToLayerCoords() across a layer with a transform, // so we should always find the ancestor at or before we find the fixed position container. @@ -1570,7 +1976,7 @@ static inline const RenderLayer* accumulateOffsetTowardsAncestor(const RenderLay // We should not reach RenderView layer past the RenderFlowThread layer for any // children of the RenderFlowThread. - if (renderer->inRenderFlowThread() && !renderer->isRenderFlowThread()) + if (renderer->flowThreadContainingBlock() && !layer->isOutOfFlowRenderFlowThread()) ASSERT(parentLayer != renderer->view()->layer()); if (foundAncestorFirst) { @@ -1594,37 +2000,83 @@ static inline const RenderLayer* accumulateOffsetTowardsAncestor(const RenderLay return 0; location += toSize(layer->location()); + + if (adjustForColumns == RenderLayer::AdjustForColumns) { + if (RenderLayer* parentLayer = layer->parent()) { + LayoutSize layerColumnOffset; + parentLayer->renderer()->adjustForColumns(layerColumnOffset, location); + location += layerColumnOffset; + } + } + return parentLayer; } -void RenderLayer::convertToLayerCoords(const RenderLayer* ancestorLayer, LayoutPoint& location) const +void RenderLayer::convertToLayerCoords(const RenderLayer* ancestorLayer, LayoutPoint& location, ColumnOffsetAdjustment adjustForColumns) const { if (ancestorLayer == this) return; const RenderLayer* currLayer = this; while (currLayer && currLayer != ancestorLayer) - currLayer = accumulateOffsetTowardsAncestor(currLayer, ancestorLayer, location); + currLayer = accumulateOffsetTowardsAncestor(currLayer, ancestorLayer, location, adjustForColumns); } -void RenderLayer::convertToLayerCoords(const RenderLayer* ancestorLayer, LayoutRect& rect) const +void RenderLayer::convertToLayerCoords(const RenderLayer* ancestorLayer, LayoutRect& rect, ColumnOffsetAdjustment adjustForColumns) const { LayoutPoint delta; - convertToLayerCoords(ancestorLayer, delta); + convertToLayerCoords(ancestorLayer, delta, adjustForColumns); rect.move(-delta.x(), -delta.y()); } #if USE(ACCELERATED_COMPOSITING) bool RenderLayer::usesCompositedScrolling() const { - if (!scrollsOverflow() || !allowsScrolling()) - return false; + return isComposited() && backing()->scrollingLayer(); +} + +bool RenderLayer::needsCompositedScrolling() const +{ + return m_needsCompositedScrolling; +} + +void RenderLayer::updateNeedsCompositedScrolling() +{ + bool oldNeedsCompositedScrolling = m_needsCompositedScrolling; + + FrameView* frameView = renderer()->view()->frameView(); + if (!frameView || !frameView->containsScrollableArea(this)) + m_needsCompositedScrolling = false; + else { + bool forceUseCompositedScrolling = acceleratedCompositingForOverflowScrollEnabled() + && canBeStackingContainer() + && !hasOutOfFlowPositionedDescendant(); #if ENABLE(ACCELERATED_OVERFLOW_SCROLLING) - return renderer()->style()->useTouchOverflowScrolling(); + m_needsCompositedScrolling = forceUseCompositedScrolling || renderer()->style()->useTouchOverflowScrolling(); #else - return false; + m_needsCompositedScrolling = forceUseCompositedScrolling; #endif + // We gather a boolean value for use with Google UMA histograms to + // quantify the actual effects of a set of patches attempting to + // relax composited scrolling requirements, thereby increasing the + // number of composited overflow divs. + if (acceleratedCompositingForOverflowScrollEnabled()) + HistogramSupport::histogramEnumeration("Renderer.NeedsCompositedScrolling", m_needsCompositedScrolling, 2); + } + + if (oldNeedsCompositedScrolling != m_needsCompositedScrolling) { + updateSelfPaintingLayer(); + if (isStackingContainer()) + dirtyZOrderLists(); + else + clearZOrderLists(); + + dirtyStackingContainerZOrderLists(); + + compositor()->setShouldReevaluateCompositingAfterLayout(); + compositor()->setCompositingLayersNeedRebuild(); + } } #endif @@ -1653,16 +2105,16 @@ void RenderLayer::panScrollFromPoint(const IntPoint& sourcePoint) if (!frame) return; - IntPoint currentMousePosition = frame->eventHandler()->currentMousePosition(); + IntPoint lastKnownMousePosition = frame->eventHandler()->lastKnownMousePosition(); - // 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 + // We need to check if the last known 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; + if (lastKnownMousePosition.x() < 0 || lastKnownMousePosition.y() < 0) + lastKnownMousePosition = previousMousePosition; else - previousMousePosition = currentMousePosition; + previousMousePosition = lastKnownMousePosition; - IntSize delta = currentMousePosition - sourcePoint; + IntSize delta = lastKnownMousePosition - sourcePoint; if (abs(delta.width()) <= ScrollView::noPanScrollRadius) // at the center we let the space for the icon delta.setWidth(0); @@ -1699,6 +2151,7 @@ void RenderLayer::scrollByRecursively(const IntSize& delta, ScrollOffsetClamping // If we are here, we were called on a renderer that can be programmatically scrolled, but doesn't // have an overflow clip. Which means that it is a document node that can be scrolled. renderer()->view()->frameView()->scrollBy(delta); + // FIXME: If we didn't scroll the whole way, do we want to try looking at the frames ownerElement? // https://bugs.webkit.org/show_bug.cgi?id=28237 } @@ -1721,7 +2174,7 @@ void RenderLayer::scrollToOffset(const IntSize& scrollOffset, ScrollOffsetClampi { IntSize newScrollOffset = clamp == ScrollOffsetClamped ? clampScrollOffset(scrollOffset) : scrollOffset; if (newScrollOffset != this->scrollOffset()) - scrollToOffsetWithoutAnimation(toPoint(newScrollOffset)); + scrollToOffsetWithoutAnimation(IntPoint(newScrollOffset)); } void RenderLayer::scrollTo(int x, int y) @@ -1784,12 +2237,19 @@ void RenderLayer::scrollTo(int x, int y) FloatQuad quadForFakeMouseMoveEvent = FloatQuad(m_repaintRect); if (repaintContainer) - quadForFakeMouseMoveEvent = repaintContainer->localToAbsoluteQuad(quadForFakeMouseMoveEvent, SnapOffsetForTransforms); + quadForFakeMouseMoveEvent = repaintContainer->localToAbsoluteQuad(quadForFakeMouseMoveEvent); frame->eventHandler()->dispatchFakeMouseMoveEventSoonInQuad(quadForFakeMouseMoveEvent); } + bool requiresRepaint = true; + +#if USE(ACCELERATED_COMPOSITING) + if (compositor()->inCompositingMode() && usesCompositedScrolling()) + requiresRepaint = false; +#endif + // Just schedule a full repaint of our object. - if (view && !usesCompositedScrolling()) + if (view && requiresRepaint) renderer()->repaintUsingContainer(repaintContainer, pixelSnappedIntRect(m_repaintRect)); // Schedule the scroll DOM event. @@ -1797,12 +2257,14 @@ void RenderLayer::scrollTo(int x, int y) renderer()->node()->document()->eventQueue()->enqueueOrDispatchScrollEvent(renderer()->node(), DocumentEventQueue::ScrollEventElementTarget); InspectorInstrumentation::didScrollLayer(frame); + if (scrollsOverflow()) + frame->loader()->client()->didChangeScrollOffset(); } -static inline bool frameElementAndViewPermitScroll(HTMLFrameElement* frameElement, FrameView* frameView) +static inline bool frameElementAndViewPermitScroll(HTMLFrameElementBase* frameElementBase, FrameView* frameView) { // If scrollbars aren't explicitly forbidden, permit scrolling. - if (frameElement && frameElement->scrollingMode() != ScrollbarAlwaysOff) + if (frameElementBase && frameElementBase->scrollingMode() != ScrollbarAlwaysOff) return true; // If scrollbars are forbidden, user initiated scrolls should obviously be ignored. @@ -1836,21 +2298,17 @@ void RenderLayer::scrollRectToVisible(const LayoutRect& rect, const ScrollAlignm // This will prevent us from revealing text hidden by the slider in Safari RSS. RenderBox* box = renderBox(); ASSERT(box); - FloatPoint absPos = box->localToAbsolute(); - absPos.move(box->borderLeft(), box->borderTop()); + LayoutRect localExposeRect(box->absoluteToLocalQuad(FloatQuad(FloatRect(rect)), UseTransforms).boundingBox()); + LayoutRect layerBounds(0, 0, box->clientWidth(), box->clientHeight()); + LayoutRect r = getRectToExpose(layerBounds, layerBounds, localExposeRect, alignX, alignY); - LayoutRect layerBounds = LayoutRect(absPos.x() + scrollXOffset(), absPos.y() + scrollYOffset(), box->clientWidth(), box->clientHeight()); - LayoutRect exposeRect = LayoutRect(rect.x() + scrollXOffset(), rect.y() + scrollYOffset(), rect.width(), rect.height()); - LayoutRect r = getRectToExpose(layerBounds, exposeRect, alignX, alignY); - - int roundedAdjustedX = roundToInt(r.x() - absPos.x()); - int roundedAdjustedY = roundToInt(r.y() - absPos.y()); - IntSize clampedScrollOffset = clampScrollOffset(IntSize(roundedAdjustedX, roundedAdjustedY)); + IntSize clampedScrollOffset = clampScrollOffset(scrollOffset() + toIntSize(roundedIntRect(r).location())); if (clampedScrollOffset != scrollOffset()) { IntSize oldScrollOffset = scrollOffset(); scrollToOffset(clampedScrollOffset); IntSize scrollOffsetDifference = scrollOffset() - oldScrollOffset; - newRect.move(-scrollOffsetDifference); + localExposeRect.move(-scrollOffsetDifference); + newRect = LayoutRect(box->localToAbsoluteQuad(FloatQuad(FloatRect(localExposeRect)), UseTransforms).boundingBox()); } } else if (!parentLayer && renderer()->isBox() && renderBox()->canBeProgramaticallyScrolled()) { if (frameView) { @@ -1859,14 +2317,14 @@ void RenderLayer::scrollRectToVisible(const LayoutRect& rect, const ScrollAlignm ownerElement = renderer()->document()->ownerElement(); if (ownerElement && ownerElement->renderer()) { - HTMLFrameElement* frameElement = 0; + HTMLFrameElementBase* frameElementBase = 0; if (ownerElement->hasTagName(frameTag) || ownerElement->hasTagName(iframeTag)) - frameElement = static_cast<HTMLFrameElement*>(ownerElement); + frameElementBase = toHTMLFrameElementBase(ownerElement); - if (frameElementAndViewPermitScroll(frameElement, frameView)) { + if (frameElementAndViewPermitScroll(frameElementBase, frameView)) { LayoutRect viewRect = frameView->visibleContentRect(); - LayoutRect exposeRect = getRectToExpose(viewRect, rect, alignX, alignY); + LayoutRect exposeRect = getRectToExpose(viewRect, viewRect, rect, alignX, alignY); int xOffset = roundToInt(exposeRect.x()); int yOffset = roundToInt(exposeRect.y()); @@ -1877,6 +2335,8 @@ void RenderLayer::scrollRectToVisible(const LayoutRect& rect, const ScrollAlignm frameView->setScrollPosition(IntPoint(xOffset, yOffset)); if (frameView->safeToPropagateScrollToParent()) { parentLayer = ownerElement->renderer()->enclosingLayer(); + // FIXME: This doesn't correctly convert the rect to + // absolute coordinates in the parent. newRect.setX(rect.x() - frameView->scrollX() + frameView->x()); newRect.setY(rect.y() - frameView->scrollY() + frameView->y()); } else @@ -1884,7 +2344,11 @@ void RenderLayer::scrollRectToVisible(const LayoutRect& rect, const ScrollAlignm } } else { LayoutRect viewRect = frameView->visibleContentRect(); - LayoutRect r = getRectToExpose(viewRect, rect, alignX, alignY); + LayoutRect visibleRectRelativeToDocument = viewRect; + IntSize scrollOffsetRelativeToDocument = frameView->scrollOffsetRelativeToDocument(); + visibleRectRelativeToDocument.setLocation(IntPoint(scrollOffsetRelativeToDocument.width(), scrollOffsetRelativeToDocument.height())); + + LayoutRect r = getRectToExpose(viewRect, visibleRectRelativeToDocument, rect, alignX, alignY); frameView->setScrollPosition(roundedIntPoint(r.location())); @@ -1895,7 +2359,7 @@ void RenderLayer::scrollRectToVisible(const LayoutRect& rect, const ScrollAlignm // The canAutoscroll function in EventHandler also knows about this. if (Frame* frame = frameView->frame()) { if (Page* page = frame->page()) - page->chrome()->scrollRectIntoView(pixelSnappedIntRect(rect)); + page->chrome().scrollRectIntoView(pixelSnappedIntRect(rect)); } } } @@ -1912,21 +2376,19 @@ void RenderLayer::updateCompositingLayersAfterScroll() { #if USE(ACCELERATED_COMPOSITING) if (compositor()->inCompositingMode()) { - // Our stacking context is guaranteed to contain all of our descendants that may need + // Our stacking container is guaranteed to contain all of our descendants that may need // repositioning, so update compositing layers from there. - if (RenderLayer* compositingAncestor = stackingContext()->enclosingCompositingLayer()) { - if (compositor()->compositingConsultsOverlap()) + if (RenderLayer* compositingAncestor = stackingContainer()->enclosingCompositingLayer()) { + if (usesCompositedScrolling() && !hasOutOfFlowPositionedDescendant()) + compositor()->updateCompositingLayers(CompositingUpdateOnCompositedScroll, compositingAncestor); + else compositor()->updateCompositingLayers(CompositingUpdateOnScroll, compositingAncestor); - else { - bool isUpdateRoot = true; - compositingAncestor->backing()->updateAfterLayout(RenderLayerBacking::AllDescendants, isUpdateRoot); - } } } #endif } -LayoutRect RenderLayer::getRectToExpose(const LayoutRect &visibleRect, const LayoutRect &exposeRect, const ScrollAlignment& alignX, const ScrollAlignment& alignY) +LayoutRect RenderLayer::getRectToExpose(const LayoutRect &visibleRect, const LayoutRect &visibleRectRelativeToDocument, const LayoutRect &exposeRect, const ScrollAlignment& alignX, const ScrollAlignment& alignY) { // Determine the appropriate X behavior. ScrollBehavior scrollX; @@ -1966,7 +2428,7 @@ LayoutRect RenderLayer::getRectToExpose(const LayoutRect &visibleRect, const Lay // Determine the appropriate Y behavior. ScrollBehavior scrollY; LayoutRect exposeRectY(visibleRect.x(), exposeRect.y(), visibleRect.width(), exposeRect.height()); - LayoutUnit intersectHeight = intersection(visibleRect, exposeRectY).height(); + LayoutUnit intersectHeight = intersection(visibleRectRelativeToDocument, exposeRectY).height(); if (intersectHeight == exposeRect.height()) // If the rectangle is fully visible, use the specified visible behavior. scrollY = ScrollAlignment::getVisibleBehavior(alignY); @@ -1999,7 +2461,7 @@ LayoutRect RenderLayer::getRectToExpose(const LayoutRect &visibleRect, const Lay return LayoutRect(LayoutPoint(x, y), visibleRect.size()); } -void RenderLayer::autoscroll() +void RenderLayer::autoscroll(const IntPoint& position) { Frame* frame = renderer()->frame(); if (!frame) @@ -2009,28 +2471,30 @@ void RenderLayer::autoscroll() if (!frameView) return; -#if ENABLE(DRAG_SUPPORT) - frame->eventHandler()->updateSelectionForMouseDrag(); -#endif - - IntPoint currentDocumentPosition = frameView->windowToContents(frame->eventHandler()->currentMousePosition()); + IntPoint currentDocumentPosition = frameView->windowToContents(position); scrollRectToVisible(LayoutRect(currentDocumentPosition, LayoutSize(1, 1)), ScrollAlignment::alignToEdgeIfNeeded, ScrollAlignment::alignToEdgeIfNeeded); } +bool RenderLayer::canResize() const +{ + if (!renderer()) + return false; + // We need a special case for <iframe> because they never have + // hasOverflowClip(). However, they do "implicitly" clip their contents, so + // we want to allow resizing them also. + return (renderer()->hasOverflowClip() || renderer()->isRenderIFrame()) && renderer()->style()->resize() != RESIZE_NONE; +} + void RenderLayer::resize(const PlatformMouseEvent& evt, const LayoutSize& oldOffset) { // FIXME: This should be possible on generated content but is not right now. - if (!inResizeMode() || !renderer()->hasOverflowClip() || !renderer()->node()) + if (!inResizeMode() || !canResize() || !renderer()->node()) return; ASSERT(renderer()->node()->isElementNode()); - Element* element = static_cast<Element*>(renderer()->node()); + Element* element = toElement(renderer()->node()); RenderBox* renderer = toRenderBox(element->renderer()); - EResize resize = renderer->style()->resize(); - if (resize == RESIZE_NONE) - return; - Document* document = element->document(); if (!document->frame()->eventHandler()->mousePressed()) return; @@ -2053,10 +2517,11 @@ void RenderLayer::resize(const PlatformMouseEvent& evt, const LayoutSize& oldOff LayoutSize difference = (currentSize + newOffset - adjustedOldOffset).expandedTo(minimumSize) - currentSize; - ASSERT(element->isStyledElement()); + ASSERT_WITH_SECURITY_IMPLICATION(element->isStyledElement()); StyledElement* styledElement = static_cast<StyledElement*>(element); bool isBoxSizingBorder = renderer->style()->boxSizing() == BORDER_BOX; + EResize resize = renderer->style()->resize(); if (resize != RESIZE_VERTICAL && difference.width()) { if (element->isFormControlElement()) { // Make implicit margins from the theme explicit (see <http://bugs.webkit.org/show_bug.cgi?id=9547>). @@ -2106,25 +2571,25 @@ int RenderLayer::scrollPosition(Scrollbar* scrollbar) const IntPoint RenderLayer::scrollPosition() const { - return scrollOrigin() + m_scrollOffset; + return IntPoint(m_scrollOffset); } IntPoint RenderLayer::minimumScrollPosition() const { - return scrollOrigin(); + return -scrollOrigin(); } IntPoint RenderLayer::maximumScrollPosition() const { // FIXME: m_scrollSize may not be up-to-date if m_scrollDimensionsDirty is true. - return scrollOrigin() + roundedIntSize(m_scrollSize) - visibleContentRect(true).size(); + return -scrollOrigin() + roundedIntSize(m_scrollSize) - visibleContentRect(IncludeScrollbars).size(); } -IntRect RenderLayer::visibleContentRect(bool includeScrollbars) const +IntRect RenderLayer::visibleContentRect(VisibleContentRectIncludesScrollbars scrollbarInclusion) const { int verticalScrollbarWidth = 0; int horizontalScrollbarHeight = 0; - if (includeScrollbars) { + if (scrollbarInclusion == IncludeScrollbars) { verticalScrollbarWidth = (verticalScrollbar() && !verticalScrollbar()->isOverlayScrollbar()) ? verticalScrollbar()->width() : 0; horizontalScrollbarHeight = (horizontalScrollbar() && !horizontalScrollbar()->isOverlayScrollbar()) ? horizontalScrollbar()->height() : 0; } @@ -2292,9 +2757,14 @@ bool RenderLayer::scrollbarsCanBeActive() const return view->frameView()->scrollbarsCanBeActive(); } -IntPoint RenderLayer::currentMousePosition() const +IntPoint RenderLayer::lastKnownMousePosition() const { - return renderer()->frame() ? renderer()->frame()->eventHandler()->currentMousePosition() : IntPoint(); + return renderer()->frame() ? renderer()->frame()->eventHandler()->lastKnownMousePosition() : IntPoint(); +} + +bool RenderLayer::isHandlingWheelEvent() const +{ + return renderer()->frame() ? renderer()->frame()->eventHandler()->isHandlingWheelEvent() : false; } IntRect RenderLayer::rectForHorizontalScrollbar(const IntRect& borderBoxRect) const @@ -2382,7 +2852,9 @@ void RenderLayer::invalidateScrollbarRect(Scrollbar* scrollbar, const IntRect& r scrollRect.move(verticalScrollbarStart(0, box->width()), box->borderTop()); else scrollRect.move(horizontalScrollbarStart(0), box->height() - box->borderBottom() - scrollbar->height()); - renderer()->repaintRectangle(scrollRect); + LayoutRect repaintRect = scrollRect; + renderBox()->flipForWritingMode(repaintRect); + renderer()->repaintRectangle(repaintRect); } void RenderLayer::invalidateScrollCornerRect(const IntRect& rect) @@ -2399,19 +2871,28 @@ void RenderLayer::invalidateScrollCornerRect(const IntRect& rect) m_resizer->repaintRectangle(rect); } +static inline RenderObject* rendererForScrollbar(RenderObject* renderer) +{ + if (Node* node = renderer->node()) { + if (ShadowRoot* shadowRoot = node->containingShadowRoot()) { + if (shadowRoot->type() == ShadowRoot::UserAgentShadowRoot) + return shadowRoot->host()->renderer(); + } + } + + return renderer; +} + PassRefPtr<Scrollbar> RenderLayer::createScrollbar(ScrollbarOrientation orientation) { RefPtr<Scrollbar> widget; - RenderObject* actualRenderer = renderer()->node() ? renderer()->node()->shadowAncestorNode()->renderer() : renderer(); + RenderObject* actualRenderer = rendererForScrollbar(renderer()); bool hasCustomScrollbarStyle = actualRenderer->isBox() && actualRenderer->style()->hasPseudoStyle(SCROLLBAR); if (hasCustomScrollbarStyle) widget = RenderScrollbar::createCustomScrollbar(this, orientation, actualRenderer->node()); else { widget = Scrollbar::createNativeScrollbar(this, orientation, RegularScrollbar); - if (orientation == HorizontalScrollbar) - didAddHorizontalScrollbar(widget.get()); - else - didAddVerticalScrollbar(widget.get()); + didAddScrollbar(widget.get(), orientation); } renderer()->document()->view()->addChild(widget.get()); return widget.release(); @@ -2423,12 +2904,8 @@ void RenderLayer::destroyScrollbar(ScrollbarOrientation orientation) if (!scrollbar) return; - if (!scrollbar->isCustomScrollbar()) { - if (orientation == HorizontalScrollbar) - willRemoveHorizontalScrollbar(scrollbar.get()); - else - willRemoveVerticalScrollbar(scrollbar.get()); - } + if (!scrollbar->isCustomScrollbar()) + willRemoveScrollbar(scrollbar.get(), orientation); scrollbar->removeFromParent(); scrollbar->disconnectFromScrollableArea(); @@ -2443,11 +2920,6 @@ bool RenderLayer::scrollsOverflow() const return toRenderBox(renderer())->scrollsOverflow(); } -bool RenderLayer::allowsScrolling() const -{ - return (m_hBar && m_hBar->enabled()) || (m_vBar && m_vBar->enabled()); -} - void RenderLayer::setHasHorizontalScrollbar(bool hasScrollbar) { if (hasScrollbar == hasHorizontalScrollbar()) @@ -2506,14 +2978,14 @@ ScrollableArea* RenderLayer::enclosingScrollableArea() const int RenderLayer::verticalScrollbarWidth(OverlayScrollbarSizeRelevancy relevancy) const { - if (!m_vBar || (m_vBar->isOverlayScrollbar() && relevancy == IgnoreOverlayScrollbarSize)) + if (!m_vBar || (m_vBar->isOverlayScrollbar() && (relevancy == IgnoreOverlayScrollbarSize || !m_vBar->shouldParticipateInHitTesting()))) return 0; return m_vBar->width(); } int RenderLayer::horizontalScrollbarHeight(OverlayScrollbarSizeRelevancy relevancy) const { - if (!m_hBar || (m_hBar->isOverlayScrollbar() && relevancy == IgnoreOverlayScrollbarSize)) + if (!m_hBar || (m_hBar->isOverlayScrollbar() && (relevancy == IgnoreOverlayScrollbarSize || !m_hBar->shouldParticipateInHitTesting()))) return 0; return m_hBar->height(); } @@ -2525,7 +2997,7 @@ IntSize RenderLayer::offsetFromResizeCorner(const IntPoint& absolutePoint) const IntSize elementSize = size(); if (renderer()->style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft()) elementSize.setWidth(0); - IntPoint resizerPoint = toPoint(elementSize); + IntPoint resizerPoint = IntPoint(elementSize); IntPoint localPoint = roundedIntPoint(absoluteToContents(absolutePoint)); return localPoint - resizerPoint; } @@ -2537,7 +3009,7 @@ bool RenderLayer::hasOverflowControls() const void RenderLayer::positionOverflowControls(const IntSize& offsetFromRoot) { - if (!m_hBar && !m_vBar && (!renderer()->hasOverflowClip() || renderer()->style()->resize() == RESIZE_NONE)) + if (!m_hBar && !m_vBar && !canResize()) return; RenderBox* box = renderBox(); @@ -2633,6 +3105,16 @@ void RenderLayer::computeScrollDimensions() setScrollOrigin(IntPoint(-scrollableLeftOverflow, -scrollableTopOverflow)); } +bool RenderLayer::hasScrollableHorizontalOverflow() const +{ + return hasHorizontalOverflow() && renderBox()->scrollsOverflowX(); +} + +bool RenderLayer::hasScrollableVerticalOverflow() const +{ + return hasVerticalOverflow() && renderBox()->scrollsOverflowY(); +} + bool RenderLayer::hasHorizontalOverflow() const { ASSERT(!m_scrollDimensionsDirty); @@ -2715,7 +3197,7 @@ void RenderLayer::updateScrollbarsAfterLayout() m_vBar->setProportion(clientHeight, m_scrollSize.height()); } - updateScrollableAreaSet((hasHorizontalOverflow || hasVerticalOverflow) && scrollsOverflow() && allowsScrolling()); + updateScrollableAreaSet(hasScrollableHorizontalOverflow() || hasScrollableVerticalOverflow()); } void RenderLayer::updateScrollInfoAfterLayout() @@ -2740,7 +3222,7 @@ void RenderLayer::updateScrollInfoAfterLayout() updateScrollbarsAfterLayout(); if (originalScrollOffset != scrollOffset()) - scrollToOffsetWithoutAnimation(toPoint(scrollOffset())); + scrollToOffsetWithoutAnimation(IntPoint(scrollOffset())); #if USE(ACCELERATED_COMPOSITING) // Composited scrolling may need to be enabled or disabled if the amount of overflow changed. @@ -2816,7 +3298,7 @@ void RenderLayer::paintOverflowControls(GraphicsContext* context, const IntPoint // 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(toSize(adjustedPaintOffset)); + positionOverflowControls(toIntSize(adjustedPaintOffset)); // Now that we're sure the scrollbars are in the right place, paint them. if (m_hBar @@ -2941,7 +3423,7 @@ void RenderLayer::paintResizer(GraphicsContext* context, const IntPoint& paintOf bool RenderLayer::isPointInResizeControl(const IntPoint& absolutePoint) const { - if (!renderer()->hasOverflowClip() || renderer()->style()->resize() == RESIZE_NONE) + if (!canResize()) return false; RenderBox* box = renderBox(); @@ -2952,10 +3434,10 @@ bool RenderLayer::isPointInResizeControl(const IntPoint& absolutePoint) const IntRect localBounds(0, 0, box->pixelSnappedWidth(), box->pixelSnappedHeight()); return resizerCornerRect(this, localBounds).contains(localPoint); } - + bool RenderLayer::hitTestOverflowControls(HitTestResult& result, const IntPoint& localPoint) { - if (!m_hBar && !m_vBar && (!renderer()->hasOverflowClip() || renderer()->style()->resize() == RESIZE_NONE)) + if (!m_hBar && !m_vBar && !canResize()) return false; RenderBox* box = renderBox(); @@ -2970,6 +3452,8 @@ bool RenderLayer::hitTestOverflowControls(HitTestResult& result, const IntPoint& int resizeControlSize = max(resizeControlRect.height(), 0); + // FIXME: We should hit test the m_scrollCorner and pass it back through the result. + if (m_vBar && m_vBar->shouldParticipateInHitTesting()) { LayoutRect vBarRect(verticalScrollbarStart(0, box->width()), box->borderTop(), @@ -3001,11 +3485,11 @@ bool RenderLayer::scroll(ScrollDirection direction, ScrollGranularity granularit return ScrollableArea::scroll(direction, granularity, multiplier); } -void RenderLayer::paint(GraphicsContext* context, const LayoutRect& damageRect, PaintBehavior paintBehavior, RenderObject* paintingRoot, RenderRegion* region, PaintLayerFlags paintFlags) +void RenderLayer::paint(GraphicsContext* context, const LayoutRect& damageRect, PaintBehavior paintBehavior, RenderObject* subtreePaintRoot, RenderRegion* region, PaintLayerFlags paintFlags) { OverlapTestRequestMap overlapTestRequests; - LayerPaintingInfo paintingInfo(this, enclosingIntRect(damageRect), paintBehavior, LayoutSize(), paintingRoot, region, &overlapTestRequests); + LayerPaintingInfo paintingInfo(this, enclosingIntRect(damageRect), paintBehavior, LayoutSize(), subtreePaintRoot, region, &overlapTestRequests); paintLayer(context, paintingInfo, paintFlags); OverlapTestRequestMap::iterator end = overlapTestRequests.end(); @@ -3013,18 +3497,17 @@ void RenderLayer::paint(GraphicsContext* context, const LayoutRect& damageRect, it->key->setOverlapTestResult(false); } -void RenderLayer::paintOverlayScrollbars(GraphicsContext* context, const LayoutRect& damageRect, PaintBehavior paintBehavior, RenderObject* paintingRoot) +void RenderLayer::paintOverlayScrollbars(GraphicsContext* context, const LayoutRect& damageRect, PaintBehavior paintBehavior, RenderObject* subtreePaintRoot) { if (!m_containsDirtyOverlayScrollbars) return; - LayerPaintingInfo paintingInfo(this, enclosingIntRect(damageRect), paintBehavior, LayoutSize(), paintingRoot); + LayerPaintingInfo paintingInfo(this, enclosingIntRect(damageRect), paintBehavior, LayoutSize(), subtreePaintRoot); paintLayer(context, paintingInfo, PaintLayerPaintingOverlayScrollbars); m_containsDirtyOverlayScrollbars = false; } -#ifndef DISABLE_ROUNDED_CORNER_CLIPPING static bool inContainingBlockChain(RenderLayer* startLayer, RenderLayer* endLayer) { if (startLayer == endLayer) @@ -3038,20 +3521,18 @@ static bool inContainingBlockChain(RenderLayer* startLayer, RenderLayer* endLaye return false; } -#endif void RenderLayer::clipToRect(RenderLayer* rootLayer, GraphicsContext* context, const LayoutRect& paintDirtyRect, const ClipRect& clipRect, BorderRadiusClippingRule rule) { - if (clipRect.rect() == paintDirtyRect) - return; - context->save(); - context->clip(pixelSnappedIntRect(clipRect.rect())); - + if (clipRect.rect() != paintDirtyRect) { + context->save(); + context->clip(pixelSnappedIntRect(clipRect.rect())); + } + if (!clipRect.hasRadius()) return; -#ifndef DISABLE_ROUNDED_CORNER_CLIPPING // If the clip rect has been tainted by a border radius, then we have to walk up our layer chain applying the clips from // any layers with overflow. The condition for being able to apply these clips is that the overflow object be in our // containing block chain so we check that also. @@ -3059,13 +3540,12 @@ void RenderLayer::clipToRect(RenderLayer* rootLayer, GraphicsContext* context, c if (layer->renderer()->hasOverflowClip() && layer->renderer()->style()->hasBorderRadius() && inContainingBlockChain(this, layer)) { LayoutPoint delta; layer->convertToLayerCoords(rootLayer, delta); - context->addRoundedRectClip(layer->renderer()->style()->getRoundedInnerBorderFor(LayoutRect(delta, layer->size()))); + context->clipRoundedRect(layer->renderer()->style()->getRoundedInnerBorderFor(LayoutRect(delta, layer->size()))); } if (layer == rootLayer) break; } -#endif } void RenderLayer::restoreClip(GraphicsContext* context, const LayoutRect& paintDirtyRect, const ClipRect& clipRect) @@ -3114,6 +3594,12 @@ static inline bool shouldSuppressPaintingLayer(RenderLayer* layer) return false; } +#if USE(ACCELERATED_COMPOSITING) +static bool paintForFixedRootBackground(const RenderLayer* layer, RenderLayer::PaintLayerFlags paintFlags) +{ + return layer->renderer()->isRoot() && (paintFlags & RenderLayer::PaintLayerPaintingRootBackgroundOnly); +} +#endif void RenderLayer::paintLayer(GraphicsContext* context, const LayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags) { @@ -3125,10 +3611,15 @@ void RenderLayer::paintLayer(GraphicsContext* context, const LayerPaintingInfo& paintFlags |= PaintLayerTemporaryClipRects; else if (!backing()->paintsIntoWindow() && !backing()->paintsIntoCompositedAncestor() - && !shouldDoSoftwarePaint(this, paintFlags & PaintLayerPaintingReflection)) { + && !shouldDoSoftwarePaint(this, paintFlags & PaintLayerPaintingReflection) + && !paintForFixedRootBackground(this, paintFlags)) { // If this RenderLayer should paint into its backing, that will be done via RenderLayerBacking::paintIntoLayer(). return; } + } else if (viewportConstrainedNotCompositedReason() == NotCompositedForBoundsOutOfView) { + // Don't paint out-of-view viewport constrained layers (when doing prepainting) because they will never be visible + // unless their position or viewport size is changed. + return; } #endif @@ -3162,6 +3653,11 @@ void RenderLayer::paintLayer(GraphicsContext* context, const LayerPaintingInfo& beginTransparencyLayers(context, paintingInfo.rootLayer, paintingInfo.paintDirtyRect, paintingInfo.paintBehavior); } + if (enclosingPaginationLayer()) { + paintTransformedLayerIntoFragments(context, paintingInfo, paintFlags); + return; + } + // Make sure the parent's clip rects have been calculated. ClipRect clipRect = paintingInfo.paintDirtyRect; if (parent()) { @@ -3174,25 +3670,7 @@ void RenderLayer::paintLayer(GraphicsContext* context, const LayerPaintingInfo& parent()->clipToRect(paintingInfo.rootLayer, context, paintingInfo.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, but preserving - // the accumulated error for sub-pixel layout. - LayoutPoint delta; - convertToLayerCoords(paintingInfo.rootLayer, delta); - TransformationMatrix transform(layerTransform); - IntPoint roundedDelta = roundedIntPoint(delta); - transform.translateRight(roundedDelta.x(), roundedDelta.y()); - LayoutSize adjustedSubPixelAccumulation = paintingInfo.subPixelAccumulation + (delta - roundedDelta); - - // Apply the transform. - { - GraphicsContextStateSaver stateSaver(*context); - context->concatCTM(transform.toAffineTransform()); - - // Now do a paint with the root layer shifted to be us. - LayerPaintingInfo transformedPaintingInfo(this, enclosingIntRect(transform.inverse().mapRect(paintingInfo.paintDirtyRect)), paintingInfo.paintBehavior, adjustedSubPixelAccumulation, paintingInfo.paintingRoot, paintingInfo.region, paintingInfo.overlapTestRequests); - paintLayerContentsAndReflection(context, transformedPaintingInfo, paintFlags); - } + paintLayerByApplyingTransform(context, paintingInfo, paintFlags); // Restore the clip. if (parent()) @@ -3222,31 +3700,11 @@ void RenderLayer::paintLayerContentsAndReflection(GraphicsContext* context, cons paintLayerContents(context, paintingInfo, localPaintFlags); } -void RenderLayer::paintLayerContents(GraphicsContext* context, const LayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags) +bool RenderLayer::setupFontSubpixelQuantization(GraphicsContext* context, bool& didQuantizeFonts) { - ASSERT(isSelfPaintingLayer() || hasSelfPaintingLayerDescendant()); - - PaintLayerFlags localPaintFlags = paintFlags & ~(PaintLayerAppliedTransform); - bool haveTransparency = localPaintFlags & PaintLayerHaveTransparency; - bool isSelfPaintingLayer = this->isSelfPaintingLayer(); - bool isPaintingOverlayScrollbars = paintFlags & PaintLayerPaintingOverlayScrollbars; - // Outline always needs to be painted even if we have no visible content. - bool shouldPaintOutline = isSelfPaintingLayer && !isPaintingOverlayScrollbars; - bool shouldPaintContent = m_hasVisibleContent && isSelfPaintingLayer && !isPaintingOverlayScrollbars; - - bool useClipRect = true; - GraphicsContext* transparencyLayerContext = context; - - // Ensure our lists are up-to-date. - updateLayerListsIfNeeded(); - - LayoutPoint offsetFromRoot; - convertToLayerCoords(paintingInfo.rootLayer, offsetFromRoot); - - IntRect rootRelativeBounds; - bool rootRelativeBoundsComputed = false; + if (context->paintingDisabled()) + return false; - bool didQuantizeFonts = true; bool scrollingOnMainThread = true; Frame* frame = renderer()->frame(); #if ENABLE(THREADED_SCROLLING) @@ -3260,180 +3718,232 @@ void RenderLayer::paintLayerContents(GraphicsContext* context, const LayerPainti // FIXME: We shouldn't have to disable subpixel quantization for overflow clips or subframes once we scroll those // things on the scrolling thread. - bool needToAdjustSubpixelQuantization = scrollingOnMainThread || (renderer()->hasOverflowClip() && !usesCompositedScrolling()) || (frame && frame->ownerElement()); - if (needToAdjustSubpixelQuantization) { + bool contentsScrollByPainting = (renderer()->hasOverflowClip() && !usesCompositedScrolling()) || (frame && frame->ownerElement()); + if (scrollingOnMainThread || contentsScrollByPainting) { didQuantizeFonts = context->shouldSubpixelQuantizeFonts(); context->setShouldSubpixelQuantizeFonts(false); + return true; } + return false; +} + +bool RenderLayer::setupClipPath(GraphicsContext* context, const LayerPaintingInfo& paintingInfo, const LayoutPoint& offsetFromRoot, LayoutRect& rootRelativeBounds, bool& rootRelativeBoundsComputed) +{ + if (!renderer()->hasClipPath() || context->paintingDisabled()) + return false; - // Apply clip-path to context. - bool hasClipPath = false; RenderStyle* style = renderer()->style(); - if (renderer()->hasClipPath() && !context->paintingDisabled() && style) { - ASSERT(style->clipPath()); - if (style->clipPath()->getOperationType() == ClipPathOperation::SHAPE) { - hasClipPath = true; - context->save(); - ShapeClipPathOperation* clipPath = static_cast<ShapeClipPathOperation*>(style->clipPath()); + ASSERT(style->clipPath()); + if (style->clipPath()->getOperationType() == ClipPathOperation::SHAPE) { + ShapeClipPathOperation* clipPath = static_cast<ShapeClipPathOperation*>(style->clipPath()); + + if (!rootRelativeBoundsComputed) { + rootRelativeBounds = calculateLayerBounds(paintingInfo.rootLayer, &offsetFromRoot, 0); + rootRelativeBoundsComputed = true; + } + + context->save(); + context->clipPath(clipPath->path(rootRelativeBounds), clipPath->windRule()); + return true; + } + +#if ENABLE(SVG) + if (style->clipPath()->getOperationType() == ClipPathOperation::REFERENCE) { + ReferenceClipPathOperation* referenceClipPathOperation = static_cast<ReferenceClipPathOperation*>(style->clipPath()); + Document* document = renderer()->document(); + Element* element = document ? document->getElementById(referenceClipPathOperation->fragment()) : 0; + if (element && element->hasTagName(SVGNames::clipPathTag) && element->renderer()) { if (!rootRelativeBoundsComputed) { rootRelativeBounds = calculateLayerBounds(paintingInfo.rootLayer, &offsetFromRoot, 0); rootRelativeBoundsComputed = true; } - context->clipPath(clipPath->path(rootRelativeBounds), clipPath->windRule()); - } -#if ENABLE(SVG) - else if (style->clipPath()->getOperationType() == ClipPathOperation::REFERENCE) { - ReferenceClipPathOperation* referenceClipPathOperation = static_cast<ReferenceClipPathOperation*>(style->clipPath()); - Document* document = renderer()->document(); - // FIXME: It doesn't work with forward or external SVG references (https://bugs.webkit.org/show_bug.cgi?id=90405) - Element* element = document ? document->getElementById(referenceClipPathOperation->fragment()) : 0; - if (element && element->hasTagName(SVGNames::clipPathTag) && element->renderer()) { - if (!rootRelativeBoundsComputed) { - rootRelativeBounds = calculateLayerBounds(paintingInfo.rootLayer, &offsetFromRoot, 0); - rootRelativeBoundsComputed = true; - } - - static_cast<RenderSVGResourceClipper*>(element->renderer())->applyClippingToContext(renderer(), rootRelativeBounds, paintingInfo.paintDirtyRect, context); - } + // FIXME: This should use a safer cast such as toRenderSVGResourceContainer(). + // FIXME: Should this do a context->save() and return true so we restore the context? + static_cast<RenderSVGResourceClipper*>(element->renderer())->applyClippingToContext(renderer(), rootRelativeBounds, paintingInfo.paintDirtyRect, context); } -#endif } +#endif + + return false; +} - LayerPaintingInfo localPaintingInfo(paintingInfo); #if ENABLE(CSS_FILTERS) - FilterEffectRendererHelper filterPainter(filterRenderer() && paintsWithFilters()); - if (filterPainter.haveFilterEffect() && !context->paintingDisabled()) { - RenderLayerFilterInfo* filterInfo = this->filterInfo(); - ASSERT(filterInfo); - LayoutRect filterRepaintRect = filterInfo->dirtySourceRect(); - filterRepaintRect.move(offsetFromRoot.x(), offsetFromRoot.y()); +PassOwnPtr<FilterEffectRendererHelper> RenderLayer::setupFilters(GraphicsContext* context, LayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags, const LayoutPoint& offsetFromRoot, LayoutRect& rootRelativeBounds, bool& rootRelativeBoundsComputed) +{ + if (context->paintingDisabled()) + return nullptr; - if (!rootRelativeBoundsComputed) { - rootRelativeBounds = calculateLayerBounds(paintingInfo.rootLayer, &offsetFromRoot, 0); - rootRelativeBoundsComputed = true; - } + if (paintFlags & PaintLayerPaintingOverlayScrollbars) + return nullptr; - if (filterPainter.prepareFilterEffect(this, rootRelativeBounds, paintingInfo.paintDirtyRect, filterRepaintRect)) { - // Now we know for sure, that the source image will be updated, so we can revert our tracking repaint rect back to zero. - filterInfo->resetDirtySourceRect(); - - // Rewire the old context to a memory buffer, so that we can capture the contents of the layer. - // NOTE: We saved the old context in the "transparencyLayerContext" local variable, to be able to start a transparency layer - // on the original context and avoid duplicating "beginFilterEffect" after each transpareny layer call. Also, note that - // beginTransparencyLayers will only create a single lazy transparency layer, even though it is called twice in this method. - context = filterPainter.beginFilterEffect(context); - - // Check that we didn't fail to allocate the graphics context for the offscreen buffer. - if (filterPainter.hasStartedFilterEffect()) { - localPaintingInfo.paintDirtyRect = filterPainter.repaintRect(); - // If the filter needs the full source image, we need to avoid using the clip rectangles. - // Otherwise, if for example this layer has overflow:hidden, a drop shadow will not compute correctly. - // Note that we will still apply the clipping on the final rendering of the filter. - useClipRect = !filterRenderer()->hasFilterThatMovesPixels(); - } - } + bool hasPaintedFilter = filterRenderer() && paintsWithFilters(); + if (!hasPaintedFilter) + return nullptr; + + OwnPtr<FilterEffectRendererHelper> filterPainter = adoptPtr(new FilterEffectRendererHelper(hasPaintedFilter)); + if (!filterPainter->haveFilterEffect()) + return nullptr; + + RenderLayerFilterInfo* filterInfo = this->filterInfo(); + ASSERT(filterInfo); + LayoutRect filterRepaintRect = filterInfo->dirtySourceRect(); + filterRepaintRect.move(offsetFromRoot.x(), offsetFromRoot.y()); + + if (!rootRelativeBoundsComputed) { + rootRelativeBounds = calculateLayerBounds(paintingInfo.rootLayer, &offsetFromRoot, 0); + rootRelativeBoundsComputed = true; + } + + if (filterPainter->prepareFilterEffect(this, rootRelativeBounds, paintingInfo.paintDirtyRect, filterRepaintRect)) { + // Now we know for sure, that the source image will be updated, so we can revert our tracking repaint rect back to zero. + filterInfo->resetDirtySourceRect(); + + if (!filterPainter->beginFilterEffect()) + return nullptr; + + // Check that we didn't fail to allocate the graphics context for the offscreen buffer. + ASSERT(filterPainter->hasStartedFilterEffect()); + + paintingInfo.paintDirtyRect = filterPainter->repaintRect(); + // If the filter needs the full source image, we need to avoid using the clip rectangles. + // Otherwise, if for example this layer has overflow:hidden, a drop shadow will not compute correctly. + // Note that we will still apply the clipping on the final rendering of the filter. + paintingInfo.clipToDirtyRect = !filterRenderer()->hasFilterThatMovesPixels(); + return filterPainter.release(); } + return nullptr; +} + +GraphicsContext* RenderLayer::applyFilters(FilterEffectRendererHelper* filterPainter, GraphicsContext* originalContext, LayerPaintingInfo& paintingInfo, LayerFragments& layerFragments) +{ + ASSERT(filterPainter->hasStartedFilterEffect()); + // Apply the correct clipping (ie. overflow: hidden). + // FIXME: It is incorrect to just clip to the damageRect here once multiple fragments are involved. + ClipRect backgroundRect = layerFragments.isEmpty() ? ClipRect() : layerFragments[0].backgroundRect; + clipToRect(paintingInfo.rootLayer, originalContext, paintingInfo.paintDirtyRect, backgroundRect); + filterPainter->applyFilterEffect(originalContext); + restoreClip(originalContext, paintingInfo.paintDirtyRect, backgroundRect); + return originalContext; +} #endif - // Calculate the clip rects we should use only when we need them. - LayoutRect layerBounds; - ClipRect damageRect, clipRectToApply, outlineRect; - LayoutPoint paintOffset; +void RenderLayer::paintLayerContents(GraphicsContext* context, const LayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags) +{ + ASSERT(isSelfPaintingLayer() || hasSelfPaintingLayerDescendant()); - if (shouldPaintContent || shouldPaintOutline || isPaintingOverlayScrollbars) { - ClipRectsContext clipRectsContext(localPaintingInfo.rootLayer, localPaintingInfo.region, (localPaintFlags & PaintLayerTemporaryClipRects) ? TemporaryClipRects : PaintingClipRects, IgnoreOverlayScrollbarSize, localPaintFlags & PaintLayerPaintingOverflowContents ? IgnoreOverflowClip : RespectOverflowClip); - calculateRects(clipRectsContext, localPaintingInfo.paintDirtyRect, layerBounds, damageRect, clipRectToApply, outlineRect, &offsetFromRoot); - paintOffset = toPoint(layerBounds.location() - renderBoxLocation() + localPaintingInfo.subPixelAccumulation); - if (this == localPaintingInfo.rootLayer) - paintOffset = roundedIntPoint(paintOffset); + PaintLayerFlags localPaintFlags = paintFlags & ~(PaintLayerAppliedTransform); + bool haveTransparency = localPaintFlags & PaintLayerHaveTransparency; + bool isSelfPaintingLayer = this->isSelfPaintingLayer(); + bool isPaintingOverlayScrollbars = paintFlags & PaintLayerPaintingOverlayScrollbars; + bool isPaintingScrollingContent = paintFlags & PaintLayerPaintingCompositingScrollingPhase; + bool isPaintingCompositedForeground = paintFlags & PaintLayerPaintingCompositingForegroundPhase; + bool isPaintingCompositedBackground = paintFlags & PaintLayerPaintingCompositingBackgroundPhase; + bool isPaintingOverflowContents = paintFlags & PaintLayerPaintingOverflowContents; + // Outline always needs to be painted even if we have no visible content. Also, + // the outline is painted in the background phase during composited scrolling. + // If it were painted in the foreground phase, it would move with the scrolled + // content. When not composited scrolling, the outline is painted in the + // foreground phase. Since scrolled contents are moved by repainting in this + // case, the outline won't get 'dragged along'. + bool shouldPaintOutline = isSelfPaintingLayer && !isPaintingOverlayScrollbars + && ((isPaintingScrollingContent && isPaintingCompositedBackground) + || (!isPaintingScrollingContent && isPaintingCompositedForeground)); + bool shouldPaintContent = m_hasVisibleContent && isSelfPaintingLayer && !isPaintingOverlayScrollbars; + + if (localPaintFlags & PaintLayerPaintingRootBackgroundOnly && !renderer()->isRenderView() && !renderer()->isRoot()) + return; + + // Ensure our lists are up-to-date. + updateLayerListsIfNeeded(); + + LayoutPoint offsetFromRoot; + convertToLayerCoords(paintingInfo.rootLayer, offsetFromRoot); + + LayoutRect rootRelativeBounds; + bool rootRelativeBoundsComputed = false; + + // FIXME: We shouldn't have to disable subpixel quantization for overflow clips or subframes once we scroll those + // things on the scrolling thread. + bool didQuantizeFonts = true; + bool needToAdjustSubpixelQuantization = setupFontSubpixelQuantization(context, didQuantizeFonts); + + // Apply clip-path to context. + bool hasClipPath = setupClipPath(context, paintingInfo, offsetFromRoot, rootRelativeBounds, rootRelativeBoundsComputed); + + LayerPaintingInfo localPaintingInfo(paintingInfo); + + GraphicsContext* transparencyLayerContext = context; +#if ENABLE(CSS_FILTERS) + OwnPtr<FilterEffectRendererHelper> filterPainter = setupFilters(context, localPaintingInfo, paintFlags, offsetFromRoot, rootRelativeBounds, rootRelativeBoundsComputed); + if (filterPainter) { + context = filterPainter->filterContext(); + if (context != transparencyLayerContext && haveTransparency) { + // If we have a filter and transparency, we have to eagerly start a transparency layer here, rather than risk a child layer lazily starts one with the wrong context. + beginTransparencyLayers(transparencyLayerContext, localPaintingInfo.rootLayer, paintingInfo.paintDirtyRect, localPaintingInfo.paintBehavior); + } } +#endif - bool forceBlackText = localPaintingInfo.paintBehavior & PaintBehaviorForceBlackText; - bool selectionOnly = localPaintingInfo.paintBehavior & PaintBehaviorSelectionOnly; - - // 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 + // If this layer's renderer is a child of the subtreePaintRoot, we render unconditionally, which + // is done by passing a nil subtreePaintRoot down to our renderer (as if no subtreePaintRoot was ever set). + // Otherwise, our renderer tree may or may not contain the subtreePaintRoot root, so we pass that root along // so it will be tested against as we descend through the renderers. - RenderObject* paintingRootForRenderer = 0; - if (localPaintingInfo.paintingRoot && !renderer()->isDescendantOf(localPaintingInfo.paintingRoot)) - paintingRootForRenderer = localPaintingInfo.paintingRoot; + RenderObject* subtreePaintRootForRenderer = 0; + if (localPaintingInfo.subtreePaintRoot && !renderer()->isDescendantOf(localPaintingInfo.subtreePaintRoot)) + subtreePaintRootForRenderer = localPaintingInfo.subtreePaintRoot; if (localPaintingInfo.overlapTestRequests && isSelfPaintingLayer) performOverlapTests(*localPaintingInfo.overlapTestRequests, localPaintingInfo.rootLayer, this); - // We want to paint our layer, but only if we intersect the damage rect. - if (this != localPaintingInfo.rootLayer || !(localPaintFlags & PaintLayerPaintingOverflowContents)) - shouldPaintContent &= intersectsDamageRect(layerBounds, damageRect.rect(), localPaintingInfo.rootLayer, &offsetFromRoot); + bool forceBlackText = localPaintingInfo.paintBehavior & PaintBehaviorForceBlackText; + bool selectionOnly = localPaintingInfo.paintBehavior & PaintBehaviorSelectionOnly; - if (localPaintFlags & PaintLayerPaintingCompositingBackgroundPhase) { - if (shouldPaintContent && !selectionOnly) { - // Begin transparency layers lazily now that we know we have to paint something. - if (haveTransparency) - beginTransparencyLayers(transparencyLayerContext, localPaintingInfo.rootLayer, localPaintingInfo.paintDirtyRect, localPaintingInfo.paintBehavior); - - if (useClipRect) { - // Paint our background first, before painting any child layers. - // Establish the clip used to paint our background. - clipToRect(localPaintingInfo.rootLayer, context, localPaintingInfo.paintDirtyRect, damageRect, DoNotIncludeSelfForBorderRadius); // Background painting will handle clipping to self. - } - - // Paint the background. - PaintInfo paintInfo(context, pixelSnappedIntRect(damageRect.rect()), PaintPhaseBlockBackground, false, paintingRootForRenderer, localPaintingInfo.region, 0); - renderer()->paint(paintInfo, paintOffset); + PaintBehavior paintBehavior = PaintBehaviorNormal; + if (localPaintFlags & PaintLayerPaintingSkipRootBackground) + paintBehavior |= PaintBehaviorSkipRootBackground; + else if (localPaintFlags & PaintLayerPaintingRootBackgroundOnly) + paintBehavior |= PaintBehaviorRootBackgroundOnly; - if (useClipRect) { - // Restore the clip. - restoreClip(context, localPaintingInfo.paintDirtyRect, damageRect); - } + LayerFragments layerFragments; + if (shouldPaintContent || shouldPaintOutline || isPaintingOverlayScrollbars) { + // Collect the fragments. This will compute the clip rectangles and paint offsets for each layer fragment, as well as whether or not the content of each + // fragment should paint. If the parent's filter dictates full repaint to ensure proper filter effect, + // use the overflow clip as dirty rect, instead of no clipping. It maintains proper clipping for overflow::scroll. + LayoutRect paintDirtyRect = localPaintingInfo.paintDirtyRect; + if (!paintingInfo.clipToDirtyRect && renderer()->hasOverflowClip()) { + // We can turn clipping back by requesting full repaint for the overflow area. + localPaintingInfo.clipToDirtyRect = true; + paintDirtyRect = selfClipRect(); } + collectFragments(layerFragments, localPaintingInfo.rootLayer, localPaintingInfo.region, paintDirtyRect, + (localPaintFlags & PaintLayerTemporaryClipRects) ? TemporaryClipRects : PaintingClipRects, IgnoreOverlayScrollbarSize, + (isPaintingOverflowContents) ? IgnoreOverflowClip : RespectOverflowClip, &offsetFromRoot); + updatePaintingInfoForFragments(layerFragments, localPaintingInfo, localPaintFlags, shouldPaintContent, &offsetFromRoot); + } + + if (isPaintingCompositedBackground) { + // Paint only the backgrounds for all of the fragments of the layer. + if (shouldPaintContent && !selectionOnly) + paintBackgroundForFragments(layerFragments, context, transparencyLayerContext, paintingInfo.paintDirtyRect, haveTransparency, + localPaintingInfo, paintBehavior, subtreePaintRootForRenderer); + } - // Now walk the sorted list of children with negative z-indices. + // Now walk the sorted list of children with negative z-indices. + if ((isPaintingScrollingContent && isPaintingOverflowContents) || (!isPaintingScrollingContent && isPaintingCompositedBackground)) paintList(negZOrderList(), context, localPaintingInfo, localPaintFlags); - } - if (localPaintFlags & PaintLayerPaintingCompositingForegroundPhase) { - // Now establish the appropriate clip and paint our child RenderObjects. - if (shouldPaintContent && !clipRectToApply.isEmpty()) { - // Begin transparency layers lazily now that we know we have to paint something. - if (haveTransparency) - beginTransparencyLayers(transparencyLayerContext, localPaintingInfo.rootLayer, localPaintingInfo.paintDirtyRect, localPaintingInfo.paintBehavior); - - if (useClipRect) { - // Set up the clip used when painting our children. - clipToRect(localPaintingInfo.rootLayer, context, localPaintingInfo.paintDirtyRect, clipRectToApply); - } - - PaintInfo paintInfo(context, pixelSnappedIntRect(clipRectToApply.rect()), - selectionOnly ? PaintPhaseSelection : PaintPhaseChildBlockBackgrounds, - forceBlackText, paintingRootForRenderer, localPaintingInfo.region, 0); - renderer()->paint(paintInfo, paintOffset); - if (!selectionOnly) { - paintInfo.phase = PaintPhaseFloat; - renderer()->paint(paintInfo, paintOffset); - paintInfo.phase = PaintPhaseForeground; - paintInfo.overlapTestRequests = localPaintingInfo.overlapTestRequests; - renderer()->paint(paintInfo, paintOffset); - paintInfo.phase = PaintPhaseChildOutlines; - renderer()->paint(paintInfo, paintOffset); - } + if (isPaintingCompositedForeground) { + if (shouldPaintContent) + paintForegroundForFragments(layerFragments, context, transparencyLayerContext, paintingInfo.paintDirtyRect, haveTransparency, + localPaintingInfo, paintBehavior, subtreePaintRootForRenderer, selectionOnly, forceBlackText); + } - if (useClipRect) { - // Now restore our clip. - restoreClip(context, localPaintingInfo.paintDirtyRect, clipRectToApply); - } - } + if (shouldPaintOutline) + paintOutlineForFragments(layerFragments, context, localPaintingInfo, paintBehavior, subtreePaintRootForRenderer); - if (shouldPaintOutline && !outlineRect.isEmpty()) { - // Paint our own outline - PaintInfo paintInfo(context, pixelSnappedIntRect(outlineRect.rect()), PaintPhaseSelfOutline, false, paintingRootForRenderer, localPaintingInfo.region, 0); - clipToRect(localPaintingInfo.rootLayer, context, localPaintingInfo.paintDirtyRect, outlineRect, DoNotIncludeSelfForBorderRadius); - renderer()->paint(paintInfo, paintOffset); - restoreClip(context, localPaintingInfo.paintDirtyRect, outlineRect); - } - + if (isPaintingCompositedForeground) { // Paint any child layers that have overflow. paintList(m_normalFlowList.get(), context, localPaintingInfo, localPaintFlags); @@ -3441,36 +3951,22 @@ void RenderLayer::paintLayerContents(GraphicsContext* context, const LayerPainti paintList(posZOrderList(), context, localPaintingInfo, localPaintFlags); } - if (isPaintingOverlayScrollbars) { - clipToRect(localPaintingInfo.rootLayer, context, localPaintingInfo.paintDirtyRect, damageRect); - paintOverflowControls(context, roundedIntPoint(paintOffset), pixelSnappedIntRect(damageRect.rect()), true); - restoreClip(context, localPaintingInfo.paintDirtyRect, damageRect); - } + if (isPaintingOverlayScrollbars) + paintOverflowControlsForFragments(layerFragments, context, localPaintingInfo); #if ENABLE(CSS_FILTERS) - if (filterPainter.hasStartedFilterEffect()) { - // Apply the correct clipping (ie. overflow: hidden). - clipToRect(localPaintingInfo.rootLayer, transparencyLayerContext, localPaintingInfo.paintDirtyRect, damageRect); - context = filterPainter.applyFilterEffect(); - restoreClip(transparencyLayerContext, localPaintingInfo.paintDirtyRect, damageRect); + if (filterPainter) { + context = applyFilters(filterPainter.get(), transparencyLayerContext, localPaintingInfo, layerFragments); + filterPainter.clear(); } #endif - + // Make sure that we now use the original transparency context. ASSERT(transparencyLayerContext == context); if ((localPaintFlags & PaintLayerPaintingCompositingMaskPhase) && shouldPaintContent && renderer()->hasMask() && !selectionOnly) { - if (useClipRect) - clipToRect(localPaintingInfo.rootLayer, context, localPaintingInfo.paintDirtyRect, damageRect, DoNotIncludeSelfForBorderRadius); // Mask painting will handle clipping to self. - - // Paint the mask. - PaintInfo paintInfo(context, pixelSnappedIntRect(damageRect.rect()), PaintPhaseMask, false, paintingRootForRenderer, localPaintingInfo.region, 0); - renderer()->paint(paintInfo, paintOffset); - - if (useClipRect) { - // Restore the clip. - restoreClip(context, localPaintingInfo.paintDirtyRect, damageRect); - } + // Paint the mask for the fragments. + paintMaskForFragments(layerFragments, context, localPaintingInfo, subtreePaintRootForRenderer); } // End our transparency layer @@ -3488,6 +3984,28 @@ void RenderLayer::paintLayerContents(GraphicsContext* context, const LayerPainti context->restore(); } +void RenderLayer::paintLayerByApplyingTransform(GraphicsContext* context, const LayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags, const LayoutPoint& translationOffset) +{ + // This involves subtracting out the position of the layer in our current coordinate space, but preserving + // the accumulated error for sub-pixel layout. + LayoutPoint delta; + convertToLayerCoords(paintingInfo.rootLayer, delta); + delta.moveBy(translationOffset); + TransformationMatrix transform(renderableTransform(paintingInfo.paintBehavior)); + IntPoint roundedDelta = roundedIntPoint(delta); + transform.translateRight(roundedDelta.x(), roundedDelta.y()); + LayoutSize adjustedSubPixelAccumulation = paintingInfo.subPixelAccumulation + (delta - roundedDelta); + + // Apply the transform. + GraphicsContextStateSaver stateSaver(*context); + context->concatCTM(transform.toAffineTransform()); + + // Now do a paint with the root layer shifted to be us. + LayerPaintingInfo transformedPaintingInfo(this, enclosingIntRect(transform.inverse().mapRect(paintingInfo.paintDirtyRect)), paintingInfo.paintBehavior, + adjustedSubPixelAccumulation, paintingInfo.subtreePaintRoot, paintingInfo.region, paintingInfo.overlapTestRequests); + paintLayerContentsAndReflection(context, transformedPaintingInfo, paintFlags); +} + void RenderLayer::paintList(Vector<RenderLayer*>* list, GraphicsContext* context, const LayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags) { if (!list) @@ -3502,6 +4020,8 @@ void RenderLayer::paintList(Vector<RenderLayer*>* list, GraphicsContext* context for (size_t i = 0; i < list->size(); ++i) { RenderLayer* childLayer = list->at(i); + if (childLayer->isOutOfFlowRenderFlowThread()) + continue; if (!childLayer->isPaginated()) childLayer->paintLayer(context, paintingInfo, paintFlags); else @@ -3509,11 +4029,267 @@ void RenderLayer::paintList(Vector<RenderLayer*>* list, GraphicsContext* context } } +void RenderLayer::collectFragments(LayerFragments& fragments, const RenderLayer* rootLayer, RenderRegion* region, const LayoutRect& dirtyRect, + ClipRectsType clipRectsType, OverlayScrollbarSizeRelevancy inOverlayScrollbarSizeRelevancy, ShouldRespectOverflowClip respectOverflowClip, const LayoutPoint* offsetFromRoot, + const LayoutRect* layerBoundingBox) +{ + if (!enclosingPaginationLayer() || hasTransform()) { + // For unpaginated layers, there is only one fragment. + LayerFragment fragment; + ClipRectsContext clipRectsContext(rootLayer, region, clipRectsType, inOverlayScrollbarSizeRelevancy, respectOverflowClip); + calculateRects(clipRectsContext, dirtyRect, fragment.layerBounds, fragment.backgroundRect, fragment.foregroundRect, fragment.outlineRect, offsetFromRoot); + fragments.append(fragment); + return; + } + + // Compute our offset within the enclosing pagination layer. + LayoutPoint offsetWithinPaginatedLayer; + convertToLayerCoords(enclosingPaginationLayer(), offsetWithinPaginatedLayer); + + // Calculate clip rects relative to the enclosingPaginationLayer. The purpose of this call is to determine our bounds clipped to intermediate + // layers between us and the pagination context. It's important to minimize the number of fragments we need to create and this helps with that. + ClipRectsContext paginationClipRectsContext(enclosingPaginationLayer(), region, clipRectsType, inOverlayScrollbarSizeRelevancy, respectOverflowClip); + LayoutRect layerBoundsInFlowThread; + ClipRect backgroundRectInFlowThread; + ClipRect foregroundRectInFlowThread; + ClipRect outlineRectInFlowThread; + calculateRects(paginationClipRectsContext, PaintInfo::infiniteRect(), layerBoundsInFlowThread, backgroundRectInFlowThread, foregroundRectInFlowThread, + outlineRectInFlowThread, &offsetWithinPaginatedLayer); + + // Take our bounding box within the flow thread and clip it. + LayoutRect layerBoundingBoxInFlowThread = layerBoundingBox ? *layerBoundingBox : boundingBox(enclosingPaginationLayer(), 0, &offsetWithinPaginatedLayer); + layerBoundingBoxInFlowThread.intersect(backgroundRectInFlowThread.rect()); + + // Shift the dirty rect into flow thread coordinates. + LayoutPoint offsetOfPaginationLayerFromRoot; + enclosingPaginationLayer()->convertToLayerCoords(rootLayer, offsetOfPaginationLayerFromRoot); + LayoutRect dirtyRectInFlowThread(dirtyRect); + dirtyRectInFlowThread.moveBy(-offsetOfPaginationLayerFromRoot); + + // Tell the flow thread to collect the fragments. We pass enough information to create a minimal number of fragments based off the pages/columns + // that intersect the actual dirtyRect as well as the pages/columns that intersect our layer's bounding box. + RenderFlowThread* enclosingFlowThread = toRenderFlowThread(enclosingPaginationLayer()->renderer()); + enclosingFlowThread->collectLayerFragments(fragments, layerBoundingBoxInFlowThread, dirtyRectInFlowThread); + + if (fragments.isEmpty()) + return; + + // Get the parent clip rects of the pagination layer, since we need to intersect with that when painting column contents. + ClipRect ancestorClipRect = dirtyRect; + if (enclosingPaginationLayer()->parent()) { + ClipRectsContext clipRectsContext(rootLayer, region, clipRectsType, inOverlayScrollbarSizeRelevancy, respectOverflowClip); + ancestorClipRect = enclosingPaginationLayer()->backgroundClipRect(clipRectsContext); + ancestorClipRect.intersect(dirtyRect); + } + + for (size_t i = 0; i < fragments.size(); ++i) { + LayerFragment& fragment = fragments.at(i); + + // Set our four rects with all clipping applied that was internal to the flow thread. + fragment.setRects(layerBoundsInFlowThread, backgroundRectInFlowThread, foregroundRectInFlowThread, outlineRectInFlowThread); + + // Shift to the root-relative physical position used when painting the flow thread in this fragment. + fragment.moveBy(fragment.paginationOffset + offsetOfPaginationLayerFromRoot); + + // Intersect the fragment with our ancestor's background clip so that e.g., columns in an overflow:hidden block are + // properly clipped by the overflow. + fragment.intersect(ancestorClipRect.rect()); + + // Now intersect with our pagination clip. This will typically mean we're just intersecting the dirty rect with the column + // clip, so the column clip ends up being all we apply. + fragment.intersect(fragment.paginationClip); + } +} + +void RenderLayer::updatePaintingInfoForFragments(LayerFragments& fragments, const LayerPaintingInfo& localPaintingInfo, PaintLayerFlags localPaintFlags, + bool shouldPaintContent, const LayoutPoint* offsetFromRoot) +{ + ASSERT(offsetFromRoot); + for (size_t i = 0; i < fragments.size(); ++i) { + LayerFragment& fragment = fragments.at(i); + fragment.shouldPaintContent = shouldPaintContent; + if (this != localPaintingInfo.rootLayer || !(localPaintFlags & PaintLayerPaintingOverflowContents)) { + LayoutPoint newOffsetFromRoot = *offsetFromRoot + fragment.paginationOffset; + fragment.shouldPaintContent &= intersectsDamageRect(fragment.layerBounds, fragment.backgroundRect.rect(), localPaintingInfo.rootLayer, &newOffsetFromRoot); + } + } +} + +void RenderLayer::paintTransformedLayerIntoFragments(GraphicsContext* context, const LayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags) +{ + LayerFragments enclosingPaginationFragments; + LayoutPoint offsetOfPaginationLayerFromRoot; + LayoutRect transformedExtent = transparencyClipBox(this, enclosingPaginationLayer(), PaintingTransparencyClipBox, RootOfTransparencyClipBox, paintingInfo.paintBehavior); + enclosingPaginationLayer()->collectFragments(enclosingPaginationFragments, paintingInfo.rootLayer, paintingInfo.region, paintingInfo.paintDirtyRect, + (paintFlags & PaintLayerTemporaryClipRects) ? TemporaryClipRects : PaintingClipRects, IgnoreOverlayScrollbarSize, + (paintFlags & PaintLayerPaintingOverflowContents) ? IgnoreOverflowClip : RespectOverflowClip, &offsetOfPaginationLayerFromRoot, &transformedExtent); + + for (size_t i = 0; i < enclosingPaginationFragments.size(); ++i) { + const LayerFragment& fragment = enclosingPaginationFragments.at(i); + + // Apply the page/column clip for this fragment, as well as any clips established by layers in between us and + // the enclosing pagination layer. + LayoutRect clipRect = fragment.backgroundRect.rect(); + + // Now compute the clips within a given fragment + if (parent() != enclosingPaginationLayer()) { + enclosingPaginationLayer()->convertToLayerCoords(paintingInfo.rootLayer, offsetOfPaginationLayerFromRoot); + + ClipRectsContext clipRectsContext(enclosingPaginationLayer(), paintingInfo.region, (paintFlags & PaintLayerTemporaryClipRects) ? TemporaryClipRects : PaintingClipRects, + IgnoreOverlayScrollbarSize, (paintFlags & PaintLayerPaintingOverflowContents) ? IgnoreOverflowClip : RespectOverflowClip); + LayoutRect parentClipRect = backgroundClipRect(clipRectsContext).rect(); + parentClipRect.moveBy(fragment.paginationOffset + offsetOfPaginationLayerFromRoot); + clipRect.intersect(parentClipRect); + } + + parent()->clipToRect(paintingInfo.rootLayer, context, paintingInfo.paintDirtyRect, clipRect); + paintLayerByApplyingTransform(context, paintingInfo, paintFlags, fragment.paginationOffset); + parent()->restoreClip(context, paintingInfo.paintDirtyRect, clipRect); + } +} + +void RenderLayer::paintBackgroundForFragments(const LayerFragments& layerFragments, GraphicsContext* context, GraphicsContext* transparencyLayerContext, + const LayoutRect& transparencyPaintDirtyRect, bool haveTransparency, const LayerPaintingInfo& localPaintingInfo, PaintBehavior paintBehavior, + RenderObject* subtreePaintRootForRenderer) +{ + for (size_t i = 0; i < layerFragments.size(); ++i) { + const LayerFragment& fragment = layerFragments.at(i); + if (!fragment.shouldPaintContent) + continue; + + // Begin transparency layers lazily now that we know we have to paint something. + if (haveTransparency) + beginTransparencyLayers(transparencyLayerContext, localPaintingInfo.rootLayer, transparencyPaintDirtyRect, localPaintingInfo.paintBehavior); + + if (localPaintingInfo.clipToDirtyRect) { + // Paint our background first, before painting any child layers. + // Establish the clip used to paint our background. + clipToRect(localPaintingInfo.rootLayer, context, localPaintingInfo.paintDirtyRect, fragment.backgroundRect, DoNotIncludeSelfForBorderRadius); // Background painting will handle clipping to self. + } + + // Paint the background. + // FIXME: Eventually we will collect the region from the fragment itself instead of just from the paint info. + PaintInfo paintInfo(context, pixelSnappedIntRect(fragment.backgroundRect.rect()), PaintPhaseBlockBackground, paintBehavior, subtreePaintRootForRenderer, localPaintingInfo.region, 0, 0, localPaintingInfo.rootLayer->renderer()); + renderer()->paint(paintInfo, toPoint(fragment.layerBounds.location() - renderBoxLocation() + localPaintingInfo.subPixelAccumulation)); + + if (localPaintingInfo.clipToDirtyRect) + restoreClip(context, localPaintingInfo.paintDirtyRect, fragment.backgroundRect); + } +} + +void RenderLayer::paintForegroundForFragments(const LayerFragments& layerFragments, GraphicsContext* context, GraphicsContext* transparencyLayerContext, + const LayoutRect& transparencyPaintDirtyRect, bool haveTransparency, const LayerPaintingInfo& localPaintingInfo, PaintBehavior paintBehavior, + RenderObject* subtreePaintRootForRenderer, bool selectionOnly, bool forceBlackText) +{ + // Begin transparency if we have something to paint. + if (haveTransparency) { + for (size_t i = 0; i < layerFragments.size(); ++i) { + const LayerFragment& fragment = layerFragments.at(i); + if (fragment.shouldPaintContent && !fragment.foregroundRect.isEmpty()) { + beginTransparencyLayers(transparencyLayerContext, localPaintingInfo.rootLayer, transparencyPaintDirtyRect, localPaintingInfo.paintBehavior); + break; + } + } + } + + PaintBehavior localPaintBehavior = forceBlackText ? (PaintBehavior)PaintBehaviorForceBlackText : paintBehavior; + + // Optimize clipping for the single fragment case. + bool shouldClip = localPaintingInfo.clipToDirtyRect && layerFragments.size() == 1 && layerFragments[0].shouldPaintContent && !layerFragments[0].foregroundRect.isEmpty(); + if (shouldClip) + clipToRect(localPaintingInfo.rootLayer, context, localPaintingInfo.paintDirtyRect, layerFragments[0].foregroundRect); + + // We have to loop through every fragment multiple times, since we have to repaint in each specific phase in order for + // interleaving of the fragments to work properly. + paintForegroundForFragmentsWithPhase(selectionOnly ? PaintPhaseSelection : PaintPhaseChildBlockBackgrounds, layerFragments, + context, localPaintingInfo, localPaintBehavior, subtreePaintRootForRenderer); + + if (!selectionOnly) { + paintForegroundForFragmentsWithPhase(PaintPhaseFloat, layerFragments, context, localPaintingInfo, localPaintBehavior, subtreePaintRootForRenderer); + paintForegroundForFragmentsWithPhase(PaintPhaseForeground, layerFragments, context, localPaintingInfo, localPaintBehavior, subtreePaintRootForRenderer); + paintForegroundForFragmentsWithPhase(PaintPhaseChildOutlines, layerFragments, context, localPaintingInfo, localPaintBehavior, subtreePaintRootForRenderer); + } + + if (shouldClip) + restoreClip(context, localPaintingInfo.paintDirtyRect, layerFragments[0].foregroundRect); +} + +void RenderLayer::paintForegroundForFragmentsWithPhase(PaintPhase phase, const LayerFragments& layerFragments, GraphicsContext* context, + const LayerPaintingInfo& localPaintingInfo, PaintBehavior paintBehavior, RenderObject* subtreePaintRootForRenderer) +{ + bool shouldClip = localPaintingInfo.clipToDirtyRect && layerFragments.size() > 1; + + for (size_t i = 0; i < layerFragments.size(); ++i) { + const LayerFragment& fragment = layerFragments.at(i); + if (!fragment.shouldPaintContent || fragment.foregroundRect.isEmpty()) + continue; + + if (shouldClip) + clipToRect(localPaintingInfo.rootLayer, context, localPaintingInfo.paintDirtyRect, fragment.foregroundRect); + + PaintInfo paintInfo(context, pixelSnappedIntRect(fragment.foregroundRect.rect()), phase, paintBehavior, subtreePaintRootForRenderer, localPaintingInfo.region, 0, 0, localPaintingInfo.rootLayer->renderer()); + if (phase == PaintPhaseForeground) + paintInfo.overlapTestRequests = localPaintingInfo.overlapTestRequests; + renderer()->paint(paintInfo, toPoint(fragment.layerBounds.location() - renderBoxLocation() + localPaintingInfo.subPixelAccumulation)); + + if (shouldClip) + restoreClip(context, localPaintingInfo.paintDirtyRect, fragment.foregroundRect); + } +} + +void RenderLayer::paintOutlineForFragments(const LayerFragments& layerFragments, GraphicsContext* context, const LayerPaintingInfo& localPaintingInfo, + PaintBehavior paintBehavior, RenderObject* subtreePaintRootForRenderer) +{ + for (size_t i = 0; i < layerFragments.size(); ++i) { + const LayerFragment& fragment = layerFragments.at(i); + if (fragment.outlineRect.isEmpty()) + continue; + + // Paint our own outline + PaintInfo paintInfo(context, pixelSnappedIntRect(fragment.outlineRect.rect()), PaintPhaseSelfOutline, paintBehavior, subtreePaintRootForRenderer, localPaintingInfo.region, 0, 0, localPaintingInfo.rootLayer->renderer()); + clipToRect(localPaintingInfo.rootLayer, context, localPaintingInfo.paintDirtyRect, fragment.outlineRect, DoNotIncludeSelfForBorderRadius); + renderer()->paint(paintInfo, toPoint(fragment.layerBounds.location() - renderBoxLocation() + localPaintingInfo.subPixelAccumulation)); + restoreClip(context, localPaintingInfo.paintDirtyRect, fragment.outlineRect); + } +} + +void RenderLayer::paintMaskForFragments(const LayerFragments& layerFragments, GraphicsContext* context, const LayerPaintingInfo& localPaintingInfo, + RenderObject* subtreePaintRootForRenderer) +{ + for (size_t i = 0; i < layerFragments.size(); ++i) { + const LayerFragment& fragment = layerFragments.at(i); + if (!fragment.shouldPaintContent) + continue; + + if (localPaintingInfo.clipToDirtyRect) + clipToRect(localPaintingInfo.rootLayer, context, localPaintingInfo.paintDirtyRect, fragment.backgroundRect, DoNotIncludeSelfForBorderRadius); // Mask painting will handle clipping to self. + + // Paint the mask. + // FIXME: Eventually we will collect the region from the fragment itself instead of just from the paint info. + PaintInfo paintInfo(context, pixelSnappedIntRect(fragment.backgroundRect.rect()), PaintPhaseMask, PaintBehaviorNormal, subtreePaintRootForRenderer, localPaintingInfo.region, 0, 0, localPaintingInfo.rootLayer->renderer()); + renderer()->paint(paintInfo, toPoint(fragment.layerBounds.location() - renderBoxLocation() + localPaintingInfo.subPixelAccumulation)); + + if (localPaintingInfo.clipToDirtyRect) + restoreClip(context, localPaintingInfo.paintDirtyRect, fragment.backgroundRect); + } +} + +void RenderLayer::paintOverflowControlsForFragments(const LayerFragments& layerFragments, GraphicsContext* context, const LayerPaintingInfo& localPaintingInfo) +{ + for (size_t i = 0; i < layerFragments.size(); ++i) { + const LayerFragment& fragment = layerFragments.at(i); + clipToRect(localPaintingInfo.rootLayer, context, localPaintingInfo.paintDirtyRect, fragment.backgroundRect); + paintOverflowControls(context, roundedIntPoint(toPoint(fragment.layerBounds.location() - renderBoxLocation() + localPaintingInfo.subPixelAccumulation)), + pixelSnappedIntRect(fragment.backgroundRect.rect()), true); + restoreClip(context, localPaintingInfo.paintDirtyRect, fragment.backgroundRect); + } +} + void RenderLayer::paintPaginatedChildLayer(RenderLayer* childLayer, GraphicsContext* context, const LayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags) { // We need to do multiple passes, breaking up our child layer into strips. Vector<RenderLayer*> columnLayers; - RenderLayer* ancestorLayer = isNormalFlowOnly() ? parent() : stackingContext(); + RenderLayer* ancestorLayer = isNormalFlowOnly() ? parent() : stackingContainer(); for (RenderLayer* curr = childLayer->parent(); curr; curr = curr->parent()) { if (curr->renderer()->hasColumns() && checkContainingBlockChainForPagination(childLayer->renderer(), curr->renderBox())) columnLayers.append(curr); @@ -3548,30 +4324,19 @@ void RenderLayer::paintChildLayerIntoColumns(RenderLayer* childLayer, GraphicsCo ColumnInfo* colInfo = columnBlock->columnInfo(); unsigned colCount = columnBlock->columnCount(colInfo); - LayoutUnit currLogicalTopOffset = 0; + LayoutUnit currLogicalTopOffset = columnBlock->initialBlockOffsetForPainting(); + LayoutUnit blockDelta = columnBlock->blockDeltaForPaintingNextColumn(); for (unsigned i = 0; i < colCount; i++) { // For each rect, we clip to the rect, and then we adjust our coords. LayoutRect colRect = columnBlock->columnRectAt(colInfo, i); columnBlock->flipForWritingMode(colRect); - LayoutUnit logicalLeftOffset = (isHorizontal ? colRect.x() : colRect.y()) - columnBlock->logicalLeftOffsetForContent(); - LayoutSize offset; - if (isHorizontal) { - if (colInfo->progressionAxis() == ColumnInfo::InlineAxis) - offset = LayoutSize(logicalLeftOffset, currLogicalTopOffset); - else - offset = LayoutSize(0, colRect.y() + currLogicalTopOffset - columnBlock->borderTop() - columnBlock->paddingTop()); - } else { - if (colInfo->progressionAxis() == ColumnInfo::InlineAxis) - offset = LayoutSize(currLogicalTopOffset, logicalLeftOffset); - else - offset = LayoutSize(colRect.x() + currLogicalTopOffset - columnBlock->borderLeft() - columnBlock->paddingLeft(), 0); - } + LayoutUnit logicalLeftOffset = (isHorizontal ? colRect.x() : colRect.y()) - columnBlock->logicalLeftOffsetForContent(); + LayoutSize offset = isHorizontal ? LayoutSize(logicalLeftOffset, currLogicalTopOffset) : LayoutSize(currLogicalTopOffset, logicalLeftOffset); colRect.moveBy(layerOffset); - + LayoutRect localDirtyRect(paintingInfo.paintDirtyRect); localDirtyRect.intersect(colRect); - if (!localDirtyRect.isEmpty()) { GraphicsContextStateSaver stateSaver(*context); @@ -3618,11 +4383,7 @@ void RenderLayer::paintChildLayerIntoColumns(RenderLayer* childLayer, GraphicsCo } // Move to the next position. - LayoutUnit blockDelta = isHorizontal ? colRect.height() : colRect.width(); - if (columnBlock->style()->isFlippedBlocksWritingMode()) - currLogicalTopOffset += blockDelta; - else - currLogicalTopOffset -= blockDelta; + currLogicalTopOffset += blockDelta; } } @@ -3646,7 +4407,7 @@ bool RenderLayer::hitTest(const HitTestRequest& request, const HitTestLocation& renderer()->document()->updateLayout(); - LayoutRect hitTestArea = renderer()->isRenderFlowThread() ? toRenderFlowThread(renderer())->borderBoxRect() : renderer()->view()->documentRect(); + LayoutRect hitTestArea = isOutOfFlowRenderFlowThread() ? toRenderFlowThread(renderer())->borderBoxRect() : renderer()->view()->documentRect(); if (!request.ignoreClipping()) hitTestArea.intersect(frameVisibleRect(renderer())); @@ -3655,7 +4416,7 @@ bool RenderLayer::hitTest(const HitTestRequest& request, const HitTestLocation& // 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.release()) && isRootLayer()) { + if (!request.isChildFrameHitTest() && (request.active() || request.release()) && isRootLayer()) { renderer()->updateHitTestResult(result, toRenderView(renderer())->flipForWritingMode(hitTestLocation.point())); insideLayer = this; } @@ -3664,7 +4425,7 @@ bool RenderLayer::hitTest(const HitTestRequest& request, const HitTestLocation& // Now determine if the result is inside an anchor - if the urlElement isn't already set. Node* node = result.innerNode(); if (node && !result.URLElement()) - result.setURLElement(static_cast<Element*>(node->enclosingLinkEventParentOrSelf())); + result.setURLElement(toElement(node->enclosingLinkEventParentOrSelf())); // Now return whether we were inside this layer (this will always be true for the root // layer). @@ -3717,7 +4478,8 @@ static double computeZOffset(const HitTestingTransformState& transformState) PassRefPtr<HitTestingTransformState> RenderLayer::createLocalTransformState(RenderLayer* rootLayer, RenderLayer* containerLayer, const LayoutRect& hitTestRect, const HitTestLocation& hitTestLocation, - const HitTestingTransformState* containerTransformState) const + const HitTestingTransformState* containerTransformState, + const LayoutPoint& translationOffset) const { RefPtr<HitTestingTransformState> transformState; LayoutPoint offset; @@ -3731,7 +4493,8 @@ PassRefPtr<HitTestingTransformState> RenderLayer::createLocalTransformState(Rend transformState = HitTestingTransformState::create(hitTestLocation.transformedPoint(), hitTestLocation.transformedRect(), FloatQuad(hitTestRect)); convertToLayerCoords(rootLayer, offset); } - + offset.moveBy(translationOffset); + RenderObject* containerRenderer = containerLayer ? containerLayer->renderer() : 0; if (renderer()->shouldUseTransformFromContainer(containerRenderer)) { TransformationMatrix containerTransform; @@ -3788,6 +4551,9 @@ RenderLayer* RenderLayer::hitTestLayer(RenderLayer* rootLayer, RenderLayer* cont // Apply a transform if we have one. if (transform() && !appliedTransform) { + if (enclosingPaginationLayer()) + return hitTestTransformedLayerInFragments(rootLayer, containerLayer, request, result, hitTestRect, hitTestLocation, transformState, zOffset); + // Make sure the parent's clip rects have been calculated. if (parent()) { ClipRectsContext clipRectsContext(rootLayer, hitTestLocation.region(), RootRelativeClipRects, IncludeOverlayScrollbarSize); @@ -3797,30 +4563,7 @@ RenderLayer* RenderLayer::hitTestLayer(RenderLayer* rootLayer, RenderLayer* cont return 0; } - // Create a transform state to accumulate this transform. - RefPtr<HitTestingTransformState> newTransformState = createLocalTransformState(rootLayer, containerLayer, hitTestRect, hitTestLocation, transformState); - - // If the transform can't be inverted, then don't hit test this layer at all. - if (!newTransformState->m_accumulatedTransform.isInvertible()) - return 0; - - // Compute the point and the hit test rect in the coords of this layer by using the values - // from the transformState, which store the point and quad in the coords of the last flattened - // layer, and the accumulated transform which lets up map through preserve-3d layers. - // - // We can't just map hitTestLocation and hitTestRect because they may have been flattened (losing z) - // by our container. - FloatPoint localPoint = newTransformState->mappedPoint(); - FloatQuad localPointQuad = newTransformState->mappedQuad(); - LayoutRect localHitTestRect = newTransformState->boundsOfMappedArea(); - HitTestLocation newHitTestLocation; - if (hitTestLocation.isRectBasedTest()) - newHitTestLocation = HitTestLocation(localPoint, localPointQuad); - else - newHitTestLocation = HitTestLocation(localPoint); - - // Now do a hit test with the root layer shifted to be us. - return hitTestLayer(this, containerLayer, request, result, localHitTestRect, newHitTestLocation, true, newTransformState.get(), zOffset); + return hitTestLayerByApplyingTransform(rootLayer, containerLayer, request, result, hitTestRect, hitTestLocation, transformState, zOffset); } // Ensure our lists and 3d status are up-to-date. @@ -3852,16 +4595,7 @@ RenderLayer* RenderLayer::hitTestLayer(RenderLayer* rootLayer, RenderLayer* cont // This layer is flattening, so flatten the state passed to descendants. localTransformState->flatten(); } - - // Calculate the clip rects we should use. - LayoutRect layerBounds; - ClipRect bgRect; - ClipRect fgRect; - ClipRect outlineRect; - ClipRectsContext clipRectsContext(rootLayer, hitTestLocation.region(), RootRelativeClipRects, IncludeOverlayScrollbarSize); - calculateRects(clipRectsContext, hitTestRect, layerBounds, bgRect, fgRect, outlineRect); - // The following are used for keeping track of the z-depth of the hit point of 3d-transformed // descendants. double localZOffset = -numeric_limits<double>::infinity(); @@ -3906,11 +4640,22 @@ RenderLayer* RenderLayer::hitTestLayer(RenderLayer* rootLayer, RenderLayer* cont candidateLayer = hitLayer; } - // Next we want to see if the mouse pos is inside the child RenderObjects of the layer. - if (fgRect.intersects(hitTestLocation) && isSelfPaintingLayer()) { + // Collect the fragments. This will compute the clip rectangles for each layer fragment. + LayerFragments layerFragments; + collectFragments(layerFragments, rootLayer, hitTestLocation.region(), hitTestRect, RootRelativeClipRects, IncludeOverlayScrollbarSize); + + if (canResize() && hitTestResizerInFragments(layerFragments, hitTestLocation)) { + renderer()->updateHitTestResult(result, hitTestLocation.point()); + return this; + } + + // Next we want to see if the mouse pos is inside the child RenderObjects of the layer. Check + // every fragment in reverse order. + if (isSelfPaintingLayer()) { // Hit test with a temporary HitTestResult, because we only want to commit to 'result' if we know we're frontmost. HitTestResult tempResult(result.hitTestLocation()); - if (hitTestContents(request, tempResult, layerBounds, hitTestLocation, HitTestDescendants) + bool insideFragmentForegroundRect = false; + if (hitTestContentsForFragments(layerFragments, request, tempResult, hitTestLocation, HitTestDescendants, insideFragmentForegroundRect) && isHitCandidate(this, false, zOffsetForContentsPtr, unflattenedTransformState.get())) { if (result.isRectBasedTest()) result.append(tempResult); @@ -3920,13 +4665,13 @@ RenderLayer* RenderLayer::hitTestLayer(RenderLayer* rootLayer, RenderLayer* cont return this; // Foreground can depth-sort with descendant layers, so keep this as a candidate. candidateLayer = this; - } else if (result.isRectBasedTest()) + } else if (insideFragmentForegroundRect && result.isRectBasedTest()) result.append(tempResult); } // Now check our negative z-index children. hitLayer = hitTestList(negZOrderList(), rootLayer, request, result, hitTestRect, hitTestLocation, - localTransformState.get(), zOffsetForDescendantsPtr, zOffset, unflattenedTransformState.get(), depthSortDescendants); + localTransformState.get(), zOffsetForDescendantsPtr, zOffset, unflattenedTransformState.get(), depthSortDescendants); if (hitLayer) { if (!depthSortDescendants) return hitLayer; @@ -3937,9 +4682,10 @@ RenderLayer* RenderLayer::hitTestLayer(RenderLayer* rootLayer, RenderLayer* cont if (candidateLayer) return candidateLayer; - if (bgRect.intersects(hitTestLocation) && isSelfPaintingLayer()) { + if (isSelfPaintingLayer()) { HitTestResult tempResult(result.hitTestLocation()); - if (hitTestContents(request, tempResult, layerBounds, hitTestLocation, HitTestSelf) + bool insideFragmentBackgroundRect = false; + if (hitTestContentsForFragments(layerFragments, request, tempResult, hitTestLocation, HitTestSelf, insideFragmentBackgroundRect) && isHitCandidate(this, false, zOffsetForContentsPtr, unflattenedTransformState.get())) { if (result.isRectBasedTest()) result.append(tempResult); @@ -3947,20 +4693,119 @@ RenderLayer* RenderLayer::hitTestLayer(RenderLayer* rootLayer, RenderLayer* cont result = tempResult; return this; } - if (result.isRectBasedTest()) + if (insideFragmentBackgroundRect && result.isRectBasedTest()) result.append(tempResult); } return 0; } +bool RenderLayer::hitTestContentsForFragments(const LayerFragments& layerFragments, const HitTestRequest& request, HitTestResult& result, + const HitTestLocation& hitTestLocation, HitTestFilter hitTestFilter, bool& insideClipRect) const +{ + if (layerFragments.isEmpty()) + return false; + + for (int i = layerFragments.size() - 1; i >= 0; --i) { + const LayerFragment& fragment = layerFragments.at(i); + if ((hitTestFilter == HitTestSelf && !fragment.backgroundRect.intersects(hitTestLocation)) + || (hitTestFilter == HitTestDescendants && !fragment.foregroundRect.intersects(hitTestLocation))) + continue; + insideClipRect = true; + if (hitTestContents(request, result, fragment.layerBounds, hitTestLocation, hitTestFilter)) + return true; + } + + return false; +} + +bool RenderLayer::hitTestResizerInFragments(const LayerFragments& layerFragments, const HitTestLocation& hitTestLocation) const +{ + if (layerFragments.isEmpty()) + return false; + + for (int i = layerFragments.size() - 1; i >= 0; --i) { + const LayerFragment& fragment = layerFragments.at(i); + if (fragment.backgroundRect.intersects(hitTestLocation) && resizerCornerRect(this, pixelSnappedIntRect(fragment.layerBounds)).contains(hitTestLocation.roundedPoint())) + return true; + } + + return false; +} + +RenderLayer* RenderLayer::hitTestTransformedLayerInFragments(RenderLayer* rootLayer, RenderLayer* containerLayer, const HitTestRequest& request, HitTestResult& result, + const LayoutRect& hitTestRect, const HitTestLocation& hitTestLocation, const HitTestingTransformState* transformState, double* zOffset) +{ + LayerFragments enclosingPaginationFragments; + LayoutPoint offsetOfPaginationLayerFromRoot; + LayoutRect transformedExtent = transparencyClipBox(this, enclosingPaginationLayer(), HitTestingTransparencyClipBox, RootOfTransparencyClipBox); + enclosingPaginationLayer()->collectFragments(enclosingPaginationFragments, rootLayer, hitTestLocation.region(), hitTestRect, + RootRelativeClipRects, IncludeOverlayScrollbarSize, RespectOverflowClip, &offsetOfPaginationLayerFromRoot, &transformedExtent); + + for (int i = enclosingPaginationFragments.size() - 1; i >= 0; --i) { + const LayerFragment& fragment = enclosingPaginationFragments.at(i); + + // Apply the page/column clip for this fragment, as well as any clips established by layers in between us and + // the enclosing pagination layer. + LayoutRect clipRect = fragment.backgroundRect.rect(); + + // Now compute the clips within a given fragment + if (parent() != enclosingPaginationLayer()) { + enclosingPaginationLayer()->convertToLayerCoords(rootLayer, offsetOfPaginationLayerFromRoot); + + ClipRectsContext clipRectsContext(enclosingPaginationLayer(), hitTestLocation.region(), RootRelativeClipRects, IncludeOverlayScrollbarSize); + LayoutRect parentClipRect = backgroundClipRect(clipRectsContext).rect(); + parentClipRect.moveBy(fragment.paginationOffset + offsetOfPaginationLayerFromRoot); + clipRect.intersect(parentClipRect); + } + + if (!hitTestLocation.intersects(clipRect)) + continue; + + RenderLayer* hitLayer = hitTestLayerByApplyingTransform(rootLayer, containerLayer, request, result, hitTestRect, hitTestLocation, + transformState, zOffset, fragment.paginationOffset); + if (hitLayer) + return hitLayer; + } + + return 0; +} + +RenderLayer* RenderLayer::hitTestLayerByApplyingTransform(RenderLayer* rootLayer, RenderLayer* containerLayer, const HitTestRequest& request, HitTestResult& result, + const LayoutRect& hitTestRect, const HitTestLocation& hitTestLocation, const HitTestingTransformState* transformState, double* zOffset, + const LayoutPoint& translationOffset) +{ + // Create a transform state to accumulate this transform. + RefPtr<HitTestingTransformState> newTransformState = createLocalTransformState(rootLayer, containerLayer, hitTestRect, hitTestLocation, transformState, translationOffset); + + // If the transform can't be inverted, then don't hit test this layer at all. + if (!newTransformState->m_accumulatedTransform.isInvertible()) + return 0; + + // Compute the point and the hit test rect in the coords of this layer by using the values + // from the transformState, which store the point and quad in the coords of the last flattened + // layer, and the accumulated transform which lets up map through preserve-3d layers. + // + // We can't just map hitTestLocation and hitTestRect because they may have been flattened (losing z) + // by our container. + FloatPoint localPoint = newTransformState->mappedPoint(); + FloatQuad localPointQuad = newTransformState->mappedQuad(); + LayoutRect localHitTestRect = newTransformState->boundsOfMappedArea(); + HitTestLocation newHitTestLocation; + if (hitTestLocation.isRectBasedTest()) + newHitTestLocation = HitTestLocation(localPoint, localPointQuad); + else + newHitTestLocation = HitTestLocation(localPoint); + + // Now do a hit test with the root layer shifted to be us. + return hitTestLayer(this, containerLayer, request, result, localHitTestRect, newHitTestLocation, true, newTransformState.get(), zOffset); +} + bool RenderLayer::hitTestContents(const HitTestRequest& request, HitTestResult& result, const LayoutRect& layerBounds, const HitTestLocation& hitTestLocation, HitTestFilter hitTestFilter) const { ASSERT(isSelfPaintingLayer() || hasSelfPaintingLayerDescendant()); - if (!renderer()->hitTest(request, result, hitTestLocation, - toLayoutPoint(layerBounds.location() - renderBoxLocation()), - hitTestFilter)) { + if (!renderer()->hitTest(request, result, hitTestLocation, toLayoutPoint(layerBounds.location() - renderBoxLocation()), hitTestFilter)) { // It's wrong to set innerNode, but then claim that you didn't hit anything, unless it is // a rect-based test. ASSERT(!result.innerNode() || (result.isRectBasedTest() && result.rectBasedTestResult().size())); @@ -3999,6 +4844,8 @@ RenderLayer* RenderLayer::hitTestList(Vector<RenderLayer*>* list, RenderLayer* r RenderLayer* resultLayer = 0; for (int i = list->size() - 1; i >= 0; --i) { RenderLayer* childLayer = list->at(i); + if (childLayer->isOutOfFlowRenderFlowThread()) + continue; RenderLayer* hitLayer = 0; HitTestResult tempResult(result.hitTestLocation()); if (childLayer->isPaginated()) @@ -4027,7 +4874,7 @@ RenderLayer* RenderLayer::hitTestPaginatedChildLayer(RenderLayer* childLayer, Re const LayoutRect& hitTestRect, const HitTestLocation& hitTestLocation, const HitTestingTransformState* transformState, double* zOffset) { Vector<RenderLayer*> columnLayers; - RenderLayer* ancestorLayer = isNormalFlowOnly() ? parent() : stackingContext(); + RenderLayer* ancestorLayer = isNormalFlowOnly() ? parent() : stackingContainer(); for (RenderLayer* curr = childLayer->parent(); curr; curr = curr->parent()) { if (curr->renderer()->hasColumns() && checkContainingBlockChainForPagination(childLayer->renderer(), curr->renderBox())) columnLayers.append(curr); @@ -4059,39 +4906,17 @@ RenderLayer* RenderLayer::hitTestChildLayerColumns(RenderLayer* childLayer, Rend // We have to go backwards from the last column to the first. bool isHorizontal = columnBlock->style()->isHorizontalWritingMode(); LayoutUnit logicalLeft = columnBlock->logicalLeftOffsetForContent(); - LayoutUnit currLogicalTopOffset = 0; - int i; - for (i = 0; i < colCount; i++) { - LayoutRect colRect = columnBlock->columnRectAt(colInfo, i); - LayoutUnit blockDelta = (isHorizontal ? colRect.height() : colRect.width()); - if (columnBlock->style()->isFlippedBlocksWritingMode()) - currLogicalTopOffset += blockDelta; - else - currLogicalTopOffset -= blockDelta; - } - for (i = colCount - 1; i >= 0; i--) { + LayoutUnit currLogicalTopOffset = columnBlock->initialBlockOffsetForPainting(); + LayoutUnit blockDelta = columnBlock->blockDeltaForPaintingNextColumn(); + currLogicalTopOffset += colCount * blockDelta; + for (int i = colCount - 1; i >= 0; i--) { // For each rect, we clip to the rect, and then we adjust our coords. LayoutRect colRect = columnBlock->columnRectAt(colInfo, i); columnBlock->flipForWritingMode(colRect); LayoutUnit currLogicalLeftOffset = (isHorizontal ? colRect.x() : colRect.y()) - logicalLeft; - LayoutUnit blockDelta = (isHorizontal ? colRect.height() : colRect.width()); - if (columnBlock->style()->isFlippedBlocksWritingMode()) - currLogicalTopOffset -= blockDelta; - else - currLogicalTopOffset += blockDelta; + currLogicalTopOffset -= blockDelta; - LayoutSize offset; - if (isHorizontal) { - if (colInfo->progressionAxis() == ColumnInfo::InlineAxis) - offset = LayoutSize(currLogicalLeftOffset, currLogicalTopOffset); - else - offset = LayoutSize(0, colRect.y() + currLogicalTopOffset - columnBlock->borderTop() - columnBlock->paddingTop()); - } else { - if (colInfo->progressionAxis() == ColumnInfo::InlineAxis) - offset = LayoutSize(currLogicalTopOffset, currLogicalLeftOffset); - else - offset = LayoutSize(colRect.x() + currLogicalTopOffset - columnBlock->borderLeft() - columnBlock->paddingLeft(), 0); - } + LayoutSize offset = isHorizontal ? LayoutSize(currLogicalLeftOffset, currLogicalTopOffset) : LayoutSize(currLogicalTopOffset, currLogicalLeftOffset); colRect.moveBy(layerOffset); @@ -4147,9 +4972,8 @@ void RenderLayer::updateClipRects(const ClipRectsContext& clipRectsContext) { ClipRectsType clipRectsType = clipRectsContext.clipRectsType; ASSERT(clipRectsType < NumCachedClipRectsTypes); - if (m_clipRectsCache && m_clipRectsCache->m_clipRects[clipRectsType]) { + if (m_clipRectsCache && m_clipRectsCache->getClipRects(clipRectsType, clipRectsContext.respectOverflowClip)) { ASSERT(clipRectsContext.rootLayer == m_clipRectsCache->m_clipRectsRoot[clipRectsType]); - ASSERT(m_clipRectsCache->m_respectingOverflowClip[clipRectsType] == (clipRectsContext.respectOverflowClip == RespectOverflowClip)); ASSERT(m_clipRectsCache->m_scrollbarRelevancy[clipRectsType] == clipRectsContext.overlayScrollbarSizeRelevancy); #ifdef CHECK_CACHED_CLIP_RECTS @@ -4158,7 +4982,7 @@ void RenderLayer::updateClipRects(const ClipRectsContext& clipRectsContext) tempContext.clipRectsType = TemporaryClipRects; ClipRects clipRects; calculateClipRects(tempContext, clipRects); - ASSERT(clipRects == *m_clipRectsCache->m_clipRects[clipRectsType].get()); + ASSERT(clipRects == *m_clipRectsCache->getClipRects(clipRectsType, clipRectsContext.respectOverflowClip).get()); #endif return; // We have the correct cached value. } @@ -4175,14 +4999,13 @@ void RenderLayer::updateClipRects(const ClipRectsContext& clipRectsContext) if (!m_clipRectsCache) m_clipRectsCache = adoptPtr(new ClipRectsCache); - if (parentLayer && parentLayer->clipRects(clipRectsType) && clipRects == *parentLayer->clipRects(clipRectsType)) - m_clipRectsCache->m_clipRects[clipRectsType] = parentLayer->clipRects(clipRectsType); + if (parentLayer && parentLayer->clipRects(clipRectsContext) && clipRects == *parentLayer->clipRects(clipRectsContext)) + m_clipRectsCache->setClipRects(clipRectsType, clipRectsContext.respectOverflowClip, parentLayer->clipRects(clipRectsContext)); else - m_clipRectsCache->m_clipRects[clipRectsType] = ClipRects::create(clipRects); + m_clipRectsCache->setClipRects(clipRectsType, clipRectsContext.respectOverflowClip, ClipRects::create(clipRects)); #ifndef NDEBUG m_clipRectsCache->m_clipRectsRoot[clipRectsType] = clipRectsContext.rootLayer; - m_clipRectsCache->m_respectingOverflowClip[clipRectsType] = clipRectsContext.respectOverflowClip == RespectOverflowClip; m_clipRectsCache->m_scrollbarRelevancy[clipRectsType] = clipRectsContext.overlayScrollbarSizeRelevancy; #endif } @@ -4204,8 +5027,8 @@ void RenderLayer::calculateClipRects(const ClipRectsContext& clipRectsContext, C // Ensure that our parent's clip has been calculated so that we can examine the values. if (parentLayer) { - if (useCached && parentLayer->clipRects(clipRectsType)) - clipRects = *parentLayer->clipRects(clipRectsType); + if (useCached && parentLayer->clipRects(clipRectsContext)) + clipRects = *parentLayer->clipRects(clipRectsContext); else { ClipRectsContext parentContext(clipRectsContext); parentContext.overlayScrollbarSizeRelevancy = IgnoreOverlayScrollbarSize; // FIXME: why? @@ -4241,7 +5064,7 @@ void RenderLayer::calculateClipRects(const ClipRectsContext& clipRectsContext, C } if (renderer()->hasOverflowClip()) { - ClipRect newOverflowClip = toRenderBox(renderer())->overflowClipRect(offset, clipRectsContext.region, clipRectsContext.overlayScrollbarSizeRelevancy); + ClipRect newOverflowClip = toRenderBox(renderer())->overflowClipRectForChildLayers(offset, clipRectsContext.region, clipRectsContext.overlayScrollbarSizeRelevancy); if (renderer()->style()->hasBorderRadius()) newOverflowClip.setHasRadius(true); clipRects.setOverflowClipRect(intersection(newOverflowClip, clipRects.overflowClipRect())); @@ -4266,7 +5089,7 @@ void RenderLayer::parentClipRects(const ClipRectsContext& clipRectsContext, Clip } parent()->updateClipRects(clipRectsContext); - clipRects = *parent()->clipRects(clipRectsContext.clipRectsType); + clipRects = *parent()->clipRects(clipRectsContext); } static inline ClipRect backgroundClipRectForPosition(const ClipRects& parentRects, EPosition position) @@ -4283,8 +5106,18 @@ static inline ClipRect backgroundClipRectForPosition(const ClipRects& parentRect ClipRect RenderLayer::backgroundClipRect(const ClipRectsContext& clipRectsContext) const { ASSERT(parent()); + ClipRects parentRects; - parentClipRects(clipRectsContext, parentRects); + + // If we cross into a different pagination context, then we can't rely on the cache. + // Just switch over to using TemporaryClipRects. + if (clipRectsContext.clipRectsType != TemporaryClipRects && parent()->enclosingPaginationLayer() != enclosingPaginationLayer()) { + ClipRectsContext tempContext(clipRectsContext); + tempContext.clipRectsType = TemporaryClipRects; + parentClipRects(tempContext, parentRects); + } else + parentClipRects(clipRectsContext, parentRects); + ClipRect backgroundClipRect = backgroundClipRectForPosition(parentRects, renderer()->style()->position()); RenderView* view = renderer()->view(); ASSERT(view); @@ -4364,7 +5197,7 @@ LayoutRect RenderLayer::childrenClipRect() const ClipRectsContext clipRectsContext(clippingRootLayer, 0, TemporaryClipRects); // Need to use temporary clip rects, because the value of 'dontClipToOverflow' may be different from the painting path (<rdar://problem/11844909>). calculateRects(clipRectsContext, renderView->unscaledDocumentRect(), layerBounds, backgroundRect, foregroundRect, outlineRect); - return clippingRootLayer->renderer()->localToAbsoluteQuad(FloatQuad(foregroundRect.rect()), SnapOffsetForTransforms).enclosingBoundingBox(); + return clippingRootLayer->renderer()->localToAbsoluteQuad(FloatQuad(foregroundRect.rect())).enclosingBoundingBox(); } LayoutRect RenderLayer::selfClipRect() const @@ -4377,7 +5210,7 @@ LayoutRect RenderLayer::selfClipRect() const ClipRect backgroundRect, foregroundRect, outlineRect; ClipRectsContext clipRectsContext(clippingRootLayer, 0, PaintingClipRects); calculateRects(clipRectsContext, renderView->documentRect(), layerBounds, backgroundRect, foregroundRect, outlineRect); - return clippingRootLayer->renderer()->localToAbsoluteQuad(FloatQuad(backgroundRect.rect()), SnapOffsetForTransforms).enclosingBoundingBox(); + return clippingRootLayer->renderer()->localToAbsoluteQuad(FloatQuad(backgroundRect.rect())).enclosingBoundingBox(); } LayoutRect RenderLayer::localClipRect() const @@ -4452,10 +5285,10 @@ bool RenderLayer::intersectsDamageRect(const LayoutRect& layerBounds, const Layo // Otherwise we need to compute the bounding box of this single layer and see if it intersects // the damage rect. - return boundingBox(rootLayer, offsetFromRoot).intersects(damageRect); + return boundingBox(rootLayer, 0, offsetFromRoot).intersects(damageRect); } -LayoutRect RenderLayer::localBoundingBox() const +LayoutRect RenderLayer::localBoundingBox(CalculateLayerBoundsFlags flags) 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 @@ -4483,7 +5316,7 @@ LayoutRect RenderLayer::localBoundingBox() const } else { RenderBox* box = renderBox(); ASSERT(box); - if (box->hasMask()) { + if (!(flags & DontConstrainForMask) && box->hasMask()) { result = box->maskClipRect(); box->flipForWritingMode(result); // The mask clip rect is in physical coordinates, so we have to flip, since localBoundingBox is not. } else { @@ -4503,14 +5336,33 @@ LayoutRect RenderLayer::localBoundingBox() const return result; } -LayoutRect RenderLayer::boundingBox(const RenderLayer* ancestorLayer, const LayoutPoint* offsetFromRoot) const +LayoutRect RenderLayer::boundingBox(const RenderLayer* ancestorLayer, CalculateLayerBoundsFlags flags, const LayoutPoint* offsetFromRoot) const { - LayoutRect result = localBoundingBox(); + LayoutRect result = localBoundingBox(flags); if (renderer()->isBox()) renderBox()->flipForWritingMode(result); else renderer()->containingBlock()->flipForWritingMode(result); + if (enclosingPaginationLayer() && (flags & UseFragmentBoxes)) { + // Split our box up into the actual fragment boxes that render in the columns/pages and unite those together to + // get our true bounding box. + LayoutPoint offsetWithinPaginationLayer; + convertToLayerCoords(enclosingPaginationLayer(), offsetWithinPaginationLayer); + result.moveBy(offsetWithinPaginationLayer); + + RenderFlowThread* enclosingFlowThread = toRenderFlowThread(enclosingPaginationLayer()->renderer()); + result = enclosingFlowThread->fragmentsBoundingBox(result); + + LayoutPoint delta; + if (offsetFromRoot) + delta = *offsetFromRoot; + else + enclosingPaginationLayer()->convertToLayerCoords(ancestorLayer, delta); + result.moveBy(delta); + return result; + } + LayoutPoint delta; if (offsetFromRoot) delta = *offsetFromRoot; @@ -4526,14 +5378,14 @@ IntRect RenderLayer::absoluteBoundingBox() const return pixelSnappedIntRect(boundingBox(root())); } -IntRect RenderLayer::calculateLayerBounds(const RenderLayer* ancestorLayer, const LayoutPoint* offsetFromRoot, CalculateLayerBoundsFlags flags) const +LayoutRect RenderLayer::calculateLayerBounds(const RenderLayer* ancestorLayer, const LayoutPoint* offsetFromRoot, CalculateLayerBoundsFlags flags) const { if (!isSelfPaintingLayer()) - return IntRect(); + return LayoutRect(); // FIXME: This could be improved to do a check like hasVisibleNonCompositingDescendantLayers() (bug 92580). if ((flags & ExcludeHiddenDescendants) && this != ancestorLayer && !hasVisibleContent() && !hasVisibleDescendant()) - return IntRect(); + return LayoutRect(); RenderLayerModelObject* renderer = this->renderer(); @@ -4542,7 +5394,7 @@ IntRect RenderLayer::calculateLayerBounds(const RenderLayer* ancestorLayer, cons return renderer->view()->unscaledDocumentRect(); } - LayoutRect boundingBoxRect = localBoundingBox(); + LayoutRect boundingBoxRect = localBoundingBox(flags); if (renderer->isBox()) toRenderBox(renderer)->flipForWritingMode(boundingBoxRect); @@ -4573,23 +5425,23 @@ IntRect RenderLayer::calculateLayerBounds(const RenderLayer* ancestorLayer, cons LayoutPoint ancestorRelOffset; convertToLayerCoords(ancestorLayer, ancestorRelOffset); localClipRect.moveBy(ancestorRelOffset); - return pixelSnappedIntRect(localClipRect); + return localClipRect; } } // FIXME: should probably just pass 'flags' down to descendants. - CalculateLayerBoundsFlags descendantFlags = DefaultCalculateLayerBoundsFlags | (flags & ExcludeHiddenDescendants); + CalculateLayerBoundsFlags descendantFlags = DefaultCalculateLayerBoundsFlags | (flags & ExcludeHiddenDescendants) | (flags & IncludeCompositedDescendants); const_cast<RenderLayer*>(this)->updateLayerListsIfNeeded(); if (RenderLayer* reflection = reflectionLayer()) { if (!reflection->isComposited()) { - IntRect childUnionBounds = reflection->calculateLayerBounds(this, 0, descendantFlags); + LayoutRect childUnionBounds = reflection->calculateLayerBounds(this, 0, descendantFlags); unionBounds.unite(childUnionBounds); } } - ASSERT(isStackingContext() || (!posZOrderList() || !posZOrderList()->size())); + ASSERT(isStackingContainer() || (!posZOrderList() || !posZOrderList()->size())); #if !ASSERT_DISABLED LayerListMutationDetector mutationChecker(const_cast<RenderLayer*>(this)); @@ -4599,8 +5451,8 @@ IntRect RenderLayer::calculateLayerBounds(const RenderLayer* ancestorLayer, cons size_t listSize = negZOrderList->size(); for (size_t i = 0; i < listSize; ++i) { RenderLayer* curLayer = negZOrderList->at(i); - if (!curLayer->isComposited()) { - IntRect childUnionBounds = curLayer->calculateLayerBounds(this, 0, descendantFlags); + if (flags & IncludeCompositedDescendants || !curLayer->isComposited()) { + LayoutRect childUnionBounds = curLayer->calculateLayerBounds(this, 0, descendantFlags); unionBounds.unite(childUnionBounds); } } @@ -4610,8 +5462,8 @@ IntRect RenderLayer::calculateLayerBounds(const RenderLayer* ancestorLayer, cons size_t listSize = posZOrderList->size(); for (size_t i = 0; i < listSize; ++i) { RenderLayer* curLayer = posZOrderList->at(i); - if (!curLayer->isComposited()) { - IntRect childUnionBounds = curLayer->calculateLayerBounds(this, 0, descendantFlags); + if (flags & IncludeCompositedDescendants || !curLayer->isComposited()) { + LayoutRect childUnionBounds = curLayer->calculateLayerBounds(this, 0, descendantFlags); unionBounds.unite(childUnionBounds); } } @@ -4621,8 +5473,11 @@ IntRect RenderLayer::calculateLayerBounds(const RenderLayer* ancestorLayer, cons size_t listSize = normalFlowList->size(); for (size_t i = 0; i < listSize; ++i) { RenderLayer* curLayer = normalFlowList->at(i); - if (!curLayer->isComposited()) { - IntRect curAbsBounds = curLayer->calculateLayerBounds(this, 0, descendantFlags); + // RenderView will always return the size of the document, before reaching this point, + // so there's no way we could hit a RenderNamedFlowThread here. + ASSERT(!curLayer->isOutOfFlowRenderFlowThread()); + if (flags & IncludeCompositedDescendants || !curLayer->isComposited()) { + LayoutRect curAbsBounds = curLayer->calculateLayerBounds(this, 0, descendantFlags); unionBounds.unite(curAbsBounds); } } @@ -4632,15 +5487,8 @@ IntRect RenderLayer::calculateLayerBounds(const RenderLayer* ancestorLayer, cons // FIXME: We can optimize the size of the composited layers, by not enlarging // filtered areas with the outsets if we know that the filter is going to render in hardware. // https://bugs.webkit.org/show_bug.cgi?id=81239 - if ((flags & IncludeLayerFilterOutsets) && renderer->style()->hasFilterOutsets()) { - int topOutset; - int rightOutset; - int bottomOutset; - int leftOutset; - renderer->style()->getFilterOutsets(topOutset, rightOutset, bottomOutset, leftOutset); - unionBounds.move(-leftOutset, -topOutset); - unionBounds.expand(leftOutset + rightOutset, topOutset + bottomOutset); - } + if (flags & IncludeLayerFilterOutsets) + renderer->style()->filterOutsets().expandRect(unionBounds); #endif if ((flags & IncludeSelfTransform) && paintsWithTransform(PaintBehaviorNormal)) { @@ -4656,7 +5504,7 @@ IntRect RenderLayer::calculateLayerBounds(const RenderLayer* ancestorLayer, cons convertToLayerCoords(ancestorLayer, ancestorRelOffset); unionBounds.moveBy(ancestorRelOffset); - return pixelSnappedIntRect(unionBounds); + return unionBounds; } void RenderLayer::clearClipRectsIncludingDescendants(ClipRectsType typeToClear) @@ -4677,7 +5525,9 @@ void RenderLayer::clearClipRects(ClipRectsType typeToClear) m_clipRectsCache = nullptr; else { ASSERT(typeToClear < NumCachedClipRectsTypes); - m_clipRectsCache->m_clipRects[typeToClear] = nullptr; + RefPtr<ClipRects> dummy; + m_clipRectsCache->setClipRects(typeToClear, RespectOverflowClip, dummy); + m_clipRectsCache->setClipRects(typeToClear, IgnoreOverflowClip, dummy); } } @@ -4717,6 +5567,11 @@ bool RenderLayer::hasCompositedMask() const return m_backing && m_backing->hasMaskLayer(); } +GraphicsLayer* RenderLayer::layerForScrolling() const +{ + return m_backing ? m_backing->scrollingContentsLayer() : 0; +} + GraphicsLayer* RenderLayer::layerForHorizontalScrollbar() const { return m_backing ? m_backing->layerForHorizontalScrollbar() : 0; @@ -4743,6 +5598,73 @@ bool RenderLayer::paintsWithTransform(PaintBehavior paintBehavior) const return transform() && ((paintBehavior & PaintBehaviorFlattenCompositingLayers) || paintsToWindow); } +bool RenderLayer::backgroundIsKnownToBeOpaqueInRect(const LayoutRect& localRect) const +{ + if (!isSelfPaintingLayer() && !hasSelfPaintingLayerDescendant()) + return false; + + if (paintsWithTransparency(PaintBehaviorNormal)) + return false; + + // We can't use hasVisibleContent(), because that will be true if our renderer is hidden, but some child + // is visible and that child doesn't cover the entire rect. + if (renderer()->style()->visibility() != VISIBLE) + return false; + +#if ENABLE(CSS_FILTERS) + if (paintsWithFilters() && renderer()->style()->filter().hasFilterThatAffectsOpacity()) + return false; +#endif + + // FIXME: Handle simple transforms. + if (paintsWithTransform(PaintBehaviorNormal)) + return false; + + // FIXME: Remove this check. + // This function should not be called when layer-lists are dirty. + // It is somehow getting triggered during style update. + if (m_zOrderListsDirty || m_normalFlowListDirty) + return false; + + // FIXME: We currently only check the immediate renderer, + // which will miss many cases. + if (renderer()->backgroundIsKnownToBeOpaqueInRect(localRect)) + return true; + + // We can't consult child layers if we clip, since they might cover + // parts of the rect that are clipped out. + if (renderer()->hasOverflowClip()) + return false; + + return listBackgroundIsKnownToBeOpaqueInRect(posZOrderList(), localRect) + || listBackgroundIsKnownToBeOpaqueInRect(negZOrderList(), localRect) + || listBackgroundIsKnownToBeOpaqueInRect(normalFlowList(), localRect); +} + +bool RenderLayer::listBackgroundIsKnownToBeOpaqueInRect(const Vector<RenderLayer*>* list, const LayoutRect& localRect) const +{ + if (!list || list->isEmpty()) + return false; + + for (Vector<RenderLayer*>::const_reverse_iterator iter = list->rbegin(); iter != list->rend(); ++iter) { + const RenderLayer* childLayer = *iter; + if (childLayer->isComposited()) + continue; + + if (!childLayer->canUseConvertToLayerCoords()) + continue; + + LayoutPoint childOffset; + LayoutRect childLocalRect(localRect); + childLayer->convertToLayerCoords(this, childOffset); + childLocalRect.moveBy(-childOffset); + + if (childLayer->backgroundIsKnownToBeOpaqueInRect(childLocalRect)) + return true; + } + return false; +} + void RenderLayer::setParent(RenderLayer* parent) { if (parent == m_parent) @@ -4770,7 +5692,7 @@ static inline bool compareZIndex(RenderLayer* first, RenderLayer* second) void RenderLayer::dirtyZOrderLists() { ASSERT(m_layerListMutationAllowed); - ASSERT(isStackingContext()); + ASSERT(isStackingContainer()); if (m_posZOrderList) m_posZOrderList->clear(); @@ -4779,14 +5701,17 @@ void RenderLayer::dirtyZOrderLists() m_zOrderListsDirty = true; #if USE(ACCELERATED_COMPOSITING) - if (!renderer()->documentBeingDestroyed()) + if (!renderer()->documentBeingDestroyed()) { compositor()->setCompositingLayersNeedRebuild(); + if (acceleratedCompositingForOverflowScrollEnabled()) + compositor()->setShouldReevaluateCompositingAfterLayout(); + } #endif } -void RenderLayer::dirtyStackingContextZOrderLists() +void RenderLayer::dirtyStackingContainerZOrderLists() { - RenderLayer* sc = stackingContext(); + RenderLayer* sc = stackingContainer(); if (sc) sc->dirtyZOrderLists(); } @@ -4800,16 +5725,24 @@ void RenderLayer::dirtyNormalFlowList() m_normalFlowListDirty = true; #if USE(ACCELERATED_COMPOSITING) - if (!renderer()->documentBeingDestroyed()) + if (!renderer()->documentBeingDestroyed()) { compositor()->setCompositingLayersNeedRebuild(); + if (acceleratedCompositingForOverflowScrollEnabled()) + compositor()->setShouldReevaluateCompositingAfterLayout(); + } #endif } void RenderLayer::rebuildZOrderLists() { ASSERT(m_layerListMutationAllowed); - ASSERT(isDirtyStackingContext()); + ASSERT(isDirtyStackingContainer()); + rebuildZOrderLists(StopAtStackingContainers, m_posZOrderList, m_negZOrderList); + m_zOrderListsDirty = false; +} +void RenderLayer::rebuildZOrderLists(CollectLayersBehavior behavior, OwnPtr<Vector<RenderLayer*> >& posZOrderList, OwnPtr<Vector<RenderLayer*> >& negZOrderList) +{ #if USE(ACCELERATED_COMPOSITING) bool includeHiddenLayers = compositor()->inCompositingMode(); #else @@ -4817,14 +5750,14 @@ void RenderLayer::rebuildZOrderLists() #endif for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) if (!m_reflection || reflectionLayer() != child) - child->collectLayers(includeHiddenLayers, m_posZOrderList, m_negZOrderList); + child->collectLayers(includeHiddenLayers, behavior, posZOrderList, negZOrderList); // Sort the two lists. - if (m_posZOrderList) - std::stable_sort(m_posZOrderList->begin(), m_posZOrderList->end(), compareZIndex); + if (posZOrderList) + std::stable_sort(posZOrderList->begin(), posZOrderList->end(), compareZIndex); - if (m_negZOrderList) - std::stable_sort(m_negZOrderList->begin(), m_negZOrderList->end(), compareZIndex); + if (negZOrderList) + std::stable_sort(negZOrderList->begin(), negZOrderList->end(), compareZIndex); #if ENABLE(DIALOG_ELEMENT) // Append layers for top layer elements after normal layer collection, to ensure they are on top regardless of z-indexes. @@ -4832,16 +5765,15 @@ void RenderLayer::rebuildZOrderLists() if (isRootLayer()) { RenderObject* view = renderer()->view(); for (RenderObject* child = view->firstChild(); child; child = child->nextSibling()) { - Element* childElement = child->node()->isElementNode() ? toElement(child->node()) : 0; + Element* childElement = (child->node() && child->node()->isElementNode()) ? toElement(child->node()) : 0; if (childElement && childElement->isInTopLayer()) { RenderLayer* layer = toRenderLayerModelObject(child)->layer(); - m_posZOrderList->append(layer); + posZOrderList->append(layer); } } } #endif - m_zOrderListsDirty = false; } void RenderLayer::updateNormalFlowList() @@ -4863,7 +5795,7 @@ void RenderLayer::updateNormalFlowList() m_normalFlowListDirty = false; } -void RenderLayer::collectLayers(bool includeHiddenLayers, OwnPtr<Vector<RenderLayer*> >& posBuffer, OwnPtr<Vector<RenderLayer*> >& negBuffer) +void RenderLayer::collectLayers(bool includeHiddenLayers, CollectLayersBehavior behavior, OwnPtr<Vector<RenderLayer*> >& posBuffer, OwnPtr<Vector<RenderLayer*> >& negBuffer) { #if ENABLE(DIALOG_ELEMENT) if (isInTopLayer()) @@ -4872,9 +5804,10 @@ void RenderLayer::collectLayers(bool includeHiddenLayers, OwnPtr<Vector<RenderLa updateDescendantDependentFlags(); + bool isStacking = behavior == StopAtStackingContexts ? isStackingContext() : isStackingContainer(); // Overflow layers are just painted by their enclosing layers, so they don't get put in zorder lists. - bool includeHiddenLayer = includeHiddenLayers || (m_hasVisibleContent || (m_hasVisibleDescendant && isStackingContext())); - if (includeHiddenLayer && !isNormalFlowOnly() && !renderer()->isRenderFlowThread()) { + bool includeHiddenLayer = includeHiddenLayers || (m_hasVisibleContent || (m_hasVisibleDescendant && isStacking)); + if (includeHiddenLayer && !isNormalFlowOnly()) { // Determine which buffer the child should be in. OwnPtr<Vector<RenderLayer*> >& buffer = (zIndex() >= 0) ? posBuffer : negBuffer; @@ -4887,18 +5820,19 @@ void RenderLayer::collectLayers(bool includeHiddenLayers, OwnPtr<Vector<RenderLa } // Recur into our children to collect more layers, but only if we don't establish - // a stacking context. - if ((includeHiddenLayers || m_hasVisibleDescendant) && !isStackingContext()) { + // a stacking context/container. + if ((includeHiddenLayers || m_hasVisibleDescendant) && !isStacking) { for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) { // Ignore reflections. if (!m_reflection || reflectionLayer() != child) - child->collectLayers(includeHiddenLayers, posBuffer, negBuffer); + child->collectLayers(includeHiddenLayers, behavior, posBuffer, negBuffer); } } } void RenderLayer::updateLayerListsIfNeeded() { + bool shouldUpdateDescendantsAreContiguousInStackingOrder = isStackingContext() && (m_zOrderListsDirty || m_normalFlowListDirty); updateZOrderLists(); updateNormalFlowList(); @@ -4906,13 +5840,21 @@ void RenderLayer::updateLayerListsIfNeeded() reflectionLayer->updateZOrderLists(); reflectionLayer->updateNormalFlowList(); } + + if (shouldUpdateDescendantsAreContiguousInStackingOrder) { + updateDescendantsAreContiguousInStackingOrder(); + // The above function can cause us to update m_needsCompositedScrolling + // and dirty our layer lists. Refresh them if necessary. + updateZOrderLists(); + updateNormalFlowList(); + } } void RenderLayer::updateCompositingAndLayerListsIfNeeded() { #if USE(ACCELERATED_COMPOSITING) if (compositor()->inCompositingMode()) { - if (isDirtyStackingContext() || m_normalFlowListDirty) + if (isDirtyStackingContainer() || m_normalFlowListDirty) compositor()->updateCompositingLayers(CompositingUpdateOnHitTest, this); return; } @@ -4993,14 +5935,15 @@ bool RenderLayer::shouldBeNormalFlowOnly() const && !renderer()->hasBlendMode() #endif && !isTransparent() - && !usesCompositedScrolling(); + && !needsCompositedScrolling() + ; } bool RenderLayer::shouldBeSelfPaintingLayer() const { return !isNormalFlowOnly() || hasOverlayScrollbars() - || usesCompositedScrolling() + || needsCompositedScrolling() || renderer()->hasReflection() || renderer()->hasMask() || renderer()->isTableRow() @@ -5025,6 +5968,59 @@ void RenderLayer::updateSelfPaintingLayer() parent()->dirtyAncestorChainHasSelfPaintingLayerDescendantStatus(); } +bool RenderLayer::hasNonEmptyChildRenderers() const +{ + // Some HTML can cause whitespace text nodes to have renderers, like: + // <div> + // <img src=...> + // </div> + // so test for 0x0 RenderTexts here + for (RenderObject* child = renderer()->firstChild(); child; child = child->nextSibling()) { + if (!child->hasLayer()) { + if (child->isRenderInline() || !child->isBox()) + return true; + + if (toRenderBox(child)->width() > 0 || toRenderBox(child)->height() > 0) + return true; + } + } + return false; +} + +static bool hasBoxDecorations(const RenderStyle* style) +{ + return style->hasBorder() || style->hasBorderRadius() || style->hasOutline() || style->hasAppearance() || style->boxShadow() || style->hasFilter(); +} + +bool RenderLayer::hasBoxDecorationsOrBackground() const +{ + return hasBoxDecorations(renderer()->style()) || renderer()->hasBackground(); +} + +bool RenderLayer::hasVisibleBoxDecorations() const +{ + if (!hasVisibleContent()) + return false; + + return hasBoxDecorationsOrBackground() || hasOverflowControls(); +} + +bool RenderLayer::isVisuallyNonEmpty() const +{ + ASSERT(!m_visibleDescendantStatusDirty); + + if (hasVisibleContent() && hasNonEmptyChildRenderers()) + return true; + + if (renderer()->isReplaced() || renderer()->hasMask()) + return true; + + if (hasVisibleBoxDecorations()) + return true; + + return false; +} + void RenderLayer::updateStackingContextsAfterStyleChange(const RenderStyle* oldStyle) { if (!oldStyle) @@ -5033,7 +6029,7 @@ void RenderLayer::updateStackingContextsAfterStyleChange(const RenderStyle* oldS bool wasStackingContext = isStackingContext(oldStyle); bool isStackingContext = this->isStackingContext(); if (isStackingContext != wasStackingContext) { - dirtyStackingContextZOrderLists(); + dirtyStackingContainerZOrderLists(); if (isStackingContext) dirtyZOrderLists(); else @@ -5044,7 +6040,7 @@ void RenderLayer::updateStackingContextsAfterStyleChange(const RenderStyle* oldS // FIXME: RenderLayer already handles visibility changes through our visiblity dirty bits. This logic could // likely be folded along with the rest. if (oldStyle->zIndex() != renderer()->style()->zIndex() || oldStyle->visibility() != renderer()->style()->visibility()) { - dirtyStackingContextZOrderLists(); + dirtyStackingContainerZOrderLists(); if (isStackingContext) dirtyZOrderLists(); } @@ -5093,9 +6089,59 @@ void RenderLayer::updateScrollbarsAfterStyleChange(const RenderStyle* oldStyle) } if (!m_scrollDimensionsDirty) - updateScrollableAreaSet((hasHorizontalOverflow() || hasVerticalOverflow()) && scrollsOverflow() && allowsScrolling()); + updateScrollableAreaSet(hasScrollableHorizontalOverflow() || hasScrollableVerticalOverflow()); +} + +void RenderLayer::setAncestorChainHasOutOfFlowPositionedDescendant(RenderObject* containingBlock) +{ + for (RenderLayer* layer = this; layer; layer = layer->parent()) { + if (!layer->m_hasOutOfFlowPositionedDescendantDirty && layer->hasOutOfFlowPositionedDescendant()) + break; + + layer->m_hasOutOfFlowPositionedDescendantDirty = false; + layer->m_hasOutOfFlowPositionedDescendant = true; +#if USE(ACCELERATED_COMPOSITING) + layer->updateNeedsCompositedScrolling(); +#endif + + if (layer->renderer() && layer->renderer() == containingBlock) + break; + } +} + +void RenderLayer::dirtyAncestorChainHasOutOfFlowPositionedDescendantStatus() +{ + m_hasOutOfFlowPositionedDescendantDirty = true; + if (parent()) + parent()->dirtyAncestorChainHasOutOfFlowPositionedDescendantStatus(); +} + +void RenderLayer::updateOutOfFlowPositioned(const RenderStyle* oldStyle) +{ + bool wasOutOfFlowPositioned = oldStyle && (oldStyle->position() == AbsolutePosition || oldStyle->position() == FixedPosition); + if (parent() && ((renderer() && renderer()->isOutOfFlowPositioned()) != wasOutOfFlowPositioned)) { + parent()->dirtyAncestorChainHasOutOfFlowPositionedDescendantStatus(); +#if USE(ACCELERATED_COMPOSITING) + if (!renderer()->documentBeingDestroyed() && acceleratedCompositingForOverflowScrollEnabled()) + compositor()->setShouldReevaluateCompositingAfterLayout(); +#endif + } } +#if USE(ACCELERATED_COMPOSITING) +inline bool RenderLayer::needsCompositingLayersRebuiltForClip(const RenderStyle* oldStyle, const RenderStyle* newStyle) const +{ + ASSERT(newStyle); + return oldStyle && (oldStyle->clip() != newStyle->clip() || oldStyle->hasClip() != newStyle->hasClip()); +} + +inline bool RenderLayer::needsCompositingLayersRebuiltForOverflow(const RenderStyle* oldStyle, const RenderStyle* newStyle) const +{ + ASSERT(newStyle); + return !isComposited() && oldStyle && (oldStyle->overflowX() != newStyle->overflowX()) && stackingContainer()->hasCompositingDescendant(); +} +#endif // USE(ACCELERATED_COMPOSITING) + void RenderLayer::styleChanged(StyleDifference, const RenderStyle* oldStyle) { bool isNormalFlowOnly = shouldBeNormalFlowOnly(); @@ -5104,29 +6150,32 @@ void RenderLayer::styleChanged(StyleDifference, const RenderStyle* oldStyle) RenderLayer* p = parent(); if (p) p->dirtyNormalFlowList(); - dirtyStackingContextZOrderLists(); + dirtyStackingContainerZOrderLists(); } if (renderer()->style()->overflowX() == OMARQUEE && renderer()->style()->marqueeBehavior() != MNONE && renderer()->isBox()) { if (!m_marquee) m_marquee = adoptPtr(new RenderMarquee(this)); + FeatureObserver::observe(renderer()->document(), renderer()->isHTMLMarquee() ? FeatureObserver::HTMLMarqueeElement : FeatureObserver::CSSOverflowMarquee); m_marquee->updateMarqueeStyle(); } else if (m_marquee) { m_marquee.clear(); } - updateStackingContextsAfterStyleChange(oldStyle); updateScrollbarsAfterStyleChange(oldStyle); + updateStackingContextsAfterStyleChange(oldStyle); // Overlay scrollbars can make this layer self-painting so we need // to recompute the bit once scrollbars have been updated. updateSelfPaintingLayer(); + updateOutOfFlowPositioned(oldStyle); if (!hasReflection() && m_reflection) removeReflection(); else if (hasReflection()) { if (!m_reflection) createReflection(); + FeatureObserver::observe(renderer()->document(), FeatureObserver::Reflection); updateReflectionStyle(); } @@ -5149,16 +6198,15 @@ void RenderLayer::styleChanged(StyleDifference, const RenderStyle* oldStyle) #endif #if USE(ACCELERATED_COMPOSITING) - if (compositor()->updateLayerCompositingState(this)) - compositor()->setCompositingLayersNeedRebuild(); - else if (oldStyle && (oldStyle->clip() != renderer()->style()->clip() || oldStyle->hasClip() != renderer()->style()->hasClip())) + updateNeedsCompositedScrolling(); + + const RenderStyle* newStyle = renderer()->style(); + if (compositor()->updateLayerCompositingState(this) + || needsCompositingLayersRebuiltForClip(oldStyle, newStyle) + || needsCompositingLayersRebuiltForOverflow(oldStyle, newStyle)) compositor()->setCompositingLayersNeedRebuild(); - else if (m_backing) - m_backing->updateGraphicsLayerGeometry(); - else if (oldStyle && oldStyle->overflowX() != renderer()->style()->overflowX()) { - if (stackingContext()->hasCompositingDescendant()) - compositor()->setCompositingLayersNeedRebuild(); - } + else if (isComposited()) + backing()->updateGraphicsLayerGeometry(); #endif #if ENABLE(CSS_FILTERS) @@ -5188,19 +6236,27 @@ void RenderLayer::updateScrollableAreaSet(bool hasOverflow) if (HTMLFrameOwnerElement* owner = frame->ownerElement()) isVisibleToHitTest &= owner->renderer() && owner->renderer()->visibleToHitTesting(); - if (hasOverflow && isVisibleToHitTest) - frameView->addScrollableArea(this); + bool isScrollable = hasOverflow && isVisibleToHitTest; + bool addedOrRemoved = false; + if (isScrollable) + addedOrRemoved = frameView->addScrollableArea(this); else - frameView->removeScrollableArea(this); + addedOrRemoved = frameView->removeScrollableArea(this); + + if (addedOrRemoved) { +#if USE(ACCELERATED_COMPOSITING) + updateNeedsCompositedScrolling(); +#endif + } } void RenderLayer::updateScrollCornerStyle() { - RenderObject* actualRenderer = renderer()->node() ? renderer()->node()->shadowAncestorNode()->renderer() : renderer(); - RefPtr<RenderStyle> corner = renderer()->hasOverflowClip() ? actualRenderer->getUncachedPseudoStyle(SCROLLBAR_CORNER, actualRenderer->style()) : PassRefPtr<RenderStyle>(0); + RenderObject* actualRenderer = rendererForScrollbar(renderer()); + RefPtr<RenderStyle> corner = renderer()->hasOverflowClip() ? actualRenderer->getUncachedPseudoStyle(PseudoStyleRequest(SCROLLBAR_CORNER), actualRenderer->style()) : PassRefPtr<RenderStyle>(0); if (corner) { if (!m_scrollCorner) { - m_scrollCorner = new (renderer()->renderArena()) RenderScrollbarPart(renderer()->document()); + m_scrollCorner = RenderScrollbarPart::createAnonymous(renderer()->document()); m_scrollCorner->setParent(renderer()); } m_scrollCorner->setStyle(corner.release()); @@ -5212,11 +6268,11 @@ void RenderLayer::updateScrollCornerStyle() void RenderLayer::updateResizerStyle() { - RenderObject* actualRenderer = renderer()->node() ? renderer()->node()->shadowAncestorNode()->renderer() : renderer(); - RefPtr<RenderStyle> resizer = renderer()->hasOverflowClip() ? actualRenderer->getUncachedPseudoStyle(RESIZER, actualRenderer->style()) : PassRefPtr<RenderStyle>(0); + RenderObject* actualRenderer = rendererForScrollbar(renderer()); + RefPtr<RenderStyle> resizer = renderer()->hasOverflowClip() ? actualRenderer->getUncachedPseudoStyle(PseudoStyleRequest(RESIZER), actualRenderer->style()) : PassRefPtr<RenderStyle>(0); if (resizer) { if (!m_resizer) { - m_resizer = new (renderer()->renderArena()) RenderScrollbarPart(renderer()->document()); + m_resizer = RenderScrollbarPart::createAnonymous(renderer()->document()); m_resizer->setParent(renderer()); } m_resizer->setStyle(resizer.release()); @@ -5234,7 +6290,7 @@ RenderLayer* RenderLayer::reflectionLayer() const void RenderLayer::createReflection() { ASSERT(!m_reflection); - m_reflection = new (renderer()->renderArena()) RenderReplica(renderer()->document()); + m_reflection = RenderReplica::createAnonymous(renderer()->document()); m_reflection->setParent(renderer()); // We create a 1-way connection. } @@ -5321,14 +6377,20 @@ FilterOperations RenderLayer::computeFilterOperations(const RenderStyle* style) RefPtr<CustomFilterProgram> program = customOperation->program(); if (!program->isLoaded()) continue; - - CustomFilterGlobalContext* globalContext = renderer()->view()->customFilterGlobalContext(); - RefPtr<CustomFilterValidatedProgram> validatedProgram = globalContext->getValidatedProgram(program->programInfo()); + + RefPtr<CustomFilterValidatedProgram> validatedProgram = program->validatedProgram(); + if (!validatedProgram) { + // Lazily create a validated program and store it on the CustomFilterProgram. + CustomFilterGlobalContext* globalContext = renderer()->view()->customFilterGlobalContext(); + validatedProgram = CustomFilterValidatedProgram::create(globalContext, program->programInfo()); + program->setValidatedProgram(validatedProgram); + } + if (!validatedProgram->isInitialized()) continue; RefPtr<ValidatedCustomFilterOperation> validatedOperation = ValidatedCustomFilterOperation::create(validatedProgram.release(), - customOperation->parameters(), customOperation->meshRows(), customOperation->meshColumns(), customOperation->meshBoxType(), customOperation->meshType()); + customOperation->parameters(), customOperation->meshRows(), customOperation->meshColumns(), customOperation->meshType()); outputFilters.operations().append(validatedOperation.release()); continue; } @@ -5391,7 +6453,7 @@ void RenderLayer::updateOrRemoveFilterEffectRenderer() // If the filter fails to build, remove it from the layer. It will still attempt to // go through regular processing (e.g. compositing), but never apply anything. - if (!filterInfo->renderer()->build(renderer()->document(), computeFilterOperations(renderer()->style()))) + if (!filterInfo->renderer()->build(renderer(), computeFilterOperations(renderer()->style()))) filterInfo->setRenderer(0); } diff --git a/Source/WebCore/rendering/RenderLayer.h b/Source/WebCore/rendering/RenderLayer.h index c5e652411..48113920a 100644 --- a/Source/WebCore/rendering/RenderLayer.h +++ b/Source/WebCore/rendering/RenderLayer.h @@ -49,19 +49,18 @@ #include "ScrollableArea.h" #include <wtf/OwnPtr.h> -#if ENABLE(CSS_FILTERS) -#include "RenderLayerFilterInfo.h" -#endif - namespace WebCore { #if ENABLE(CSS_FILTERS) class FilterEffectRenderer; +class FilterEffectRendererHelper; class FilterOperations; +class RenderLayerFilterInfo; #endif class HitTestRequest; class HitTestResult; class HitTestingTransformState; +class RenderFlowThread; class RenderGeometryMap; class RenderMarquee; class RenderReplica; @@ -77,11 +76,12 @@ class RenderLayerCompositor; #endif enum BorderRadiusClippingRule { IncludeSelfForBorderRadius, DoNotIncludeSelfForBorderRadius }; +enum IncludeSelfOrNot { IncludeSelf, ExcludeSelf }; enum RepaintStatus { - NeedsNormalRepaint = 0, - NeedsFullRepaint = 1 << 0, - NeedsFullRepaintForPositionedMovementLayout = 1 << 1 + NeedsNormalRepaint, + NeedsFullRepaint, + NeedsFullRepaintForPositionedMovementLayout }; class ClipRect { @@ -114,10 +114,11 @@ public: } void move(LayoutUnit x, LayoutUnit y) { m_rect.move(x, y); } void move(const LayoutSize& size) { m_rect.move(size); } + void moveBy(const LayoutPoint& point) { m_rect.moveBy(point); } bool isEmpty() const { return m_rect.isEmpty(); } - bool intersects(const LayoutRect& rect) { return m_rect.intersects(rect); } - bool intersects(const HitTestLocation&); + bool intersects(const LayoutRect& rect) const { return m_rect.intersects(rect); } + bool intersects(const HitTestLocation&) const; private: LayoutRect m_rect; @@ -229,6 +230,11 @@ enum ClipRectsType { TemporaryClipRects }; +enum ShouldRespectOverflowClip { + IgnoreOverflowClip, + RespectOverflowClip +}; + struct ClipRectsCache { WTF_MAKE_FAST_ALLOCATED; public: @@ -237,20 +243,77 @@ public: #ifndef NDEBUG for (int i = 0; i < NumCachedClipRectsTypes; ++i) { m_clipRectsRoot[i] = 0; - m_respectingOverflowClip[i] = false; m_scrollbarRelevancy[i] = IgnoreOverlayScrollbarSize; } #endif } - RefPtr<ClipRects> m_clipRects[NumCachedClipRectsTypes]; + PassRefPtr<ClipRects> getClipRects(ClipRectsType clipRectsType, ShouldRespectOverflowClip respectOverflow) { return m_clipRects[getIndex(clipRectsType, respectOverflow)]; } + void setClipRects(ClipRectsType clipRectsType, ShouldRespectOverflowClip respectOverflow, PassRefPtr<ClipRects> clipRects) { m_clipRects[getIndex(clipRectsType, respectOverflow)] = clipRects; } + #ifndef NDEBUG const RenderLayer* m_clipRectsRoot[NumCachedClipRectsTypes]; - bool m_respectingOverflowClip[NumCachedClipRectsTypes]; OverlayScrollbarSizeRelevancy m_scrollbarRelevancy[NumCachedClipRectsTypes]; #endif + +private: + int getIndex(ClipRectsType clipRectsType, ShouldRespectOverflowClip respectOverflow) + { + int index = static_cast<int>(clipRectsType); + if (respectOverflow == RespectOverflowClip) + index += static_cast<int>(NumCachedClipRectsTypes); + return index; + } + + RefPtr<ClipRects> m_clipRects[NumCachedClipRectsTypes * 2]; +}; + +struct LayerFragment { +public: + LayerFragment() + : shouldPaintContent(false) + { } + + void setRects(const LayoutRect& bounds, const ClipRect& background, const ClipRect& foreground, const ClipRect& outline) + { + layerBounds = bounds; + backgroundRect = background; + foregroundRect = foreground; + outlineRect = outline; + } + + void moveBy(const LayoutPoint& offset) + { + layerBounds.moveBy(offset); + backgroundRect.moveBy(offset); + foregroundRect.moveBy(offset); + outlineRect.moveBy(offset); + paginationClip.moveBy(offset); + } + + void intersect(const LayoutRect& rect) + { + backgroundRect.intersect(rect); + foregroundRect.intersect(rect); + outlineRect.intersect(rect); + } + + bool shouldPaintContent; + LayoutRect layerBounds; + ClipRect backgroundRect; + ClipRect foregroundRect; + ClipRect outlineRect; + + // Unique to paginated fragments. The physical translation to apply to shift the layer when painting/hit-testing. + LayoutPoint paginationOffset; + + // Also unique to paginated fragments. An additional clip that applies to the layer. It is in layer-local + // (physical) coordinates. + LayoutRect paginationClip; }; +typedef Vector<LayerFragment, 1> LayerFragments; + class RenderLayer : public ScrollableArea { public: friend class RenderReplica; @@ -258,6 +321,8 @@ public: RenderLayer(RenderLayerModelObject*); ~RenderLayer(); + String name() const; + RenderLayerModelObject* renderer() const { return m_renderer; } RenderBox* renderBox() const { return m_renderer && m_renderer->isBox() ? toRenderBox(m_renderer) : 0; } RenderLayer* parent() const { return m_parent; } @@ -338,10 +403,9 @@ public: void scrollRectToVisible(const LayoutRect&, const ScrollAlignment& alignX, const ScrollAlignment& alignY); - LayoutRect getRectToExpose(const LayoutRect& visibleRect, const LayoutRect& exposeRect, const ScrollAlignment& alignX, const ScrollAlignment& alignY); + LayoutRect getRectToExpose(const LayoutRect& visibleRect, const LayoutRect& visibleRectRelativeToDocument, const LayoutRect& exposeRect, const ScrollAlignment& alignX, const ScrollAlignment& alignY); bool scrollsOverflow() const; - bool allowsScrolling() const; // Returns true if at least one scrollbar is visible and enabled. bool hasScrollbars() const { return m_hBar || m_vBar; } void setHasHorizontalScrollbar(bool); void setHasVerticalScrollbar(bool); @@ -372,8 +436,9 @@ public: void updateScrollInfoAfterLayout(); bool scroll(ScrollDirection, ScrollGranularity, float multiplier = 1); - void autoscroll(); + void autoscroll(const IntPoint&); + bool canResize() const; void resize(const PlatformMouseEvent&, const LayoutSize&); bool inResizeMode() const { return m_inResizeMode; } void setInResizeMode(bool b) { m_inResizeMode = b; } @@ -390,14 +455,12 @@ public: bool canRender3DTransforms() const; - // Returns true if the position changed. - bool updateLayerPosition(); - enum UpdateLayerPositionsFlag { - CheckForRepaint = 1, - IsCompositingUpdateRoot = 1 << 1, - UpdateCompositingLayers = 1 << 2, - UpdatePagination = 1 << 3 + CheckForRepaint = 1 << 0, + NeedsFullRepaintInBacking = 1 << 1, + IsCompositingUpdateRoot = 1 << 2, + UpdateCompositingLayers = 1 << 3, + UpdatePagination = 1 << 4 }; typedef unsigned UpdateLayerPositionsFlags; static const UpdateLayerPositionsFlags defaultFlags = CheckForRepaint | IsCompositingUpdateRoot | UpdateCompositingLayers; @@ -406,8 +469,13 @@ public: void updateLayerPositionsAfterOverflowScroll(); void updateLayerPositionsAfterDocumentScroll(); + +#if USE(ACCELERATED_COMPOSITING) + void positionNewlyCreatedOverflowControls(); +#endif bool isPaginated() const { return m_isPaginated; } + RenderLayer* enclosingPaginationLayer() const { return m_enclosingPaginationLayer; } void updateTransform(); @@ -424,18 +492,30 @@ public: void clearBlockSelectionGapsBounds(); void repaintBlockSelectionGaps(); - // Get the enclosing stacking context for this layer. A stacking context is a layer - // that has a non-auto z-index. - RenderLayer* stackingContext() const; + // A stacking context is a layer that has a non-auto z-index. bool isStackingContext() const { return isStackingContext(renderer()->style()); } + // A stacking container can have z-order lists. All stacking contexts are + // stacking containers, but the converse is not true. Layers that use + // composited scrolling are stacking containers, but they may not + // necessarily be stacking contexts. + bool isStackingContainer() const { return isStackingContext() || needsCompositedScrolling(); } + + // Gets the enclosing stacking container for this layer, excluding this + // layer itself. + RenderLayer* stackingContainer() const; + + // Gets the enclosing stacking container for this layer, possibly the layer + // itself, if it is a stacking container. + RenderLayer* enclosingStackingContainer() { return isStackingContainer() ? this : stackingContainer(); } + void dirtyZOrderLists(); - void dirtyStackingContextZOrderLists(); + void dirtyStackingContainerZOrderLists(); Vector<RenderLayer*>* posZOrderList() const { ASSERT(!m_zOrderListsDirty); - ASSERT(isStackingContext() || !m_posZOrderList); + ASSERT(isStackingContainer() || !m_posZOrderList); return m_posZOrderList.get(); } @@ -444,7 +524,7 @@ public: Vector<RenderLayer*>* negZOrderList() const { ASSERT(!m_zOrderListsDirty); - ASSERT(isStackingContext() || !m_negZOrderList); + ASSERT(isStackingContainer() || !m_negZOrderList); return m_negZOrderList.get(); } @@ -462,10 +542,25 @@ public: void setHasVisibleContent(); void dirtyVisibleContentStatus(); + bool hasBoxDecorationsOrBackground() const; + bool hasVisibleBoxDecorations() const; + // Returns true if this layer has visible content (ignoring any child layers). + bool isVisuallyNonEmpty() const; + // True if this layer container renderers that paint. + bool hasNonEmptyChildRenderers() const; + // FIXME: We should ASSERT(!m_hasSelfPaintingLayerDescendantDirty); here but we hit the same bugs as visible content above. // Part of the issue is with subtree relayout: we don't check if our ancestors have some descendant flags dirty, missing some updates. bool hasSelfPaintingLayerDescendant() const { return m_hasSelfPaintingLayerDescendant; } + // This returns true if we have an out of flow positioned descendant whose + // containing block is not a descendant of ours. If this is true, we cannot + // automatically opt into composited scrolling since this out of flow + // positioned descendant would become clipped by us, possibly altering the + // rendering of the page. + // FIXME: We should ASSERT(!m_hasOutOfFlowPositionedDescendantDirty); here but we may hit the same bugs as visible content above. + bool hasOutOfFlowPositionedDescendant() const { return m_hasOutOfFlowPositionedDescendant; } + // Gets the nearest enclosing positioned ancestor layer (also includes // the <html> layer and the root layer). RenderLayer* enclosingPositionedAncestor() const; @@ -476,16 +571,18 @@ public: // The layer relative to which clipping rects for this layer are computed. RenderLayer* clippingRootForPainting() const; + RenderLayer* enclosingOverflowClipLayer(IncludeSelfOrNot) const; + #if USE(ACCELERATED_COMPOSITING) // Enclosing compositing layer; if includeSelf is true, may return this. - RenderLayer* enclosingCompositingLayer(bool includeSelf = true) const; - RenderLayer* enclosingCompositingLayerForRepaint(bool includeSelf = true) const; + RenderLayer* enclosingCompositingLayer(IncludeSelfOrNot = IncludeSelf) const; + RenderLayer* enclosingCompositingLayerForRepaint(IncludeSelfOrNot = IncludeSelf) const; // Ancestor compositing layer, excluding this. - RenderLayer* ancestorCompositingLayer() const { return enclosingCompositingLayer(false); } + RenderLayer* ancestorCompositingLayer() const { return enclosingCompositingLayer(ExcludeSelf); } #endif #if ENABLE(CSS_FILTERS) - RenderLayer* enclosingFilterLayer(bool includeSelf = true) const; + RenderLayer* enclosingFilterLayer(IncludeSelfOrNot = IncludeSelf) const; RenderLayer* enclosingFilterRepaintLayer() const; void setFilterBackendNeedsRepaintingInRect(const LayoutRect&, bool immediate); bool hasAncestorWithFilterOutsets() const; @@ -501,10 +598,12 @@ public: ; } - void convertToPixelSnappedLayerCoords(const RenderLayer* ancestorLayer, IntPoint& location) const; - void convertToPixelSnappedLayerCoords(const RenderLayer* ancestorLayer, IntRect&) const; - void convertToLayerCoords(const RenderLayer* ancestorLayer, LayoutPoint& location) const; - void convertToLayerCoords(const RenderLayer* ancestorLayer, LayoutRect&) const; + // FIXME: adjustForColumns allows us to position compositing layers in columns correctly, but eventually they need to be split across columns too. + enum ColumnOffsetAdjustment { DontAdjustForColumns, AdjustForColumns }; + void convertToPixelSnappedLayerCoords(const RenderLayer* ancestorLayer, IntPoint& location, ColumnOffsetAdjustment adjustForColumns = DontAdjustForColumns) const; + void convertToPixelSnappedLayerCoords(const RenderLayer* ancestorLayer, IntRect&, ColumnOffsetAdjustment adjustForColumns = DontAdjustForColumns) const; + void convertToLayerCoords(const RenderLayer* ancestorLayer, LayoutPoint&, ColumnOffsetAdjustment adjustForColumns = DontAdjustForColumns) const; + void convertToLayerCoords(const RenderLayer* ancestorLayer, LayoutRect&, ColumnOffsetAdjustment adjustForColumns = DontAdjustForColumns) const; int zIndex() const { return renderer()->style()->zIndex(); } @@ -517,7 +616,10 @@ public: PaintLayerPaintingCompositingBackgroundPhase = 1 << 5, PaintLayerPaintingCompositingForegroundPhase = 1 << 6, PaintLayerPaintingCompositingMaskPhase = 1 << 7, - PaintLayerPaintingOverflowContents = 1 << 8, + PaintLayerPaintingCompositingScrollingPhase = 1 << 8, + PaintLayerPaintingOverflowContents = 1 << 9, + PaintLayerPaintingRootBackgroundOnly = 1 << 10, + PaintLayerPaintingSkipRootBackground = 1 << 11, PaintLayerPaintingCompositingAllPhases = (PaintLayerPaintingCompositingBackgroundPhase | PaintLayerPaintingCompositingForegroundPhase | PaintLayerPaintingCompositingMaskPhase) }; @@ -527,13 +629,11 @@ public: // paints the layers that intersect the damage rect from back to // front. The hitTest method looks for mouse events by walking // layers that intersect the point from front to back. - void paint(GraphicsContext*, const LayoutRect& damageRect, PaintBehavior = PaintBehaviorNormal, RenderObject* paintingRoot = 0, + void paint(GraphicsContext*, const LayoutRect& damageRect, PaintBehavior = PaintBehaviorNormal, RenderObject* subtreePaintRoot = 0, RenderRegion* = 0, PaintLayerFlags = 0); bool hitTest(const HitTestRequest&, HitTestResult&); bool hitTest(const HitTestRequest&, const HitTestLocation&, HitTestResult&); - void paintOverlayScrollbars(GraphicsContext*, const LayoutRect& damageRect, PaintBehavior, RenderObject* paintingRoot = 0); - - enum ShouldRespectOverflowClip { IgnoreOverflowClip, RespectOverflowClip }; + void paintOverlayScrollbars(GraphicsContext*, const LayoutRect& damageRect, PaintBehavior, RenderObject* subtreePaintRoot = 0); struct ClipRectsContext { ClipRectsContext(const RenderLayer* inRootLayer, RenderRegion* inRegion, ClipRectsType inClipRectsType, OverlayScrollbarSizeRelevancy inOverlayScrollbarSizeRelevancy = IgnoreOverlayScrollbarSize, ShouldRespectOverflowClip inRespectOverflowClip = RespectOverflowClip) @@ -563,10 +663,10 @@ public: // (rather than computing them all from scratch up the parent chain). void calculateClipRects(const ClipRectsContext&, ClipRects&) const; - ClipRects* clipRects(ClipRectsType type) const + ClipRects* clipRects(const ClipRectsContext& context) const { - ASSERT(type < NumCachedClipRectsTypes); - return m_clipRectsCache ? m_clipRectsCache->m_clipRects[type].get() : 0; + ASSERT(context.clipRectsType < NumCachedClipRectsTypes); + return m_clipRectsCache ? m_clipRectsCache->getClipRects(context.clipRectsType, context.respectOverflowClip).get() : 0; } LayoutRect childrenClipRect() const; // Returns the foreground clip rect of the layer in the document's coordinate space. @@ -576,23 +676,38 @@ public: // Pass offsetFromRoot if known. bool intersectsDamageRect(const LayoutRect& layerBounds, const LayoutRect& damageRect, const RenderLayer* rootLayer, const LayoutPoint* offsetFromRoot = 0) const; - // Bounding box relative to some ancestor layer. Pass offsetFromRoot if known. - LayoutRect boundingBox(const RenderLayer* rootLayer, const LayoutPoint* offsetFromRoot = 0) const; - // Bounding box in the coordinates of this layer. - LayoutRect localBoundingBox() const; - // Pixel snapped bounding box relative to the root. - IntRect absoluteBoundingBox() const; - enum CalculateLayerBoundsFlag { IncludeSelfTransform = 1 << 0, UseLocalClipRectIfPossible = 1 << 1, IncludeLayerFilterOutsets = 1 << 2, ExcludeHiddenDescendants = 1 << 3, - DefaultCalculateLayerBoundsFlags = IncludeSelfTransform | UseLocalClipRectIfPossible | IncludeLayerFilterOutsets + DontConstrainForMask = 1 << 4, + IncludeCompositedDescendants = 1 << 5, + UseFragmentBoxes = 1 << 6, + DefaultCalculateLayerBoundsFlags = IncludeSelfTransform | UseLocalClipRectIfPossible | IncludeLayerFilterOutsets | UseFragmentBoxes }; typedef unsigned CalculateLayerBoundsFlags; + + // Bounding box relative to some ancestor layer. Pass offsetFromRoot if known. + LayoutRect boundingBox(const RenderLayer* rootLayer, CalculateLayerBoundsFlags = 0, const LayoutPoint* offsetFromRoot = 0) const; + // Bounding box in the coordinates of this layer. + LayoutRect localBoundingBox(CalculateLayerBoundsFlags = 0) const; + // Pixel snapped bounding box relative to the root. + IntRect absoluteBoundingBox() const; + + // Bounds used for layer overlap testing in RenderLayerCompositor. + LayoutRect overlapBounds() const { return overlapBoundsIncludeChildren() ? calculateLayerBounds(this) : localBoundingBox(); } + +#if ENABLE(CSS_FILTERS) + // If true, this layer's children are included in its bounds for overlap testing. + // We can't rely on the children's positions if this layer has a filter that could have moved the children's pixels around. + bool overlapBoundsIncludeChildren() const { return hasFilter() && renderer()->style()->filter().hasFilterThatMovesPixels(); } +#else + bool overlapBoundsIncludeChildren() const { return false; } +#endif + // Can pass offsetFromRoot if known. - IntRect calculateLayerBounds(const RenderLayer* ancestorLayer, const LayoutPoint* offsetFromRoot = 0, CalculateLayerBoundsFlags = DefaultCalculateLayerBoundsFlags) const; + LayoutRect calculateLayerBounds(const RenderLayer* ancestorLayer, const LayoutPoint* offsetFromRoot = 0, CalculateLayerBoundsFlags = DefaultCalculateLayerBoundsFlags) const; // WARNING: This method returns the offset for the parent as this is what updateLayerPositions expects. LayoutPoint computeOffsetFromRoot(bool& hasLayerOffset) const; @@ -652,14 +767,19 @@ public: RenderLayerBacking* backing() const { return m_backing.get(); } RenderLayerBacking* ensureBacking(); void clearBacking(bool layerBeingDestroyed = false); + virtual GraphicsLayer* layerForScrolling() const; virtual GraphicsLayer* layerForHorizontalScrollbar() const; virtual GraphicsLayer* layerForVerticalScrollbar() const; virtual GraphicsLayer* layerForScrollCorner() const; virtual bool usesCompositedScrolling() const OVERRIDE; + bool needsCompositedScrolling() const; + bool needsCompositingLayersRebuiltForClip(const RenderStyle* oldStyle, const RenderStyle* newStyle) const; + bool needsCompositingLayersRebuiltForOverflow(const RenderStyle* oldStyle, const RenderStyle* newStyle) const; #else bool isComposited() const { return false; } bool hasCompositedMask() const { return false; } bool usesCompositedScrolling() const { return false; } + bool needsCompositedScrolling() const { return false; } #endif bool paintsWithTransparency(PaintBehavior paintBehavior) const @@ -669,6 +789,10 @@ public: bool paintsWithTransform(PaintBehavior) const; + // Returns true if background phase is painted opaque in the given rect. + // The query rect is given in local coordinates. + bool backgroundIsKnownToBeOpaqueInRect(const LayoutRect&) const; + bool containsDirtyOverlayScrollbars() const { return m_containsDirtyOverlayScrollbars; } void setContainsDirtyOverlayScrollbars(bool dirtyScrollbars) { m_containsDirtyOverlayScrollbars = dirtyScrollbars; } @@ -680,19 +804,11 @@ public: FilterOperations computeFilterOperations(const RenderStyle*); bool paintsWithFilters() const; bool requiresFullLayerImageForFilters() const; - FilterEffectRenderer* filterRenderer() const - { - RenderLayerFilterInfo* filterInfo = this->filterInfo(); - return filterInfo ? filterInfo->renderer() : 0; - } - - RenderLayerFilterInfo* filterInfo() const { return hasFilterInfo() ? RenderLayerFilterInfo::filterInfoForRenderLayer(this) : 0; } - RenderLayerFilterInfo* ensureFilterInfo() { return RenderLayerFilterInfo::createFilterInfoForRenderLayerIfNeeded(this); } - void removeFilterInfoIfNeeded() - { - if (hasFilterInfo()) - RenderLayerFilterInfo::removeFilterInfoForRenderLayer(this); - } + FilterEffectRenderer* filterRenderer() const; + + RenderLayerFilterInfo* filterInfo() const; + RenderLayerFilterInfo* ensureFilterInfo(); + void removeFilterInfoIfNeeded(); bool hasFilterInfo() const { return m_hasFilterInfo; } void setHasFilterInfo(bool hasFilterInfo) { m_hasFilterInfo = hasFilterInfo; } @@ -710,19 +826,43 @@ public: bool isInTopLayerSubtree() const; #endif +#if USE(ACCELERATED_COMPOSITING) + enum ViewportConstrainedNotCompositedReason { + NoNotCompositedReason, + NotCompositedForBoundsOutOfView, + NotCompositedForNonViewContainer, + NotCompositedForNoVisibleContent, + }; + + void setViewportConstrainedNotCompositedReason(ViewportConstrainedNotCompositedReason reason) { m_viewportConstrainedNotCompositedReason = reason; } + ViewportConstrainedNotCompositedReason viewportConstrainedNotCompositedReason() const { return static_cast<ViewportConstrainedNotCompositedReason>(m_viewportConstrainedNotCompositedReason); } +#endif + + bool isOutOfFlowRenderFlowThread() const { return renderer()->isOutOfFlowRenderFlowThread(); } + private: + enum CollectLayersBehavior { StopAtStackingContexts, StopAtStackingContainers }; + void updateZOrderLists(); void rebuildZOrderLists(); + void rebuildZOrderLists(CollectLayersBehavior, OwnPtr<Vector<RenderLayer*> >&, OwnPtr<Vector<RenderLayer*> >&); void clearZOrderLists(); void updateNormalFlowList(); + // Non-auto z-index always implies stacking context here, because StyleResolver::adjustRenderStyle already adjusts z-index + // based on positioning and other criteria. bool isStackingContext(const RenderStyle* style) const { return !style->hasAutoZIndex() || isRootLayer(); } - bool isDirtyStackingContext() const { return m_zOrderListsDirty && isStackingContext(); } + + bool isDirtyStackingContainer() const { return m_zOrderListsDirty && isStackingContainer(); } void setAncestorChainHasSelfPaintingLayerDescendant(); void dirtyAncestorChainHasSelfPaintingLayerDescendantStatus(); + bool acceleratedCompositingForOverflowScrollEnabled() const; + void updateDescendantsAreContiguousInStackingOrder(); + void updateDescendantsAreContiguousInStackingOrderRecursive(const HashMap<const RenderLayer*, int>&, int& minIndex, int& maxIndex, int& count, bool firstIteration); + void computeRepaintRects(const RenderLayerModelObject* repaintContainer, const RenderGeometryMap* = 0); void computeRepaintRectsIncludingDescendants(); void clearRepaintRects(); @@ -739,6 +879,15 @@ private: void updateScrollbarsAfterStyleChange(const RenderStyle* oldStyle); void updateScrollbarsAfterLayout(); + void setAncestorChainHasOutOfFlowPositionedDescendant(RenderObject* containingBlock); + void dirtyAncestorChainHasOutOfFlowPositionedDescendantStatus(); + void updateOutOfFlowPositioned(const RenderStyle* oldStyle); + + void updateNeedsCompositedScrolling(); + + // Returns true if the position changed. + bool updateLayerPosition(); + void updateLayerPositions(RenderGeometryMap* = 0, UpdateLayerPositionsFlags = defaultFlags); enum UpdateLayerPositionsAfterScrollFlag { @@ -766,42 +915,68 @@ private: void setLastChild(RenderLayer* last) { m_last = last; } LayoutPoint renderBoxLocation() const { return renderer()->isBox() ? toRenderBox(renderer())->location() : LayoutPoint(); } - LayoutUnit renderBoxX() const { return renderBoxLocation().x(); } - LayoutUnit renderBoxY() const { return renderBoxLocation().y(); } - void collectLayers(bool includeHiddenLayers, OwnPtr<Vector<RenderLayer*> >&, OwnPtr<Vector<RenderLayer*> >&); + void collectLayers(bool includeHiddenLayers, CollectLayersBehavior, OwnPtr<Vector<RenderLayer*> >&, OwnPtr<Vector<RenderLayer*> >&); void updateCompositingAndLayerListsIfNeeded(); struct LayerPaintingInfo { - LayerPaintingInfo(RenderLayer* inRootLayer, const LayoutRect& inDirtyRect, PaintBehavior inPaintBehavior, const LayoutSize& inSubPixelAccumulation, RenderObject* inPaintingRoot = 0, RenderRegion*inRegion = 0, OverlapTestRequestMap* inOverlapTestRequests = 0) + LayerPaintingInfo(RenderLayer* inRootLayer, const LayoutRect& inDirtyRect, PaintBehavior inPaintBehavior, const LayoutSize& inSubPixelAccumulation, RenderObject* inSubtreePaintRoot = 0, RenderRegion*inRegion = 0, OverlapTestRequestMap* inOverlapTestRequests = 0) : rootLayer(inRootLayer) - , paintingRoot(inPaintingRoot) + , subtreePaintRoot(inSubtreePaintRoot) , paintDirtyRect(inDirtyRect) , subPixelAccumulation(inSubPixelAccumulation) , region(inRegion) , overlapTestRequests(inOverlapTestRequests) , paintBehavior(inPaintBehavior) + , clipToDirtyRect(true) { } RenderLayer* rootLayer; - RenderObject* paintingRoot; // only paint descendants of this object + RenderObject* subtreePaintRoot; // only paint descendants of this object LayoutRect paintDirtyRect; // relative to rootLayer; LayoutSize subPixelAccumulation; RenderRegion* region; // May be null. OverlapTestRequestMap* overlapTestRequests; // May be null. PaintBehavior paintBehavior; + bool clipToDirtyRect; }; - + + bool setupFontSubpixelQuantization(GraphicsContext*, bool& didQuantizeFonts); + bool setupClipPath(GraphicsContext*, const LayerPaintingInfo&, const LayoutPoint& offsetFromRoot, LayoutRect& rootRelativeBounds, bool& rootRelativeBoundsComputed); +#if ENABLE(CSS_FILTERS) + PassOwnPtr<FilterEffectRendererHelper> setupFilters(GraphicsContext*, LayerPaintingInfo&, PaintLayerFlags, const LayoutPoint& offsetFromRoot, LayoutRect& rootRelativeBounds, bool& rootRelativeBoundsComputed); + GraphicsContext* applyFilters(FilterEffectRendererHelper*, GraphicsContext* originalContext, LayerPaintingInfo&, LayerFragments&); +#endif + void paintLayer(GraphicsContext*, const LayerPaintingInfo&, PaintLayerFlags); void paintLayerContentsAndReflection(GraphicsContext*, const LayerPaintingInfo&, PaintLayerFlags); + void paintLayerByApplyingTransform(GraphicsContext*, const LayerPaintingInfo&, PaintLayerFlags, const LayoutPoint& translationOffset = LayoutPoint()); void paintLayerContents(GraphicsContext*, const LayerPaintingInfo&, PaintLayerFlags); void paintList(Vector<RenderLayer*>*, GraphicsContext*, const LayerPaintingInfo&, PaintLayerFlags); void paintPaginatedChildLayer(RenderLayer* childLayer, GraphicsContext*, const LayerPaintingInfo&, PaintLayerFlags); void paintChildLayerIntoColumns(RenderLayer* childLayer, GraphicsContext*, const LayerPaintingInfo&, PaintLayerFlags, const Vector<RenderLayer*>& columnLayers, size_t columnIndex); + void collectFragments(LayerFragments&, const RenderLayer* rootLayer, RenderRegion*, const LayoutRect& dirtyRect, + ClipRectsType, OverlayScrollbarSizeRelevancy inOverlayScrollbarSizeRelevancy = IgnoreOverlayScrollbarSize, + ShouldRespectOverflowClip = RespectOverflowClip, const LayoutPoint* offsetFromRoot = 0, const LayoutRect* layerBoundingBox = 0); + void updatePaintingInfoForFragments(LayerFragments&, const LayerPaintingInfo&, PaintLayerFlags, bool shouldPaintContent, const LayoutPoint* offsetFromRoot); + void paintBackgroundForFragments(const LayerFragments&, GraphicsContext*, GraphicsContext* transparencyLayerContext, + const LayoutRect& transparencyPaintDirtyRect, bool haveTransparency, const LayerPaintingInfo&, PaintBehavior, RenderObject* paintingRootForRenderer); + void paintForegroundForFragments(const LayerFragments&, GraphicsContext*, GraphicsContext* transparencyLayerContext, + const LayoutRect& transparencyPaintDirtyRect, bool haveTransparency, const LayerPaintingInfo&, PaintBehavior, RenderObject* paintingRootForRenderer, + bool selectionOnly, bool forceBlackText); + void paintForegroundForFragmentsWithPhase(PaintPhase, const LayerFragments&, GraphicsContext*, const LayerPaintingInfo&, PaintBehavior, RenderObject* paintingRootForRenderer); + void paintOutlineForFragments(const LayerFragments&, GraphicsContext*, const LayerPaintingInfo&, PaintBehavior, RenderObject* paintingRootForRenderer); + void paintOverflowControlsForFragments(const LayerFragments&, GraphicsContext*, const LayerPaintingInfo&); + void paintMaskForFragments(const LayerFragments&, GraphicsContext*, const LayerPaintingInfo&, RenderObject* paintingRootForRenderer); + void paintTransformedLayerIntoFragments(GraphicsContext*, const LayerPaintingInfo&, PaintLayerFlags); + RenderLayer* hitTestLayer(RenderLayer* rootLayer, RenderLayer* containerLayer, const HitTestRequest& request, HitTestResult& result, const LayoutRect& hitTestRect, const HitTestLocation&, bool appliedTransform, const HitTestingTransformState* transformState = 0, double* zOffset = 0); + RenderLayer* hitTestLayerByApplyingTransform(RenderLayer* rootLayer, RenderLayer* containerLayer, const HitTestRequest&, HitTestResult&, + const LayoutRect& hitTestRect, const HitTestLocation&, const HitTestingTransformState* = 0, double* zOffset = 0, + const LayoutPoint& translationOffset = LayoutPoint()); RenderLayer* hitTestList(Vector<RenderLayer*>*, RenderLayer* rootLayer, const HitTestRequest& request, HitTestResult& result, const LayoutRect& hitTestRect, const HitTestLocation&, const HitTestingTransformState* transformState, double* zOffsetForDescendants, double* zOffset, @@ -816,13 +991,22 @@ private: PassRefPtr<HitTestingTransformState> createLocalTransformState(RenderLayer* rootLayer, RenderLayer* containerLayer, const LayoutRect& hitTestRect, const HitTestLocation&, - const HitTestingTransformState* containerTransformState) const; + const HitTestingTransformState* containerTransformState, + const LayoutPoint& translationOffset = LayoutPoint()) const; bool hitTestContents(const HitTestRequest&, HitTestResult&, const LayoutRect& layerBounds, const HitTestLocation&, HitTestFilter) const; + bool hitTestContentsForFragments(const LayerFragments&, const HitTestRequest&, HitTestResult&, const HitTestLocation&, HitTestFilter, bool& insideClipRect) const; + bool hitTestResizerInFragments(const LayerFragments&, const HitTestLocation&) const; + RenderLayer* hitTestTransformedLayerInFragments(RenderLayer* rootLayer, RenderLayer* containerLayer, const HitTestRequest&, HitTestResult&, + const LayoutRect& hitTestRect, const HitTestLocation&, const HitTestingTransformState* = 0, double* zOffset = 0); + + bool listBackgroundIsKnownToBeOpaqueInRect(const Vector<RenderLayer*>*, const LayoutRect&) const; void computeScrollDimensions(); bool hasHorizontalOverflow() const; bool hasVerticalOverflow() const; + bool hasScrollableHorizontalOverflow() const; + bool hasScrollableVerticalOverflow() const; bool shouldBeNormalFlowOnly() const; @@ -845,15 +1029,17 @@ private: virtual IntPoint scrollPosition() const; virtual IntPoint minimumScrollPosition() const; virtual IntPoint maximumScrollPosition() const; - virtual IntRect visibleContentRect(bool includeScrollbars) const; + virtual IntRect visibleContentRect(VisibleContentRectIncludesScrollbars) const; virtual int visibleHeight() const; virtual int visibleWidth() const; virtual IntSize contentsSize() const; virtual IntSize overhangAmount() const; - virtual IntPoint currentMousePosition() const; + virtual IntPoint lastKnownMousePosition() const; + virtual bool isHandlingWheelEvent() const OVERRIDE; virtual bool shouldSuspendScrollAnimations() const; virtual bool scrollbarsCanBeActive() const; virtual IntRect scrollableAreaBoundingBox() const OVERRIDE; + virtual bool scrollbarAnimationsAreSuppressed() const OVERRIDE; // Rectangle encompassing the scroll corner and resizer rect. IntRect scrollCornerAndResizerRect() const; @@ -869,7 +1055,7 @@ private: void dirtyAncestorChainVisibleDescendantStatus(); void setAncestorChainHasVisibleDescendant(); - void updateDescendantDependentFlags(); + void updateDescendantDependentFlags(HashSet<const RenderObject*>* outOfFlowDescendantContainingBlocks = 0); // This flag is computed by RenderLayerCompositor, which knows more about 3d hierarchies than we do. void setHas3DTransformedDescendant(bool b) { m_has3DTransformedDescendant = b; } @@ -909,6 +1095,9 @@ private: void updatePagination(); + // FIXME: Temporary. Remove when new columns come online. + bool useRegionBasedColumns() const; + #if USE(ACCELERATED_COMPOSITING) bool hasCompositingDescendant() const { return m_hasCompositingDescendant; } void setHasCompositingDescendant(bool b) { m_hasCompositingDescendant = b; } @@ -928,6 +1117,9 @@ private: bool mustCompositeForIndirectReasons() const { return m_indirectCompositingReason; } #endif + // Returns true if z ordering would not change if this layer were a stacking container. + bool canBeStackingContainer() const; + friend class RenderLayerBacking; friend class RenderLayerCompositor; friend class RenderLayerModelObject; @@ -966,6 +1158,19 @@ protected: bool m_hasSelfPaintingLayerDescendant : 1; bool m_hasSelfPaintingLayerDescendantDirty : 1; + // If we have no out of flow positioned descendants and no non-descendant + // appears between our descendants in stacking order, then we may become a + // stacking context. + bool m_hasOutOfFlowPositionedDescendant : 1; + bool m_hasOutOfFlowPositionedDescendantDirty : 1; + + bool m_needsCompositedScrolling : 1; + + // If this is true, then no non-descendant appears between any of our + // descendants in stacking order. This is one of the requirements of being + // able to safely become a stacking context. + bool m_descendantsAreContiguousInStackingOrder : 1; + const bool m_isRootLayer : 1; bool m_usedTransparency : 1; // Tracks whether we need to close a transparent layer, i.e., whether @@ -988,6 +1193,7 @@ protected: #if USE(ACCELERATED_COMPOSITING) bool m_hasCompositingDescendant : 1; // In the z-order tree. unsigned m_indirectCompositingReason : 3; + unsigned m_viewportConstrainedNotCompositedReason : 2; #endif bool m_containsDirtyOverlayScrollbars : 1; @@ -1065,6 +1271,9 @@ protected: RenderScrollbarPart* m_scrollCorner; RenderScrollbarPart* m_resizer; + // Pointer to the enclosing RenderLayer that caused us to be paginated. It is 0 if we are not paginated. + RenderLayer* m_enclosingPaginationLayer; + private: IntRect m_blockSelectionGapsBounds; @@ -1075,7 +1284,7 @@ private: inline void RenderLayer::clearZOrderLists() { - ASSERT(!isStackingContext()); + ASSERT(!isStackingContainer()); m_posZOrderList.clear(); m_negZOrderList.clear(); @@ -1086,7 +1295,7 @@ inline void RenderLayer::updateZOrderLists() if (!m_zOrderListsDirty) return; - if (!isStackingContext()) { + if (!isStackingContainer()) { clearZOrderLists(); m_zOrderListsDirty = false; return; @@ -1116,6 +1325,7 @@ private: }; #endif +void makeMatrixRenderable(TransformationMatrix&, bool has3DRendering); } // namespace WebCore diff --git a/Source/WebCore/rendering/RenderLayerBacking.cpp b/Source/WebCore/rendering/RenderLayerBacking.cpp index 88be08dfe..955315eef 100644 --- a/Source/WebCore/rendering/RenderLayerBacking.cpp +++ b/Source/WebCore/rendering/RenderLayerBacking.cpp @@ -32,6 +32,7 @@ #include "AnimationController.h" #include "CanvasRenderingContext.h" #include "CSSPropertyNames.h" +#include "CachedImage.h" #include "Chrome.h" #include "FontCache.h" #include "FrameView.h" @@ -41,9 +42,11 @@ #include "HTMLIFrameElement.h" #include "HTMLMediaElement.h" #include "HTMLNames.h" +#include "HTMLPlugInElement.h" #include "InspectorInstrumentation.h" #include "KeyframeList.h" #include "PluginViewBase.h" +#include "ProgressTracker.h" #include "RenderApplet.h" #include "RenderIFrame.h" #include "RenderImage.h" @@ -55,7 +58,6 @@ #include "Settings.h" #include "StyleResolver.h" #include "TiledBacking.h" -#include <wtf/CurrentTime.h> #include <wtf/text/StringBuilder.h> #if ENABLE(CSS_FILTERS) @@ -75,8 +77,6 @@ namespace WebCore { using namespace HTMLNames; -static bool hasBoxDecorations(const RenderStyle*); -static bool hasBoxDecorationsOrBackground(const RenderObject*); static bool hasBoxDecorationsOrBackgroundImage(const RenderStyle*); static IntRect clipBox(RenderBox* renderer); @@ -94,18 +94,31 @@ static inline bool isAcceleratedCanvas(RenderObject* renderer) return false; } +// Get the scrolling coordinator in a way that works inside RenderLayerBacking's destructor. +static ScrollingCoordinator* scrollingCoordinatorFromLayer(RenderLayer* layer) +{ + Page* page = layer->renderer()->frame()->page(); + if (!page) + return 0; + + return page->scrollingCoordinator(); +} + bool RenderLayerBacking::m_creatingPrimaryGraphicsLayer = false; RenderLayerBacking::RenderLayerBacking(RenderLayer* layer) : m_owningLayer(layer) , m_scrollLayerID(0) , m_artificiallyInflatedBounds(false) + , m_boundsConstrainedByClipping(false) , m_isMainFrameRenderViewLayer(false) , m_usingTiledCacheLayer(false) , m_requiresOwnBackingStore(true) #if ENABLE(CSS_FILTERS) , m_canCompositeFilters(false) #endif + , m_backgroundLayerPaintsFixedRootBackground(false) + , m_didSwitchToFullTileCoverageDuringLoading(false) { if (layer->isRootLayer()) { Frame* frame = toRenderView(renderer())->frameView()->frame(); @@ -127,9 +140,13 @@ RenderLayerBacking::RenderLayerBacking(RenderLayer* layer) TiledBacking* tiledBacking = this->tiledBacking(); if (Page* page = renderer()->frame()->page()) { Frame* frame = renderer()->frame(); - tiledBacking->setIsInWindow(page->isOnscreen()); + tiledBacking->setIsInWindow(page->isInWindow()); + + if (m_isMainFrameRenderViewLayer) + tiledBacking->setUnparentsOffscreenTiles(true); + tiledBacking->setScrollingPerformanceLoggingEnabled(frame->settings() && frame->settings()->scrollingPerformanceLoggingEnabled()); - adjustTileCacheCoverage(); + adjustTiledBackingCoverage(); } } } @@ -139,17 +156,26 @@ RenderLayerBacking::~RenderLayerBacking() updateClippingLayers(false, false); updateOverflowControlsLayers(false, false, false); updateForegroundLayer(false); + updateBackgroundLayer(false); updateMaskLayer(false); updateScrollingLayers(false); detachFromScrollingCoordinator(); destroyGraphicsLayers(); } +void RenderLayerBacking::willDestroyLayer(const GraphicsLayer* layer) +{ + if (layer && layer->usingTiledBacking()) { + if (RenderLayerCompositor* compositor = this->compositor()) + compositor->layerTiledBackingUsageChanged(layer, false); + } +} + PassOwnPtr<GraphicsLayer> RenderLayerBacking::createGraphicsLayer(const String& name) { GraphicsLayerFactory* graphicsLayerFactory = 0; if (Page* page = renderer()->frame()->page()) - graphicsLayerFactory = page->chrome()->client()->graphicsLayerFactory(); + graphicsLayerFactory = page->chrome().client()->graphicsLayerFactory(); OwnPtr<GraphicsLayer> graphicsLayer = GraphicsLayer::create(graphicsLayerFactory, this); @@ -167,39 +193,61 @@ PassOwnPtr<GraphicsLayer> RenderLayerBacking::createGraphicsLayer(const String& return graphicsLayer.release(); } -bool RenderLayerBacking::shouldUseTileCache(const GraphicsLayer*) const +bool RenderLayerBacking::shouldUseTiledBacking(const GraphicsLayer*) const { return m_usingTiledCacheLayer && m_creatingPrimaryGraphicsLayer; } +void RenderLayerBacking::tiledBackingUsageChanged(const GraphicsLayer* layer, bool usingTiledBacking) +{ + compositor()->layerTiledBackingUsageChanged(layer, usingTiledBacking); +} + TiledBacking* RenderLayerBacking::tiledBacking() const { return m_graphicsLayer->tiledBacking(); } -void RenderLayerBacking::adjustTileCacheCoverage() +static TiledBacking::TileCoverage computeTileCoverage(RenderLayerBacking* backing) { - if (!m_usingTiledCacheLayer) - return; + // FIXME: When we use TiledBacking for overflow, this should look at RenderView scrollability. + Frame* frame = backing->owningLayer()->renderer()->frame(); + if (!frame) + return TiledBacking::CoverageForVisibleArea; TiledBacking::TileCoverage tileCoverage = TiledBacking::CoverageForVisibleArea; - - // FIXME: When we use TiledBacking for overflow, this should look at RenderView scrollability. - Frame* frame = renderer()->frame(); - if (frame) { - FrameView* frameView = frame->view(); - if (frameView->horizontalScrollbarMode() != ScrollbarAlwaysOff) + FrameView* frameView = frame->view(); + bool useMinimalTilesDuringLiveResize = frameView->inLiveResize(); + bool useMinimalTilesDuringLoading = false; + // Avoid churn. + if (!backing->didSwitchToFullTileCoverageDuringLoading()) { + useMinimalTilesDuringLoading = !frameView->isVisuallyNonEmpty() || (frame->page()->progress()->isMainLoadProgressing() && !frameView->wasScrolledByUser()); + if (!useMinimalTilesDuringLoading) + backing->setDidSwitchToFullTileCoverageDuringLoading(); + } + if (!(useMinimalTilesDuringLoading || useMinimalTilesDuringLiveResize)) { + bool clipsToExposedRect = backing->tiledBacking()->clipsToExposedRect(); + if (frameView->horizontalScrollbarMode() != ScrollbarAlwaysOff || clipsToExposedRect) tileCoverage |= TiledBacking::CoverageForHorizontalScrolling; - if (frameView->verticalScrollbarMode() != ScrollbarAlwaysOff) + if (frameView->verticalScrollbarMode() != ScrollbarAlwaysOff || clipsToExposedRect) tileCoverage |= TiledBacking::CoverageForVerticalScrolling; - - if (ScrollingCoordinator* scrollingCoordinator = frame->page()->scrollingCoordinator()) { - if (scrollingCoordinator->shouldUpdateScrollLayerPositionOnMainThread()) - tileCoverage |= TiledBacking::CoverageForSlowScrolling; - } } + if (ScrollingCoordinator* scrollingCoordinator = scrollingCoordinatorFromLayer(backing->owningLayer())) { + // Ask our TiledBacking for large tiles unless the only reason we're main-thread-scrolling + // is a page overlay (find-in-page, the Web Inspector highlight mechanism, etc.). + if (scrollingCoordinator->mainThreadScrollingReasons() & ~ScrollingCoordinator::ForcedOnMainThread) + tileCoverage |= TiledBacking::CoverageForSlowScrolling; + } + return tileCoverage; +} + +void RenderLayerBacking::adjustTiledBackingCoverage() +{ + if (!m_usingTiledCacheLayer) + return; + TiledBacking::TileCoverage tileCoverage = computeTileCoverage(this); tiledBacking()->setTileCoverage(tileCoverage); } @@ -215,6 +263,14 @@ void RenderLayerBacking::updateDebugIndicators(bool showBorder, bool showRepaint m_foregroundLayer->setShowDebugBorder(showBorder); m_foregroundLayer->setShowRepaintCounter(showRepaintCounter); } + + if (m_contentsContainmentLayer) + m_contentsContainmentLayer->setShowDebugBorder(showBorder); + + if (m_backgroundLayer) { + m_backgroundLayer->setShowDebugBorder(showBorder); + m_backgroundLayer->setShowRepaintCounter(showRepaintCounter); + } if (m_maskLayer) { m_maskLayer->setShowDebugBorder(showBorder); @@ -243,11 +299,11 @@ void RenderLayerBacking::createPrimaryGraphicsLayer() { String layerName; #ifndef NDEBUG - layerName = nameForLayer(); + layerName = m_owningLayer->name(); #endif // The call to createGraphicsLayer ends calling back into here as - // a GraphicsLayerClient to ask if it shouldUseTileCache(). We only want + // a GraphicsLayerClient to ask if it shouldUseTiledBacking(). We only want // the tile cache on our main layer. This is pretty ugly, but saves us from // exposing the API to all clients. @@ -256,14 +312,10 @@ void RenderLayerBacking::createPrimaryGraphicsLayer() m_creatingPrimaryGraphicsLayer = false; if (m_usingTiledCacheLayer) - m_containmentLayer = createGraphicsLayer("TileCache Flattening Layer"); + m_childContainmentLayer = createGraphicsLayer("TiledBacking Flattening Layer"); if (m_isMainFrameRenderViewLayer) { - bool isTransparent = false; - if (FrameView* frameView = toRenderView(renderer())->frameView()) - isTransparent = frameView->isTransparent(); - - m_graphicsLayer->setContentsOpaque(!isTransparent); + m_graphicsLayer->setContentsOpaque(true); m_graphicsLayer->setAppliesPageScale(); } @@ -287,12 +339,17 @@ void RenderLayerBacking::createPrimaryGraphicsLayer() void RenderLayerBacking::destroyGraphicsLayers() { - if (m_graphicsLayer) + if (m_graphicsLayer) { + willDestroyLayer(m_graphicsLayer.get()); m_graphicsLayer->removeFromParent(); + } + m_ancestorClippingLayer = nullptr; + m_contentsContainmentLayer = nullptr; m_graphicsLayer = nullptr; m_foregroundLayer = nullptr; - m_containmentLayer = nullptr; + m_backgroundLayer = nullptr; + m_childContainmentLayer = nullptr; m_maskLayer = nullptr; m_scrollingLayer = nullptr; @@ -314,7 +371,11 @@ void RenderLayerBacking::updateTransform(const RenderStyle* style) makeMatrixRenderable(t, compositor()->canRender3DTransforms()); } - m_graphicsLayer->setTransform(t); + if (m_contentsContainmentLayer) { + m_contentsContainmentLayer->setTransform(t); + m_graphicsLayer->setTransform(TransformationMatrix()); + } else + m_graphicsLayer->setTransform(t); } #if ENABLE(CSS_FILTERS) @@ -340,7 +401,7 @@ static bool hasNonZeroTransformOrigin(const RenderObject* renderer) static bool layerOrAncestorIsTransformedOrUsingCompositedScrolling(RenderLayer* layer) { for (RenderLayer* curr = layer; curr; curr = curr->parent()) { - if (curr->hasTransform() || curr->usesCompositedScrolling()) + if (curr->hasTransform() || curr->needsCompositedScrolling()) return true; } @@ -354,9 +415,6 @@ bool RenderLayerBacking::shouldClipCompositedBounds() const return false; if (m_usingTiledCacheLayer) - return true; - - if (!compositor()->compositingConsultsOverlap()) return false; if (layerOrAncestorIsTransformedOrUsingCompositedScrolling(m_owningLayer)) @@ -365,10 +423,9 @@ bool RenderLayerBacking::shouldClipCompositedBounds() const return true; } - void RenderLayerBacking::updateCompositedBounds() { - IntRect layerBounds = compositor()->calculateCompositedBounds(m_owningLayer, m_owningLayer); + LayoutRect layerBounds = compositor()->calculateCompositedBounds(m_owningLayer, m_owningLayer); // Clip to the size of the document or enclosing overflow-scroll layer. // If this or an ancestor is transformed, we can't currently compute the correct rect to intersect with. @@ -377,18 +434,23 @@ void RenderLayerBacking::updateCompositedBounds() RenderView* view = m_owningLayer->renderer()->view(); RenderLayer* rootLayer = view->layer(); - // Start by clipping to the view's bounds. - LayoutRect clippingBounds = view->unscaledDocumentRect(); + LayoutRect clippingBounds; + if (renderer()->style()->position() == FixedPosition && renderer()->container() == view) + clippingBounds = view->frameView()->viewportConstrainedVisibleContentRect(); + else + clippingBounds = view->unscaledDocumentRect(); if (m_owningLayer != rootLayer) clippingBounds.intersect(m_owningLayer->backgroundClipRect(RenderLayer::ClipRectsContext(rootLayer, 0, AbsoluteClipRects)).rect()); // FIXME: Incorrect for CSS regions. LayoutPoint delta; - m_owningLayer->convertToLayerCoords(rootLayer, delta); + m_owningLayer->convertToLayerCoords(rootLayer, delta, RenderLayer::AdjustForColumns); clippingBounds.move(-delta.x(), -delta.y()); - layerBounds.intersect(pixelSnappedIntRect(clippingBounds)); - } + layerBounds.intersect(clippingBounds); + m_boundsConstrainedByClipping = true; + } else + m_boundsConstrainedByClipping = false; // If the element has a transform-origin that has fixed lengths, and the renderer has zero size, // then we need to ensure that the compositing layer has non-zero size so that we can apply @@ -408,12 +470,12 @@ void RenderLayerBacking::updateAfterWidgetResize() if (renderer()->isRenderPart()) { if (RenderLayerCompositor* innerCompositor = RenderLayerCompositor::frameContentsCompositor(toRenderPart(renderer()))) { innerCompositor->frameViewDidChangeSize(); - innerCompositor->frameViewDidChangeLocation(contentsBox().location()); + innerCompositor->frameViewDidChangeLocation(flooredIntPoint(contentsBox().location())); } } } -void RenderLayerBacking::updateAfterLayout(UpdateDepth updateDepth, bool isUpdateRoot) +void RenderLayerBacking::updateAfterLayout(UpdateAfterLayoutFlags flags) { RenderLayerCompositor* layerCompositor = compositor(); if (!layerCompositor->compositingLayersNeedRebuild()) { @@ -425,13 +487,19 @@ void RenderLayerBacking::updateAfterLayout(UpdateDepth updateDepth, bool isUpdat // The solution is to update compositing children of this layer here, // via updateCompositingChildrenGeometry(). updateCompositedBounds(); - layerCompositor->updateCompositingDescendantGeometry(m_owningLayer, m_owningLayer, updateDepth); + layerCompositor->updateCompositingDescendantGeometry(m_owningLayer, m_owningLayer, flags & CompositingChildrenOnly); - if (isUpdateRoot) { + if (flags & IsUpdateRoot) { updateGraphicsLayerGeometry(); layerCompositor->updateRootLayerPosition(); + RenderLayer* stackingContainer = m_owningLayer->enclosingStackingContainer(); + if (!layerCompositor->compositingLayersNeedRebuild() && stackingContainer && (stackingContainer != m_owningLayer)) + layerCompositor->updateCompositingDescendantGeometry(stackingContainer, stackingContainer, flags & CompositingChildrenOnly); } } + + if (flags & NeedsFullRepaint && !paintsIntoWindow() && !paintsIntoCompositedAncestor()) + setContentsNeedDisplay(); } bool RenderLayerBacking::updateGraphicsLayerConfiguration() @@ -439,16 +507,23 @@ bool RenderLayerBacking::updateGraphicsLayerConfiguration() RenderLayerCompositor* compositor = this->compositor(); RenderObject* renderer = this->renderer(); + m_owningLayer->updateDescendantDependentFlags(); m_owningLayer->updateZOrderLists(); bool layerConfigChanged = false; + setBackgroundLayerPaintsFixedRootBackground(compositor->needsFixedRootBackgroundLayer(m_owningLayer)); + + // The background layer is currently only used for fixed root backgrounds. + if (updateBackgroundLayer(m_backgroundLayerPaintsFixedRootBackground)) + layerConfigChanged = true; + if (updateForegroundLayer(compositor->needsContentsCompositingLayer(m_owningLayer))) layerConfigChanged = true; bool needsDescendentsClippingLayer = compositor->clipsCompositingDescendants(m_owningLayer); // Our scrolling layer will clip. - if (m_owningLayer->usesCompositedScrolling()) + if (m_owningLayer->needsCompositedScrolling()) needsDescendentsClippingLayer = false; if (updateClippingLayers(compositor->clippedByAncestor(m_owningLayer), needsDescendentsClippingLayer)) @@ -457,7 +532,7 @@ bool RenderLayerBacking::updateGraphicsLayerConfiguration() if (updateOverflowControlsLayers(requiresHorizontalScrollbarLayer(), requiresVerticalScrollbarLayer(), requiresScrollCornerLayer())) layerConfigChanged = true; - if (updateScrollingLayers(m_owningLayer->usesCompositedScrolling())) + if (updateScrollingLayers(m_owningLayer->needsCompositedScrolling())) layerConfigChanged = true; if (layerConfigChanged) @@ -479,16 +554,23 @@ bool RenderLayerBacking::updateGraphicsLayerConfiguration() } else m_graphicsLayer->setReplicatedByLayer(0); + bool isSimpleContainer = isSimpleContainerCompositingLayer(); + bool didUpdateContentsRect = false; + updateDirectlyCompositedContents(isSimpleContainer, didUpdateContentsRect); + + updateRootLayerConfiguration(); + if (isDirectlyCompositedImage()) updateImageContents(); if (renderer->isEmbeddedObject() && toRenderEmbeddedObject(renderer)->allowsAcceleratedCompositing()) { - PluginViewBase* pluginViewBase = static_cast<PluginViewBase*>(toRenderWidget(renderer)->widget()); - m_graphicsLayer->setContentsToMedia(pluginViewBase->platformLayer()); + PluginViewBase* pluginViewBase = toPluginViewBase(toRenderWidget(renderer)->widget()); + if (!pluginViewBase->shouldNotAddLayer()) + m_graphicsLayer->setContentsToMedia(pluginViewBase->platformLayer()); } #if ENABLE(VIDEO) else if (renderer->isVideo()) { - HTMLMediaElement* mediaElement = static_cast<HTMLMediaElement*>(renderer->node()); + HTMLMediaElement* mediaElement = toHTMLMediaElement(renderer->node()); m_graphicsLayer->setContentsToMedia(mediaElement->platformLayer()); } #endif @@ -500,19 +582,6 @@ bool RenderLayerBacking::updateGraphicsLayerConfiguration() layerConfigChanged = true; } #endif -#if ENABLE(FULLSCREEN_API) - else if (renderer->isRenderFullScreen()) { - // RenderFullScreen renderers have no content, and only a solid - // background color. They also can be large enough to trigger the - // creation of a tiled-layer, which can cause flashing problems - // during repainting. Special case the RenderFullScreen case because - // we know its style does not come from CSS and it is therefore will - // not contain paintable content (e.g. background images, gradients, - // etc), so safe to set the layer's background color to the renderer's - // style's background color. - updateBackgroundColor(); - } -#endif if (renderer->isRenderPart()) layerConfigChanged = RenderLayerCompositor::parentFrameContentLayers(toRenderPart(renderer)); @@ -534,7 +603,7 @@ static IntRect clipBox(RenderBox* renderer) void RenderLayerBacking::updateGraphicsLayerGeometry() { // If we haven't built z-order lists yet, wait until later. - if (m_owningLayer->isStackingContext() && m_owningLayer->m_zOrderListsDirty) + if (m_owningLayer->isStackingContainer() && m_owningLayer->m_zOrderListsDirty) return; // Set transform property, if it is not animating. We have to do this here because the transform @@ -553,6 +622,8 @@ void RenderLayerBacking::updateGraphicsLayerGeometry() #if ENABLE(CSS_COMPOSITING) updateLayerBlendMode(renderer()->style()); #endif + + bool isSimpleContainer = isSimpleContainerCompositingLayer(); m_owningLayer->updateDescendantDependentFlags(); @@ -562,26 +633,11 @@ void RenderLayerBacking::updateGraphicsLayerGeometry() m_graphicsLayer->setContentsVisible(m_owningLayer->hasVisibleContent() || hasVisibleNonCompositingDescendantLayers()); RenderStyle* style = renderer()->style(); - m_graphicsLayer->setPreserves3D(style->transformStyle3D() == TransformStyle3DPreserve3D && !renderer()->hasReflection()); + // FIXME: reflections should force transform-style to be flat in the style: https://bugs.webkit.org/show_bug.cgi?id=106959 + bool preserves3D = style->transformStyle3D() == TransformStyle3DPreserve3D && !renderer()->hasReflection(); + m_graphicsLayer->setPreserves3D(preserves3D); m_graphicsLayer->setBackfaceVisibility(style->backfaceVisibility() == BackfaceVisibilityVisible); - // Register fixed position layers and their containers with the scrolling coordinator. - if (Page* page = renderer()->frame()->page()) { - if (ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator()) { - if (style->position() == FixedPosition || compositor()->fixedPositionedByAncestor(m_owningLayer)) - scrollingCoordinator->setLayerIsFixedToContainerLayer(childForSuperlayers(), true); - else { - if (m_ancestorClippingLayer) - scrollingCoordinator->setLayerIsFixedToContainerLayer(m_ancestorClippingLayer.get(), false); - scrollingCoordinator->setLayerIsFixedToContainerLayer(m_graphicsLayer.get(), false); - } - // Page scale is applied as a transform on the root render view layer. Because the scroll - // layer is further up in the hierarchy, we need to avoid marking the root render view - // layer as a container. - bool isContainer = m_owningLayer->hasTransform() && !m_owningLayer->isRootLayer(); - scrollingCoordinator->setLayerIsContainerForFixedPositionLayers(childForSuperlayers(), isContainer); - } - } RenderLayer* compAncestor = m_owningLayer->ancestorCompositingLayer(); // We compute everything relative to the enclosing compositing layer. @@ -591,11 +647,16 @@ void RenderLayerBacking::updateGraphicsLayerGeometry() ancestorCompositingBounds = pixelSnappedIntRect(compAncestor->backing()->compositedBounds()); } - IntRect localCompositingBounds = pixelSnappedIntRect(compositedBounds()); + LayoutRect localRawCompositingBounds = compositedBounds(); + LayoutPoint rawDelta; + m_owningLayer->convertToLayerCoords(compAncestor, rawDelta, RenderLayer::AdjustForColumns); + IntPoint delta = flooredIntPoint(rawDelta); + m_subpixelAccumulation = toLayoutSize(rawDelta.fraction()); + // Move the bounds by the subpixel accumulation so that it pixel-snaps relative to absolute pixels instead of local coordinates. + localRawCompositingBounds.move(m_subpixelAccumulation); + IntRect localCompositingBounds = pixelSnappedIntRect(localRawCompositingBounds); IntRect relativeCompositingBounds(localCompositingBounds); - IntPoint delta; - m_owningLayer->convertToPixelSnappedLayerCoords(compAncestor, delta); relativeCompositingBounds.moveBy(delta); IntPoint graphicsLayerParentLocation; @@ -609,7 +670,7 @@ void RenderLayerBacking::updateGraphicsLayerGeometry() else graphicsLayerParentLocation = renderer()->view()->documentRect().location(); - if (compAncestor && compAncestor->usesCompositedScrolling()) { + if (compAncestor && compAncestor->needsCompositedScrolling()) { RenderBox* renderBox = toRenderBox(compAncestor->renderer()); IntSize scrollOffset = compAncestor->scrolledContentOffset(); IntPoint scrollOrigin(renderBox->borderLeft(), renderBox->borderTop()); @@ -620,10 +681,10 @@ void RenderLayerBacking::updateGraphicsLayerGeometry() // Call calculateRects to get the backgroundRect which is what is used to clip the contents of this // layer. Note that we call it with temporaryClipRects = true because normally when computing clip rects // for a compositing layer, rootLayer is the layer itself. - RenderLayer::ClipRectsContext clipRectsContext(compAncestor, 0, TemporaryClipRects, IgnoreOverlayScrollbarSize, RenderLayer::IgnoreOverflowClip); + RenderLayer::ClipRectsContext clipRectsContext(compAncestor, 0, TemporaryClipRects, IgnoreOverlayScrollbarSize, IgnoreOverflowClip); IntRect parentClipRect = pixelSnappedIntRect(m_owningLayer->backgroundClipRect(clipRectsContext).rect()); // FIXME: Incorrect for CSS regions. ASSERT(parentClipRect != PaintInfo::infiniteRect()); - m_ancestorClippingLayer->setPosition(FloatPoint() + (parentClipRect.location() - graphicsLayerParentLocation)); + m_ancestorClippingLayer->setPosition(FloatPoint(parentClipRect.location() - graphicsLayerParentLocation)); m_ancestorClippingLayer->setSize(parentClipRect.size()); // backgroundRect is relative to compAncestor, so subtract deltaX/deltaY to get back to local coords. @@ -633,26 +694,41 @@ void RenderLayerBacking::updateGraphicsLayerGeometry() graphicsLayerParentLocation = parentClipRect.location(); } - m_graphicsLayer->setPosition(FloatPoint() + (relativeCompositingBounds.location() - graphicsLayerParentLocation)); - m_graphicsLayer->setOffsetFromRenderer(localCompositingBounds.location() - IntPoint()); + FloatSize contentsSize = relativeCompositingBounds.size(); + if (m_contentsContainmentLayer) { + m_contentsContainmentLayer->setPreserves3D(preserves3D); + m_contentsContainmentLayer->setPosition(FloatPoint(relativeCompositingBounds.location() - graphicsLayerParentLocation)); + // Use the same size as m_graphicsLayer so transforms behave correctly. + m_contentsContainmentLayer->setSize(contentsSize); + graphicsLayerParentLocation = relativeCompositingBounds.location(); + } + + m_graphicsLayer->setPosition(FloatPoint(relativeCompositingBounds.location() - graphicsLayerParentLocation)); + m_graphicsLayer->setOffsetFromRenderer(toIntSize(localCompositingBounds.location())); + FloatSize oldSize = m_graphicsLayer->size(); - FloatSize newSize = relativeCompositingBounds.size(); - if (oldSize != newSize) { - m_graphicsLayer->setSize(newSize); - // A bounds change will almost always require redisplay. Usually that redisplay - // will happen because of a repaint elsewhere, but not always: - // e.g. see RenderView::setMaximalOutlineSize() - m_graphicsLayer->setNeedsDisplay(); + if (oldSize != contentsSize) { + m_graphicsLayer->setSize(contentsSize); + // Usually invalidation will happen via layout etc, but if we've affected the layer + // size by constraining relative to a clipping ancestor or the viewport, we + // have to invalidate to avoid showing stretched content. + if (m_boundsConstrainedByClipping) + m_graphicsLayer->setNeedsDisplay(); + } + if (!m_isMainFrameRenderViewLayer) { + // For non-root layers, background is always painted by the primary graphics layer. + ASSERT(!m_backgroundLayer); + m_graphicsLayer->setContentsOpaque(m_owningLayer->backgroundIsKnownToBeOpaqueInRect(localCompositingBounds)); } // If we have a layer that clips children, position it. IntRect clippingBox; if (GraphicsLayer* clipLayer = clippingLayer()) { clippingBox = clipBox(toRenderBox(renderer())); - clipLayer->setPosition(FloatPoint() + (clippingBox.location() - localCompositingBounds.location())); + clipLayer->setPosition(FloatPoint(clippingBox.location() - localCompositingBounds.location())); clipLayer->setSize(clippingBox.size()); - clipLayer->setOffsetFromRenderer(clippingBox.location() - IntPoint()); + clipLayer->setOffsetFromRenderer(toIntSize(clippingBox.location())); } if (m_maskLayer) { @@ -668,7 +744,7 @@ void RenderLayerBacking::updateGraphicsLayerGeometry() const IntRect borderBox = toRenderBox(renderer())->pixelSnappedBorderBoxRect(); // Get layout bounds in the coords of compAncestor to match relativeCompositingBounds. - IntRect layerBounds = IntRect(delta, borderBox.size()); + IntRect layerBounds(delta, borderBox.size()); // Update properties that depend on layer dimensions FloatPoint3D transformOrigin = computeTransformOrigin(borderBox); @@ -676,7 +752,10 @@ void RenderLayerBacking::updateGraphicsLayerGeometry() FloatPoint3D anchor(relativeCompositingBounds.width() != 0.0f ? ((layerBounds.x() - relativeCompositingBounds.x()) + transformOrigin.x()) / relativeCompositingBounds.width() : 0.5f, relativeCompositingBounds.height() != 0.0f ? ((layerBounds.y() - relativeCompositingBounds.y()) + transformOrigin.y()) / relativeCompositingBounds.height() : 0.5f, transformOrigin.z()); - m_graphicsLayer->setAnchorPoint(anchor); + if (m_contentsContainmentLayer) + m_contentsContainmentLayer->setAnchorPoint(anchor); + else + m_graphicsLayer->setAnchorPoint(anchor); RenderStyle* style = renderer()->style(); GraphicsLayer* clipLayer = clippingLayer(); @@ -697,17 +776,19 @@ void RenderLayerBacking::updateGraphicsLayerGeometry() } } else { m_graphicsLayer->setAnchorPoint(FloatPoint3D(0.5f, 0.5f, 0)); + if (m_contentsContainmentLayer) + m_contentsContainmentLayer->setAnchorPoint(FloatPoint3D(0.5f, 0.5f, 0)); } if (m_foregroundLayer) { FloatPoint foregroundPosition; - FloatSize foregroundSize = newSize; + FloatSize foregroundSize = contentsSize; IntSize foregroundOffset = m_graphicsLayer->offsetFromRenderer(); if (hasClippingLayer()) { // If we have a clipping layer (which clips descendants), then the foreground layer is a child of it, // so that it gets correctly sorted with children. In that case, position relative to the clipping layer. foregroundSize = FloatSize(clippingBox.size()); - foregroundOffset = clippingBox.location() - IntPoint(); + foregroundOffset = toIntSize(clippingBox.location()); } m_foregroundLayer->setPosition(foregroundPosition); @@ -718,6 +799,22 @@ void RenderLayerBacking::updateGraphicsLayerGeometry() m_foregroundLayer->setOffsetFromRenderer(foregroundOffset); } + if (m_backgroundLayer) { + FloatPoint backgroundPosition; + FloatSize backgroundSize = contentsSize; + if (backgroundLayerPaintsFixedRootBackground()) { + FrameView* frameView = toRenderView(renderer())->frameView(); + backgroundPosition = IntPoint(frameView->scrollOffsetForFixedPosition()); + backgroundSize = frameView->visibleContentRect().size(); + } + m_backgroundLayer->setPosition(backgroundPosition); + if (backgroundSize != m_backgroundLayer->size()) { + m_backgroundLayer->setSize(backgroundSize); + m_backgroundLayer->setNeedsDisplay(); + } + m_backgroundLayer->setOffsetFromRenderer(m_graphicsLayer->offsetFromRenderer()); + } + if (m_owningLayer->reflectionLayer() && m_owningLayer->reflectionLayer()->isComposited()) { RenderLayerBacking* reflectionBacking = m_owningLayer->reflectionLayer()->backing(); reflectionBacking->updateGraphicsLayerGeometry(); @@ -726,22 +823,22 @@ void RenderLayerBacking::updateGraphicsLayerGeometry() // but the reflected layer is the bounds of this layer, so we need to position it appropriately. FloatRect layerBounds = compositedBounds(); FloatRect reflectionLayerBounds = reflectionBacking->compositedBounds(); - reflectionBacking->graphicsLayer()->setReplicatedLayerPosition(FloatPoint() + (layerBounds.location() - reflectionLayerBounds.location())); + reflectionBacking->graphicsLayer()->setReplicatedLayerPosition(FloatPoint(layerBounds.location() - reflectionLayerBounds.location())); } if (m_scrollingLayer) { ASSERT(m_scrollingContentsLayer); RenderBox* renderBox = toRenderBox(renderer()); IntRect paddingBox(renderBox->borderLeft(), renderBox->borderTop(), renderBox->width() - renderBox->borderLeft() - renderBox->borderRight(), renderBox->height() - renderBox->borderTop() - renderBox->borderBottom()); - IntSize scrollOffset = m_owningLayer->scrolledContentOffset(); + IntSize scrollOffset = m_owningLayer->scrollOffset(); - m_scrollingLayer->setPosition(FloatPoint() + (paddingBox.location() - localCompositingBounds.location())); + m_scrollingLayer->setPosition(FloatPoint(paddingBox.location() - localCompositingBounds.location())); m_scrollingLayer->setSize(paddingBox.size()); m_scrollingContentsLayer->setPosition(FloatPoint(-scrollOffset.width(), -scrollOffset.height())); IntSize oldScrollingLayerOffset = m_scrollingLayer->offsetFromRenderer(); - m_scrollingLayer->setOffsetFromRenderer(IntPoint() - paddingBox.location()); + m_scrollingLayer->setOffsetFromRenderer(-toIntSize(paddingBox.location())); bool paddingBoxOffsetChanged = oldScrollingLayerOffset != m_scrollingLayer->offsetFromRenderer(); @@ -749,41 +846,96 @@ void RenderLayerBacking::updateGraphicsLayerGeometry() if (scrollSize != m_scrollingContentsLayer->size() || paddingBoxOffsetChanged) m_scrollingContentsLayer->setNeedsDisplay(); - IntSize scrollingContentsOffset = paddingBox.location() - IntPoint() - scrollOffset; + IntSize scrollingContentsOffset = toIntSize(paddingBox.location() - scrollOffset); if (scrollingContentsOffset != m_scrollingContentsLayer->offsetFromRenderer() || scrollSize != m_scrollingContentsLayer->size()) compositor()->scrollingLayerDidChange(m_owningLayer); m_scrollingContentsLayer->setSize(scrollSize); // FIXME: The paint offset and the scroll offset should really be separate concepts. m_scrollingContentsLayer->setOffsetFromRenderer(scrollingContentsOffset, GraphicsLayer::DontSetNeedsDisplay); - } - m_graphicsLayer->setContentsRect(contentsBox()); + if (m_foregroundLayer) { + if (m_foregroundLayer->size() != m_scrollingContentsLayer->size()) + m_foregroundLayer->setSize(m_scrollingContentsLayer->size()); + m_foregroundLayer->setNeedsDisplay(); + m_foregroundLayer->setOffsetFromRenderer(m_scrollingContentsLayer->offsetFromRenderer()); + } + } // If this layer was created just for clipping or to apply perspective, it doesn't need its own backing store. - setRequiresOwnBackingStore(compositor()->requiresOwnBackingStore(m_owningLayer, compAncestor)); + setRequiresOwnBackingStore(compositor()->requiresOwnBackingStore(m_owningLayer, compAncestor, relativeCompositingBounds, ancestorCompositingBounds)); + + bool didUpdateContentsRect = false; + updateDirectlyCompositedContents(isSimpleContainer, didUpdateContentsRect); + if (!didUpdateContentsRect && m_graphicsLayer->hasContentsLayer()) + resetContentsRect(); - updateDrawsContent(); + updateDrawsContent(isSimpleContainer); updateAfterWidgetResize(); + registerScrollingLayers(); +} + +void RenderLayerBacking::updateDirectlyCompositedContents(bool isSimpleContainer, bool& didUpdateContentsRect) +{ + if (!m_owningLayer->hasVisibleContent()) + return; + + // The order of operations here matters, since the last valid type of contents needs + // to also update the contentsRect. + updateDirectlyCompositedBackgroundColor(isSimpleContainer, didUpdateContentsRect); + updateDirectlyCompositedBackgroundImage(isSimpleContainer, didUpdateContentsRect); +} + +void RenderLayerBacking::registerScrollingLayers() +{ + // Register fixed position layers and their containers with the scrolling coordinator. + ScrollingCoordinator* scrollingCoordinator = scrollingCoordinatorFromLayer(m_owningLayer); + if (!scrollingCoordinator) + return; + + compositor()->updateViewportConstraintStatus(m_owningLayer); + + if (!scrollingCoordinator->supportsFixedPositionLayers()) + return; + + scrollingCoordinator->updateLayerPositionConstraint(m_owningLayer); + + // Page scale is applied as a transform on the root render view layer. Because the scroll + // layer is further up in the hierarchy, we need to avoid marking the root render view + // layer as a container. + bool isContainer = m_owningLayer->hasTransform() && !m_owningLayer->isRootLayer(); + scrollingCoordinator->setLayerIsContainerForFixedPositionLayers(childForSuperlayers(), isContainer); } void RenderLayerBacking::updateInternalHierarchy() { // m_foregroundLayer has to be inserted in the correct order with child layers, // so it's not inserted here. - if (m_ancestorClippingLayer) { + if (m_ancestorClippingLayer) m_ancestorClippingLayer->removeAllChildren(); - m_graphicsLayer->removeFromParent(); - m_ancestorClippingLayer->addChild(m_graphicsLayer.get()); + + if (m_contentsContainmentLayer) { + m_contentsContainmentLayer->removeAllChildren(); + if (m_ancestorClippingLayer) + m_ancestorClippingLayer->addChild(m_contentsContainmentLayer.get()); } + + if (m_backgroundLayer) + m_contentsContainmentLayer->addChild(m_backgroundLayer.get()); - if (m_containmentLayer) { - m_containmentLayer->removeFromParent(); - m_graphicsLayer->addChild(m_containmentLayer.get()); + m_graphicsLayer->removeFromParent(); + if (m_contentsContainmentLayer) + m_contentsContainmentLayer->addChild(m_graphicsLayer.get()); + else if (m_ancestorClippingLayer) + m_ancestorClippingLayer->addChild(m_graphicsLayer.get()); + + if (m_childContainmentLayer) { + m_childContainmentLayer->removeFromParent(); + m_graphicsLayer->addChild(m_childContainmentLayer.get()); } if (m_scrollingLayer) { - GraphicsLayer* superlayer = m_containmentLayer ? m_containmentLayer.get() : m_graphicsLayer.get(); + GraphicsLayer* superlayer = m_childContainmentLayer ? m_childContainmentLayer.get() : m_graphicsLayer.get(); m_scrollingLayer->removeFromParent(); superlayer->addChild(m_scrollingLayer.get()); } @@ -805,14 +957,27 @@ void RenderLayerBacking::updateInternalHierarchy() } } +void RenderLayerBacking::resetContentsRect() +{ + IntRect rect = pixelSnappedIntRect(contentsBox()); + m_graphicsLayer->setContentsRect(rect); + m_graphicsLayer->setContentsTileSize(IntSize()); + m_graphicsLayer->setContentsTilePhase(IntPoint()); +} + void RenderLayerBacking::updateDrawsContent() { + updateDrawsContent(isSimpleContainerCompositingLayer()); +} + +void RenderLayerBacking::updateDrawsContent(bool isSimpleContainer) +{ if (m_scrollingLayer) { // We don't have to consider overflow controls, because we know that the scrollbars are drawn elsewhere. // m_graphicsLayer only needs backing store if the non-scrolling parts (background, outlines, borders, shadows etc) need to paint. // m_scrollingLayer never has backing store. // m_scrollingContentsLayer only needs backing store if the scrolled contents need to paint. - bool hasNonScrollingPaintedContent = m_owningLayer->hasVisibleContent() && hasBoxDecorationsOrBackground(renderer()); + bool hasNonScrollingPaintedContent = m_owningLayer->hasVisibleContent() && m_owningLayer->hasBoxDecorationsOrBackground(); m_graphicsLayer->setDrawsContent(hasNonScrollingPaintedContent); bool hasScrollingPaintedContent = m_owningLayer->hasVisibleContent() && (renderer()->hasBackground() || paintsChildren()); @@ -820,12 +985,15 @@ void RenderLayerBacking::updateDrawsContent() return; } - bool hasPaintedContent = containsPaintedContent(); + bool hasPaintedContent = containsPaintedContent(isSimpleContainer); // FIXME: we could refine this to only allocate backing for one of these layers if possible. m_graphicsLayer->setDrawsContent(hasPaintedContent); if (m_foregroundLayer) m_foregroundLayer->setDrawsContent(hasPaintedContent); + + if (m_backgroundLayer) + m_backgroundLayer->setDrawsContent(hasPaintedContent); } // Return true if the layers changed. @@ -840,87 +1008,100 @@ bool RenderLayerBacking::updateClippingLayers(bool needsAncestorClip, bool needs layersChanged = true; } } else if (m_ancestorClippingLayer) { + willDestroyLayer(m_ancestorClippingLayer.get()); m_ancestorClippingLayer->removeFromParent(); m_ancestorClippingLayer = nullptr; layersChanged = true; } if (needsDescendantClip) { - if (!m_containmentLayer && !m_usingTiledCacheLayer) { - m_containmentLayer = createGraphicsLayer("Child clipping Layer"); - m_containmentLayer->setMasksToBounds(true); + if (!m_childContainmentLayer && !m_usingTiledCacheLayer) { + m_childContainmentLayer = createGraphicsLayer("Child clipping Layer"); + m_childContainmentLayer->setMasksToBounds(true); layersChanged = true; } } else if (hasClippingLayer()) { - m_containmentLayer->removeFromParent(); - m_containmentLayer = nullptr; + willDestroyLayer(m_childContainmentLayer.get()); + m_childContainmentLayer->removeFromParent(); + m_childContainmentLayer = nullptr; layersChanged = true; } return layersChanged; } +void RenderLayerBacking::setBackgroundLayerPaintsFixedRootBackground(bool backgroundLayerPaintsFixedRootBackground) +{ + m_backgroundLayerPaintsFixedRootBackground = backgroundLayerPaintsFixedRootBackground; +} + bool RenderLayerBacking::requiresHorizontalScrollbarLayer() const { -#if !PLATFORM(CHROMIUM) - if (!m_owningLayer->hasOverlayScrollbars()) + if (!m_owningLayer->hasOverlayScrollbars() && !m_owningLayer->needsCompositedScrolling()) return false; -#endif return m_owningLayer->horizontalScrollbar(); } bool RenderLayerBacking::requiresVerticalScrollbarLayer() const { -#if !PLATFORM(CHROMIUM) - if (!m_owningLayer->hasOverlayScrollbars()) + if (!m_owningLayer->hasOverlayScrollbars() && !m_owningLayer->needsCompositedScrolling()) return false; -#endif return m_owningLayer->verticalScrollbar(); } bool RenderLayerBacking::requiresScrollCornerLayer() const { -#if !PLATFORM(CHROMIUM) - if (!m_owningLayer->hasOverlayScrollbars()) + if (!m_owningLayer->hasOverlayScrollbars() && !m_owningLayer->needsCompositedScrolling()) return false; -#endif return !m_owningLayer->scrollCornerAndResizerRect().isEmpty(); } bool RenderLayerBacking::updateOverflowControlsLayers(bool needsHorizontalScrollbarLayer, bool needsVerticalScrollbarLayer, bool needsScrollCornerLayer) { - bool layersChanged = false; + bool horizontalScrollbarLayerChanged = false; if (needsHorizontalScrollbarLayer) { if (!m_layerForHorizontalScrollbar) { m_layerForHorizontalScrollbar = createGraphicsLayer("horizontal scrollbar"); - layersChanged = true; + horizontalScrollbarLayerChanged = true; } } else if (m_layerForHorizontalScrollbar) { - m_layerForHorizontalScrollbar.clear(); - layersChanged = true; + willDestroyLayer(m_layerForHorizontalScrollbar.get()); + m_layerForHorizontalScrollbar = nullptr; + horizontalScrollbarLayerChanged = true; } + bool verticalScrollbarLayerChanged = false; if (needsVerticalScrollbarLayer) { if (!m_layerForVerticalScrollbar) { m_layerForVerticalScrollbar = createGraphicsLayer("vertical scrollbar"); - layersChanged = true; + verticalScrollbarLayerChanged = true; } } else if (m_layerForVerticalScrollbar) { - m_layerForVerticalScrollbar.clear(); - layersChanged = true; + willDestroyLayer(m_layerForVerticalScrollbar.get()); + m_layerForVerticalScrollbar = nullptr; + verticalScrollbarLayerChanged = true; } + bool scrollCornerLayerChanged = false; if (needsScrollCornerLayer) { if (!m_layerForScrollCorner) { m_layerForScrollCorner = createGraphicsLayer("scroll corner"); - layersChanged = true; + scrollCornerLayerChanged = true; } } else if (m_layerForScrollCorner) { - m_layerForScrollCorner.clear(); - layersChanged = true; + willDestroyLayer(m_layerForScrollCorner.get()); + m_layerForScrollCorner = nullptr; + scrollCornerLayerChanged = true; } - return layersChanged; + if (ScrollingCoordinator* scrollingCoordinator = scrollingCoordinatorFromLayer(m_owningLayer)) { + if (horizontalScrollbarLayerChanged) + scrollingCoordinator->scrollableAreaScrollbarLayerDidChange(m_owningLayer, HorizontalScrollbar); + if (verticalScrollbarLayerChanged) + scrollingCoordinator->scrollableAreaScrollbarLayerDidChange(m_owningLayer, VerticalScrollbar); + } + + return horizontalScrollbarLayerChanged || verticalScrollbarLayerChanged || scrollCornerLayerChanged; } void RenderLayerBacking::positionOverflowControlsLayers(const IntSize& offsetFromRoot) @@ -931,8 +1112,10 @@ void RenderLayerBacking::positionOverflowControlsLayers(const IntSize& offsetFro if (hBar) { layer->setPosition(hBar->frameRect().location() - offsetFromRoot - offsetFromRenderer); layer->setSize(hBar->frameRect().size()); + if (layer->hasContentsLayer()) + layer->setContentsRect(IntRect(IntPoint(), hBar->frameRect().size())); } - layer->setDrawsContent(hBar); + layer->setDrawsContent(hBar && !layer->hasContentsLayer()); } if (GraphicsLayer* layer = layerForVerticalScrollbar()) { @@ -940,8 +1123,10 @@ void RenderLayerBacking::positionOverflowControlsLayers(const IntSize& offsetFro if (vBar) { layer->setPosition(vBar->frameRect().location() - offsetFromRoot - offsetFromRenderer); layer->setSize(vBar->frameRect().size()); + if (layer->hasContentsLayer()) + layer->setContentsRect(IntRect(IntPoint(), vBar->frameRect().size())); } - layer->setDrawsContent(vBar); + layer->setDrawsContent(vBar && !layer->hasContentsLayer()); } if (GraphicsLayer* layer = layerForScrollCorner()) { @@ -952,6 +1137,23 @@ void RenderLayerBacking::positionOverflowControlsLayers(const IntSize& offsetFro } } +bool RenderLayerBacking::hasUnpositionedOverflowControlsLayers() const +{ + if (GraphicsLayer* layer = layerForHorizontalScrollbar()) + if (!layer->drawsContent()) + return true; + + if (GraphicsLayer* layer = layerForVerticalScrollbar()) + if (!layer->drawsContent()) + return true; + + if (GraphicsLayer* layer = layerForScrollCorner()) + if (!layer->drawsContent()) + return true; + + return false; +} + bool RenderLayerBacking::updateForegroundLayer(bool needsForegroundLayer) { bool layerChanged = false; @@ -959,7 +1161,7 @@ bool RenderLayerBacking::updateForegroundLayer(bool needsForegroundLayer) if (!m_foregroundLayer) { String layerName; #ifndef NDEBUG - layerName = nameForLayer() + " (foreground)"; + layerName = m_owningLayer->name() + " (foreground)"; #endif m_foregroundLayer = createGraphicsLayer(layerName); m_foregroundLayer->setDrawsContent(true); @@ -967,17 +1169,72 @@ bool RenderLayerBacking::updateForegroundLayer(bool needsForegroundLayer) layerChanged = true; } } else if (m_foregroundLayer) { + willDestroyLayer(m_foregroundLayer.get()); m_foregroundLayer->removeFromParent(); m_foregroundLayer = nullptr; layerChanged = true; } - if (layerChanged) + if (layerChanged) { + m_graphicsLayer->setNeedsDisplay(); m_graphicsLayer->setPaintingPhase(paintingPhaseForPrimaryLayer()); + } return layerChanged; } +bool RenderLayerBacking::updateBackgroundLayer(bool needsBackgroundLayer) +{ + bool layerChanged = false; + if (needsBackgroundLayer) { + if (!m_backgroundLayer) { + String layerName; +#ifndef NDEBUG + layerName = m_owningLayer->name() + " (background)"; +#endif + m_backgroundLayer = createGraphicsLayer(layerName); + m_backgroundLayer->setDrawsContent(true); + m_backgroundLayer->setAnchorPoint(FloatPoint3D()); + m_backgroundLayer->setPaintingPhase(GraphicsLayerPaintBackground); + layerChanged = true; + } + + if (!m_contentsContainmentLayer) { + String layerName; +#ifndef NDEBUG + layerName = m_owningLayer->name() + " (contents containment)"; +#endif + m_contentsContainmentLayer = createGraphicsLayer(layerName); + m_contentsContainmentLayer->setAppliesPageScale(true); + m_graphicsLayer->setAppliesPageScale(false); + layerChanged = true; + } + } else { + if (m_backgroundLayer) { + willDestroyLayer(m_backgroundLayer.get()); + m_backgroundLayer->removeFromParent(); + m_backgroundLayer = nullptr; + layerChanged = true; + } + if (m_contentsContainmentLayer) { + willDestroyLayer(m_contentsContainmentLayer.get()); + m_contentsContainmentLayer->removeFromParent(); + m_contentsContainmentLayer = nullptr; + layerChanged = true; + m_graphicsLayer->setAppliesPageScale(true); + } + } + + if (layerChanged) { + m_graphicsLayer->setNeedsDisplay(); + // This assumes that the background layer is only used for fixed backgrounds, which is currently a correct assumption. + if (renderer()->view()) + compositor()->fixedRootBackgroundLayerChanged(); + } + + return layerChanged; +} + bool RenderLayerBacking::updateMaskLayer(bool needsMaskLayer) { bool layerChanged = false; @@ -989,6 +1246,7 @@ bool RenderLayerBacking::updateMaskLayer(bool needsMaskLayer) layerChanged = true; } } else if (m_maskLayer) { + willDestroyLayer(m_maskLayer.get()); m_maskLayer = nullptr; layerChanged = true; } @@ -1001,6 +1259,8 @@ bool RenderLayerBacking::updateMaskLayer(bool needsMaskLayer) bool RenderLayerBacking::updateScrollingLayers(bool needsScrollingLayers) { + ScrollingCoordinator* scrollingCoordinator = scrollingCoordinatorFromLayer(m_owningLayer); + bool layerChanged = false; if (needsScrollingLayers) { if (!m_scrollingLayer) { @@ -1012,15 +1272,24 @@ bool RenderLayerBacking::updateScrollingLayers(bool needsScrollingLayers) // Inner layer which renders the content that scrolls. m_scrollingContentsLayer = createGraphicsLayer("Scrolled Contents"); m_scrollingContentsLayer->setDrawsContent(true); - m_scrollingContentsLayer->setPaintingPhase(GraphicsLayerPaintForeground | GraphicsLayerPaintOverflowContents); + GraphicsLayerPaintingPhase paintPhase = GraphicsLayerPaintOverflowContents | GraphicsLayerPaintCompositedScroll; + if (!m_foregroundLayer) + paintPhase |= GraphicsLayerPaintForeground; + m_scrollingContentsLayer->setPaintingPhase(paintPhase); m_scrollingLayer->addChild(m_scrollingContentsLayer.get()); layerChanged = true; + if (scrollingCoordinator) + scrollingCoordinator->scrollableAreaScrollLayerDidChange(m_owningLayer); } } else if (m_scrollingLayer) { + willDestroyLayer(m_scrollingLayer.get()); + willDestroyLayer(m_scrollingContentsLayer.get()); m_scrollingLayer = nullptr; m_scrollingContentsLayer = nullptr; layerChanged = true; + if (scrollingCoordinator) + scrollingCoordinator->scrollableAreaScrollLayerDidChange(m_owningLayer); } if (layerChanged) { @@ -1034,17 +1303,9 @@ bool RenderLayerBacking::updateScrollingLayers(bool needsScrollingLayers) return layerChanged; } -void RenderLayerBacking::attachToScrollingCoordinator(RenderLayerBacking* parent) +void RenderLayerBacking::attachToScrollingCoordinatorWithParent(RenderLayerBacking* parent) { - // If m_scrollLayerID non-zero, then this backing is already attached to the ScrollingCoordinator. - if (m_scrollLayerID) - return; - - Page* page = renderer()->frame()->page(); - if (!page) - return; - - ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator(); + ScrollingCoordinator* scrollingCoordinator = scrollingCoordinatorFromLayer(m_owningLayer); if (!scrollingCoordinator) return; @@ -1053,11 +1314,13 @@ void RenderLayerBacking::attachToScrollingCoordinator(RenderLayerBacking* parent ScrollingNodeType nodeType; if (renderer()->style()->position() == FixedPosition) nodeType = FixedNode; + else if (renderer()->style()->position() == StickyPosition) + nodeType = StickyNode; else nodeType = ScrollingNode; ScrollingNodeID parentID = parent ? parent->scrollLayerID() : 0; - m_scrollLayerID = scrollingCoordinator->attachToStateTree(nodeType, scrollingCoordinator->uniqueScrollLayerID(), parentID); + m_scrollLayerID = scrollingCoordinator->attachToStateTree(nodeType, m_scrollLayerID ? m_scrollLayerID : scrollingCoordinator->uniqueScrollLayerID(), parentID); } void RenderLayerBacking::detachFromScrollingCoordinator() @@ -1066,11 +1329,7 @@ void RenderLayerBacking::detachFromScrollingCoordinator() if (!m_scrollLayerID) return; - Page* page = renderer()->frame()->page(); - if (!page) - return; - - ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator(); + ScrollingCoordinator* scrollingCoordinator = scrollingCoordinatorFromLayer(m_owningLayer); if (!scrollingCoordinator) return; @@ -1080,14 +1339,18 @@ void RenderLayerBacking::detachFromScrollingCoordinator() GraphicsLayerPaintingPhase RenderLayerBacking::paintingPhaseForPrimaryLayer() const { - unsigned phase = GraphicsLayerPaintBackground; + unsigned phase = 0; + if (!m_backgroundLayer) + phase |= GraphicsLayerPaintBackground; if (!m_foregroundLayer) phase |= GraphicsLayerPaintForeground; if (!m_maskLayer) phase |= GraphicsLayerPaintMask; - if (m_scrollingContentsLayer) + if (m_scrollingContentsLayer) { phase &= ~GraphicsLayerPaintForeground; + phase |= GraphicsLayerPaintCompositedScroll; + } return static_cast<GraphicsLayerPaintingPhase>(phase); } @@ -1099,7 +1362,7 @@ float RenderLayerBacking::compositingOpacity(float rendererOpacity) const for (RenderLayer* curr = m_owningLayer->parent(); curr; curr = curr->parent()) { // We only care about parents that are stacking contexts. // Recall that opacity creates stacking context. - if (!curr->isStackingContext()) + if (!curr->isStackingContainer()) continue; // If we found a compositing layer, we want to compute opacity @@ -1118,14 +1381,17 @@ static bool hasBoxDecorations(const RenderStyle* style) return style->hasBorder() || style->hasBorderRadius() || style->hasOutline() || style->hasAppearance() || style->boxShadow() || style->hasFilter(); } -static bool hasBoxDecorationsOrBackground(const RenderObject* renderer) -{ - return hasBoxDecorations(renderer->style()) || renderer->hasBackground(); -} +static bool canCreateTiledImage(const RenderStyle*); static bool hasBoxDecorationsOrBackgroundImage(const RenderStyle* style) { - return hasBoxDecorations(style) || style->hasBackgroundImage(); + if (hasBoxDecorations(style)) + return true; + + if (!style->hasBackgroundImage()) + return false; + + return !GraphicsLayer::supportsContentsTiling() || !canCreateTiledImage(style); } Color RenderLayerBacking::rendererBackgroundColor() const @@ -1137,20 +1403,128 @@ Color RenderLayerBacking::rendererBackgroundColor() const return backgroundRenderer->style()->visitedDependentColor(CSSPropertyBackgroundColor); } -void RenderLayerBacking::updateBackgroundColor() +void RenderLayerBacking::updateDirectlyCompositedBackgroundColor(bool isSimpleContainer, bool& didUpdateContentsRect) { - m_graphicsLayer->setContentsToBackgroundColor(rendererBackgroundColor()); + if (!isSimpleContainer) { + m_graphicsLayer->setContentsToSolidColor(Color()); + return; + } + + Color backgroundColor = rendererBackgroundColor(); + + // An unset (invalid) color will remove the solid color. + m_graphicsLayer->setContentsToSolidColor(backgroundColor); + m_graphicsLayer->setContentsRect(backgroundBox()); + didUpdateContentsRect = true; } -bool RenderLayerBacking::paintsBoxDecorations() const +bool canCreateTiledImage(const RenderStyle* style) { - if (!m_owningLayer->hasVisibleContent()) + const FillLayer* fillLayer = style->backgroundLayers(); + if (fillLayer->next()) return false; - if (hasBoxDecorationsOrBackground(renderer())) - return true; + if (!fillLayer->imagesAreLoaded()) + return false; + + if (fillLayer->attachment() != ScrollBackgroundAttachment) + return false; + + Color color = style->visitedDependentColor(CSSPropertyBackgroundColor); + + // FIXME: Allow color+image compositing when it makes sense. + // For now bailing out. + if (color.isValid() && color.alpha()) + return false; + + StyleImage* styleImage = fillLayer->image(); + + // FIXME: support gradients with isGeneratedImage. + if (!styleImage->isCachedImage()) + return false; + + Image* image = styleImage->cachedImage()->image(); + if (!image->isBitmapImage()) + return false; + + return true; +} + +void RenderLayerBacking::updateDirectlyCompositedBackgroundImage(bool isSimpleContainer, bool& didUpdateContentsRect) +{ + if (!GraphicsLayer::supportsContentsTiling()) + return; + + if (isDirectlyCompositedImage()) + return; + + const RenderStyle* style = renderer()->style(); + + if (!isSimpleContainer || !style->hasBackgroundImage()) { + m_graphicsLayer->setContentsToImage(0); + return; + } + + IntRect destRect = backgroundBox(); + IntPoint phase; + IntSize tileSize; + + RefPtr<Image> image = style->backgroundLayers()->image()->cachedImage()->image(); + toRenderBox(renderer())->getGeometryForBackgroundImage(m_owningLayer->renderer(), destRect, phase, tileSize); + m_graphicsLayer->setContentsTileSize(tileSize); + m_graphicsLayer->setContentsTilePhase(phase); + m_graphicsLayer->setContentsRect(destRect); + m_graphicsLayer->setContentsToImage(image.get()); + didUpdateContentsRect = true; +} + +void RenderLayerBacking::updateRootLayerConfiguration() +{ + if (!m_usingTiledCacheLayer) + return; + + Color backgroundColor; + bool viewIsTransparent = compositor()->viewHasTransparentBackground(&backgroundColor); + + if (m_backgroundLayerPaintsFixedRootBackground && m_backgroundLayer) { + m_backgroundLayer->setBackgroundColor(backgroundColor); + m_backgroundLayer->setContentsOpaque(!viewIsTransparent); + + m_graphicsLayer->setBackgroundColor(Color()); + m_graphicsLayer->setContentsOpaque(false); + } else { + m_graphicsLayer->setBackgroundColor(backgroundColor); + m_graphicsLayer->setContentsOpaque(!viewIsTransparent); + } +} + +static bool supportsDirectBoxDecorationsComposition(const RenderObject* renderer) +{ + if (!GraphicsLayer::supportsBackgroundColorContent()) + return false; + + if (renderer->hasClip()) + return false; + + if (hasBoxDecorationsOrBackgroundImage(renderer->style())) + return false; + + // FIXME: we should be able to allow backgroundComposite; However since this is not a common use case it has been deferred for now. + if (renderer->style()->backgroundComposite() != CompositeSourceOver) + return false; - if (m_owningLayer->hasOverflowControls()) + if (renderer->style()->backgroundClip() == TextFillBox) + return false; + + return true; +} + +bool RenderLayerBacking::paintsBoxDecorations() const +{ + if (!m_owningLayer->hasVisibleBoxDecorations()) + return false; + + if (!supportsDirectBoxDecorationsComposition(renderer())) return true; return false; @@ -1158,15 +1532,27 @@ bool RenderLayerBacking::paintsBoxDecorations() const bool RenderLayerBacking::paintsChildren() const { - if (m_owningLayer->hasVisibleContent() && containsNonEmptyRenderers()) + if (m_owningLayer->hasVisibleContent() && m_owningLayer->hasNonEmptyChildRenderers()) return true; - + if (hasVisibleNonCompositingDescendantLayers()) return true; return false; } +static bool isRestartedPlugin(RenderObject* renderer) +{ + if (!renderer->isEmbeddedObject()) + return false; + + Element* element = toElement(renderer->node()); + if (!element || !element->isPluginElement()) + return false; + + return toHTMLPlugInElement(element)->isRestartedPlugin(); +} + static bool isCompositedPlugin(RenderObject* renderer) { return renderer->isEmbeddedObject() && toRenderEmbeddedObject(renderer)->allowsAcceleratedCompositing(); @@ -1181,12 +1567,15 @@ bool RenderLayerBacking::isSimpleContainerCompositingLayer() const if (renderObject->hasMask()) // masks require special treatment return false; - if (renderObject->isReplaced() && !isCompositedPlugin(renderObject)) - return false; - + if (renderObject->isReplaced() && (!isCompositedPlugin(renderObject) || isRestartedPlugin(renderObject))) + return false; + if (paintsBoxDecorations() || paintsChildren()) return false; - + + if (renderObject->isRenderRegion()) + return false; + if (renderObject->node() && renderObject->node()->isDocumentNode()) { // Look to see if the root object has a non-simple background RenderObject* rootObject = renderObject->document()->documentElement() ? renderObject->document()->documentElement()->renderer() : 0; @@ -1215,63 +1604,46 @@ bool RenderLayerBacking::isSimpleContainerCompositingLayer() const return true; } -bool RenderLayerBacking::containsNonEmptyRenderers() const -{ - // Some HTML can cause whitespace text nodes to have renderers, like: - // <div> - // <img src=...> - // </div> - // so test for 0x0 RenderTexts here - for (RenderObject* child = renderer()->firstChild(); child; child = child->nextSibling()) { - if (!child->hasLayer()) { - if (child->isRenderInline() || !child->isBox()) - return true; - - if (toRenderBox(child)->width() > 0 || toRenderBox(child)->height() > 0) - return true; - } - } - return false; -} - -// Conservative test for having no rendered children. -bool RenderLayerBacking::hasVisibleNonCompositingDescendantLayers() const +static bool hasVisibleNonCompositingDescendant(RenderLayer* parent) { // FIXME: We shouldn't be called with a stale z-order lists. See bug 85512. - m_owningLayer->updateLayerListsIfNeeded(); + parent->updateLayerListsIfNeeded(); #if !ASSERT_DISABLED - LayerListMutationDetector mutationChecker(m_owningLayer); + LayerListMutationDetector mutationChecker(parent); #endif - if (Vector<RenderLayer*>* normalFlowList = m_owningLayer->normalFlowList()) { + if (Vector<RenderLayer*>* normalFlowList = parent->normalFlowList()) { size_t listSize = normalFlowList->size(); for (size_t i = 0; i < listSize; ++i) { RenderLayer* curLayer = normalFlowList->at(i); - if (!curLayer->isComposited() && curLayer->hasVisibleContent()) + if (!curLayer->isComposited() + && (curLayer->hasVisibleContent() || hasVisibleNonCompositingDescendant(curLayer))) return true; } } - if (m_owningLayer->isStackingContext()) { - if (!m_owningLayer->hasVisibleDescendant()) + if (parent->isStackingContainer()) { + if (!parent->hasVisibleDescendant()) return false; // Use the m_hasCompositingDescendant bit to optimize? - if (Vector<RenderLayer*>* negZOrderList = m_owningLayer->negZOrderList()) { + if (Vector<RenderLayer*>* negZOrderList = parent->negZOrderList()) { size_t listSize = negZOrderList->size(); for (size_t i = 0; i < listSize; ++i) { RenderLayer* curLayer = negZOrderList->at(i); - if (!curLayer->isComposited() && curLayer->hasVisibleContent()) + if (!curLayer->isComposited() + && (curLayer->hasVisibleContent() || hasVisibleNonCompositingDescendant(curLayer))) return true; } } - if (Vector<RenderLayer*>* posZOrderList = m_owningLayer->posZOrderList()) { + if (Vector<RenderLayer*>* posZOrderList = parent->posZOrderList()) { size_t listSize = posZOrderList->size(); for (size_t i = 0; i < listSize; ++i) { RenderLayer* curLayer = posZOrderList->at(i); - if (!curLayer->isComposited() && curLayer->hasVisibleContent()) + if (!curLayer->isComposited() + && (curLayer->hasVisibleContent() || hasVisibleNonCompositingDescendant(curLayer))) return true; } } @@ -1280,9 +1652,15 @@ bool RenderLayerBacking::hasVisibleNonCompositingDescendantLayers() const return false; } -bool RenderLayerBacking::containsPaintedContent() const +// Conservative test for having no rendered children. +bool RenderLayerBacking::hasVisibleNonCompositingDescendantLayers() const { - if (isSimpleContainerCompositingLayer() || paintsIntoWindow() || paintsIntoCompositedAncestor() || m_artificiallyInflatedBounds || m_owningLayer->isReflection()) + return hasVisibleNonCompositingDescendant(m_owningLayer); +} + +bool RenderLayerBacking::containsPaintedContent(bool isSimpleContainer) const +{ + if (isSimpleContainer || paintsIntoWindow() || paintsIntoCompositedAncestor() || m_artificiallyInflatedBounds || m_owningLayer->isReflection()) return false; if (isDirectlyCompositedImage()) @@ -1292,12 +1670,12 @@ bool RenderLayerBacking::containsPaintedContent() const // and set background color on the layer in that case, instead of allocating backing store and painting. #if ENABLE(VIDEO) if (renderer()->isVideo() && toRenderVideo(renderer())->shouldDisplayVideo()) - return hasBoxDecorationsOrBackground(renderer()); + return m_owningLayer->hasBoxDecorationsOrBackground(); #endif #if PLATFORM(MAC) && USE(CA) && (PLATFORM(IOS) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070) #elif ENABLE(WEBGL) || ENABLE(ACCELERATED_2D_CANVAS) if (isAcceleratedCanvas(renderer())) - return hasBoxDecorationsOrBackground(renderer()); + return m_owningLayer->hasBoxDecorationsOrBackground(); #endif return true; @@ -1308,8 +1686,8 @@ bool RenderLayerBacking::containsPaintedContent() const bool RenderLayerBacking::isDirectlyCompositedImage() const { RenderObject* renderObject = renderer(); - - if (!renderObject->isImage() || hasBoxDecorationsOrBackground(renderObject) || renderObject->hasClip()) + + if (!renderObject->isImage() || m_owningLayer->hasBoxDecorationsOrBackground() || renderObject->hasClip()) return false; RenderImage* imageRenderer = toRenderImage(renderObject); @@ -1333,12 +1711,14 @@ void RenderLayerBacking::contentChanged(ContentChangeType changeType) updateImageContents(); return; } - + + if ((changeType == BackgroundImageChanged) && canCreateTiledImage(renderer()->style())) + updateGraphicsLayerGeometry(); + if ((changeType == MaskImageChanged) && m_maskLayer) { // The composited layer bounds relies on box->maskClipRect(), which changes // when the mask image becomes available. - bool isUpdateRoot = true; - updateAfterLayout(CompositingChildren, isUpdateRoot); + updateAfterLayout(CompositingChildrenOnly | IsUpdateRoot); } #if ENABLE(WEBGL) || ENABLE(ACCELERATED_2D_CANVAS) @@ -1367,8 +1747,10 @@ void RenderLayerBacking::updateImageContents() return; // This is a no-op if the layer doesn't have an inner layer for the image. + m_graphicsLayer->setContentsRect(pixelSnappedIntRect(contentsBox())); m_graphicsLayer->setContentsToImage(image); - updateDrawsContent(); + bool isSimpleContainer = false; + updateDrawsContent(isSimpleContainer); // Image animation is "lazy", in that it automatically stops unless someone is drawing // the image. So we have to kick the animation each time; this has the downside that the @@ -1403,36 +1785,73 @@ FloatPoint RenderLayerBacking::computePerspectiveOrigin(const IntRect& borderBox } // Return the offset from the top-left of this compositing layer at which the renderer's contents are painted. -IntSize RenderLayerBacking::contentOffsetInCompostingLayer() const +LayoutSize RenderLayerBacking::contentOffsetInCompostingLayer() const { - return IntSize(-m_compositedBounds.x(), -m_compositedBounds.y()); + return LayoutSize(-m_compositedBounds.x(), -m_compositedBounds.y()); } -IntRect RenderLayerBacking::contentsBox() const +LayoutRect RenderLayerBacking::contentsBox() const { if (!renderer()->isBox()) - return IntRect(); + return LayoutRect(); - IntRect contentsRect; + LayoutRect contentsRect; #if ENABLE(VIDEO) if (renderer()->isVideo()) { RenderVideo* videoRenderer = toRenderVideo(renderer()); contentsRect = videoRenderer->videoBox(); } else #endif - contentsRect = pixelSnappedIntRect(toRenderBox(renderer())->contentBoxRect()); + contentsRect = toRenderBox(renderer())->contentBoxRect(); - IntSize contentOffset = contentOffsetInCompostingLayer(); - contentsRect.move(contentOffset); + contentsRect.move(contentOffsetInCompostingLayer()); return contentsRect; } +static LayoutRect backgroundRectForBox(const RenderBox* box) +{ + switch (box->style()->backgroundClip()) { + case BorderFillBox: + return box->borderBoxRect(); + case PaddingFillBox: + return box->paddingBoxRect(); + case ContentFillBox: + return box->contentBoxRect(); + case TextFillBox: + break; + } + + ASSERT_NOT_REACHED(); + return LayoutRect(); +} + +IntRect RenderLayerBacking::backgroundBox() const +{ + if (!renderer()->isBox()) + return IntRect(); + + LayoutRect backgroundBox = backgroundRectForBox(toRenderBox(renderer())); + backgroundBox.move(contentOffsetInCompostingLayer()); + return pixelSnappedIntRect(backgroundBox); +} + GraphicsLayer* RenderLayerBacking::parentForSublayers() const { if (m_scrollingContentsLayer) return m_scrollingContentsLayer.get(); - return m_containmentLayer ? m_containmentLayer.get() : m_graphicsLayer.get(); + return m_childContainmentLayer ? m_childContainmentLayer.get() : m_graphicsLayer.get(); +} + +GraphicsLayer* RenderLayerBacking::childForSuperlayers() const +{ + if (m_ancestorClippingLayer) + return m_ancestorClippingLayer.get(); + + if (m_contentsContainmentLayer) + return m_contentsContainmentLayer.get(); + + return m_graphicsLayer.get(); } bool RenderLayerBacking::paintsIntoWindow() const @@ -1441,7 +1860,7 @@ bool RenderLayerBacking::paintsIntoWindow() const return false; if (m_owningLayer->isRootLayer()) { -#if PLATFORM(BLACKBERRY) +#if PLATFORM(BLACKBERRY) || USE(COORDINATED_GRAPHICS) if (compositor()->inForcedCompositingMode()) return false; #endif @@ -1483,6 +1902,9 @@ void RenderLayerBacking::setContentsNeedDisplay() if (m_foregroundLayer && m_foregroundLayer->drawsContent()) m_foregroundLayer->setNeedsDisplay(); + if (m_backgroundLayer && m_backgroundLayer->drawsContent()) + m_backgroundLayer->setNeedsDisplay(); + if (m_maskLayer && m_maskLayer->drawsContent()) m_maskLayer->setNeedsDisplay(); @@ -1507,6 +1929,13 @@ void RenderLayerBacking::setContentsNeedDisplayInRect(const IntRect& r) m_foregroundLayer->setNeedsDisplayInRect(layerDirtyRect); } + // FIXME: need to split out repaints for the background. + if (m_backgroundLayer && m_backgroundLayer->drawsContent()) { + IntRect layerDirtyRect = r; + layerDirtyRect.move(-m_backgroundLayer->offsetFromRenderer()); + m_backgroundLayer->setNeedsDisplayInRect(layerDirtyRect); + } + if (m_maskLayer && m_maskLayer->drawsContent()) { IntRect layerDirtyRect = r; layerDirtyRect.move(-m_maskLayer->offsetFromRenderer()); @@ -1520,7 +1949,7 @@ void RenderLayerBacking::setContentsNeedDisplayInRect(const IntRect& r) } } -void RenderLayerBacking::paintIntoLayer(RenderLayer* rootLayer, GraphicsContext* context, +void RenderLayerBacking::paintIntoLayer(const GraphicsLayer* graphicsLayer, GraphicsContext* context, const IntRect& paintDirtyRect, // In the coords of rootLayer. PaintBehavior paintBehavior, GraphicsLayerPaintingPhase paintingPhase) { @@ -1540,14 +1969,23 @@ void RenderLayerBacking::paintIntoLayer(RenderLayer* rootLayer, GraphicsContext* paintFlags |= RenderLayer::PaintLayerPaintingCompositingMaskPhase; if (paintingPhase & GraphicsLayerPaintOverflowContents) paintFlags |= RenderLayer::PaintLayerPaintingOverflowContents; + if (paintingPhase & GraphicsLayerPaintCompositedScroll) + paintFlags |= RenderLayer::PaintLayerPaintingCompositingScrollingPhase; + + if (graphicsLayer == m_backgroundLayer) + paintFlags |= (RenderLayer::PaintLayerPaintingRootBackgroundOnly | RenderLayer::PaintLayerPaintingCompositingForegroundPhase); // Need PaintLayerPaintingCompositingForegroundPhase to walk child layers. + else if (compositor()->fixedRootBackgroundLayer()) + paintFlags |= RenderLayer::PaintLayerPaintingSkipRootBackground; // FIXME: GraphicsLayers need a way to split for RenderRegions. - RenderLayer::LayerPaintingInfo paintingInfo(rootLayer, paintDirtyRect, paintBehavior, LayoutSize()); + RenderLayer::LayerPaintingInfo paintingInfo(m_owningLayer, paintDirtyRect, paintBehavior, m_subpixelAccumulation); m_owningLayer->paintLayerContents(context, paintingInfo, paintFlags); if (m_owningLayer->containsDirtyOverlayScrollbars()) m_owningLayer->paintLayerContents(context, paintingInfo, paintFlags | RenderLayer::PaintLayerPaintingOverlayScrollbars); + compositor()->didPaintBacking(this); + ASSERT(!m_owningLayer->m_usedTransparency); } @@ -1573,21 +2011,22 @@ void RenderLayerBacking::paintContents(const GraphicsLayer* graphicsLayer, Graph page->setIsPainting(true); #endif - if (graphicsLayer == m_graphicsLayer.get() || graphicsLayer == m_foregroundLayer.get() || graphicsLayer == m_maskLayer.get() || graphicsLayer == m_scrollingContentsLayer.get()) { - InspectorInstrumentationCookie cookie = InspectorInstrumentation::willPaint(m_owningLayer->renderer()->frame()); + if (graphicsLayer == m_graphicsLayer.get() + || graphicsLayer == m_foregroundLayer.get() + || graphicsLayer == m_backgroundLayer.get() + || graphicsLayer == m_maskLayer.get() + || graphicsLayer == m_scrollingContentsLayer.get()) { + InspectorInstrumentation::willPaint(renderer()); // The dirtyRect is in the coords of the painting root. IntRect dirtyRect = clip; if (!(paintingPhase & GraphicsLayerPaintOverflowContents)) - dirtyRect.intersect(compositedBounds()); + dirtyRect.intersect(enclosingIntRect(compositedBounds())); // We have to use the same root as for hit testing, because both methods can compute and cache clipRects. - paintIntoLayer(m_owningLayer, &context, dirtyRect, PaintBehaviorNormal, paintingPhase); - - if (m_usingTiledCacheLayer) - m_owningLayer->renderer()->frame()->view()->setLastPaintTime(currentTime()); + paintIntoLayer(graphicsLayer, &context, dirtyRect, PaintBehaviorNormal, paintingPhase); - InspectorInstrumentation::didPaint(cookie, &context, clip); + InspectorInstrumentation::didPaint(renderer(), &context, clip); } else if (graphicsLayer == layerForHorizontalScrollbar()) { paintScrollbar(m_owningLayer->horizontalScrollbar(), context, clip); } else if (graphicsLayer == layerForVerticalScrollbar()) { @@ -1618,14 +2057,15 @@ float RenderLayerBacking::deviceScaleFactor() const return compositor()->deviceScaleFactor(); } -void RenderLayerBacking::didCommitChangesForLayer(const GraphicsLayer*) const +void RenderLayerBacking::didCommitChangesForLayer(const GraphicsLayer* layer) const { - compositor()->didFlushChangesForLayer(m_owningLayer); + compositor()->didFlushChangesForLayer(m_owningLayer, layer); } bool RenderLayerBacking::getCurrentTransform(const GraphicsLayer* graphicsLayer, TransformationMatrix& transform) const { - if (graphicsLayer != m_graphicsLayer) + GraphicsLayer* transformedLayer = m_contentsContainmentLayer.get() ? m_contentsContainmentLayer.get() : m_graphicsLayer.get(); + if (graphicsLayer != transformedLayer) return false; if (m_owningLayer->hasTransform()) { @@ -1681,39 +2121,31 @@ bool RenderLayerBacking::startAnimation(double timeOffset, const Animation* anim bool isFirstOrLastKeyframe = key == 0 || key == 1; if ((hasTransform && isFirstOrLastKeyframe) || currentKeyframe.containsProperty(CSSPropertyWebkitTransform)) - transformVector.insert(new TransformAnimationValue(key, &(keyframeStyle->transform()), tf)); - + transformVector.insert(TransformAnimationValue::create(key, keyframeStyle->transform(), tf)); + if ((hasOpacity && isFirstOrLastKeyframe) || currentKeyframe.containsProperty(CSSPropertyOpacity)) - opacityVector.insert(new FloatAnimationValue(key, keyframeStyle->opacity(), tf)); + opacityVector.insert(FloatAnimationValue::create(key, keyframeStyle->opacity(), tf)); #if ENABLE(CSS_FILTERS) if ((hasFilter && isFirstOrLastKeyframe) || currentKeyframe.containsProperty(CSSPropertyWebkitFilter)) - filterVector.insert(new FilterAnimationValue(key, &(keyframeStyle->filter()), tf)); + filterVector.insert(FilterAnimationValue::create(key, keyframeStyle->filter(), tf)); #endif } - bool didAnimateTransform = false; - bool didAnimateOpacity = false; -#if ENABLE(CSS_FILTERS) - bool didAnimateFilter = false; -#endif + bool didAnimate = false; if (hasTransform && m_graphicsLayer->addAnimation(transformVector, toRenderBox(renderer())->pixelSnappedBorderBoxRect().size(), anim, keyframes.animationName(), timeOffset)) - didAnimateTransform = true; + didAnimate = true; if (hasOpacity && m_graphicsLayer->addAnimation(opacityVector, IntSize(), anim, keyframes.animationName(), timeOffset)) - didAnimateOpacity = true; + didAnimate = true; #if ENABLE(CSS_FILTERS) if (hasFilter && m_graphicsLayer->addAnimation(filterVector, IntSize(), anim, keyframes.animationName(), timeOffset)) - didAnimateFilter = true; + didAnimate = true; #endif -#if ENABLE(CSS_FILTERS) - return didAnimateTransform || didAnimateOpacity || didAnimateFilter; -#else - return didAnimateTransform || didAnimateOpacity; -#endif + return didAnimate; } void RenderLayerBacking::animationPaused(double timeOffset, const String& animationName) @@ -1728,11 +2160,7 @@ void RenderLayerBacking::animationFinished(const String& animationName) bool RenderLayerBacking::startTransition(double timeOffset, CSSPropertyID property, const RenderStyle* fromStyle, const RenderStyle* toStyle) { - bool didAnimateOpacity = false; - bool didAnimateTransform = false; -#if ENABLE(CSS_FILTERS) - bool didAnimateFilter = false; -#endif + bool didAnimate = false; ASSERT(property != CSSPropertyInvalid); @@ -1740,13 +2168,13 @@ bool RenderLayerBacking::startTransition(double timeOffset, CSSPropertyID proper const Animation* opacityAnim = toStyle->transitionForProperty(CSSPropertyOpacity); if (opacityAnim && !opacityAnim->isEmptyOrZeroDuration()) { KeyframeValueList opacityVector(AnimatedPropertyOpacity); - opacityVector.insert(new FloatAnimationValue(0, compositingOpacity(fromStyle->opacity()))); - opacityVector.insert(new FloatAnimationValue(1, compositingOpacity(toStyle->opacity()))); + opacityVector.insert(FloatAnimationValue::create(0, compositingOpacity(fromStyle->opacity()))); + opacityVector.insert(FloatAnimationValue::create(1, compositingOpacity(toStyle->opacity()))); // The boxSize param is only used for transform animations (which can only run on RenderBoxes), so we pass an empty size here. if (m_graphicsLayer->addAnimation(opacityVector, IntSize(), opacityAnim, GraphicsLayer::animationNameForTransition(AnimatedPropertyOpacity), timeOffset)) { // To ensure that the correct opacity is visible when the animation ends, also set the final opacity. updateOpacity(toStyle); - didAnimateOpacity = true; + didAnimate = true; } } } @@ -1755,12 +2183,12 @@ bool RenderLayerBacking::startTransition(double timeOffset, CSSPropertyID proper const Animation* transformAnim = toStyle->transitionForProperty(CSSPropertyWebkitTransform); if (transformAnim && !transformAnim->isEmptyOrZeroDuration()) { KeyframeValueList transformVector(AnimatedPropertyWebkitTransform); - transformVector.insert(new TransformAnimationValue(0, &fromStyle->transform())); - transformVector.insert(new TransformAnimationValue(1, &toStyle->transform())); + transformVector.insert(TransformAnimationValue::create(0, fromStyle->transform())); + transformVector.insert(TransformAnimationValue::create(1, toStyle->transform())); if (m_graphicsLayer->addAnimation(transformVector, toRenderBox(renderer())->pixelSnappedBorderBoxRect().size(), transformAnim, GraphicsLayer::animationNameForTransition(AnimatedPropertyWebkitTransform), timeOffset)) { // To ensure that the correct transform is visible when the animation ends, also set the final transform. updateTransform(toStyle); - didAnimateTransform = true; + didAnimate = true; } } } @@ -1770,22 +2198,18 @@ bool RenderLayerBacking::startTransition(double timeOffset, CSSPropertyID proper const Animation* filterAnim = toStyle->transitionForProperty(CSSPropertyWebkitFilter); if (filterAnim && !filterAnim->isEmptyOrZeroDuration()) { KeyframeValueList filterVector(AnimatedPropertyWebkitFilter); - filterVector.insert(new FilterAnimationValue(0, &fromStyle->filter())); - filterVector.insert(new FilterAnimationValue(1, &toStyle->filter())); + filterVector.insert(FilterAnimationValue::create(0, fromStyle->filter())); + filterVector.insert(FilterAnimationValue::create(1, toStyle->filter())); if (m_graphicsLayer->addAnimation(filterVector, IntSize(), filterAnim, GraphicsLayer::animationNameForTransition(AnimatedPropertyWebkitFilter), timeOffset)) { // To ensure that the correct filter is visible when the animation ends, also set the final filter. updateFilters(toStyle); - didAnimateFilter = true; + didAnimate = true; } } } #endif -#if ENABLE(CSS_FILTERS) - return didAnimateOpacity || didAnimateTransform || didAnimateFilter; -#else - return didAnimateOpacity || didAnimateTransform; -#endif + return didAnimate; } void RenderLayerBacking::transitionPaused(double timeOffset, CSSPropertyID property) @@ -1807,10 +2231,11 @@ void RenderLayerBacking::notifyAnimationStarted(const GraphicsLayer*, double tim renderer()->animation()->notifyAnimationStarted(renderer(), time); } -void RenderLayerBacking::notifyFlushRequired(const GraphicsLayer*) +void RenderLayerBacking::notifyFlushRequired(const GraphicsLayer* layer) { - if (!renderer()->documentBeingDestroyed()) - compositor()->scheduleLayerFlush(); + if (renderer()->documentBeingDestroyed()) + return; + compositor()->scheduleLayerFlush(layer->canThrottleLayerFlush()); } void RenderLayerBacking::notifyFlushBeforeDisplayRefresh(const GraphicsLayer* layer) @@ -1829,12 +2254,12 @@ void RenderLayerBacking::resumeAnimations() m_graphicsLayer->resumeAnimations(); } -IntRect RenderLayerBacking::compositedBounds() const +LayoutRect RenderLayerBacking::compositedBounds() const { return m_compositedBounds; } -void RenderLayerBacking::setCompositedBounds(const IntRect& bounds) +void RenderLayerBacking::setCompositedBounds(const LayoutRect& bounds) { m_compositedBounds = bounds; } @@ -1885,46 +2310,13 @@ AnimatedPropertyID RenderLayerBacking::cssToGraphicsLayerProperty(CSSPropertyID return AnimatedPropertyInvalid; } -String RenderLayerBacking::nameForLayer() const -{ - StringBuilder name; - name.append(renderer()->renderName()); - if (Node* node = renderer()->node()) { - if (node->isElementNode()) { - name.append(' '); - name.append(static_cast<Element*>(node)->tagName()); - } - if (node->hasID()) { - name.appendLiteral(" id=\'"); - name.append(static_cast<Element*>(node)->getIdAttribute()); - name.append('\''); - } - - if (node->hasClass()) { - name.appendLiteral(" class=\'"); - StyledElement* styledElement = static_cast<StyledElement*>(node); - for (size_t i = 0; i < styledElement->classNames().size(); ++i) { - if (i > 0) - name.append(' '); - name.append(styledElement->classNames()[i]); - } - name.append('\''); - } - } - - if (m_owningLayer->isReflection()) - name.appendLiteral(" (reflection)"); - - return name.toString(); -} - CompositingLayerType RenderLayerBacking::compositingLayerType() const { if (m_graphicsLayer->hasContentsLayer()) return MediaCompositingLayer; if (m_graphicsLayer->drawsContent()) - return m_graphicsLayer->usingTiledLayer() ? TiledCompositingLayer : NormalCompositingLayer; + return m_graphicsLayer->usingTiledBacking() ? TiledCompositingLayer : NormalCompositingLayer; return ContainerCompositingLayer; } @@ -1933,10 +2325,12 @@ double RenderLayerBacking::backingStoreMemoryEstimate() const { double backingMemory; - // m_ancestorClippingLayer and m_containmentLayer are just used for masking or containment, so have no backing. + // m_ancestorClippingLayer, m_contentsContainmentLayer and m_childContainmentLayer are just used for masking or containment, so have no backing. backingMemory = m_graphicsLayer->backingStoreMemoryEstimate(); if (m_foregroundLayer) backingMemory += m_foregroundLayer->backingStoreMemoryEstimate(); + if (m_backgroundLayer) + backingMemory += m_backgroundLayer->backingStoreMemoryEstimate(); if (m_maskLayer) backingMemory += m_maskLayer->backingStoreMemoryEstimate(); @@ -1955,20 +2349,6 @@ double RenderLayerBacking::backingStoreMemoryEstimate() const return backingMemory; } -#if PLATFORM(BLACKBERRY) -bool RenderLayerBacking::contentsVisible(const GraphicsLayer*, const IntRect& localContentRect) const -{ - Frame* frame = renderer()->frame(); - FrameView* view = frame ? frame->view() : 0; - if (!view) - return false; - - IntRect visibleContentRect(view->visibleContentRect()); - FloatQuad absoluteContentQuad = renderer()->localToAbsoluteQuad(FloatRect(localContentRect), SnapOffsetForTransforms); - return absoluteContentQuad.enclosingBoundingBox().intersects(visibleContentRect); -} -#endif - } // namespace WebCore #endif // USE(ACCELERATED_COMPOSITING) diff --git a/Source/WebCore/rendering/RenderLayerBacking.h b/Source/WebCore/rendering/RenderLayerBacking.h index cdcc030c2..134001ebf 100644 --- a/Source/WebCore/rendering/RenderLayerBacking.h +++ b/Source/WebCore/rendering/RenderLayerBacking.h @@ -33,13 +33,13 @@ #include "GraphicsLayer.h" #include "GraphicsLayerClient.h" #include "RenderLayer.h" -#include "TransformationMatrix.h" namespace WebCore { class KeyframeList; class RenderLayerCompositor; class TiledBacking; +class TransformationMatrix; enum CompositingLayerType { NormalCompositingLayer, // non-tiled layer with backing store @@ -57,13 +57,18 @@ enum CompositingLayerType { class RenderLayerBacking : public GraphicsLayerClient { WTF_MAKE_NONCOPYABLE(RenderLayerBacking); WTF_MAKE_FAST_ALLOCATED; public: - RenderLayerBacking(RenderLayer*); + explicit RenderLayerBacking(RenderLayer*); ~RenderLayerBacking(); RenderLayer* owningLayer() const { return m_owningLayer; } - enum UpdateDepth { CompositingChildren, AllDescendants }; - void updateAfterLayout(UpdateDepth, bool isUpdateRoot); + enum UpdateAfterLayoutFlag { + CompositingChildrenOnly = 1 << 0, + NeedsFullRepaint = 1 << 1, + IsUpdateRoot = 1 << 2 + }; + typedef unsigned UpdateAfterLayoutFlags; + void updateAfterLayout(UpdateAfterLayoutFlags); // Returns true if layer configuration changed. bool updateGraphicsLayerConfiguration(); @@ -75,28 +80,33 @@ public: GraphicsLayer* graphicsLayer() const { return m_graphicsLayer.get(); } // Layer to clip children - bool hasClippingLayer() const { return (m_containmentLayer && !m_usingTiledCacheLayer); } - GraphicsLayer* clippingLayer() const { return !m_usingTiledCacheLayer ? m_containmentLayer.get() : 0; } + bool hasClippingLayer() const { return (m_childContainmentLayer && !m_usingTiledCacheLayer); } + GraphicsLayer* clippingLayer() const { return !m_usingTiledCacheLayer ? m_childContainmentLayer.get() : 0; } // Layer to get clipped by ancestor bool hasAncestorClippingLayer() const { return m_ancestorClippingLayer != 0; } GraphicsLayer* ancestorClippingLayer() const { return m_ancestorClippingLayer.get(); } + GraphicsLayer* contentsContainmentLayer() const { return m_contentsContainmentLayer.get(); } + bool hasContentsLayer() const { return m_foregroundLayer != 0; } GraphicsLayer* foregroundLayer() const { return m_foregroundLayer.get(); } + GraphicsLayer* backgroundLayer() const { return m_backgroundLayer.get(); } + bool backgroundLayerPaintsFixedRootBackground() const { return m_backgroundLayerPaintsFixedRootBackground; } + bool hasScrollingLayer() const { return m_scrollingLayer; } GraphicsLayer* scrollingLayer() const { return m_scrollingLayer.get(); } GraphicsLayer* scrollingContentsLayer() const { return m_scrollingContentsLayer.get(); } - void attachToScrollingCoordinator(RenderLayerBacking* parent); + void attachToScrollingCoordinatorWithParent(RenderLayerBacking* parent); void detachFromScrollingCoordinator(); uint64_t scrollLayerID() const { return m_scrollLayerID; } bool hasMaskLayer() const { return m_maskLayer != 0; } GraphicsLayer* parentForSublayers() const; - GraphicsLayer* childForSuperlayers() const { return m_ancestorClippingLayer ? m_ancestorClippingLayer.get() : m_graphicsLayer.get(); } + GraphicsLayer* childForSuperlayers() const; // RenderLayers with backing normally short-circuit paintLayer() because // their content is rendered via callbacks from GraphicsLayer. However, the document @@ -131,21 +141,23 @@ public: void suspendAnimations(double time = 0); void resumeAnimations(); - IntRect compositedBounds() const; - void setCompositedBounds(const IntRect&); + LayoutRect compositedBounds() const; + void setCompositedBounds(const LayoutRect&); void updateCompositedBounds(); void updateAfterWidgetResize(); void positionOverflowControlsLayers(const IntSize& offsetFromRoot); + bool hasUnpositionedOverflowControlsLayers() const; - bool usingTileCache() const { return m_usingTiledCacheLayer; } + bool usingTiledBacking() const { return m_usingTiledCacheLayer; } TiledBacking* tiledBacking() const; - void adjustTileCacheCoverage(); + void adjustTiledBackingCoverage(); void updateDebugIndicators(bool showBorder, bool showRepaintCounter); - + // GraphicsLayerClient interface - virtual bool shouldUseTileCache(const GraphicsLayer*) const OVERRIDE; + virtual bool shouldUseTiledBacking(const GraphicsLayer*) const OVERRIDE; + virtual void tiledBackingUsageChanged(const GraphicsLayer*, bool /*usingTiledBacking*/) OVERRIDE; virtual void notifyAnimationStarted(const GraphicsLayer*, double startTime) OVERRIDE; virtual void notifyFlushRequired(const GraphicsLayer*) OVERRIDE; virtual void notifyFlushBeforeDisplayRefresh(const GraphicsLayer*) OVERRIDE; @@ -163,7 +175,8 @@ public: virtual void verifyNotPainting(); #endif - IntRect contentsBox() const; + LayoutRect contentsBox() const; + IntRect backgroundBox() const; // For informative purposes only. CompositingLayerType compositingLayerType() const; @@ -179,8 +192,9 @@ public: // Return an estimate of the backing store area (in pixels) allocated by this object's GraphicsLayers. double backingStoreMemoryEstimate() const; - String nameForLayer() const; - + bool didSwitchToFullTileCoverageDuringLoading() const { return m_didSwitchToFullTileCoverageDuringLoading; } + void setDidSwitchToFullTileCoverageDuringLoading() { m_didSwitchToFullTileCoverageDuringLoading = true; } + #if ENABLE(CSS_COMPOSITING) void setBlendMode(BlendMode); #endif @@ -189,6 +203,8 @@ private: void createPrimaryGraphicsLayer(); void destroyGraphicsLayers(); + void willDestroyLayer(const GraphicsLayer*); + PassOwnPtr<GraphicsLayer> createGraphicsLayer(const String&); RenderLayerModelObject* renderer() const { return m_owningLayer->renderer(); } @@ -198,15 +214,22 @@ private: bool updateClippingLayers(bool needsAncestorClip, bool needsDescendantClip); bool updateOverflowControlsLayers(bool needsHorizontalScrollbarLayer, bool needsVerticalScrollbarLayer, bool needsScrollCornerLayer); bool updateForegroundLayer(bool needsForegroundLayer); + bool updateBackgroundLayer(bool needsBackgroundLayer); bool updateMaskLayer(bool needsMaskLayer); bool requiresHorizontalScrollbarLayer() const; bool requiresVerticalScrollbarLayer() const; bool requiresScrollCornerLayer() const; bool updateScrollingLayers(bool scrollingLayers); + void updateDrawsContent(bool isSimpleContainer); + void registerScrollingLayers(); + + void updateRootLayerConfiguration(); + + void setBackgroundLayerPaintsFixedRootBackground(bool); GraphicsLayerPaintingPhase paintingPhaseForPrimaryLayer() const; - IntSize contentOffsetInCompostingLayer() const; + LayoutSize contentOffsetInCompostingLayer() const; // Result is transform origin in pixels. FloatPoint3D computeTransformOrigin(const IntRect& borderBox) const; // Result is perspective origin in pixels. @@ -231,53 +254,62 @@ private: // Returns true if this compositing layer has no visible content. bool isSimpleContainerCompositingLayer() const; // Returns true if this layer has content that needs to be rendered by painting into the backing store. - bool containsPaintedContent() const; + bool containsPaintedContent(bool isSimpleContainer) const; // Returns true if the RenderLayer just contains an image that we can composite directly. bool isDirectlyCompositedImage() const; void updateImageContents(); Color rendererBackgroundColor() const; - void updateBackgroundColor(); + void updateDirectlyCompositedBackgroundColor(bool isSimpleContainer, bool& didUpdateContentsRect); + void updateDirectlyCompositedBackgroundImage(bool isSimpleContainer, bool& didUpdateContentsRect); + void updateDirectlyCompositedContents(bool isSimpleContainer, bool& didUpdateContentsRect); + + void resetContentsRect(); - bool containsNonEmptyRenderers() const; bool hasVisibleNonCompositingDescendantLayers() const; bool shouldClipCompositedBounds() const; - bool hasTileCacheFlatteningLayer() const { return (m_containmentLayer && m_usingTiledCacheLayer); } - GraphicsLayer* tileCacheFlatteningLayer() const { return m_usingTiledCacheLayer ? m_containmentLayer.get() : 0; } + bool hasTiledBackingFlatteningLayer() const { return (m_childContainmentLayer && m_usingTiledCacheLayer); } + GraphicsLayer* tileCacheFlatteningLayer() const { return m_usingTiledCacheLayer ? m_childContainmentLayer.get() : 0; } - void paintIntoLayer(RenderLayer* rootLayer, GraphicsContext*, const IntRect& paintDirtyRect, PaintBehavior, GraphicsLayerPaintingPhase); + void paintIntoLayer(const GraphicsLayer*, GraphicsContext*, const IntRect& paintDirtyRect, PaintBehavior, GraphicsLayerPaintingPhase); static CSSPropertyID graphicsLayerToCSSProperty(AnimatedPropertyID); static AnimatedPropertyID cssToGraphicsLayerProperty(CSSPropertyID); RenderLayer* m_owningLayer; - OwnPtr<GraphicsLayer> m_ancestorClippingLayer; // only used if we are clipped by an ancestor which is not a stacking context + OwnPtr<GraphicsLayer> m_ancestorClippingLayer; // Only used if we are clipped by an ancestor which is not a stacking context. + OwnPtr<GraphicsLayer> m_contentsContainmentLayer; // Only used if we have a background layer; takes the transform. OwnPtr<GraphicsLayer> m_graphicsLayer; - OwnPtr<GraphicsLayer> m_foregroundLayer; // only used in cases where we need to draw the foreground separately - OwnPtr<GraphicsLayer> m_containmentLayer; // Only used if we have clipping on a stacking context with compositing children, or if the layer has a tile cache. - OwnPtr<GraphicsLayer> m_maskLayer; // only used if we have a mask + OwnPtr<GraphicsLayer> m_foregroundLayer; // Only used in cases where we need to draw the foreground separately. + OwnPtr<GraphicsLayer> m_backgroundLayer; // Only used in cases where we need to draw the background separately. + OwnPtr<GraphicsLayer> m_childContainmentLayer; // Only used if we have clipping on a stacking context with compositing children, or if the layer has a tile cache. + OwnPtr<GraphicsLayer> m_maskLayer; // Only used if we have a mask. OwnPtr<GraphicsLayer> m_layerForHorizontalScrollbar; OwnPtr<GraphicsLayer> m_layerForVerticalScrollbar; OwnPtr<GraphicsLayer> m_layerForScrollCorner; - OwnPtr<GraphicsLayer> m_scrollingLayer; // only used if the layer is using composited scrolling. - OwnPtr<GraphicsLayer> m_scrollingContentsLayer; // only used if the layer is using composited scrolling. + OwnPtr<GraphicsLayer> m_scrollingLayer; // Only used if the layer is using composited scrolling. + OwnPtr<GraphicsLayer> m_scrollingContentsLayer; // Only used if the layer is using composited scrolling. uint64_t m_scrollLayerID; - IntRect m_compositedBounds; + LayoutRect m_compositedBounds; + LayoutSize m_subpixelAccumulation; // The accumulated subpixel offset of the compositedBounds compared to absolute coordinates. - bool m_artificiallyInflatedBounds; // bounds had to be made non-zero to make transform-origin work + bool m_artificiallyInflatedBounds; // bounds had to be made non-zero to make transform-origin work + bool m_boundsConstrainedByClipping; bool m_isMainFrameRenderViewLayer; bool m_usingTiledCacheLayer; bool m_requiresOwnBackingStore; #if ENABLE(CSS_FILTERS) bool m_canCompositeFilters; #endif + bool m_backgroundLayerPaintsFixedRootBackground; + bool m_didSwitchToFullTileCoverageDuringLoading; static bool m_creatingPrimaryGraphicsLayer; }; diff --git a/Source/WebCore/rendering/RenderLayerCompositor.cpp b/Source/WebCore/rendering/RenderLayerCompositor.cpp index badb782c4..6f1fd3e0b 100644 --- a/Source/WebCore/rendering/RenderLayerCompositor.cpp +++ b/Source/WebCore/rendering/RenderLayerCompositor.cpp @@ -40,6 +40,8 @@ #include "HTMLIFrameElement.h" #include "HTMLNames.h" #include "HitTestResult.h" +#include "InspectorInstrumentation.h" +#include "Logging.h" #include "NodeList.h" #include "Page.h" #include "RenderApplet.h" @@ -57,15 +59,16 @@ #include "Settings.h" #include "TiledBacking.h" #include "TransformState.h" +#include <wtf/CurrentTime.h> +#include <wtf/TemporaryChange.h> +#include <wtf/text/CString.h> +#include <wtf/text/StringBuilder.h> #if ENABLE(PLUGIN_PROXY_FOR_VIDEO) +#include "HTMLAudioElement.h" #include "HTMLMediaElement.h" #endif -#if !LOG_DISABLED -#include <wtf/CurrentTime.h> -#endif - #ifndef NDEBUG #include "RenderTreeAsText.h" #endif @@ -75,8 +78,16 @@ bool WebCoreHas3DRendering = true; #endif +#if !PLATFORM(MAC) && !PLATFORM(IOS) +#define WTF_USE_COMPOSITING_FOR_SMALL_CANVASES 1 +#endif + namespace WebCore { +static const int canvasAreaThresholdRequiringCompositing = 50 * 100; +// During page loading delay layer flushes up to this many seconds to allow them coalesce, reducing workload. +static const double throttledLayerFlushDelay = .5; + using namespace HTMLNames; class RenderLayerCompositor::OverlapMap { @@ -108,12 +119,7 @@ public: bool overlapsLayers(const IntRect& bounds) const { - const RectList& layerRects = m_overlapStack.last(); - for (unsigned i = 0; i < layerRects.size(); i++) { - if (layerRects[i].intersects(bounds)) - return true; - } - return false; + return m_overlapStack.last().intersects(bounds); } bool isEmpty() @@ -135,14 +141,42 @@ public: RenderGeometryMap& geometryMap() { return m_geometryMap; } private: - typedef Vector<IntRect> RectList; + struct RectList { + Vector<IntRect> rects; + IntRect boundingRect; + + void append(const IntRect& rect) + { + rects.append(rect); + boundingRect.unite(rect); + } + + void append(const RectList& rectList) + { + rects.appendVector(rectList.rects); + boundingRect.unite(rectList.boundingRect); + } + + bool intersects(const IntRect& rect) const + { + if (!rects.size() || !boundingRect.intersects(rect)) + return false; + + for (unsigned i = 0; i < rects.size(); i++) { + if (rects[i].intersects(rect)) + return true; + } + return false; + } + }; + Vector<RectList> m_overlapStack; HashSet<const RenderLayer*> m_layers; RenderGeometryMap m_geometryMap; }; struct CompositingState { - CompositingState(RenderLayer* compAncestor, bool testOverlap) + CompositingState(RenderLayer* compAncestor, bool testOverlap = true) : m_compositingAncestor(compAncestor) , m_subtreeIsCompositing(false) , m_testingOverlap(testOverlap) @@ -189,15 +223,21 @@ RenderLayerCompositor::RenderLayerCompositor(RenderView* renderView) , m_showDebugBorders(false) , m_showRepaintCounter(false) , m_acceleratedDrawingEnabled(false) - , m_compositingConsultsOverlap(true) , m_reevaluateCompositingAfterLayout(false) , m_compositing(false) , m_compositingLayersNeedRebuild(false) , m_flushingLayers(false) , m_shouldFlushOnReattach(false) , m_forceCompositingMode(false) + , m_inPostLayoutUpdate(false) , m_isTrackingRepaints(false) + , m_layersWithTiledBackingCount(0) , m_rootLayerAttachment(RootLayerUnattached) + , m_layerFlushTimer(this, &RenderLayerCompositor::layerFlushTimerFired) + , m_layerFlushThrottlingEnabled(false) + , m_layerFlushThrottlingTemporarilyDisabledForInteraction(false) + , m_hasPendingLayerFlush(false) + , m_paintRelatedMilestonesTimer(this, &RenderLayerCompositor::paintRelatedMilestonesTimerFired) #if !LOG_DISABLED , m_rootLayerUpdateCount(0) , m_obligateCompositedLayerCount(0) @@ -210,6 +250,9 @@ RenderLayerCompositor::RenderLayerCompositor(RenderView* renderView) RenderLayerCompositor::~RenderLayerCompositor() { + // Take care that the owned GraphicsLayers are deleted first as their destructors may call back here. + m_clipLayer = nullptr; + m_scrollLayer = nullptr; ASSERT(m_rootLayerAttachment == RootLayerUnattached); } @@ -241,7 +284,7 @@ void RenderLayerCompositor::cacheAcceleratedCompositingFlags() // on a chrome that doesn't allow accelerated compositing. if (hasAcceleratedCompositing) { if (Page* page = this->page()) { - ChromeClient* chromeClient = page->chrome()->client(); + ChromeClient* chromeClient = page->chrome().client(); m_compositingTriggers = chromeClient->allowedCompositingTriggers(); hasAcceleratedCompositing = m_compositingTriggers; } @@ -252,7 +295,7 @@ void RenderLayerCompositor::cacheAcceleratedCompositingFlags() forceCompositingMode = settings->forceCompositingMode() && hasAcceleratedCompositing; if (forceCompositingMode && m_renderView->document()->ownerElement()) - forceCompositingMode = settings->acceleratedCompositingForScrollableFramesEnabled() && requiresCompositingForScrollableFrame(); + forceCompositingMode = requiresCompositingForScrollableFrame(); acceleratedDrawingEnabled = settings->acceleratedDrawingEnabled(); } @@ -290,10 +333,39 @@ void RenderLayerCompositor::setCompositingLayersNeedRebuild(bool needRebuild) m_compositingLayersNeedRebuild = needRebuild; } -void RenderLayerCompositor::scheduleLayerFlush() +void RenderLayerCompositor::customPositionForVisibleRectComputation(const GraphicsLayer* graphicsLayer, FloatPoint& position) const +{ + if (graphicsLayer != m_scrollLayer.get()) + return; + + FrameView* frameView = m_renderView ? m_renderView->frameView() : 0; + if (!frameView) + return; + + FloatPoint scrollPosition = -position; + scrollPosition = frameView->constrainScrollPositionForOverhang(roundedIntPoint(scrollPosition)); + position = -scrollPosition; +} + +void RenderLayerCompositor::notifyFlushRequired(const GraphicsLayer* layer) { + scheduleLayerFlush(layer->canThrottleLayerFlush()); +} + +void RenderLayerCompositor::scheduleLayerFlushNow() +{ + m_hasPendingLayerFlush = false; if (Page* page = this->page()) - page->chrome()->client()->scheduleCompositingLayerFlush(); + page->chrome().client()->scheduleCompositingLayerFlush(); +} + +void RenderLayerCompositor::scheduleLayerFlush(bool canThrottle) +{ + if (canThrottle && isThrottlingLayerFlushes()) { + m_hasPendingLayerFlush = true; + return; + } + scheduleLayerFlushNow(); } void RenderLayerCompositor::flushPendingLayerChanges(bool isFlushRoot) @@ -315,8 +387,8 @@ void RenderLayerCompositor::flushPendingLayerChanges(bool isFlushRoot) ASSERT(!m_flushingLayers); m_flushingLayers = true; + FrameView* frameView = m_renderView ? m_renderView->frameView() : 0; if (GraphicsLayer* rootLayer = rootGraphicsLayer()) { - FrameView* frameView = m_renderView ? m_renderView->frameView() : 0; if (frameView) { // Having a m_clipLayer indicates that we're doing scrolling via GraphicsLayers. IntRect visibleRect = m_clipLayer ? IntRect(IntPoint(), frameView->contentsSize()) : frameView->visibleContentRect(); @@ -334,12 +406,43 @@ void RenderLayerCompositor::flushPendingLayerChanges(bool isFlushRoot) m_viewportConstrainedLayersNeedingUpdate.clear(); } + startLayerFlushTimerIfNeeded(); } -void RenderLayerCompositor::didFlushChangesForLayer(RenderLayer* layer) +void RenderLayerCompositor::didFlushChangesForLayer(RenderLayer* layer, const GraphicsLayer* graphicsLayer) { if (m_viewportConstrainedLayers.contains(layer)) m_viewportConstrainedLayersNeedingUpdate.add(layer); + + RenderLayerBacking* backing = layer->backing(); + if (backing->backgroundLayerPaintsFixedRootBackground() && graphicsLayer == backing->backgroundLayer()) + fixedRootBackgroundLayerChanged(); +} + +void RenderLayerCompositor::didPaintBacking(RenderLayerBacking*) +{ + FrameView* frameView = m_renderView->frameView(); + + frameView->setLastPaintTime(currentTime()); + + if (frameView->milestonesPendingPaint() && !m_paintRelatedMilestonesTimer.isActive()) + m_paintRelatedMilestonesTimer.startOneShot(0); +} + +void RenderLayerCompositor::didChangeVisibleRect() +{ + GraphicsLayer* rootLayer = rootGraphicsLayer(); + if (!rootLayer) + return; + + FrameView* frameView = m_renderView ? m_renderView->frameView() : 0; + if (!frameView) + return; + + IntRect visibleRect = m_clipLayer ? IntRect(IntPoint(), frameView->contentsSize()) : frameView->visibleContentRect(); + if (!rootLayer->visibleRectChangeRequiresFlush(visibleRect)) + return; + scheduleLayerFlushNow(); } void RenderLayerCompositor::notifyFlushBeforeDisplayRefresh(const GraphicsLayer*) @@ -347,7 +450,7 @@ void RenderLayerCompositor::notifyFlushBeforeDisplayRefresh(const GraphicsLayer* if (!m_layerUpdater) { PlatformDisplayID displayID = 0; if (Page* page = this->page()) - displayID = page->displayID(); + displayID = page->chrome().displayID(); m_layerUpdater = adoptPtr(new GraphicsLayerUpdater(this, displayID)); } @@ -360,6 +463,16 @@ void RenderLayerCompositor::flushLayers(GraphicsLayerUpdater*) flushPendingLayerChanges(true); // FIXME: deal with iframes } +void RenderLayerCompositor::layerTiledBackingUsageChanged(const GraphicsLayer*, bool usingTiledBacking) +{ + if (usingTiledBacking) + ++m_layersWithTiledBackingCount; + else { + ASSERT(m_layersWithTiledBackingCount > 0); + --m_layersWithTiledBackingCount; + } +} + RenderLayerCompositor* RenderLayerCompositor::enclosingCompositorFlushingLayers() const { if (!m_renderView->frameView()) @@ -398,6 +511,10 @@ void RenderLayerCompositor::updateCompositingLayers(CompositingUpdateType update if (!m_renderView->document()->visualUpdatesAllowed()) return; + // Avoid updating the layers with old values. Compositing layers will be updated after the layout is finished. + if (m_renderView->needsLayout()) + return; + if (m_forceCompositingMode && !m_compositing) enableCompositingMode(true); @@ -406,6 +523,8 @@ void RenderLayerCompositor::updateCompositingLayers(CompositingUpdateType update AnimationUpdateBlock animationUpdateBlock(m_renderView->frameView()->frame()->animation()); + TemporaryChange<bool> postLayoutChange(m_inPostLayoutUpdate, true); + bool checkForHierarchyUpdate = m_reevaluateCompositingAfterLayout; bool needGeometryUpdate = false; @@ -416,11 +535,13 @@ void RenderLayerCompositor::updateCompositingLayers(CompositingUpdateType update checkForHierarchyUpdate = true; break; case CompositingUpdateOnScroll: - if (m_compositingConsultsOverlap) - checkForHierarchyUpdate = true; // Overlap can change with scrolling, so need to check for hierarchy updates. + checkForHierarchyUpdate = true; // Overlap can change with scrolling, so need to check for hierarchy updates. needGeometryUpdate = true; break; + case CompositingUpdateOnCompositedScroll: + needGeometryUpdate = true; + break; } if (!checkForHierarchyUpdate && !needGeometryUpdate) @@ -428,11 +549,10 @@ void RenderLayerCompositor::updateCompositingLayers(CompositingUpdateType update bool needHierarchyUpdate = m_compositingLayersNeedRebuild; bool isFullUpdate = !updateRoot; - if (!updateRoot || m_compositingConsultsOverlap) { - // Only clear the flag if we're updating the entire hierarchy. - m_compositingLayersNeedRebuild = false; - updateRoot = rootRenderLayer(); - } + + // Only clear the flag if we're updating the entire hierarchy. + m_compositingLayersNeedRebuild = false; + updateRoot = rootRenderLayer(); if (isFullUpdate && updateType == CompositingUpdateAfterLayout) m_reevaluateCompositingAfterLayout = false; @@ -448,15 +568,11 @@ void RenderLayerCompositor::updateCompositingLayers(CompositingUpdateType update if (checkForHierarchyUpdate) { // Go through the layers in presentation order, so that we can compute which RenderLayers need compositing layers. // FIXME: we could maybe do this and the hierarchy udpate in one pass, but the parenting logic would be more complex. - CompositingState compState(updateRoot, m_compositingConsultsOverlap); + CompositingState compState(updateRoot); bool layersChanged = false; bool saw3DTransform = false; - if (m_compositingConsultsOverlap) { - OverlapMap overlapTestRequestMap; - computeCompositingRequirements(0, updateRoot, &overlapTestRequestMap, compState, layersChanged, saw3DTransform); - } else - computeCompositingRequirements(0, updateRoot, 0, compState, layersChanged, saw3DTransform); - + OverlapMap overlapTestRequestMap; + computeCompositingRequirements(0, updateRoot, &overlapTestRequestMap, compState, layersChanged, saw3DTransform); needHierarchyUpdate |= layersChanged; } @@ -469,8 +585,7 @@ void RenderLayerCompositor::updateCompositingLayers(CompositingUpdateType update Frame* frame = m_renderView->frameView()->frame(); bool isMainFrame = !m_renderView->document()->ownerElement(); - LOG(Compositing, "\nUpdate %d of %s. Overlap testing is %s\n", m_rootLayerUpdateCount, isMainFrame ? "main frame" : frame->tree()->uniqueName().string().utf8().data(), - m_compositingConsultsOverlap ? "on" : "off"); + LOG(Compositing, "\nUpdate %d of %s.\n", m_rootLayerUpdateCount, isMainFrame ? "main frame" : frame->tree()->uniqueName().string().utf8().data()); } #endif @@ -508,6 +623,18 @@ void RenderLayerCompositor::updateCompositingLayers(CompositingUpdateType update if (!hasAcceleratedCompositing()) enableCompositingMode(false); + + // Inform the inspector that the layer tree has changed. + InspectorInstrumentation::layerTreeDidChange(page()); +} + +void RenderLayerCompositor::layerBecameNonComposited(const RenderLayer* renderLayer) +{ + // Inform the inspector that the given RenderLayer was destroyed. + InspectorInstrumentation::renderLayerDestroyed(page(), renderLayer); + + ASSERT(m_compositedLayerCount > 0); + --m_compositedLayerCount; } #if !LOG_DISABLED @@ -525,17 +652,36 @@ void RenderLayerCompositor::logLayerInfo(const RenderLayer* layer, int depth) m_secondaryBackingStoreBytes += backing->backingStoreMemoryEstimate(); } - LOG(Compositing, "%*p %dx%d %.2fKB (%s) %s\n", 12 + depth * 2, layer, backing->compositedBounds().width(), backing->compositedBounds().height(), - backing->backingStoreMemoryEstimate() / 1024, - reasonForCompositing(layer), layer->backing()->nameForLayer().utf8().data()); + StringBuilder logString; + logString.append(String::format("%*p %dx%d %.2fKB", 12 + depth * 2, layer, + backing->compositedBounds().width().round(), backing->compositedBounds().height().round(), + backing->backingStoreMemoryEstimate() / 1024)); + + logString.append(" ("); + logString.append(logReasonsForCompositing(layer)); + logString.append(") "); + + if (backing->graphicsLayer()->contentsOpaque() || backing->paintsIntoCompositedAncestor()) { + logString.append('['); + if (backing->graphicsLayer()->contentsOpaque()) + logString.append("opaque"); + if (backing->paintsIntoCompositedAncestor()) + logString.append("paints into ancestor"); + logString.append("] "); + } + + logString.append(layer->name()); + + LOG(Compositing, "%s", logString.toString().utf8().data()); } #endif bool RenderLayerCompositor::updateBacking(RenderLayer* layer, CompositingChangeRepaint shouldRepaint) { bool layerChanged = false; + RenderLayer::ViewportConstrainedNotCompositedReason viewportConstrainedNotCompositedReason = RenderLayer::NoNotCompositedReason; - if (needsToBeComposited(layer)) { + if (needsToBeComposited(layer, &viewportConstrainedNotCompositedReason)) { enableCompositingMode(); if (!layer->backing()) { @@ -547,9 +693,15 @@ bool RenderLayerCompositor::updateBacking(RenderLayer* layer, CompositingChangeR // At this time, the ScrollingCooridnator only supports the top-level frame. if (layer->isRootLayer() && !m_renderView->document()->ownerElement()) { - layer->backing()->attachToScrollingCoordinator(0); + layer->backing()->attachToScrollingCoordinatorWithParent(0); if (ScrollingCoordinator* scrollingCoordinator = this->scrollingCoordinator()) scrollingCoordinator->frameViewRootLayerDidChange(m_renderView->frameView()); +#if ENABLE(RUBBER_BANDING) + if (Page* page = this->page()) { + updateLayerForHeader(page->headerHeight()); + updateLayerForFooter(page->footerHeight()); + } +#endif } // This layer and all of its descendants have cached repaints rects that are relative to @@ -559,9 +711,6 @@ bool RenderLayerCompositor::updateBacking(RenderLayer* layer, CompositingChangeR layerChanged = true; } - - // Need to add for every compositing layer, because a composited layer may change from being non-fixed to fixed. - updateViewportConstraintStatus(layer); } else { if (layer->backing()) { // If we're removing backing on a reflection, clear the source GraphicsLayer's pointer to @@ -607,10 +756,17 @@ bool RenderLayerCompositor::updateBacking(RenderLayer* layer, CompositingChangeR if (layerChanged) layer->clearClipRectsIncludingDescendants(PaintingClipRects); - // If a fixed position layer gained/lost a backing, the scrolling coordinator needs to recalculate whether it can do fast scrolling. - if (layerChanged && layer->renderer()->style()->position() == FixedPosition) { - if (ScrollingCoordinator* scrollingCoordinator = this->scrollingCoordinator()) - scrollingCoordinator->frameViewFixedObjectsDidChange(m_renderView->frameView()); + // If a fixed position layer gained/lost a backing or the reason not compositing it changed, + // the scrolling coordinator needs to recalculate whether it can do fast scrolling. + if (layer->renderer()->style()->position() == FixedPosition) { + if (layer->viewportConstrainedNotCompositedReason() != viewportConstrainedNotCompositedReason) { + layer->setViewportConstrainedNotCompositedReason(viewportConstrainedNotCompositedReason); + layerChanged = true; + } + if (layerChanged) { + if (ScrollingCoordinator* scrollingCoordinator = this->scrollingCoordinator()) + scrollingCoordinator->frameViewFixedObjectsDidChange(m_renderView->frameView()); + } } if (layer->backing()) @@ -653,7 +809,7 @@ void RenderLayerCompositor::repaintOnCompositingChange(RenderLayer* layer) // This method assumes that layout is up-to-date, unlike repaintOnCompositingChange(). void RenderLayerCompositor::repaintInCompositedAncestor(RenderLayer* layer, const LayoutRect& rect) { - RenderLayer* compositedAncestor = layer->enclosingCompositingLayerForRepaint(false /*exclude self*/); + RenderLayer* compositedAncestor = layer->enclosingCompositingLayerForRepaint(ExcludeSelf); if (compositedAncestor) { ASSERT(compositedAncestor->backing()); @@ -674,11 +830,11 @@ void RenderLayerCompositor::repaintInCompositedAncestor(RenderLayer* layer, cons // The bounds of the GraphicsLayer created for a compositing layer is the union of the bounds of all the descendant // RenderLayers that are rendered by the composited RenderLayer. -IntRect RenderLayerCompositor::calculateCompositedBounds(const RenderLayer* layer, const RenderLayer* ancestorLayer) const +LayoutRect RenderLayerCompositor::calculateCompositedBounds(const RenderLayer* layer, const RenderLayer* ancestorLayer) const { if (!canBeComposited(layer)) - return IntRect(); - return layer->calculateLayerBounds(ancestorLayer, 0, RenderLayer::DefaultCalculateLayerBoundsFlags | RenderLayer::ExcludeHiddenDescendants); + return LayoutRect(); + return layer->calculateLayerBounds(ancestorLayer, 0, RenderLayer::DefaultCalculateLayerBoundsFlags | RenderLayer::ExcludeHiddenDescendants | RenderLayer::DontConstrainForMask); } void RenderLayerCompositor::layerWasAdded(RenderLayer* /*parent*/, RenderLayer* /*child*/) @@ -701,7 +857,7 @@ void RenderLayerCompositor::layerWillBeRemoved(RenderLayer* parent, RenderLayer* RenderLayer* RenderLayerCompositor::enclosingNonStackingClippingLayer(const RenderLayer* layer) const { for (RenderLayer* curr = layer->parent(); curr != 0; curr = curr->parent()) { - if (curr->isStackingContext()) + if (curr->isStackingContainer()) return 0; if (curr->renderer()->hasClipOrOverflowClip()) @@ -716,7 +872,9 @@ void RenderLayerCompositor::addToOverlapMap(OverlapMap& overlapMap, RenderLayer* return; if (!boundsComputed) { - layerBounds = enclosingIntRect(overlapMap.geometryMap().absoluteRect(layer->localBoundingBox())); + // FIXME: If this layer's overlap bounds include its children, we don't need to add its + // children's bounds to the overlap map. + layerBounds = enclosingIntRect(overlapMap.geometryMap().absoluteRect(layer->overlapBounds())); // Empty rects never intersect, but we need them to for the purposes of overlap testing. if (layerBounds.isEmpty()) layerBounds.setSize(IntSize(1, 1)); @@ -724,7 +882,9 @@ void RenderLayerCompositor::addToOverlapMap(OverlapMap& overlapMap, RenderLayer* } IntRect clipRect = pixelSnappedIntRect(layer->backgroundClipRect(RenderLayer::ClipRectsContext(rootRenderLayer(), 0, AbsoluteClipRects)).rect()); // FIXME: Incorrect for CSS regions. - clipRect.scale(pageScaleFactor()); + if (Settings* settings = m_renderView->document()->settings()) + if (!settings->applyPageScaleFactorInCompositor()) + clipRect.scale(pageScaleFactor()); clipRect.intersect(layerBounds); overlapMap.add(layer, clipRect); } @@ -746,7 +906,7 @@ void RenderLayerCompositor::addToOverlapMapRecursive(OverlapMap& overlapMap, Ren LayerListMutationDetector mutationChecker(layer); #endif - if (layer->isStackingContext()) { + if (layer->isStackingContainer()) { if (Vector<RenderLayer*>* negZOrderList = layer->negZOrderList()) { size_t listSize = negZOrderList->size(); for (size_t i = 0; i < listSize; ++i) { @@ -764,7 +924,7 @@ void RenderLayerCompositor::addToOverlapMapRecursive(OverlapMap& overlapMap, Ren } } - if (layer->isStackingContext()) { + if (layer->isStackingContainer()) { if (Vector<RenderLayer*>* posZOrderList = layer->posZOrderList()) { size_t listSize = posZOrderList->size(); for (size_t i = 0; i < listSize; ++i) { @@ -790,20 +950,23 @@ void RenderLayerCompositor::addToOverlapMapRecursive(OverlapMap& overlapMap, Ren void RenderLayerCompositor::computeCompositingRequirements(RenderLayer* ancestorLayer, RenderLayer* layer, OverlapMap* overlapMap, CompositingState& compositingState, bool& layersChanged, bool& descendantHas3DTransform) { layer->updateLayerListsIfNeeded(); - + + if (layer->isOutOfFlowRenderFlowThread()) + return; + if (overlapMap) overlapMap->geometryMap().pushMappingsToAncestor(layer, ancestorLayer); // Clear the flag layer->setHasCompositingDescendant(false); - + RenderLayer::IndirectCompositingReason compositingReason = compositingState.m_subtreeIsCompositing ? RenderLayer::IndirectCompositingForStacking : RenderLayer::NoIndirectCompositingReason; bool haveComputedBounds = false; IntRect absBounds; if (overlapMap && !overlapMap->isEmpty() && compositingState.m_testingOverlap) { // If we're testing for overlap, we only need to composite if we overlap something that is already composited. - absBounds = enclosingIntRect(overlapMap->geometryMap().absoluteRect(layer->localBoundingBox())); + absBounds = enclosingIntRect(overlapMap->geometryMap().absoluteRect(layer->overlapBounds())); // Empty rects never intersect, but we need them to for the purposes of overlap testing. if (absBounds.isEmpty()) @@ -838,6 +1001,9 @@ void RenderLayerCompositor::computeCompositingRequirements(RenderLayer* ancestor if (overlapMap) overlapMap->pushCompositingContainer(); + // This layer is going to be composited, so children can safely ignore the fact that there's an + // animation running behind this layer, meaning they can rely on the overlap map testing again. + childState.m_testingOverlap = true; } #if !ASSERT_DISABLED @@ -846,7 +1012,7 @@ void RenderLayerCompositor::computeCompositingRequirements(RenderLayer* ancestor bool anyDescendantHas3DTransform = false; - if (layer->isStackingContext()) { + if (layer->isStackingContainer()) { if (Vector<RenderLayer*>* negZOrderList = layer->negZOrderList()) { size_t listSize = negZOrderList->size(); for (size_t i = 0; i < listSize; ++i) { @@ -861,6 +1027,9 @@ void RenderLayerCompositor::computeCompositingRequirements(RenderLayer* ancestor childState.m_compositingAncestor = layer; if (overlapMap) overlapMap->pushCompositingContainer(); + // This layer is going to be composited, so children can safely ignore the fact that there's an + // animation running behind this layer, meaning they can rely on the overlap map testing again + childState.m_testingOverlap = true; willBeComposited = true; } } @@ -875,7 +1044,7 @@ void RenderLayerCompositor::computeCompositingRequirements(RenderLayer* ancestor } } - if (layer->isStackingContext()) { + if (layer->isStackingContainer()) { if (Vector<RenderLayer*>* posZOrderList = layer->posZOrderList()) { size_t listSize = posZOrderList->size(); for (size_t i = 0; i < listSize; ++i) { @@ -922,16 +1091,20 @@ void RenderLayerCompositor::computeCompositingRequirements(RenderLayer* ancestor if (childState.m_subtreeIsCompositing) compositingState.m_subtreeIsCompositing = true; - // Turn overlap testing off for later layers if it's already off, or if we have a 3D transform or an animating transform. - if (!childState.m_testingOverlap || layer->has3DTransform() || isRunningAcceleratedTransformAnimation(layer->renderer())) - compositingState.m_testingOverlap = false; - // Set the flag to say that this SC has compositing children. layer->setHasCompositingDescendant(childState.m_subtreeIsCompositing); // setHasCompositingDescendant() may have changed the answer to needsToBeComposited() when clipping, // so test that again. - if (canBeComposited(layer) && clipsCompositingDescendants(layer)) { + bool isCompositedClippingLayer = canBeComposited(layer) && clipsCompositingDescendants(layer); + + // Turn overlap testing off for later layers if it's already off, or if we have an animating transform. + // Note that if the layer clips its descendants, there's no reason to propagate the child animation to the parent layers. That's because + // we know for sure the animation is contained inside the clipping rectangle, which is already added to the overlap map. + if ((!childState.m_testingOverlap && !isCompositedClippingLayer) || isRunningAcceleratedTransformAnimation(layer->renderer())) + compositingState.m_testingOverlap = false; + + if (isCompositedClippingLayer) { if (!willBeComposited) { childState.m_compositingAncestor = layer; if (overlapMap) { @@ -940,9 +1113,6 @@ void RenderLayerCompositor::computeCompositingRequirements(RenderLayer* ancestor } willBeComposited = true; } - - // We're done processing an element that clips. The container can keep testing overlap. - compositingState.m_testingOverlap = true; } if (overlapMap && childState.m_compositingAncestor == layer && !layer->isRootLayer()) @@ -1017,6 +1187,9 @@ void RenderLayerCompositor::rebuildCompositingLayerTree(RenderLayer* layer, Vect // Make the layer compositing if necessary, and set up clipping and content layers. // Note that we can only do work here that is independent of whether the descendant layers // have been processed. computeCompositingRequirements() will already have done the repaint if necessary. + + if (layer->isOutOfFlowRenderFlowThread()) + return; RenderLayerBacking* layerBacking = layer->backing(); if (layerBacking) { @@ -1029,7 +1202,9 @@ void RenderLayerCompositor::rebuildCompositingLayerTree(RenderLayer* layer, Vect reflection->backing()->updateCompositedBounds(); } - layerBacking->updateGraphicsLayerConfiguration(); + if (layerBacking->updateGraphicsLayerConfiguration()) + layerBacking->updateDebugIndicators(m_showDebugBorders, m_showRepaintCounter); + layerBacking->updateGraphicsLayerGeometry(); if (!layer->parent()) @@ -1040,6 +1215,8 @@ void RenderLayerCompositor::rebuildCompositingLayerTree(RenderLayer* layer, Vect #else UNUSED_PARAM(depth); #endif + if (layerBacking->hasUnpositionedOverflowControlsLayers()) + layer->positionNewlyCreatedOverflowControls(); } // If this layer has backing, then we are collecting its children, otherwise appending @@ -1051,7 +1228,7 @@ void RenderLayerCompositor::rebuildCompositingLayerTree(RenderLayer* layer, Vect LayerListMutationDetector mutationChecker(layer); #endif - if (layer->isStackingContext()) { + if (layer->isStackingContainer()) { if (Vector<RenderLayer*>* negZOrderList = layer->negZOrderList()) { size_t listSize = negZOrderList->size(); for (size_t i = 0; i < listSize; ++i) { @@ -1073,7 +1250,7 @@ void RenderLayerCompositor::rebuildCompositingLayerTree(RenderLayer* layer, Vect } } - if (layer->isStackingContext()) { + if (layer->isStackingContainer()) { if (Vector<RenderLayer*>* posZOrderList = layer->posZOrderList()) { size_t listSize = posZOrderList->size(); for (size_t i = 0; i < listSize; ++i) { @@ -1124,7 +1301,7 @@ void RenderLayerCompositor::frameViewDidChangeSize() { if (m_clipLayer) { FrameView* frameView = m_renderView->frameView(); - m_clipLayer->setSize(frameView->visibleContentRect(false /* exclude scrollbars */).size()); + m_clipLayer->setSize(frameView->unscaledVisibleContentSize()); frameViewDidScroll(); updateOverflowControlsLayers(); @@ -1136,6 +1313,12 @@ void RenderLayerCompositor::frameViewDidChangeSize() } } +bool RenderLayerCompositor::hasCoordinatedScrolling() const +{ + ScrollingCoordinator* scrollingCoordinator = this->scrollingCoordinator(); + return scrollingCoordinator && scrollingCoordinator->coordinatesScrollingForFrameView(m_renderView->frameView()); +} + void RenderLayerCompositor::frameViewDidScroll() { FrameView* frameView = m_renderView->frameView(); @@ -1146,26 +1329,51 @@ void RenderLayerCompositor::frameViewDidScroll() // If there's a scrolling coordinator that manages scrolling for this frame view, // it will also manage updating the scroll layer position. - if (ScrollingCoordinator* scrollingCoordinator = this->scrollingCoordinator()) { - if (scrollingCoordinator->coordinatesScrollingForFrameView(frameView)) - return; + if (hasCoordinatedScrolling()) + return; + + if (Settings* settings = m_renderView->document()->settings()) { + if (settings->compositedScrollingForFramesEnabled()) { + if (ScrollingCoordinator* scrollingCoordinator = this->scrollingCoordinator()) + scrollingCoordinator->scrollableAreaScrollLayerDidChange(frameView); + } } m_scrollLayer->setPosition(FloatPoint(-scrollPosition.x(), -scrollPosition.y())); + + if (GraphicsLayer* fixedBackgroundLayer = fixedRootBackgroundLayer()) + fixedBackgroundLayer->setPosition(IntPoint(frameView->scrollOffsetForFixedPosition())); } void RenderLayerCompositor::frameViewDidLayout() { RenderLayerBacking* renderViewBacking = m_renderView->layer()->backing(); if (renderViewBacking) - renderViewBacking->adjustTileCacheCoverage(); + renderViewBacking->adjustTiledBackingCoverage(); +} + +void RenderLayerCompositor::rootFixedBackgroundsChanged() +{ + RenderLayerBacking* renderViewBacking = m_renderView->layer()->backing(); + if (renderViewBacking && renderViewBacking->usingTiledBacking()) + setCompositingLayersNeedRebuild(); } void RenderLayerCompositor::scrollingLayerDidChange(RenderLayer* layer) { - RenderLayerBacking* backing = layer->backing(); if (ScrollingCoordinator* scrollingCoordinator = this->scrollingCoordinator()) - scrollingCoordinator->scrollableAreaScrollLayerDidChange(layer, backing ? backing->scrollingContentsLayer() : 0); + scrollingCoordinator->scrollableAreaScrollLayerDidChange(layer); +} + +void RenderLayerCompositor::fixedRootBackgroundLayerChanged() +{ + if (ScrollingCoordinator* scrollingCoordinator = this->scrollingCoordinator()) { + RenderLayerBacking* renderViewBacking = m_renderView->layer()->backing(); + if (!renderViewBacking) + return; + + scrollingCoordinator->updateScrollingNode(renderViewBacking->scrollLayerID(), scrollLayer(), fixedRootBackgroundLayer()); + } } String RenderLayerCompositor::layerTreeAsText(LayerTreeFlags flags) @@ -1186,6 +1394,8 @@ String RenderLayerCompositor::layerTreeAsText(LayerTreeFlags flags) layerTreeBehavior |= LayerTreeAsTextIncludeTileCaches; if (flags & LayerTreeFlagsIncludeRepaintRects) layerTreeBehavior |= LayerTreeAsTextIncludeRepaintRects; + if (flags & LayerTreeFlagsIncludePaintingPhases) + layerTreeBehavior |= LayerTreeAsTextIncludePaintingPhases; // We skip dumping the scroll and clip layers to keep layerTreeAsText output // similar between platforms. @@ -1207,7 +1417,7 @@ RenderLayerCompositor* RenderLayerCompositor::frameContentsCompositor(RenderPart if (!renderer->node()->isFrameOwnerElement()) return 0; - HTMLFrameOwnerElement* element = static_cast<HTMLFrameOwnerElement*>(renderer->node()); + HTMLFrameOwnerElement* element = toFrameOwnerElement(renderer->node()); if (Document* contentDocument = element->contentDocument()) { if (RenderView* view = contentDocument->renderView()) return view->compositor(); @@ -1265,7 +1475,7 @@ void RenderLayerCompositor::updateLayerTreeGeometry(RenderLayer* layer, int dept LayerListMutationDetector mutationChecker(layer); #endif - if (layer->isStackingContext()) { + if (layer->isStackingContainer()) { if (Vector<RenderLayer*>* negZOrderList = layer->negZOrderList()) { size_t listSize = negZOrderList->size(); for (size_t i = 0; i < listSize; ++i) @@ -1279,7 +1489,7 @@ void RenderLayerCompositor::updateLayerTreeGeometry(RenderLayer* layer, int dept updateLayerTreeGeometry(normalFlowList->at(i), depth + 1); } - if (layer->isStackingContext()) { + if (layer->isStackingContainer()) { if (Vector<RenderLayer*>* posZOrderList = layer->posZOrderList()) { size_t listSize = posZOrderList->size(); for (size_t i = 0; i < listSize; ++i) @@ -1289,7 +1499,7 @@ void RenderLayerCompositor::updateLayerTreeGeometry(RenderLayer* layer, int dept } // Recurs down the RenderLayer tree until its finds the compositing descendants of compositingAncestor and updates their geometry. -void RenderLayerCompositor::updateCompositingDescendantGeometry(RenderLayer* compositingAncestor, RenderLayer* layer, RenderLayerBacking::UpdateDepth updateDepth) +void RenderLayerCompositor::updateCompositingDescendantGeometry(RenderLayer* compositingAncestor, RenderLayer* layer, bool compositedChildrenOnly) { if (layer != compositingAncestor) { if (RenderLayerBacking* layerBacking = layer->backing()) { @@ -1301,13 +1511,13 @@ void RenderLayerCompositor::updateCompositingDescendantGeometry(RenderLayer* com } layerBacking->updateGraphicsLayerGeometry(); - if (updateDepth == RenderLayerBacking::CompositingChildren) + if (compositedChildrenOnly) return; } } if (layer->reflectionLayer()) - updateCompositingDescendantGeometry(compositingAncestor, layer->reflectionLayer(), updateDepth); + updateCompositingDescendantGeometry(compositingAncestor, layer->reflectionLayer(), compositedChildrenOnly); if (!layer->hasCompositingDescendant()) return; @@ -1316,25 +1526,25 @@ void RenderLayerCompositor::updateCompositingDescendantGeometry(RenderLayer* com LayerListMutationDetector mutationChecker(layer); #endif - if (layer->isStackingContext()) { + if (layer->isStackingContainer()) { if (Vector<RenderLayer*>* negZOrderList = layer->negZOrderList()) { size_t listSize = negZOrderList->size(); for (size_t i = 0; i < listSize; ++i) - updateCompositingDescendantGeometry(compositingAncestor, negZOrderList->at(i), updateDepth); + updateCompositingDescendantGeometry(compositingAncestor, negZOrderList->at(i), compositedChildrenOnly); } } if (Vector<RenderLayer*>* normalFlowList = layer->normalFlowList()) { size_t listSize = normalFlowList->size(); for (size_t i = 0; i < listSize; ++i) - updateCompositingDescendantGeometry(compositingAncestor, normalFlowList->at(i), updateDepth); + updateCompositingDescendantGeometry(compositingAncestor, normalFlowList->at(i), compositedChildrenOnly); } - if (layer->isStackingContext()) { + if (layer->isStackingContainer()) { if (Vector<RenderLayer*>* posZOrderList = layer->posZOrderList()) { size_t listSize = posZOrderList->size(); for (size_t i = 0; i < listSize; ++i) - updateCompositingDescendantGeometry(compositingAncestor, posZOrderList->at(i), updateDepth); + updateCompositingDescendantGeometry(compositingAncestor, posZOrderList->at(i), compositedChildrenOnly); } } } @@ -1417,33 +1627,44 @@ GraphicsLayer* RenderLayerCompositor::scrollLayer() const return m_scrollLayer.get(); } +#if ENABLE(RUBBER_BANDING) +GraphicsLayer* RenderLayerCompositor::headerLayer() const +{ + return m_layerForHeader.get(); +} + +GraphicsLayer* RenderLayerCompositor::footerLayer() const +{ + return m_layerForFooter.get(); +} +#endif + TiledBacking* RenderLayerCompositor::pageTiledBacking() const { RenderLayerBacking* renderViewBacking = m_renderView->layer()->backing(); return renderViewBacking ? renderViewBacking->tiledBacking() : 0; } -void RenderLayerCompositor::didMoveOnscreen() +void RenderLayerCompositor::setIsInWindow(bool isInWindow) { if (TiledBacking* tiledBacking = pageTiledBacking()) - tiledBacking->setIsInWindow(true); + tiledBacking->setIsInWindow(isInWindow); - if (!inCompositingMode() || m_rootLayerAttachment != RootLayerUnattached) + if (!inCompositingMode()) return; - RootLayerAttachment attachment = shouldPropagateCompositingToEnclosingFrame() ? RootLayerAttachedViaEnclosingFrame : RootLayerAttachedViaChromeClient; - attachRootLayer(attachment); -} - -void RenderLayerCompositor::willMoveOffscreen() -{ - if (TiledBacking* tiledBacking = pageTiledBacking()) - tiledBacking->setIsInWindow(false); + if (isInWindow) { + if (m_rootLayerAttachment != RootLayerUnattached) + return; - if (!inCompositingMode() || m_rootLayerAttachment == RootLayerUnattached) - return; + RootLayerAttachment attachment = shouldPropagateCompositingToEnclosingFrame() ? RootLayerAttachedViaEnclosingFrame : RootLayerAttachedViaChromeClient; + attachRootLayer(attachment); + } else { + if (m_rootLayerAttachment == RootLayerUnattached) + return; - detachRootLayer(); + detachRootLayer(); + } } void RenderLayerCompositor::clearBackingForLayerIncludingDescendants(RenderLayer* layer) @@ -1470,11 +1691,11 @@ void RenderLayerCompositor::updateRootLayerPosition() if (m_rootContentLayer) { const IntRect& documentRect = m_renderView->documentRect(); m_rootContentLayer->setSize(documentRect.size()); - m_rootContentLayer->setPosition(documentRect.location()); + m_rootContentLayer->setPosition(FloatPoint(documentRect.x(), documentRect.y() + m_renderView->frameView()->headerHeight())); } if (m_clipLayer) { FrameView* frameView = m_renderView->frameView(); - m_clipLayer->setSize(frameView->visibleContentRect(false /* exclude scrollbars */).size()); + m_clipLayer->setSize(frameView->unscaledVisibleContentSize()); } #if ENABLE(RUBBER_BANDING) @@ -1487,6 +1708,11 @@ void RenderLayerCompositor::updateRootLayerPosition() ScrollbarTheme::theme()->setUpContentShadowLayer(m_contentShadowLayer.get()); } } + + updateLayerForTopOverhangArea(m_layerForTopOverhangArea); + updateLayerForBottomOverhangArea(m_layerForBottomOverhangArea); + updateLayerForHeader(m_layerForHeader); + updateLayerForFooter(m_layerForFooter); #endif } @@ -1533,7 +1759,7 @@ bool RenderLayerCompositor::shouldPropagateCompositingToEnclosingFrame() const RenderPart* frameRenderer = toRenderPart(renderer); if (frameRenderer->widget()) { ASSERT(frameRenderer->widget()->isFrameView()); - FrameView* view = static_cast<FrameView*>(frameRenderer->widget()); + FrameView* view = toFrameView(frameRenderer->widget()); if (view->isOverlappedIncludingAncestors() || view->hasCompositingAncestor()) return true; } @@ -1541,18 +1767,18 @@ bool RenderLayerCompositor::shouldPropagateCompositingToEnclosingFrame() const return false; } -bool RenderLayerCompositor::needsToBeComposited(const RenderLayer* layer) const +bool RenderLayerCompositor::needsToBeComposited(const RenderLayer* layer, RenderLayer::ViewportConstrainedNotCompositedReason* viewportConstrainedNotCompositedReason) const { if (!canBeComposited(layer)) return false; - return requiresCompositingLayer(layer) || layer->mustCompositeForIndirectReasons() || (inCompositingMode() && layer->isRootLayer()); + return requiresCompositingLayer(layer, viewportConstrainedNotCompositedReason) || layer->mustCompositeForIndirectReasons() || (inCompositingMode() && layer->isRootLayer()); } // Note: this specifies whether the RL needs a compositing layer for intrinsic reasons. // Use needsToBeComposited() to determine if a RL actually needs a compositing layer. // static -bool RenderLayerCompositor::requiresCompositingLayer(const RenderLayer* layer) const +bool RenderLayerCompositor::requiresCompositingLayer(const RenderLayer* layer, RenderLayer::ViewportConstrainedNotCompositedReason* viewportConstrainedNotCompositedReason) const { RenderObject* renderer = layer->renderer(); // The compositing state of a reflection should match that of its reflected layer. @@ -1570,7 +1796,7 @@ bool RenderLayerCompositor::requiresCompositingLayer(const RenderLayer* layer) c || clipsCompositingDescendants(layer) || requiresCompositingForAnimation(renderer) || requiresCompositingForFilters(renderer) - || requiresCompositingForPosition(renderer, layer) + || requiresCompositingForPosition(renderer, layer, viewportConstrainedNotCompositedReason) || requiresCompositingForOverflowScrolling(layer) || requiresCompositingForBlending(renderer); } @@ -1579,10 +1805,10 @@ bool RenderLayerCompositor::canBeComposited(const RenderLayer* layer) const { // FIXME: We disable accelerated compositing for elements in a RenderFlowThread as it doesn't work properly. // See http://webkit.org/b/84900 to re-enable it. - return m_hasAcceleratedCompositing && layer->isSelfPaintingLayer() && !layer->renderer()->inRenderFlowThread(); + return m_hasAcceleratedCompositing && layer->isSelfPaintingLayer() && layer->renderer()->flowThreadState() == RenderObject::NotInsideFlowThread; } -bool RenderLayerCompositor::requiresOwnBackingStore(const RenderLayer* layer, const RenderLayer* compositingAncestorLayer) const +bool RenderLayerCompositor::requiresOwnBackingStore(const RenderLayer* layer, const RenderLayer* compositingAncestorLayer, const IntRect& layerCompositedBoundsInAncestor, const IntRect& ancestorCompositedBounds) const { RenderObject* renderer = layer->renderer(); if (compositingAncestorLayer @@ -1618,12 +1844,20 @@ bool RenderLayerCompositor::requiresOwnBackingStore(const RenderLayer* layer, co || reason == RenderLayer::IndirectCompositingForGraphicalEffect || reason == RenderLayer::IndirectCompositingForPreserve3D; // preserve-3d has to create backing store to ensure that 3d-transformed elements intersect. } + + if (!ancestorCompositedBounds.contains(layerCompositedBoundsInAncestor)) + return true; + return false; } -#if !LOG_DISABLED -const char* RenderLayerCompositor::reasonForCompositing(const RenderLayer* layer) +CompositingReasons RenderLayerCompositor::reasonsForCompositing(const RenderLayer* layer) const { + CompositingReasons reasons = CompositingReasonNone; + + if (!layer || !layer->isComposited()) + return reasons; + RenderObject* renderer = layer->renderer(); if (layer->isReflection()) { renderer = renderer->parent(); @@ -1631,74 +1865,142 @@ const char* RenderLayerCompositor::reasonForCompositing(const RenderLayer* layer } if (requiresCompositingForTransform(renderer)) - return "3D transform"; + reasons |= CompositingReason3DTransform; if (requiresCompositingForVideo(renderer)) - return "video"; + reasons |= CompositingReasonVideo; + else if (requiresCompositingForCanvas(renderer)) + reasons |= CompositingReasonCanvas; + else if (requiresCompositingForPlugin(renderer)) + reasons |= CompositingReasonPlugin; + else if (requiresCompositingForFrame(renderer)) + reasons |= CompositingReasonIFrame; + + if ((canRender3DTransforms() && renderer->style()->backfaceVisibility() == BackfaceVisibilityHidden)) + reasons |= CompositingReasonBackfaceVisibilityHidden; - if (requiresCompositingForCanvas(renderer)) - return "canvas"; + if (clipsCompositingDescendants(layer)) + reasons |= CompositingReasonClipsCompositingDescendants; - if (requiresCompositingForPlugin(renderer)) - return "plugin"; + if (requiresCompositingForAnimation(renderer)) + reasons |= CompositingReasonAnimation; + + if (requiresCompositingForFilters(renderer)) + reasons |= CompositingReasonFilters; + + if (requiresCompositingForPosition(renderer, layer)) + reasons |= renderer->style()->position() == FixedPosition ? CompositingReasonPositionFixed : CompositingReasonPositionSticky; + + if (requiresCompositingForOverflowScrolling(layer)) + reasons |= CompositingReasonOverflowScrollingTouch; + + if (layer->indirectCompositingReason() == RenderLayer::IndirectCompositingForStacking) + reasons |= CompositingReasonStacking; + else if (layer->indirectCompositingReason() == RenderLayer::IndirectCompositingForOverlap) + reasons |= CompositingReasonOverlap; + else if (layer->indirectCompositingReason() == RenderLayer::IndirectCompositingForBackgroundLayer) + reasons |= CompositingReasonNegativeZIndexChildren; + else if (layer->indirectCompositingReason() == RenderLayer::IndirectCompositingForGraphicalEffect) { + if (layer->transform()) + reasons |= CompositingReasonTransformWithCompositedDescendants; - if (requiresCompositingForFrame(renderer)) + if (renderer->isTransparent()) + reasons |= CompositingReasonOpacityWithCompositedDescendants; + + if (renderer->hasMask()) + reasons |= CompositingReasonMaskWithCompositedDescendants; + + if (renderer->hasReflection()) + reasons |= CompositingReasonReflectionWithCompositedDescendants; + + if (renderer->hasFilter()) + reasons |= CompositingReasonFilterWithCompositedDescendants; + + if (renderer->hasBlendMode()) + reasons |= CompositingReasonBlendingWithCompositedDescendants; + } else if (layer->indirectCompositingReason() == RenderLayer::IndirectCompositingForPerspective) + reasons |= CompositingReasonPerspective; + else if (layer->indirectCompositingReason() == RenderLayer::IndirectCompositingForPreserve3D) + reasons |= CompositingReasonPreserve3D; + + if (inCompositingMode() && layer->isRootLayer()) + reasons |= CompositingReasonRoot; + + return reasons; +} + +#if !LOG_DISABLED +const char* RenderLayerCompositor::logReasonsForCompositing(const RenderLayer* layer) +{ + CompositingReasons reasons = reasonsForCompositing(layer); + + if (reasons & CompositingReason3DTransform) + return "3D transform"; + + if (reasons & CompositingReasonVideo) + return "video"; + else if (reasons & CompositingReasonCanvas) + return "canvas"; + else if (reasons & CompositingReasonPlugin) + return "plugin"; + else if (reasons & CompositingReasonIFrame) return "iframe"; - if ((canRender3DTransforms() && renderer->style()->backfaceVisibility() == BackfaceVisibilityHidden)) + if (reasons & CompositingReasonBackfaceVisibilityHidden) return "backface-visibility: hidden"; - if (clipsCompositingDescendants(layer)) + if (reasons & CompositingReasonClipsCompositingDescendants) return "clips compositing descendants"; - if (requiresCompositingForAnimation(renderer)) + if (reasons & CompositingReasonAnimation) return "animation"; - if (requiresCompositingForFilters(renderer)) + if (reasons & CompositingReasonFilters) return "filters"; - if (requiresCompositingForPosition(renderer, layer)) + if (reasons & CompositingReasonPositionFixed) return "position: fixed"; - if (requiresCompositingForOverflowScrolling(layer)) + if (reasons & CompositingReasonPositionSticky) + return "position: sticky"; + + if (reasons & CompositingReasonOverflowScrollingTouch) return "-webkit-overflow-scrolling: touch"; - if (layer->indirectCompositingReason() == RenderLayer::IndirectCompositingForStacking) + if (reasons & CompositingReasonStacking) return "stacking"; - if (layer->indirectCompositingReason() == RenderLayer::IndirectCompositingForOverlap) + if (reasons & CompositingReasonOverlap) return "overlap"; - if (layer->indirectCompositingReason() == RenderLayer::IndirectCompositingForBackgroundLayer) + if (reasons & CompositingReasonNegativeZIndexChildren) return "negative z-index children"; - if (layer->indirectCompositingReason() == RenderLayer::IndirectCompositingForGraphicalEffect) { - if (layer->transform()) - return "transform with composited descendants"; + if (reasons & CompositingReasonTransformWithCompositedDescendants) + return "transform with composited descendants"; - if (renderer->isTransparent()) - return "opacity with composited descendants"; + if (reasons & CompositingReasonOpacityWithCompositedDescendants) + return "opacity with composited descendants"; - if (renderer->hasMask()) - return "mask with composited descendants"; + if (reasons & CompositingReasonMaskWithCompositedDescendants) + return "mask with composited descendants"; - if (renderer->hasReflection()) - return "reflection with composited descendants"; + if (reasons & CompositingReasonReflectionWithCompositedDescendants) + return "reflection with composited descendants"; - if (renderer->hasFilter()) - return "filter with composited descendants"; + if (reasons & CompositingReasonFilterWithCompositedDescendants) + return "filter with composited descendants"; - if (renderer->hasBlendMode()) - return "blending with composited descendants"; - } + if (reasons & CompositingReasonBlendingWithCompositedDescendants) + return "blending with composited descendants"; - if (layer->indirectCompositingReason() == RenderLayer::IndirectCompositingForPerspective) + if (reasons & CompositingReasonPerspective) return "perspective"; - if (layer->indirectCompositingReason() == RenderLayer::IndirectCompositingForPreserve3D) + if (reasons & CompositingReasonPreserve3D) return "preserve-3d"; - if (inCompositingMode() && layer->isRootLayer()) + if (reasons & CompositingReasonRoot) return "root"; return ""; @@ -1747,38 +2049,19 @@ bool RenderLayerCompositor::clipsCompositingDescendants(const RenderLayer* layer return layer->hasCompositingDescendant() && layer->renderer()->hasClipOrOverflowClip(); } -// Return true if there is an ancestor layer that is fixed positioned to the view. -// Note that if the ancestor has a stacking context and is fixed position then this method -// will return false. -bool RenderLayerCompositor::fixedPositionedByAncestor(const RenderLayer* layer) const -{ - if (!layer->isComposited() || !layer->parent()) - return false; - - const RenderLayer* compositingAncestor = layer->ancestorCompositingLayer(); - if (!compositingAncestor) - return false; - - const RenderLayer* curr = layer; - while (curr) { - const RenderLayer* next = curr->parent(); - if (next == compositingAncestor) - return false; - - if (next && next->renderer()->style()->position() == FixedPosition) - return true; - curr = next; - } - return false; -} - bool RenderLayerCompositor::requiresCompositingForScrollableFrame() const { // Need this done first to determine overflow. ASSERT(!m_renderView->needsLayout()); + HTMLFrameOwnerElement* ownerElement = m_renderView->document()->ownerElement(); + if (!ownerElement) + return false; + + if (!(m_compositingTriggers & ChromeClient::ScrollableInnerFrameTrigger)) + return false; - ScrollView* scrollView = m_renderView->frameView(); - return scrollView->verticalScrollbar() || scrollView->horizontalScrollbar(); + FrameView* frameView = m_renderView->frameView(); + return frameView->isScrollable(); } bool RenderLayerCompositor::requiresCompositingForTransform(RenderObject* renderer) const @@ -1807,10 +2090,10 @@ bool RenderLayerCompositor::requiresCompositingForVideo(RenderObject* renderer) return false; Node* node = renderer->node(); - if (!node || (!node->hasTagName(HTMLNames::videoTag) && !node->hasTagName(HTMLNames::audioTag))) + if (!node || (!node->hasTagName(HTMLNames::videoTag) && !isHTMLAudioElement(node))) return false; - HTMLMediaElement* mediaElement = static_cast<HTMLMediaElement*>(node); + HTMLMediaElement* mediaElement = toHTMLMediaElement(node); return mediaElement->player() ? mediaElement->player()->supportsAcceleratedRendering() : false; } #endif // ENABLE(PLUGIN_PROXY_FOR_VIDEO) @@ -1827,7 +2110,12 @@ bool RenderLayerCompositor::requiresCompositingForCanvas(RenderObject* renderer) if (renderer->isCanvas()) { HTMLCanvasElement* canvas = static_cast<HTMLCanvasElement*>(renderer->node()); - return canvas->renderingContext() && canvas->renderingContext()->isAccelerated(); +#if USE(COMPOSITING_FOR_SMALL_CANVASES) + bool isCanvasLargeEnoughToForceCompositing = true; +#else + bool isCanvasLargeEnoughToForceCompositing = canvas->size().area() >= canvasAreaThresholdRequiringCompositing; +#endif + return canvas->renderingContext() && canvas->renderingContext()->isAccelerated() && (canvas->renderingContext()->is3d() || isCanvasLargeEnoughToForceCompositing); } return false; } @@ -1870,7 +2158,7 @@ bool RenderLayerCompositor::requiresCompositingForFrame(RenderObject* renderer) return false; // If we can't reliably know the size of the iframe yet, don't change compositing state. - if (renderer->needsLayout()) + if (!renderer->parent() || renderer->needsLayout()) return frameRenderer->hasLayer() && frameRenderer->layer()->isComposited(); // Don't go into compositing mode if height or width are zero. @@ -1949,17 +2237,47 @@ bool RenderLayerCompositor::requiresCompositingForBlending(RenderObject* rendere #endif } -bool RenderLayerCompositor::requiresCompositingForPosition(RenderObject* renderer, const RenderLayer* layer) const +static bool isViewportConstrainedFixedOrStickyLayer(const RenderLayer* layer) +{ + if (layer->renderer()->isStickyPositioned()) + return !layer->enclosingOverflowClipLayer(ExcludeSelf); + + if (layer->renderer()->style()->position() != FixedPosition) + return false; + + for (RenderLayer* stackingContainer = layer->stackingContainer(); stackingContainer; stackingContainer = stackingContainer->stackingContainer()) { + if (stackingContainer->isComposited() && stackingContainer->renderer()->style()->position() == FixedPosition) + return false; + } + + return true; +} + +bool RenderLayerCompositor::requiresCompositingForPosition(RenderObject* renderer, const RenderLayer* layer, RenderLayer::ViewportConstrainedNotCompositedReason* viewportConstrainedNotCompositedReason) const { // position:fixed elements that create their own stacking context (e.g. have an explicit z-index, // opacity, transform) can get their own composited layer. A stacking context is required otherwise // z-index and clipping will be broken. - if (!(renderer->isOutOfFlowPositioned() && renderer->style()->position() == FixedPosition && layer->isStackingContext())) + if (!renderer->isPositioned()) + return false; + + EPosition position = renderer->style()->position(); + bool isFixed = renderer->isOutOfFlowPositioned() && position == FixedPosition; + if (isFixed && !layer->isStackingContainer()) + return false; + + bool isSticky = renderer->isInFlowPositioned() && position == StickyPosition; + if (!isFixed && !isSticky) return false; - if (Settings* settings = m_renderView->document()->settings()) + // FIXME: acceleratedCompositingForFixedPositionEnabled should probably be renamed acceleratedCompositingForViewportConstrainedPositionEnabled(). + if (Settings* settings = m_renderView->document()->settings()) { if (!settings->acceleratedCompositingForFixedPositionEnabled()) return false; + } + + if (isSticky) + return hasCoordinatedScrolling() && isViewportConstrainedFixedOrStickyLayer(layer); RenderObject* container = renderer->container(); // If the renderer is not hooked up yet then we have to wait until it is. @@ -1968,25 +2286,45 @@ bool RenderLayerCompositor::requiresCompositingForPosition(RenderObject* rendere return false; } - // Don't promote fixed position elements that are descendants of transformed elements. - // They will stay fixed wrt the transformed element rather than the enclosing frame. - if (container != m_renderView) + // Don't promote fixed position elements that are descendants of a non-view container, e.g. transformed elements. + // They will stay fixed wrt the container rather than the enclosing frame. + if (container != m_renderView) { + if (viewportConstrainedNotCompositedReason) + *viewportConstrainedNotCompositedReason = RenderLayer::NotCompositedForNonViewContainer; return false; + } + + // Subsequent tests depend on layout. If we can't tell now, just keep things the way they are until layout is done. + if (!m_inPostLayoutUpdate) { + m_reevaluateCompositingAfterLayout = true; + return layer->isComposited(); + } + + bool paintsContent = layer->isVisuallyNonEmpty() || layer->hasVisibleDescendant(); + if (!paintsContent) { + if (viewportConstrainedNotCompositedReason) + *viewportConstrainedNotCompositedReason = RenderLayer::NotCompositedForNoVisibleContent; + return false; + } // Fixed position elements that are invisible in the current view don't get their own layer. if (FrameView* frameView = m_renderView->frameView()) { - IntRect viewBounds = IntRect(IntPoint(frameView->scrollOffsetForFixedPosition()), frameView->layoutSize()); - IntRect layerBounds = calculateCompositedBounds(layer, rootRenderLayer()); - if (!viewBounds.intersects(layerBounds)) + LayoutRect viewBounds = frameView->viewportConstrainedVisibleContentRect(); + LayoutRect layerBounds = layer->calculateLayerBounds(rootRenderLayer(), 0, RenderLayer::DefaultCalculateLayerBoundsFlags + | RenderLayer::ExcludeHiddenDescendants | RenderLayer::DontConstrainForMask | RenderLayer::IncludeCompositedDescendants); + if (!viewBounds.intersects(enclosingIntRect(layerBounds))) { + if (viewportConstrainedNotCompositedReason) + *viewportConstrainedNotCompositedReason = RenderLayer::NotCompositedForBoundsOutOfView; return false; + } } - + return true; } bool RenderLayerCompositor::requiresCompositingForOverflowScrolling(const RenderLayer* layer) const { - return layer->usesCompositedScrolling(); + return layer->needsCompositedScrolling(); } bool RenderLayerCompositor::isRunningAcceleratedTransformAnimation(RenderObject* renderer) const @@ -2047,26 +2385,34 @@ void RenderLayerCompositor::paintContents(const GraphicsLayer* graphicsLayer, Gr transformedClip.moveBy(scrollCorner.location()); m_renderView->frameView()->paintScrollCorner(&context, transformedClip); context.restore(); -#if PLATFORM(CHROMIUM) && ENABLE(RUBBER_BANDING) - } else if (graphicsLayer == layerForOverhangAreas()) { - ScrollView* view = m_renderView->frameView(); - view->calculateAndPaintOverhangAreas(&context, clip); -#endif } } -void RenderLayerCompositor::documentBackgroundColorDidChange() +bool RenderLayerCompositor::supportsFixedRootBackgroundCompositing() const { - RenderLayerBacking* backing = rootRenderLayer()->backing(); - if (!backing || !backing->usingTileCache()) - return; + RenderLayerBacking* renderViewBacking = m_renderView->layer()->backing(); + return renderViewBacking && renderViewBacking->usingTiledBacking(); +} - GraphicsLayer* graphicsLayer = backing->graphicsLayer(); - Color backgroundColor = m_renderView->frameView()->documentBackgroundColor(); - if (!backgroundColor.isValid() || backgroundColor.hasAlpha()) - backgroundColor = Color::white; +bool RenderLayerCompositor::needsFixedRootBackgroundLayer(const RenderLayer* layer) const +{ + if (layer != m_renderView->layer()) + return false; + + return supportsFixedRootBackgroundCompositing() && m_renderView->rootBackgroundIsEntirelyFixed(); +} + +GraphicsLayer* RenderLayerCompositor::fixedRootBackgroundLayer() const +{ + // Get the fixed root background from the RenderView layer's backing. + RenderLayer* viewLayer = m_renderView->layer(); + if (!viewLayer) + return 0; + + if (viewLayer->isComposited() && viewLayer->backing()->backgroundLayerPaintsFixedRootBackground()) + return viewLayer->backing()->backgroundLayer(); - graphicsLayer->setBackgroundColor(backgroundColor); + return 0; } static void resetTrackedRepaintRectsRecursive(GraphicsLayer* graphicsLayer) @@ -2125,40 +2471,38 @@ bool RenderLayerCompositor::keepLayersPixelAligned() const return true; } -static bool shouldCompositeOverflowControls(FrameView* view) +bool RenderLayerCompositor::shouldCompositeOverflowControls() const { + FrameView* view = m_renderView->frameView(); + if (view->platformWidget()) return false; - if (Page* page = view->frame()->page()) { - if (ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator()) - if (scrollingCoordinator->coordinatesScrollingForFrameView(view)) - return true; - } + if (hasCoordinatedScrolling()) + return true; -#if !PLATFORM(CHROMIUM) if (!view->hasOverlayScrollbars()) return false; -#endif + return true; } bool RenderLayerCompositor::requiresHorizontalScrollbarLayer() const { FrameView* view = m_renderView->frameView(); - return shouldCompositeOverflowControls(view) && view->horizontalScrollbar(); + return shouldCompositeOverflowControls() && view->horizontalScrollbar(); } bool RenderLayerCompositor::requiresVerticalScrollbarLayer() const { FrameView* view = m_renderView->frameView(); - return shouldCompositeOverflowControls(view) && view->verticalScrollbar(); + return shouldCompositeOverflowControls() && view->verticalScrollbar(); } bool RenderLayerCompositor::requiresScrollCornerLayer() const { FrameView* view = m_renderView->frameView(); - return shouldCompositeOverflowControls(view) && view->isScrollCornerVisible(); + return shouldCompositeOverflowControls() && view->isScrollCornerVisible(); } #if ENABLE(RUBBER_BANDING) @@ -2168,15 +2512,10 @@ bool RenderLayerCompositor::requiresOverhangAreasLayer() const if (m_renderView->document()->ownerElement()) return false; - // We do want a layer if we have a scrolling coordinator. - if (scrollingCoordinator()) + // We do want a layer if we have a scrolling coordinator and can scroll. + if (scrollingCoordinator() && m_renderView->frameView()->hasOpaqueBackground() && !m_renderView->frameView()->prohibitsScrolling()) return true; - // Chromium always wants a layer. -#if PLATFORM(CHROMIUM) - return true; -#endif - return false; } @@ -2187,14 +2526,165 @@ bool RenderLayerCompositor::requiresContentShadowLayer() const return false; #if PLATFORM(MAC) - // On Mac, we want a content shadow layer if we have a scrolling coordinator. - if (scrollingCoordinator()) + if (viewHasTransparentBackground()) + return false; + + // On Mac, we want a content shadow layer if we have a scrolling coordinator and can scroll. + if (scrollingCoordinator() && !m_renderView->frameView()->prohibitsScrolling()) return true; #endif return false; } + +GraphicsLayer* RenderLayerCompositor::updateLayerForTopOverhangArea(bool wantsLayer) +{ + if (m_renderView->document()->ownerElement()) + return 0; + + if (!wantsLayer) { + if (m_layerForTopOverhangArea) { + m_layerForTopOverhangArea->removeFromParent(); + m_layerForTopOverhangArea = nullptr; + } + return 0; + } + + if (!m_layerForTopOverhangArea) { + m_layerForTopOverhangArea = GraphicsLayer::create(graphicsLayerFactory(), this); +#ifndef NDEBUG + m_layerForTopOverhangArea->setName("top overhang area"); +#endif + m_scrollLayer->addChildBelow(m_layerForTopOverhangArea.get(), m_rootContentLayer.get()); + } + + return m_layerForTopOverhangArea.get(); +} + +GraphicsLayer* RenderLayerCompositor::updateLayerForBottomOverhangArea(bool wantsLayer) +{ + if (m_renderView->document()->ownerElement()) + return 0; + + if (!wantsLayer) { + if (m_layerForBottomOverhangArea) { + m_layerForBottomOverhangArea->removeFromParent(); + m_layerForBottomOverhangArea = nullptr; + } + return 0; + } + + if (!m_layerForBottomOverhangArea) { + m_layerForBottomOverhangArea = GraphicsLayer::create(graphicsLayerFactory(), this); +#ifndef NDEBUG + m_layerForBottomOverhangArea->setName("bottom overhang area"); #endif + m_scrollLayer->addChildBelow(m_layerForBottomOverhangArea.get(), m_rootContentLayer.get()); + } + + m_layerForBottomOverhangArea->setPosition(FloatPoint(0, m_rootContentLayer->size().height() + m_renderView->frameView()->headerHeight() + m_renderView->frameView()->footerHeight())); + return m_layerForBottomOverhangArea.get(); +} + +GraphicsLayer* RenderLayerCompositor::updateLayerForHeader(bool wantsLayer) +{ + if (m_renderView->document()->ownerElement()) + return 0; + + if (!wantsLayer) { + if (m_layerForHeader) { + m_layerForHeader->removeFromParent(); + m_layerForHeader = nullptr; + + // The ScrollingTree knows about the header layer, and the position of the root layer is affected + // by the header layer, so if we remove the header, we need to tell the scrolling tree. + if (ScrollingCoordinator* scrollingCoordinator = this->scrollingCoordinator()) + scrollingCoordinator->frameViewRootLayerDidChange(m_renderView->frameView()); + } + return 0; + } + + if (!m_layerForHeader) { + m_layerForHeader = GraphicsLayer::create(graphicsLayerFactory(), this); +#ifndef NDEBUG + m_layerForHeader->setName("header"); +#endif + m_scrollLayer->addChildBelow(m_layerForHeader.get(), m_rootContentLayer.get()); + m_renderView->frameView()->addPaintPendingMilestones(DidFirstFlushForHeaderLayer); + } + + m_layerForHeader->setPosition(FloatPoint()); + m_layerForHeader->setAnchorPoint(FloatPoint3D()); + m_layerForHeader->setSize(FloatSize(m_renderView->frameView()->visibleWidth(), m_renderView->frameView()->headerHeight())); + + if (ScrollingCoordinator* scrollingCoordinator = this->scrollingCoordinator()) + scrollingCoordinator->frameViewRootLayerDidChange(m_renderView->frameView()); + + if (Page* page = this->page()) + page->chrome().client()->didAddHeaderLayer(m_layerForHeader.get()); + + return m_layerForHeader.get(); +} + +GraphicsLayer* RenderLayerCompositor::updateLayerForFooter(bool wantsLayer) +{ + if (m_renderView->document()->ownerElement()) + return 0; + + if (!wantsLayer) { + if (m_layerForFooter) { + m_layerForFooter->removeFromParent(); + m_layerForFooter = nullptr; + + // The ScrollingTree knows about the footer layer, and the total scrollable size is affected + // by the footer layer, so if we remove the footer, we need to tell the scrolling tree. + if (ScrollingCoordinator* scrollingCoordinator = this->scrollingCoordinator()) + scrollingCoordinator->frameViewRootLayerDidChange(m_renderView->frameView()); + } + return 0; + } + + if (!m_layerForFooter) { + m_layerForFooter = GraphicsLayer::create(graphicsLayerFactory(), this); +#ifndef NDEBUG + m_layerForFooter->setName("footer"); +#endif + m_scrollLayer->addChildBelow(m_layerForFooter.get(), m_rootContentLayer.get()); + } + + m_layerForFooter->setPosition(FloatPoint(0, m_rootContentLayer->size().height() + m_renderView->frameView()->headerHeight())); + m_layerForFooter->setAnchorPoint(FloatPoint3D()); + m_layerForFooter->setSize(FloatSize(m_renderView->frameView()->visibleWidth(), m_renderView->frameView()->footerHeight())); + + if (ScrollingCoordinator* scrollingCoordinator = this->scrollingCoordinator()) + scrollingCoordinator->frameViewRootLayerDidChange(m_renderView->frameView()); + + if (Page* page = this->page()) + page->chrome().client()->didAddFooterLayer(m_layerForFooter.get()); + + return m_layerForFooter.get(); +} + +#endif + +bool RenderLayerCompositor::viewHasTransparentBackground(Color* backgroundColor) const +{ + FrameView* frameView = m_renderView->frameView(); + if (frameView->isTransparent()) { + if (backgroundColor) + *backgroundColor = Color(); // Return an invalid color. + return true; + } + + Color documentBackgroundColor = frameView->documentBackgroundColor(); + if (!documentBackgroundColor.isValid()) + documentBackgroundColor = Color::white; + + if (backgroundColor) + *backgroundColor = documentBackgroundColor; + + return documentBackgroundColor.hasAlpha(); +} void RenderLayerCompositor::updateOverflowControlsLayers() { @@ -2208,7 +2698,7 @@ void RenderLayerCompositor::updateOverflowControlsLayers() m_layerForOverhangAreas->setDrawsContent(false); m_layerForOverhangAreas->setSize(m_renderView->frameView()->frameRect().size()); - ScrollbarTheme::theme()->setUpOverhangAreasLayerContents(m_layerForOverhangAreas.get()); + ScrollbarTheme::theme()->setUpOverhangAreasLayerContents(m_layerForOverhangAreas.get(), this->page()->chrome().client()->underlayColor()); // We want the overhang areas layer to be positioned below the frame contents, // so insert it below the clip layer. @@ -2250,14 +2740,14 @@ void RenderLayerCompositor::updateOverflowControlsLayers() m_overflowControlsHostLayer->addChild(m_layerForHorizontalScrollbar.get()); if (ScrollingCoordinator* scrollingCoordinator = this->scrollingCoordinator()) - scrollingCoordinator->frameViewHorizontalScrollbarLayerDidChange(m_renderView->frameView(), m_layerForHorizontalScrollbar.get()); + scrollingCoordinator->scrollableAreaScrollbarLayerDidChange(m_renderView->frameView(), HorizontalScrollbar); } } else if (m_layerForHorizontalScrollbar) { m_layerForHorizontalScrollbar->removeFromParent(); m_layerForHorizontalScrollbar = nullptr; if (ScrollingCoordinator* scrollingCoordinator = this->scrollingCoordinator()) - scrollingCoordinator->frameViewHorizontalScrollbarLayerDidChange(m_renderView->frameView(), 0); + scrollingCoordinator->scrollableAreaScrollbarLayerDidChange(m_renderView->frameView(), HorizontalScrollbar); } if (requiresVerticalScrollbarLayer()) { @@ -2273,14 +2763,14 @@ void RenderLayerCompositor::updateOverflowControlsLayers() m_overflowControlsHostLayer->addChild(m_layerForVerticalScrollbar.get()); if (ScrollingCoordinator* scrollingCoordinator = this->scrollingCoordinator()) - scrollingCoordinator->frameViewVerticalScrollbarLayerDidChange(m_renderView->frameView(), m_layerForVerticalScrollbar.get()); + scrollingCoordinator->scrollableAreaScrollbarLayerDidChange(m_renderView->frameView(), VerticalScrollbar); } } else if (m_layerForVerticalScrollbar) { m_layerForVerticalScrollbar->removeFromParent(); m_layerForVerticalScrollbar = nullptr; if (ScrollingCoordinator* scrollingCoordinator = this->scrollingCoordinator()) - scrollingCoordinator->frameViewVerticalScrollbarLayerDidChange(m_renderView->frameView(), 0); + scrollingCoordinator->scrollableAreaScrollbarLayerDidChange(m_renderView->frameView(), VerticalScrollbar); } if (requiresScrollCornerLayer()) { @@ -2387,6 +2877,8 @@ void RenderLayerCompositor::destroyRootLayer() if (m_layerForHorizontalScrollbar) { m_layerForHorizontalScrollbar->removeFromParent(); m_layerForHorizontalScrollbar = nullptr; + if (ScrollingCoordinator* scrollingCoordinator = this->scrollingCoordinator()) + scrollingCoordinator->scrollableAreaScrollbarLayerDidChange(m_renderView->frameView(), HorizontalScrollbar); if (Scrollbar* horizontalScrollbar = m_renderView->frameView()->verticalScrollbar()) m_renderView->frameView()->invalidateScrollbar(horizontalScrollbar, IntRect(IntPoint(0, 0), horizontalScrollbar->frameRect().size())); } @@ -2394,6 +2886,8 @@ void RenderLayerCompositor::destroyRootLayer() if (m_layerForVerticalScrollbar) { m_layerForVerticalScrollbar->removeFromParent(); m_layerForVerticalScrollbar = nullptr; + if (ScrollingCoordinator* scrollingCoordinator = this->scrollingCoordinator()) + scrollingCoordinator->scrollableAreaScrollbarLayerDidChange(m_renderView->frameView(), VerticalScrollbar); if (Scrollbar* verticalScrollbar = m_renderView->frameView()->verticalScrollbar()) m_renderView->frameView()->invalidateScrollbar(verticalScrollbar, IntRect(IntPoint(0, 0), verticalScrollbar->frameRect().size())); } @@ -2429,7 +2923,7 @@ void RenderLayerCompositor::attachRootLayer(RootLayerAttachment attachment) if (!page) return; - page->chrome()->client()->attachRootGraphicsLayer(frame, rootGraphicsLayer()); + page->chrome().client()->attachRootGraphicsLayer(frame, rootGraphicsLayer()); break; } case RootLayerAttachedViaEnclosingFrame: { @@ -2473,7 +2967,7 @@ void RenderLayerCompositor::detachRootLayer() if (!page) return; - page->chrome()->client()->attachRootGraphicsLayer(frame, 0); + page->chrome().client()->attachRootGraphicsLayer(frame, 0); } break; case RootLayerUnattached: @@ -2534,7 +3028,7 @@ bool RenderLayerCompositor::layerHas3DContent(const RenderLayer* layer) const LayerListMutationDetector mutationChecker(const_cast<RenderLayer*>(layer)); #endif - if (layer->isStackingContext()) { + if (layer->isStackingContainer()) { if (Vector<RenderLayer*>* negZOrderList = layer->negZOrderList()) { size_t listSize = negZOrderList->size(); for (size_t i = 0; i < listSize; ++i) { @@ -2572,29 +3066,13 @@ void RenderLayerCompositor::deviceOrPageScaleFactorChanged() if (!viewLayer->isComposited()) return; - if (GraphicsLayer* rootLayer = viewLayer->backing()->graphicsLayer()) + if (GraphicsLayer* rootLayer = viewLayer->backing()->childForSuperlayers()) rootLayer->noteDeviceOrPageScaleFactorChangedIncludingDescendants(); } -static bool isRootmostFixedOrStickyLayer(RenderLayer* layer) -{ - if (layer->renderer()->isStickyPositioned()) - return true; - - if (layer->renderer()->style()->position() != FixedPosition) - return false; - - for (RenderLayer* stackingContext = layer->stackingContext(); stackingContext; stackingContext = stackingContext->stackingContext()) { - if (stackingContext->isComposited() && stackingContext->renderer()->style()->position() == FixedPosition) - return false; - } - - return true; -} - void RenderLayerCompositor::updateViewportConstraintStatus(RenderLayer* layer) { - if (isRootmostFixedOrStickyLayer(layer)) + if (isViewportConstrainedFixedOrStickyLayer(layer)) addViewportConstrainedLayer(layer); else removeViewportConstrainedLayer(layer); @@ -2613,24 +3091,23 @@ void RenderLayerCompositor::removeViewportConstrainedLayer(RenderLayer* layer) unregisterViewportConstrainedLayer(layer); m_viewportConstrainedLayers.remove(layer); + m_viewportConstrainedLayersNeedingUpdate.remove(layer); } -const FixedPositionViewportConstraints RenderLayerCompositor::computeFixedViewportConstraints(RenderLayer* layer) +FixedPositionViewportConstraints RenderLayerCompositor::computeFixedViewportConstraints(RenderLayer* layer) const { ASSERT(layer->isComposited()); FrameView* frameView = m_renderView->frameView(); + LayoutRect viewportRect = frameView->viewportConstrainedVisibleContentRect(); - LayoutRect viewportRect = frameView->visibleContentRect(); - viewportRect.setLocation(toPoint(frameView->scrollOffsetForFixedPosition())); - - FixedPositionViewportConstraints constraints = FixedPositionViewportConstraints(); + FixedPositionViewportConstraints constraints; GraphicsLayer* graphicsLayer = layer->backing()->graphicsLayer(); constraints.setLayerPositionAtLastLayout(graphicsLayer->position()); constraints.setViewportRectAtLastLayout(viewportRect); - + RenderStyle* style = layer->renderer()->style(); if (!style->left().isAuto()) constraints.addAnchorEdge(ViewportConstraints::AnchorEdgeLeft); @@ -2655,17 +3132,18 @@ const FixedPositionViewportConstraints RenderLayerCompositor::computeFixedViewpo return constraints; } -const StickyPositionViewportConstraints RenderLayerCompositor::computeStickyViewportConstraints(RenderLayer* layer) +StickyPositionViewportConstraints RenderLayerCompositor::computeStickyViewportConstraints(RenderLayer* layer) const { ASSERT(layer->isComposited()); + // We should never get here for stickies constrained by an enclosing clipping layer. + ASSERT(!layer->enclosingOverflowClipLayer(ExcludeSelf)); FrameView* frameView = m_renderView->frameView(); - LayoutRect viewportRect = frameView->visibleContentRect(); - - StickyPositionViewportConstraints constraints = StickyPositionViewportConstraints(); + LayoutRect viewportRect = frameView->viewportConstrainedVisibleContentRect(); RenderBoxModelObject* renderer = toRenderBoxModelObject(layer->renderer()); + StickyPositionViewportConstraints constraints; renderer->computeStickyPositionConstraints(constraints, viewportRect); GraphicsLayer* graphicsLayer = layer->backing()->graphicsLayer(); @@ -2694,17 +3172,17 @@ void RenderLayerCompositor::registerOrUpdateViewportConstrainedLayer(RenderLayer { // FIXME: We should support sticky position here! And we should eventuall support fixed/sticky elements // that are inside non-main frames once we get non-main frames scrolling with the ScrollingCoordinator. - if (layer->renderer()->isStickyPositioned() || m_renderView->document()->ownerElement()) + if (m_renderView->document()->ownerElement()) return; ScrollingCoordinator* scrollingCoordinator = this->scrollingCoordinator(); if (!scrollingCoordinator) return; + // FIXME: rename to supportsViewportConstrainedPositionLayers()? if (!scrollingCoordinator->supportsFixedPositionLayers() || !layer->parent()) return; - ASSERT(layer->renderer()->style()->position() == FixedPosition); ASSERT(m_viewportConstrainedLayers.contains(layer)); ASSERT(layer->isComposited()); @@ -2713,13 +3191,13 @@ void RenderLayerCompositor::registerOrUpdateViewportConstrainedLayer(RenderLayer return; ScrollingNodeID nodeID = backing->scrollLayerID(); - if (!nodeID) { - RenderLayerBacking* parent = nearestScrollingCoordinatorAncestor(layer); - if (!parent) - return; - backing->attachToScrollingCoordinator(parent); - nodeID = backing->scrollLayerID(); - } + RenderLayerBacking* parent = nearestScrollingCoordinatorAncestor(layer); + if (!parent) + return; + + // Always call this even if the backing is already attached because the parent may have changed. + backing->attachToScrollingCoordinatorWithParent(parent); + nodeID = backing->scrollLayerID(); if (layer->renderer()->isStickyPositioned()) scrollingCoordinator->updateViewportConstrainedNode(nodeID, computeStickyViewportConstraints(layer), backing->graphicsLayer()); @@ -2752,7 +3230,7 @@ ScrollingCoordinator* RenderLayerCompositor::scrollingCoordinator() const GraphicsLayerFactory* RenderLayerCompositor::graphicsLayerFactory() const { if (Page* page = this->page()) - return page->chrome()->client()->graphicsLayerFactory(); + return page->chrome().client()->graphicsLayerFactory(); return 0; } @@ -2765,6 +3243,69 @@ Page* RenderLayerCompositor::page() const return 0; } +void RenderLayerCompositor::setLayerFlushThrottlingEnabled(bool enabled) +{ + m_layerFlushThrottlingEnabled = enabled; + if (m_layerFlushThrottlingEnabled) + return; + m_layerFlushTimer.stop(); + if (!m_hasPendingLayerFlush) + return; + scheduleLayerFlushNow(); +} + +void RenderLayerCompositor::disableLayerFlushThrottlingTemporarilyForInteraction() +{ + if (m_layerFlushThrottlingTemporarilyDisabledForInteraction) + return; + m_layerFlushThrottlingTemporarilyDisabledForInteraction = true; +} + +bool RenderLayerCompositor::isThrottlingLayerFlushes() const +{ + if (!m_layerFlushThrottlingEnabled) + return false; + if (!m_layerFlushTimer.isActive()) + return false; + if (m_layerFlushThrottlingTemporarilyDisabledForInteraction) + return false; + return true; +} + +void RenderLayerCompositor::startLayerFlushTimerIfNeeded() +{ + m_layerFlushThrottlingTemporarilyDisabledForInteraction = false; + m_layerFlushTimer.stop(); + if (!m_layerFlushThrottlingEnabled) + return; + m_layerFlushTimer.startOneShot(throttledLayerFlushDelay); +} + +void RenderLayerCompositor::layerFlushTimerFired(Timer<RenderLayerCompositor>*) +{ + if (!m_hasPendingLayerFlush) + return; + scheduleLayerFlushNow(); +} + +void RenderLayerCompositor::paintRelatedMilestonesTimerFired(Timer<RenderLayerCompositor>*) +{ + FrameView* frameView = m_renderView ? m_renderView->frameView() : 0; + if (!frameView) + return; + + Frame* frame = frameView->frame(); + Page* page = frame ? frame->page() : 0; + if (!page) + return; + + // If the layer tree is frozen, we'll paint when it's unfrozen and schedule the timer again. + if (page->chrome().client()->layerTreeStateIsFrozen()) + return; + + frameView->firePaintRelatedMilestones(); +} + } // namespace WebCore #endif // USE(ACCELERATED_COMPOSITING) diff --git a/Source/WebCore/rendering/RenderLayerCompositor.h b/Source/WebCore/rendering/RenderLayerCompositor.h index 5193411a2..b0ea17d2f 100644 --- a/Source/WebCore/rendering/RenderLayerCompositor.h +++ b/Source/WebCore/rendering/RenderLayerCompositor.h @@ -26,11 +26,14 @@ #ifndef RenderLayerCompositor_h #define RenderLayerCompositor_h +#if USE(ACCELERATED_COMPOSITING) + #include "ChromeClient.h" #include "Frame.h" +#include "GraphicsLayerClient.h" #include "GraphicsLayerUpdater.h" #include "RenderLayer.h" -#include "RenderLayerBacking.h" +#include <wtf/HashMap.h> namespace WebCore { @@ -50,8 +53,38 @@ enum CompositingUpdateType { CompositingUpdateAfterStyleChange, CompositingUpdateAfterLayout, CompositingUpdateOnHitTest, - CompositingUpdateOnScroll + CompositingUpdateOnScroll, + CompositingUpdateOnCompositedScroll +}; + +enum { + CompositingReasonNone = 0, + CompositingReason3DTransform = 1 << 0, + CompositingReasonVideo = 1 << 1, + CompositingReasonCanvas = 1 << 2, + CompositingReasonPlugin = 1 << 3, + CompositingReasonIFrame = 1 << 4, + CompositingReasonBackfaceVisibilityHidden = 1 << 5, + CompositingReasonClipsCompositingDescendants = 1 << 6, + CompositingReasonAnimation = 1 << 7, + CompositingReasonFilters = 1 << 8, + CompositingReasonPositionFixed = 1 << 9, + CompositingReasonPositionSticky = 1 << 10, + CompositingReasonOverflowScrollingTouch = 1 << 11, + CompositingReasonStacking = 1 << 12, + CompositingReasonOverlap = 1 << 13, + CompositingReasonNegativeZIndexChildren = 1 << 14, + CompositingReasonTransformWithCompositedDescendants = 1 << 15, + CompositingReasonOpacityWithCompositedDescendants = 1 << 16, + CompositingReasonMaskWithCompositedDescendants = 1 << 17, + CompositingReasonReflectionWithCompositedDescendants = 1 << 18, + CompositingReasonFilterWithCompositedDescendants = 1 << 19, + CompositingReasonBlendingWithCompositedDescendants = 1 << 20, + CompositingReasonPerspective = 1 << 21, + CompositingReasonPreserve3D = 1 << 22, + CompositingReasonRoot = 1 << 23 }; +typedef unsigned CompositingReasons; // RenderLayerCompositor manages the hierarchy of // composited RenderLayers. It determines which RenderLayers @@ -63,7 +96,7 @@ enum CompositingUpdateType { class RenderLayerCompositor : public GraphicsLayerClient, public GraphicsLayerUpdaterClient { WTF_MAKE_FAST_ALLOCATED; public: - RenderLayerCompositor(RenderView*); + explicit RenderLayerCompositor(RenderView*); ~RenderLayerCompositor(); // Return true if this RenderView is in "compositing mode" (i.e. has one or more @@ -88,14 +121,9 @@ public: void setCompositingLayersNeedRebuild(bool needRebuild = true); bool compositingLayersNeedRebuild() const { return m_compositingLayersNeedRebuild; } - // Controls whether or not to consult geometry when deciding which layers need - // to be composited. Defaults to true. - void setCompositingConsultsOverlap(bool b) { m_compositingConsultsOverlap = b; } - bool compositingConsultsOverlap() const { return m_compositingConsultsOverlap; } - // GraphicsLayers buffer state, which gets pushed to the underlying platform layers // at specific times. - void scheduleLayerFlush(); + void scheduleLayerFlush(bool canThrottle); void flushPendingLayerChanges(bool isFlushRoot = true); // flushPendingLayerChanges() flushes the entire GraphicsLayer tree, which can cross frame boundaries. @@ -103,7 +131,10 @@ public: RenderLayerCompositor* enclosingCompositorFlushingLayers() const; // Called when the GraphicsLayer for the given RenderLayer has flushed changes inside of flushPendingLayerChanges(). - void didFlushChangesForLayer(RenderLayer*); + void didFlushChangesForLayer(RenderLayer*, const GraphicsLayer*); + + // Called when something outside WebKit affects the visible rect (e.g. delegated scrolling). Might schedule a layer flush. + void didChangeVisibleRect(); // Rebuild the tree of compositing layers void updateCompositingLayers(CompositingUpdateType, RenderLayer* updateRoot = 0); @@ -116,21 +147,23 @@ public: bool updateLayerCompositingState(RenderLayer*, CompositingChangeRepaint = CompositingChangeRepaintNow); // Update the geometry for compositing children of compositingAncestor. - void updateCompositingDescendantGeometry(RenderLayer* compositingAncestor, RenderLayer* layer, RenderLayerBacking::UpdateDepth); + void updateCompositingDescendantGeometry(RenderLayer* compositingAncestor, RenderLayer*, bool compositedChildrenOnly); // Whether layer's backing needs a graphics layer to do clipping by an ancestor (non-stacking-context parent with overflow). bool clippedByAncestor(RenderLayer*) const; // Whether layer's backing needs a graphics layer to clip z-order children of the given layer. bool clipsCompositingDescendants(const RenderLayer*) const; - // Whether the layer is fixed positioned to the view by an ancestor layer. - bool fixedPositionedByAncestor(const RenderLayer*) const; - // Whether the given layer needs an extra 'contents' layer. bool needsContentsCompositingLayer(const RenderLayer*) const; + + bool supportsFixedRootBackgroundCompositing() const; + bool needsFixedRootBackgroundLayer(const RenderLayer*) const; + GraphicsLayer* fixedRootBackgroundLayer() const; + // Return the bounding box required for compositing layer and its childern, relative to ancestorLayer. // If layerBoundingBox is not 0, on return it contains the bounding box of this layer only. - IntRect calculateCompositedBounds(const RenderLayer*, const RenderLayer* ancestorLayer) const; + LayoutRect calculateCompositedBounds(const RenderLayer*, const RenderLayer* ancestorLayer) const; // Repaint the appropriate layers when the given RenderLayer starts or stops being composited. void repaintOnCompositingChange(RenderLayer*); @@ -148,12 +181,17 @@ public: void repaintCompositedLayers(const IntRect* = 0); // Returns true if the given layer needs it own backing store. - bool requiresOwnBackingStore(const RenderLayer*, const RenderLayer* compositingAncestorLayer) const; + bool requiresOwnBackingStore(const RenderLayer*, const RenderLayer* compositingAncestorLayer, const IntRect& layerCompositedBoundsInAncestor, const IntRect& ancestorCompositedBounds) const; RenderLayer* rootRenderLayer() const; GraphicsLayer* rootGraphicsLayer() const; GraphicsLayer* scrollLayer() const; +#if ENABLE(RUBBER_BANDING) + GraphicsLayer* headerLayer() const; + GraphicsLayer* footerLayer() const; +#endif + enum RootLayerAttachment { RootLayerUnattached, RootLayerAttachedViaChromeClient, @@ -164,17 +202,12 @@ public: void updateRootLayerAttachment(); void updateRootLayerPosition(); - void didMoveOnscreen(); - void willMoveOffscreen(); + void setIsInWindow(bool); void clearBackingForAllLayers(); void layerBecameComposited(const RenderLayer*) { ++m_compositedLayerCount; } - void layerBecameNonComposited(const RenderLayer*) - { - ASSERT(m_compositedLayerCount > 0); - --m_compositedLayerCount; - } + void layerBecameNonComposited(const RenderLayer*); #if ENABLE(VIDEO) // Use by RenderVideo to ask if it should try to use accelerated compositing. @@ -199,8 +232,10 @@ public: void frameViewDidChangeSize(); void frameViewDidScroll(); void frameViewDidLayout(); + void rootFixedBackgroundsChanged(); void scrollingLayerDidChange(RenderLayer*); + void fixedRootBackgroundLayerChanged(); String layerTreeAsText(LayerTreeFlags); @@ -208,6 +243,8 @@ public: virtual float pageScaleFactor() const OVERRIDE; virtual void didCommitChangesForLayer(const GraphicsLayer*) const OVERRIDE; virtual void notifyFlushBeforeDisplayRefresh(const GraphicsLayer*) OVERRIDE; + + void layerTiledBackingUsageChanged(const GraphicsLayer*, bool /*usingTiledBacking*/); bool keepLayersPixelAligned() const; bool acceleratedDrawingEnabled() const { return m_acceleratedDrawingEnabled; } @@ -221,9 +258,12 @@ public: GraphicsLayer* layerForScrollCorner() const { return m_layerForScrollCorner.get(); } #if ENABLE(RUBBER_BANDING) GraphicsLayer* layerForOverhangAreas() const { return m_layerForOverhangAreas.get(); } -#endif - void documentBackgroundColorDidChange(); + GraphicsLayer* updateLayerForTopOverhangArea(bool wantsLayer); + GraphicsLayer* updateLayerForBottomOverhangArea(bool wantsLayer); + GraphicsLayer* updateLayerForHeader(bool wantsLayer); + GraphicsLayer* updateLayerForFooter(bool wantsLayer); +#endif void updateViewportConstraintStatus(RenderLayer*); void removeViewportConstrainedLayer(RenderLayer*); @@ -231,23 +271,37 @@ public: void resetTrackedRepaintRects(); void setTracksRepaints(bool); + void setShouldReevaluateCompositingAfterLayout() { m_reevaluateCompositingAfterLayout = true; } + + bool viewHasTransparentBackground(Color* backgroundColor = 0) const; + + bool hasNonMainLayersWithTiledBacking() const { return m_layersWithTiledBackingCount; } + + CompositingReasons reasonsForCompositing(const RenderLayer*) const; + + void setLayerFlushThrottlingEnabled(bool); + void disableLayerFlushThrottlingTemporarilyForInteraction(); + + void didPaintBacking(RenderLayerBacking*); + private: class OverlapMap; // GraphicsLayerClient implementation virtual void notifyAnimationStarted(const GraphicsLayer*, double) OVERRIDE { } - virtual void notifyFlushRequired(const GraphicsLayer*) OVERRIDE { scheduleLayerFlush(); } + virtual void notifyFlushRequired(const GraphicsLayer*) OVERRIDE; virtual void paintContents(const GraphicsLayer*, GraphicsContext&, GraphicsLayerPaintingPhase, const IntRect&) OVERRIDE; virtual bool isTrackingRepaints() const OVERRIDE; // GraphicsLayerUpdaterClient implementation virtual void flushLayers(GraphicsLayerUpdater*) OVERRIDE; + virtual void customPositionForVisibleRectComputation(const GraphicsLayer*, FloatPoint&) const OVERRIDE; // Whether the given RL needs a compositing layer. - bool needsToBeComposited(const RenderLayer*) const; + bool needsToBeComposited(const RenderLayer*, RenderLayer::ViewportConstrainedNotCompositedReason* = 0) const; // Whether the layer has an intrinsic need for compositing layer. - bool requiresCompositingLayer(const RenderLayer*) const; + bool requiresCompositingLayer(const RenderLayer*, RenderLayer::ViewportConstrainedNotCompositedReason* = 0) const; // Whether the layer could ever be composited. bool canBeComposited(const RenderLayer*) const; @@ -298,7 +352,7 @@ private: Page* page() const; TiledBacking* pageTiledBacking() const; - + GraphicsLayerFactory* graphicsLayerFactory() const; ScrollingCoordinator* scrollingCoordinator() const; @@ -312,7 +366,7 @@ private: bool requiresCompositingForFilters(RenderObject*) const; bool requiresCompositingForBlending(RenderObject* renderer) const; bool requiresCompositingForScrollableFrame() const; - bool requiresCompositingForPosition(RenderObject*, const RenderLayer*) const; + bool requiresCompositingForPosition(RenderObject*, const RenderLayer*, RenderLayer::ViewportConstrainedNotCompositedReason* = 0) const; bool requiresCompositingForOverflowScrolling(const RenderLayer*) const; bool requiresCompositingForIndirectReason(RenderObject*, bool hasCompositedDescendants, bool has3DTransformedDescendants, RenderLayer::IndirectCompositingReason&) const; @@ -320,8 +374,8 @@ private: void registerOrUpdateViewportConstrainedLayer(RenderLayer*); void unregisterViewportConstrainedLayer(RenderLayer*); - const FixedPositionViewportConstraints computeFixedViewportConstraints(RenderLayer*); - const StickyPositionViewportConstraints computeStickyViewportConstraints(RenderLayer*); + FixedPositionViewportConstraints computeFixedViewportConstraints(RenderLayer*) const; + StickyPositionViewportConstraints computeStickyViewportConstraints(RenderLayer*) const; bool requiresScrollLayer(RootLayerAttachment) const; bool requiresHorizontalScrollbarLayer() const; @@ -332,8 +386,18 @@ private: bool requiresContentShadowLayer() const; #endif + bool hasCoordinatedScrolling() const; + bool shouldCompositeOverflowControls() const; + + void scheduleLayerFlushNow(); + bool isThrottlingLayerFlushes() const; + void startLayerFlushTimerIfNeeded(); + void layerFlushTimerFired(Timer<RenderLayerCompositor>*); + + void paintRelatedMilestonesTimerFired(Timer<RenderLayerCompositor>*); + #if !LOG_DISABLED - const char* reasonForCompositing(const RenderLayer*); + const char* logReasonsForCompositing(const RenderLayer*); void logLayerInfo(const RenderLayer*, int depth); #endif @@ -349,7 +413,6 @@ private: bool m_showDebugBorders; bool m_showRepaintCounter; bool m_acceleratedDrawingEnabled; - bool m_compositingConsultsOverlap; // When true, we have to wait until layout has happened before we can decide whether to enter compositing mode, // because only then do we know the final size of plugins and iframes. @@ -360,8 +423,11 @@ private: bool m_flushingLayers; bool m_shouldFlushOnReattach; bool m_forceCompositingMode; + bool m_inPostLayoutUpdate; // true when it's OK to trust layout information (e.g. layer sizes and positions) bool m_isTrackingRepaints; // Used for testing. + + unsigned m_layersWithTiledBackingCount; RootLayerAttachment m_rootLayerAttachment; @@ -382,10 +448,21 @@ private: #if ENABLE(RUBBER_BANDING) OwnPtr<GraphicsLayer> m_layerForOverhangAreas; OwnPtr<GraphicsLayer> m_contentShadowLayer; + OwnPtr<GraphicsLayer> m_layerForTopOverhangArea; + OwnPtr<GraphicsLayer> m_layerForBottomOverhangArea; + OwnPtr<GraphicsLayer> m_layerForHeader; + OwnPtr<GraphicsLayer> m_layerForFooter; #endif OwnPtr<GraphicsLayerUpdater> m_layerUpdater; // Updates tiled layer visible area periodically while animations are running. + Timer<RenderLayerCompositor> m_layerFlushTimer; + bool m_layerFlushThrottlingEnabled; + bool m_layerFlushThrottlingTemporarilyDisabledForInteraction; + bool m_hasPendingLayerFlush; + + Timer<RenderLayerCompositor> m_paintRelatedMilestonesTimer; + #if !LOG_DISABLED int m_rootLayerUpdateCount; int m_obligateCompositedLayerCount; // count of layer that have to be composited. @@ -398,4 +475,6 @@ private: } // namespace WebCore +#endif // USE(ACCELERATED_COMPOSITING) + #endif // RenderLayerCompositor_h diff --git a/Source/WebCore/rendering/RenderLayerFilterInfo.h b/Source/WebCore/rendering/RenderLayerFilterInfo.h index e514a5a21..8a4d1dc54 100644 --- a/Source/WebCore/rendering/RenderLayerFilterInfo.h +++ b/Source/WebCore/rendering/RenderLayerFilterInfo.h @@ -32,9 +32,7 @@ #if ENABLE(CSS_FILTERS) -#if ENABLE(SVG) -#include "CachedSVGDocument.h" -#endif +#include "CachedResourceHandle.h" #include "FilterOperation.h" #include "LayoutRect.h" #include <wtf/HashMap.h> @@ -45,6 +43,11 @@ #include "CustomFilterProgramClient.h" #endif +#if ENABLE(SVG) +#include "CachedSVGDocumentClient.h" +#include "Element.h" +#endif + namespace WebCore { class FilterEffectRenderer; diff --git a/Source/WebCore/rendering/RenderLayerModelObject.cpp b/Source/WebCore/rendering/RenderLayerModelObject.cpp index aeb32b7a2..bf32d15fd 100644 --- a/Source/WebCore/rendering/RenderLayerModelObject.cpp +++ b/Source/WebCore/rendering/RenderLayerModelObject.cpp @@ -37,7 +37,7 @@ bool RenderLayerModelObject::s_hadLayer = false; bool RenderLayerModelObject::s_hadTransform = false; bool RenderLayerModelObject::s_layerWasSelfPainting = false; -RenderLayerModelObject::RenderLayerModelObject(Node* node) +RenderLayerModelObject::RenderLayerModelObject(ContainerNode* node) : RenderObject(node) , m_layer(0) { @@ -75,6 +75,16 @@ bool RenderLayerModelObject::hasSelfPaintingLayer() const void RenderLayerModelObject::willBeDestroyed() { + if (isPositioned()) { + // Don't use this->view() because the document's renderView has been set to 0 during destruction. + if (Frame* frame = this->frame()) { + if (FrameView* frameView = frame->view()) { + if (style()->hasViewportConstrainedPosition()) + frameView->removeViewportConstrainedObject(this); + } + } + } + // RenderObject::willBeDestroyed calls back to destroyLayer() for layer destruction RenderObject::willBeDestroyed(); } diff --git a/Source/WebCore/rendering/RenderLayerModelObject.h b/Source/WebCore/rendering/RenderLayerModelObject.h index c6241d377..2a7439601 100644 --- a/Source/WebCore/rendering/RenderLayerModelObject.h +++ b/Source/WebCore/rendering/RenderLayerModelObject.h @@ -31,7 +31,7 @@ class RenderLayer; class RenderLayerModelObject : public RenderObject { public: - RenderLayerModelObject(Node*); + explicit RenderLayerModelObject(ContainerNode*); virtual ~RenderLayerModelObject(); // Called by RenderObject::willBeDestroyed() and is the only way layers should ever be destroyed @@ -46,6 +46,13 @@ public: virtual bool requiresLayer() const = 0; + // Returns true if the background is painted opaque in the given rect. + // The query rect is given in local coordinate system. + virtual bool backgroundIsKnownToBeOpaqueInRect(const LayoutRect&) const { return false; } + + // This is null for anonymous renderers. + ContainerNode* node() const { return toContainerNode(RenderObject::node()); } + protected: void ensureLayer(); @@ -65,13 +72,13 @@ private: inline RenderLayerModelObject* toRenderLayerModelObject(RenderObject* object) { - ASSERT(!object || object->isLayerModelObject()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isLayerModelObject()); return static_cast<RenderLayerModelObject*>(object); } inline const RenderLayerModelObject* toRenderLayerModelObject(const RenderObject* object) { - ASSERT(!object || object->isLayerModelObject()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isLayerModelObject()); return static_cast<const RenderLayerModelObject*>(object); } diff --git a/Source/WebCore/rendering/RenderLineBoxList.cpp b/Source/WebCore/rendering/RenderLineBoxList.cpp index ea50d52e8..7b562b426 100644 --- a/Source/WebCore/rendering/RenderLineBoxList.cpp +++ b/Source/WebCore/rendering/RenderLineBoxList.cpp @@ -267,7 +267,7 @@ void RenderLineBoxList::paint(RenderBoxModelObject* renderer, PaintInfo& paintIn ListHashSet<RenderInline*>::iterator end = info.outlineObjects->end(); for (ListHashSet<RenderInline*>::iterator it = info.outlineObjects->begin(); it != end; ++it) { RenderInline* flow = *it; - flow->paintOutline(info.context, paintOffset); + flow->paintOutline(info, paintOffset); } info.outlineObjects->clear(); } @@ -386,7 +386,11 @@ void RenderLineBoxList::dirtyLinesFromChangedChild(RenderObject* container, Rend if (adjacentBox) adjacentBox->markDirty(); adjacentBox = box->nextRootBox(); - if (adjacentBox && (adjacentBox->lineBreakObj() == child || child->isBR() || (curr && curr->isBR()))) + // If |child| has been inserted before the first element in the linebox, but after collapsed leading + // space, the search for |child|'s linebox will go past the leading space to the previous linebox and select that + // one as |box|. If we hit that situation here, dirty the |box| actually containing the child too. + bool insertedAfterLeadingSpace = box->lineBreakObj() == child->previousSibling(); + if (adjacentBox && (adjacentBox->lineBreakObj() == child || child->isBR() || (curr && curr->isBR()) || insertedAfterLeadingSpace)) adjacentBox->markDirty(); } } diff --git a/Source/WebCore/rendering/RenderLineBoxList.h b/Source/WebCore/rendering/RenderLineBoxList.h index e5b085edf..0f9c65797 100644 --- a/Source/WebCore/rendering/RenderLineBoxList.h +++ b/Source/WebCore/rendering/RenderLineBoxList.h @@ -34,6 +34,8 @@ namespace WebCore { +class InlineFlowBox; + class RenderLineBoxList { public: RenderLineBoxList() diff --git a/Source/WebCore/rendering/RenderListBox.cpp b/Source/WebCore/rendering/RenderListBox.cpp index b92d9f08d..0502c6d9f 100644 --- a/Source/WebCore/rendering/RenderListBox.cpp +++ b/Source/WebCore/rendering/RenderListBox.cpp @@ -59,6 +59,7 @@ #include "SpatialNavigation.h" #include "StyleResolver.h" #include <math.h> +#include <wtf/StackStats.h> using namespace std; @@ -123,14 +124,14 @@ void RenderListBox::updateFromElement() HTMLElement* element = listItems[i]; String text; Font itemFont = style()->font(); - if (element->hasTagName(optionTag)) + if (isHTMLOptionElement(element)) text = toHTMLOptionElement(element)->textIndentedToRespectGroupLabel(); - else if (element->hasTagName(optgroupTag)) { - text = static_cast<const HTMLOptGroupElement*>(element)->groupLabelText(); + else if (isHTMLOptGroupElement(element)) { + text = toHTMLOptGroupElement(element)->groupLabelText(); FontDescription d = itemFont.fontDescription(); d.setWeight(d.bolderWeight()); itemFont = Font(d, itemFont.letterSpacing(), itemFont.wordSpacing()); - itemFont.update(document()->styleResolver()->fontSelector()); + itemFont.update(document()->ensureStyleResolver()->fontSelector()); } if (!text.isEmpty()) { @@ -166,8 +167,8 @@ void RenderListBox::selectionChanged() scrollToRevealSelection(); } - if (AXObjectCache::accessibilityEnabled()) - document()->axObjectCache()->selectedChildrenChanged(this); + if (AXObjectCache* cache = document()->existingAXObjectCache()) + cache->selectedChildrenChanged(this); } void RenderListBox::layout() @@ -203,6 +204,15 @@ void RenderListBox::scrollToRevealSelection() scrollToRevealElementAtListIndex(firstIndex); } +void RenderListBox::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const +{ + maxLogicalWidth = m_optionsWidth + 2 * optionsSpacingHorizontal; + if (m_vBar) + maxLogicalWidth += m_vBar->width(); + if (!style()->width().isPercent()) + minLogicalWidth = maxLogicalWidth; +} + void RenderListBox::computePreferredLogicalWidths() { ASSERT(!m_optionsChanged); @@ -212,19 +222,13 @@ void RenderListBox::computePreferredLogicalWidths() if (style()->width().isFixed() && style()->width().value() > 0) m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = adjustContentBoxLogicalWidthForBoxSizing(style()->width().value()); - else { - m_maxPreferredLogicalWidth = m_optionsWidth + 2 * optionsSpacingHorizontal; - if (m_vBar) - m_maxPreferredLogicalWidth += m_vBar->width(); - } + else + computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth); if (style()->minWidth().isFixed() && style()->minWidth().value() > 0) { m_maxPreferredLogicalWidth = max(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style()->minWidth().value())); m_minPreferredLogicalWidth = max(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style()->minWidth().value())); - } else if (style()->width().isPercent() || (style()->width().isAuto() && style()->height().isPercent())) - m_minPreferredLogicalWidth = 0; - else - m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth; + } if (style()->maxWidth().isFixed()) { m_maxPreferredLogicalWidth = min(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style()->maxWidth().value())); @@ -324,10 +328,10 @@ void RenderListBox::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOf } } -void RenderListBox::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint& additionalOffset) +void RenderListBox::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint& additionalOffset, const RenderLayerModelObject* paintContainer) { if (!isSpatialNavigationEnabled(frame())) - return RenderBlock::addFocusRingRects(rects, additionalOffset); + return RenderBlock::addFocusRingRects(rects, additionalOffset, paintContainer); HTMLSelectElement* select = selectElement(); @@ -343,7 +347,8 @@ void RenderListBox::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint& const Vector<HTMLElement*>& listItems = select->listItems(); for (int i = 0; i < size; ++i) { HTMLElement* element = listItems[i]; - if (element->hasTagName(optionTag) && !toHTMLOptionElement(element)->disabled()) { + if (isHTMLOptionElement(element) && !element->isDisabledFormControl()) { + select->setActiveSelectionEndIndex(i); rects.append(pixelSnappedIntRect(itemBoundingBoxRect(additionalOffset, i))); return; } @@ -399,19 +404,19 @@ void RenderListBox::paintItemForeground(PaintInfo& paintInfo, const LayoutPoint& return; String itemText; - bool isOptionElement = element->hasTagName(optionTag); + bool isOptionElement = isHTMLOptionElement(element); if (isOptionElement) itemText = toHTMLOptionElement(element)->textIndentedToRespectGroupLabel(); - else if (element->hasTagName(optgroupTag)) - itemText = static_cast<const HTMLOptGroupElement*>(element)->groupLabelText(); + else if (isHTMLOptGroupElement(element)) + itemText = toHTMLOptGroupElement(element)->groupLabelText(); applyTextTransform(style(), itemText, ' '); Color textColor = element->renderStyle() ? element->renderStyle()->visitedDependentColor(CSSPropertyColor) : style()->visitedDependentColor(CSSPropertyColor); if (isOptionElement && toHTMLOptionElement(element)->selected()) { - if (frame()->selection()->isFocusedAndActive() && document()->focusedNode() == node()) + if (frame()->selection()->isFocusedAndActive() && document()->focusedElement() == node()) textColor = theme()->activeListBoxSelectionForegroundColor(); // Honor the foreground color for disabled items - else if (!element->disabled() && !select->disabled()) + else if (!element->isDisabledFormControl() && !select->isDisabledFormControl()) textColor = theme()->inactiveListBoxSelectionForegroundColor(); } @@ -423,11 +428,11 @@ void RenderListBox::paintItemForeground(PaintInfo& paintInfo, const LayoutPoint& LayoutRect r = itemBoundingBoxRect(paintOffset, listIndex); r.move(itemOffsetForAlignment(textRun, itemStyle, itemFont, r)); - if (element->hasTagName(optgroupTag)) { + if (isHTMLOptGroupElement(element)) { FontDescription d = itemFont.fontDescription(); d.setWeight(d.bolderWeight()); itemFont = Font(d, itemFont.letterSpacing(), itemFont.wordSpacing()); - itemFont.update(document()->styleResolver()->fontSelector()); + itemFont.update(document()->ensureStyleResolver()->fontSelector()); } // Draw the item text @@ -440,8 +445,8 @@ void RenderListBox::paintItemBackground(PaintInfo& paintInfo, const LayoutPoint& HTMLElement* element = listItems[listIndex]; Color backColor; - if (element->hasTagName(optionTag) && toHTMLOptionElement(element)->selected()) { - if (frame()->selection()->isFocusedAndActive() && document()->focusedNode() == node()) + if (isHTMLOptionElement(element) && toHTMLOptionElement(element)->selected()) { + if (frame()->selection()->isFocusedAndActive() && document()->focusedElement() == node()) backColor = theme()->activeListBoxSelectionBackgroundColor(); else backColor = theme()->inactiveListBoxSelectionBackgroundColor(); @@ -499,15 +504,15 @@ void RenderListBox::panScroll(const IntPoint& panStartMousePosition) // FIXME: This doesn't work correctly with transforms. FloatPoint absOffset = localToAbsolute(); - 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 + IntPoint lastKnownMousePosition = frame()->eventHandler()->lastKnownMousePosition(); + // We need to check if the last known mouse position is out of the window. When the mouse is out of the window, the position is incoherent static IntPoint previousMousePosition; - if (currentMousePosition.y() < 0) - currentMousePosition = previousMousePosition; + if (lastKnownMousePosition.y() < 0) + lastKnownMousePosition = previousMousePosition; else - previousMousePosition = currentMousePosition; + previousMousePosition = lastKnownMousePosition; - int yDelta = currentMousePosition.y() - panStartMousePosition.y(); + int yDelta = lastKnownMousePosition.y() - panStartMousePosition.y(); // If the point is too far from the center we limit the speed yDelta = max<int>(min<int>(yDelta, maxSpeed), -maxSpeed); @@ -554,11 +559,14 @@ int RenderListBox::scrollToward(const IntPoint& destination) return listIndexAtOffset(positionOffset); } -void RenderListBox::autoscroll() +void RenderListBox::autoscroll(const IntPoint&) { - IntPoint pos = frame()->view()->windowToContents(frame()->eventHandler()->currentMousePosition()); + IntPoint pos = frame()->view()->windowToContents(frame()->eventHandler()->lastKnownMousePosition()); int endIndex = scrollToward(pos); + if (selectElement()->isDisabledFormControl()) + return; + if (endIndex >= 0) { HTMLSelectElement* select = selectElement(); m_inAutoscroll = true; @@ -574,6 +582,9 @@ void RenderListBox::autoscroll() void RenderListBox::stopAutoscroll() { + if (selectElement()->isDisabledFormControl()) + return; + selectElement()->listBoxOnChange(); } @@ -803,12 +814,20 @@ int RenderListBox::visibleWidth() const return width(); } -IntPoint RenderListBox::currentMousePosition() const +IntPoint RenderListBox::lastKnownMousePosition() const { RenderView* view = this->view(); if (!view) return IntPoint(); - return view->frameView()->currentMousePosition(); + return view->frameView()->lastKnownMousePosition(); +} + +bool RenderListBox::isHandlingWheelEvent() const +{ + RenderView* view = this->view(); + if (!view) + return false; + return view->frameView()->isHandlingWheelEvent(); } bool RenderListBox::shouldSuspendScrollAnimations() const @@ -827,6 +846,14 @@ bool RenderListBox::scrollbarsCanBeActive() const return view->frameView()->scrollbarsCanBeActive(); } +bool RenderListBox::scrollbarAnimationsAreSuppressed() const +{ + RenderView* view = this->view(); + if (!view) + return false; + return view->frameView()->scrollbarAnimationsAreSuppressed(); +} + ScrollableArea* RenderListBox::enclosingScrollableArea() const { // FIXME: Return a RenderLayer that's scrollable. @@ -846,7 +873,7 @@ PassRefPtr<Scrollbar> RenderListBox::createScrollbar() widget = RenderScrollbar::createCustomScrollbar(this, VerticalScrollbar, this->node()); else { widget = Scrollbar::createNativeScrollbar(this, VerticalScrollbar, theme()->scrollbarControlSizeForPart(ListboxPart)); - didAddVerticalScrollbar(widget.get()); + didAddScrollbar(widget.get(), VerticalScrollbar); } document()->view()->addChild(widget.get()); return widget.release(); @@ -858,7 +885,7 @@ void RenderListBox::destroyScrollbar() return; if (!m_vBar->isCustomScrollbar()) - ScrollableArea::willRemoveVerticalScrollbar(m_vBar.get()); + ScrollableArea::willRemoveScrollbar(m_vBar.get(), VerticalScrollbar); m_vBar->removeFromParent(); m_vBar->disconnectFromScrollableArea(); m_vBar = 0; diff --git a/Source/WebCore/rendering/RenderListBox.h b/Source/WebCore/rendering/RenderListBox.h index c662af27f..f4f584942 100644 --- a/Source/WebCore/rendering/RenderListBox.h +++ b/Source/WebCore/rendering/RenderListBox.h @@ -40,7 +40,7 @@ class HTMLSelectElement; class RenderListBox : public RenderBlock, private ScrollableArea { public: - RenderListBox(Element*); + explicit RenderListBox(Element*); virtual ~RenderListBox(); void selectionChanged(); @@ -75,16 +75,17 @@ private: virtual bool scroll(ScrollDirection, ScrollGranularity, float multiplier = 1, Node** stopNode = 0); virtual bool logicalScroll(ScrollLogicalDirection, ScrollGranularity, float multiplier = 1, Node** stopNode = 0); - virtual void computePreferredLogicalWidths(); + virtual void computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const OVERRIDE; + virtual void computePreferredLogicalWidths() OVERRIDE; virtual int baselinePosition(FontBaseline, bool firstLine, LineDirectionMode, LinePositionMode = PositionOnContainingLine) const; virtual void computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues&) const OVERRIDE; virtual void layout(); - virtual void addFocusRingRects(Vector<IntRect>&, const LayoutPoint&); + virtual void addFocusRingRects(Vector<IntRect>&, const LayoutPoint& additionalOffset, const RenderLayerModelObject* paintContainer = 0) OVERRIDE; virtual bool canBeProgramaticallyScrolled() const { return true; } - virtual void autoscroll(); + virtual void autoscroll(const IntPoint&); virtual void stopAutoscroll(); virtual bool shouldPanScroll() const { return true; } @@ -117,9 +118,11 @@ private: virtual IntSize contentsSize() const OVERRIDE; virtual int visibleHeight() const OVERRIDE; virtual int visibleWidth() const OVERRIDE; - virtual IntPoint currentMousePosition() const OVERRIDE; + virtual IntPoint lastKnownMousePosition() const OVERRIDE; + virtual bool isHandlingWheelEvent() const OVERRIDE; virtual bool shouldSuspendScrollAnimations() const OVERRIDE; virtual bool scrollbarsCanBeActive() const OVERRIDE; + virtual bool scrollbarAnimationsAreSuppressed() const OVERRIDE; virtual ScrollableArea* enclosingScrollableArea() const OVERRIDE; virtual IntRect scrollableAreaBoundingBox() const OVERRIDE; @@ -152,7 +155,7 @@ private: inline RenderListBox* toRenderListBox(RenderObject* object) { - ASSERT(!object || object->isListBox()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isListBox()); return static_cast<RenderListBox*>(object); } diff --git a/Source/WebCore/rendering/RenderListItem.cpp b/Source/WebCore/rendering/RenderListItem.cpp index 84a579460..7d8f4c842 100644 --- a/Source/WebCore/rendering/RenderListItem.cpp +++ b/Source/WebCore/rendering/RenderListItem.cpp @@ -24,12 +24,13 @@ #include "config.h" #include "RenderListItem.h" -#include "CachedImage.h" #include "HTMLNames.h" #include "HTMLOListElement.h" +#include "NodeTraversal.h" #include "RenderListMarker.h" #include "RenderView.h" #include "StyleInheritedData.h" +#include <wtf/StackStats.h> #include <wtf/StdLibExtras.h> #include <wtf/text/StringBuilder.h> @@ -39,8 +40,8 @@ namespace WebCore { using namespace HTMLNames; -RenderListItem::RenderListItem(Node* node) - : RenderBlock(node) +RenderListItem::RenderListItem(Element* element) + : RenderBlock(element) , m_marker(0) , m_hasExplicitValue(false) , m_isValueUpToDate(false) @@ -60,7 +61,7 @@ void RenderListItem::styleDidChange(StyleDifference diff, const RenderStyle* old // up (e.g., in some deeply nested line box). See CSS3 spec. newStyle->inheritFrom(style()); if (!m_marker) - m_marker = new (renderArena()) RenderListMarker(this); + m_marker = RenderListMarker::createAnonymous(this); m_marker->setStyle(newStyle.release()); } else if (m_marker) { m_marker->destroy(); @@ -91,23 +92,22 @@ void RenderListItem::willBeRemovedFromTree() updateListMarkerNumbers(); } -static bool isList(Node* node) +static bool isList(const Node* node) { return (node->hasTagName(ulTag) || node->hasTagName(olTag)); } +// Returns the enclosing list with respect to the DOM order. static Node* enclosingList(const RenderListItem* listItem) { + Node* listItemNode = listItem->node(); Node* firstNode = 0; - - for (const RenderObject* renderer = listItem->parent(); renderer; renderer = renderer->parent()) { - Node* node = renderer->node(); - if (node) { - if (isList(node)) - return node; - if (!firstNode) - firstNode = node; - } + // We use parentNode because the enclosing list could be a ShadowRoot that's not Element. + for (Node* parent = listItemNode->parentNode(); parent; parent = parent->parentNode()) { + if (isList(parent)) + return parent; + if (!firstNode) + firstNode = parent; } // If there's no actual <ul> or <ol> list element, then the first found @@ -116,53 +116,80 @@ static Node* enclosingList(const RenderListItem* listItem) return firstNode; } -RenderListItem* RenderListItem::nextListItem(RenderObject* list, const RenderListItem* item) +// Returns the next list item with respect to the DOM order. +static RenderListItem* nextListItem(const Node* listNode, const RenderListItem* item = 0) { - if (!list) + if (!listNode) return 0; - RenderObject* renderer = item ? item->nextInPreOrder(list) : list->nextInPreOrder(list); - while (renderer) { - if (renderer->node() && isList(renderer->node())) { + const Node* current = item ? item->node() : listNode; + current = ElementTraversal::nextIncludingPseudo(current, listNode); + + while (current) { + if (isList(current)) { // We've found a nested, independent list: nothing to do here. - renderer = renderer->nextInPreOrderAfterChildren(list); + current = ElementTraversal::nextIncludingPseudoSkippingChildren(current, listNode); continue; } - if (renderer->isListItem()) + RenderObject* renderer = current->renderer(); + if (renderer && renderer->isListItem()) return toRenderListItem(renderer); - renderer = renderer->nextInPreOrder(list); + // FIXME: Can this be optimized to skip the children of the elements without a renderer? + current = ElementTraversal::nextIncludingPseudo(current, listNode); } + return 0; } -static RenderListItem* previousListItem(RenderObject* list, const RenderListItem* item) +// Returns the previous list item with respect to the DOM order. +static RenderListItem* previousListItem(const Node* listNode, const RenderListItem* item) { - for (RenderObject* renderer = item->previousInPreOrder(); renderer && renderer != list; renderer = renderer->previousInPreOrder()) { - if (!renderer->isListItem()) + Node* current = item->node(); + for (current = ElementTraversal::previousIncludingPseudo(current, listNode); current; current = ElementTraversal::previousIncludingPseudo(current, listNode)) { + RenderObject* renderer = current->renderer(); + if (!renderer || (renderer && !renderer->isListItem())) continue; Node* otherList = enclosingList(toRenderListItem(renderer)); // This item is part of our current list, so it's what we're looking for. - if (list->node() == otherList) + if (listNode == otherList) return toRenderListItem(renderer); // We found ourself inside another list; lets skip the rest of it. - // Use nextInPreOrder() here because the other list itself may actually + // Use nextIncludingPseudo() here because the other list itself may actually // be a list item itself. We need to examine it, so we do this to counteract - // the previousInPreOrder() that will be done by the loop. + // the previousIncludingPseudo() that will be done by the loop. if (otherList) - renderer = otherList->renderer()->nextInPreOrder(); + current = ElementTraversal::nextIncludingPseudo(otherList); } return 0; } +void RenderListItem::updateItemValuesForOrderedList(const HTMLOListElement* listNode) +{ + ASSERT(listNode); + + for (RenderListItem* listItem = nextListItem(listNode); listItem; listItem = nextListItem(listNode, listItem)) + listItem->updateValue(); +} + +unsigned RenderListItem::itemCountForOrderedList(const HTMLOListElement* listNode) +{ + ASSERT(listNode); + + unsigned itemCount = 0; + for (RenderListItem* listItem = nextListItem(listNode); listItem; listItem = nextListItem(listNode, listItem)) + itemCount++; + + return itemCount; +} + inline int RenderListItem::calcValue() const { if (m_hasExplicitValue) return m_explicitValue; Node* list = enclosingList(this); - RenderObject* listRenderer = list ? list->renderer() : 0; HTMLOListElement* oListElement = (list && list->hasTagName(olTag)) ? static_cast<HTMLOListElement*>(list) : 0; int valueStep = 1; if (oListElement && oListElement->isReversed()) @@ -170,7 +197,7 @@ inline int RenderListItem::calcValue() const // FIXME: This recurses to a possible depth of the length of the list. // That's not good -- we need to change this to an iterative algorithm. - if (RenderListItem* previousItem = previousListItem(listRenderer, this)) + if (RenderListItem* previousItem = previousListItem(list, this)) return previousItem->value() + valueStep; if (oListElement) @@ -265,25 +292,14 @@ void RenderListItem::updateMarkerLocation() if (!lineBoxParent) lineBoxParent = this; lineBoxParent->addChild(m_marker, firstNonMarkerChild(lineBoxParent)); - if (m_marker->preferredLogicalWidthsDirty()) - m_marker->computePreferredLogicalWidths(); + m_marker->updateMarginsAndContent(); // If markerPar is an anonymous block that has lost all its children, destroy it. - // Extraneous anonymous blocks can cause problems for RenderBlock::updateBeforeAfterContent. if (markerPar && markerPar->isAnonymousBlock() && !markerPar->firstChild() && !toRenderBlock(markerPar)->continuation()) markerPar->destroy(); } } } -void RenderListItem::computePreferredLogicalWidths() -{ - ASSERT(preferredLogicalWidthsDirty()); - - updateMarkerLocation(); - - RenderBlock::computePreferredLogicalWidths(); -} - void RenderListItem::layout() { StackStats::LayoutCheckPoint layoutCheckPoint; @@ -344,7 +360,6 @@ void RenderListItem::positionListMarker() hitSelfPaintingLayer = true; } } else { - markerLogicalLeft = m_marker->logicalLeft() + paddingStart() + borderStart() + m_marker->marginEnd(); LayoutUnit rightLineOffset = logicalRightOffsetForLine(blockOffset, logicalRightOffsetForLine(blockOffset, false), false); markerLogicalLeft = rightLineOffset - lineOffset + paddingStart() + borderStart() + m_marker->marginEnd(); m_marker->inlineBoxWrapper()->adjustLineDirectionPosition(markerLogicalLeft - markerOldLogicalLeft); @@ -407,8 +422,7 @@ const String& RenderListItem::markerText() const { if (m_marker) return m_marker->text(); - DEFINE_STATIC_LOCAL(String, staticNullString, ()); - return staticNullString; + return nullAtom.string(); } String RenderListItem::markerTextWithSuffix() const @@ -439,10 +453,7 @@ void RenderListItem::explicitValueChanged() if (m_marker) m_marker->setNeedsLayoutAndPrefWidthsRecalc(); Node* listNode = enclosingList(this); - RenderObject* listRenderer = 0; - if (listNode) - listRenderer = listNode->renderer(); - for (RenderListItem* item = this; item; item = nextListItem(listRenderer, item)) + for (RenderListItem* item = this; item; item = nextListItem(listNode, item)) item->updateValue(); } @@ -469,26 +480,26 @@ void RenderListItem::clearExplicitValue() explicitValueChanged(); } -static RenderListItem* previousOrNextItem(bool isListReversed, RenderObject* list, RenderListItem* item) +static RenderListItem* previousOrNextItem(bool isListReversed, Node* list, RenderListItem* item) { - return isListReversed ? previousListItem(list, item) : RenderListItem::nextListItem(list, item); + return isListReversed ? previousListItem(list, item) : nextListItem(list, item); } void RenderListItem::updateListMarkerNumbers() { Node* listNode = enclosingList(this); - ASSERT(listNode && listNode->renderer()); - if (!listNode || !listNode->renderer()) + // The list node can be the shadow root which has no renderer. + ASSERT(listNode); + if (!listNode) return; bool isListReversed = false; - RenderObject* list = listNode->renderer(); HTMLOListElement* oListElement = (listNode && listNode->hasTagName(olTag)) ? static_cast<HTMLOListElement*>(listNode) : 0; if (oListElement) { oListElement->itemCountChanged(); isListReversed = oListElement->isReversed(); } - for (RenderListItem* item = previousOrNextItem(isListReversed, list, this); item; item = previousOrNextItem(isListReversed, list, item)) { + for (RenderListItem* item = previousOrNextItem(isListReversed, listNode, this); item; item = previousOrNextItem(isListReversed, listNode, item)) { if (!item->m_isValueUpToDate) { // If an item has been marked for update before, we can safely // assume that all the following ones have too. diff --git a/Source/WebCore/rendering/RenderListItem.h b/Source/WebCore/rendering/RenderListItem.h index 65b1297f5..f319cfd70 100644 --- a/Source/WebCore/rendering/RenderListItem.h +++ b/Source/WebCore/rendering/RenderListItem.h @@ -27,11 +27,12 @@ namespace WebCore { +class HTMLOListElement; class RenderListMarker; class RenderListItem : public RenderBlock { public: - explicit RenderListItem(Node*); + explicit RenderListItem(Element*); int value() const { if (!m_isValueUpToDate) updateValueNow(); return m_value; } void updateValue(); @@ -49,7 +50,8 @@ public: void updateListMarkerNumbers(); - static RenderListItem* nextListItem(RenderObject* listRenderer, const RenderListItem* = 0); + static void updateItemValuesForOrderedList(const HTMLOListElement*); + static unsigned itemCountForOrderedList(const HTMLOListElement*); private: virtual const char* renderName() const { return "RenderListItem"; } @@ -65,7 +67,6 @@ private: virtual void paint(PaintInfo&, const LayoutPoint&); virtual void layout(); - virtual void computePreferredLogicalWidths(); void positionListMarker(); @@ -91,7 +92,7 @@ private: inline RenderListItem* toRenderListItem(RenderObject* object) { - ASSERT(!object || object->isListItem()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isListItem()); return static_cast<RenderListItem*>(object); } diff --git a/Source/WebCore/rendering/RenderListMarker.cpp b/Source/WebCore/rendering/RenderListMarker.cpp index 7ccb686ff..9a37e47f9 100644 --- a/Source/WebCore/rendering/RenderListMarker.cpp +++ b/Source/WebCore/rendering/RenderListMarker.cpp @@ -25,13 +25,13 @@ #include "config.h" #include "RenderListMarker.h" -#include "CachedImage.h" #include "Document.h" #include "Font.h" #include "GraphicsContext.h" #include "RenderLayer.h" #include "RenderListItem.h" #include "RenderView.h" +#include <wtf/StackStats.h> #include <wtf/text/StringBuilder.h> #include <wtf/unicode/CharacterNames.h> @@ -1054,7 +1054,7 @@ String listMarkerText(EListStyleType type, int value) } RenderListMarker::RenderListMarker(RenderListItem* item) - : RenderBox(item->document()) + : RenderBox(0) , m_listItem(item) { // init RenderObject attributes @@ -1068,6 +1068,14 @@ RenderListMarker::~RenderListMarker() m_image->removeClient(this); } +RenderListMarker* RenderListMarker::createAnonymous(RenderListItem* item) +{ + Document* document = item->document(); + RenderListMarker* renderer = new (document->renderArena()) RenderListMarker(item); + renderer->setDocumentForAnonymous(document); + return renderer; +} + void RenderListMarker::styleWillChange(StyleDifference diff, const RenderStyle* newStyle) { if (style() && (newStyle->listStylePosition() != style()->listStylePosition() || newStyle->listStyleType() != style()->listStyleType())) @@ -1317,6 +1325,7 @@ void RenderListMarker::layout() ASSERT(needsLayout()); if (isImage()) { + updateMarginsAndContent(); setWidth(m_image->imageSize(this, style()->effectiveZoom()).width()); setHeight(m_image->imageSize(this, style()->effectiveZoom()).height()); } else { @@ -1349,20 +1358,126 @@ void RenderListMarker::imageChanged(WrappedImagePtr o, const IntRect*) repaint(); } -void RenderListMarker::computePreferredLogicalWidths() +void RenderListMarker::updateMarginsAndContent() { - ASSERT(preferredLogicalWidthsDirty()); + updateContent(); + updateMargins(); +} - m_text = ""; +void RenderListMarker::updateContent() +{ + // FIXME: This if-statement is just a performance optimization, but it's messy to use the preferredLogicalWidths dirty bit for this. + // It's unclear if this is a premature optimization. + if (!preferredLogicalWidthsDirty()) + return; - const Font& font = style()->font(); - const FontMetrics& fontMetrics = font.fontMetrics(); + m_text = ""; if (isImage()) { // FIXME: This is a somewhat arbitrary width. Generated images for markers really won't become particularly useful // until we support the CSS3 marker pseudoclass to allow control over the width and height of the marker box. - int bulletWidth = fontMetrics.ascent() / 2; + int bulletWidth = style()->fontMetrics().ascent() / 2; m_image->setContainerSizeForRenderer(this, IntSize(bulletWidth, bulletWidth), style()->effectiveZoom()); + return; + } + + EListStyleType type = style()->listStyleType(); + switch (type) { + case NoneListStyle: + break; + case Circle: + case Disc: + case Square: + m_text = listMarkerText(type, 0); // value is ignored for these types + break; + case Asterisks: + case Footnotes: + case Afar: + case Amharic: + case AmharicAbegede: + case ArabicIndic: + case Armenian: + case BinaryListStyle: + case Bengali: + case Cambodian: + case CJKIdeographic: + case CjkEarthlyBranch: + case CjkHeavenlyStem: + case DecimalLeadingZero: + case DecimalListStyle: + case Devanagari: + case Ethiopic: + case EthiopicAbegede: + case EthiopicAbegedeAmEt: + case EthiopicAbegedeGez: + case EthiopicAbegedeTiEr: + case EthiopicAbegedeTiEt: + case EthiopicHalehameAaEr: + case EthiopicHalehameAaEt: + case EthiopicHalehameAmEt: + case EthiopicHalehameGez: + case EthiopicHalehameOmEt: + case EthiopicHalehameSidEt: + case EthiopicHalehameSoEt: + case EthiopicHalehameTiEr: + case EthiopicHalehameTiEt: + case EthiopicHalehameTig: + case Georgian: + case Gujarati: + case Gurmukhi: + case Hangul: + case HangulConsonant: + case Hebrew: + case Hiragana: + case HiraganaIroha: + case Kannada: + case Katakana: + case KatakanaIroha: + case Khmer: + case Lao: + case LowerAlpha: + case LowerArmenian: + case LowerGreek: + case LowerHexadecimal: + case LowerLatin: + case LowerNorwegian: + case LowerRoman: + case Malayalam: + case Mongolian: + case Myanmar: + case Octal: + case Oriya: + case Oromo: + case Persian: + case Sidama: + case Somali: + case Telugu: + case Thai: + case Tibetan: + case Tigre: + case TigrinyaEr: + case TigrinyaErAbegede: + case TigrinyaEt: + case TigrinyaEtAbegede: + case UpperAlpha: + case UpperArmenian: + case UpperGreek: + case UpperHexadecimal: + case UpperLatin: + case UpperNorwegian: + case UpperRoman: + case Urdu: + m_text = listMarkerText(type, m_listItem->value()); + break; + } +} + +void RenderListMarker::computePreferredLogicalWidths() +{ + ASSERT(preferredLogicalWidthsDirty()); + updateContent(); + + if (isImage()) { LayoutSize imageSize = m_image->imageSize(this, style()->effectiveZoom()); m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = style()->isHorizontalWritingMode() ? imageSize.width() : imageSize.height(); setPreferredLogicalWidthsDirty(false); @@ -1370,6 +1485,8 @@ void RenderListMarker::computePreferredLogicalWidths() return; } + const Font& font = style()->font(); + LayoutUnit logicalWidth = 0; EListStyleType type = style()->listStyleType(); switch (type) { @@ -1377,14 +1494,12 @@ void RenderListMarker::computePreferredLogicalWidths() break; case Asterisks: case Footnotes: - m_text = listMarkerText(type, m_listItem->value()); logicalWidth = font.width(m_text); // no suffix for these types break; case Circle: case Disc: case Square: - m_text = listMarkerText(type, 0); // value is ignored for these types - logicalWidth = (fontMetrics.ascent() * 2 / 3 + 1) / 2 + 2; + logicalWidth = (font.fontMetrics().ascent() * 2 / 3 + 1) / 2 + 2; break; case Afar: case Amharic: @@ -1461,7 +1576,6 @@ void RenderListMarker::computePreferredLogicalWidths() case UpperNorwegian: case UpperRoman: case Urdu: - m_text = listMarkerText(type, m_listItem->value()); if (m_text.isEmpty()) logicalWidth = 0; else { @@ -1477,7 +1591,7 @@ void RenderListMarker::computePreferredLogicalWidths() m_maxPreferredLogicalWidth = logicalWidth; setPreferredLogicalWidthsDirty(false); - + updateMargins(); } diff --git a/Source/WebCore/rendering/RenderListMarker.h b/Source/WebCore/rendering/RenderListMarker.h index 98480ea0f..ffac4fbb7 100644 --- a/Source/WebCore/rendering/RenderListMarker.h +++ b/Source/WebCore/rendering/RenderListMarker.h @@ -35,18 +35,22 @@ String listMarkerText(EListStyleType, int value); // The RenderListMarker always has to be a child of a RenderListItem. class RenderListMarker : public RenderBox { public: - RenderListMarker(RenderListItem*); - virtual ~RenderListMarker(); + static RenderListMarker* createAnonymous(RenderListItem*); - virtual void computePreferredLogicalWidths(); + virtual ~RenderListMarker(); const String& text() const { return m_text; } String suffix() const; bool isInside() const; + void updateMarginsAndContent(); + private: + RenderListMarker(RenderListItem*); + virtual const char* renderName() const { return "RenderListMarker"; } + virtual void computePreferredLogicalWidths() OVERRIDE; virtual bool isListMarker() const { return true; } @@ -69,6 +73,7 @@ private: virtual bool canBeSelectionLeaf() const { return true; } void updateMargins(); + void updateContent(); virtual void styleWillChange(StyleDifference, const RenderStyle* newStyle); virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); @@ -83,13 +88,13 @@ private: inline RenderListMarker* toRenderListMarker(RenderObject* object) { - ASSERT(!object || object->isListMarker()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isListMarker()); return static_cast<RenderListMarker*>(object); } inline const RenderListMarker* toRenderListMarker(const RenderObject* object) { - ASSERT(!object || object->isListMarker()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isListMarker()); return static_cast<const RenderListMarker*>(object); } diff --git a/Source/WebCore/rendering/RenderMarquee.cpp b/Source/WebCore/rendering/RenderMarquee.cpp index d54c90900..89b093f5a 100644 --- a/Source/WebCore/rendering/RenderMarquee.cpp +++ b/Source/WebCore/rendering/RenderMarquee.cpp @@ -50,6 +50,7 @@ #include "HTMLMarqueeElement.h" #include "HTMLNames.h" #include "RenderLayer.h" +#include "RenderView.h" using namespace std; @@ -254,7 +255,7 @@ void RenderMarquee::updateMarqueeStyle() void RenderMarquee::timerFired(Timer<RenderMarquee>*) { - if (m_layer->renderer()->needsLayout()) + if (m_layer->renderer()->view()->needsLayout()) return; if (m_reset) { diff --git a/Source/WebCore/rendering/RenderMedia.cpp b/Source/WebCore/rendering/RenderMedia.cpp index 385fb5068..ae488569c 100644 --- a/Source/WebCore/rendering/RenderMedia.cpp +++ b/Source/WebCore/rendering/RenderMedia.cpp @@ -29,7 +29,9 @@ #include "RenderMedia.h" #include "HTMLMediaElement.h" +#include "RenderFlowThread.h" #include "RenderView.h" +#include <wtf/StackStats.h> namespace WebCore { @@ -52,7 +54,7 @@ RenderMedia::~RenderMedia() HTMLMediaElement* RenderMedia::mediaElement() const { - return static_cast<HTMLMediaElement*>(node()); + return toHTMLMediaElement(node()); } void RenderMedia::layout() @@ -66,8 +68,17 @@ void RenderMedia::layout() if (!controlsRenderer) return; + bool controlsNeedLayout = controlsRenderer->needsLayout(); + // If the region chain has changed we also need to relayout the controls to update the region box info. + // FIXME: We can do better once we compute region box info for RenderReplaced, not only for RenderBlock. + const RenderFlowThread* flowThread = flowThreadContainingBlock(); + if (flowThread && !controlsNeedLayout) { + if (flowThread->pageLogicalSizeChanged()) + controlsNeedLayout = true; + } + LayoutSize newSize = contentBoxRect().size(); - if (newSize == oldSize && !controlsRenderer->needsLayout()) + if (newSize == oldSize && !controlsNeedLayout) return; // When calling layout() on a child node, a parent must either push a LayoutStateMaintainter, or diff --git a/Source/WebCore/rendering/RenderMedia.h b/Source/WebCore/rendering/RenderMedia.h index 4694cabc4..48b852edf 100644 --- a/Source/WebCore/rendering/RenderMedia.h +++ b/Source/WebCore/rendering/RenderMedia.h @@ -36,7 +36,7 @@ class HTMLMediaElement; class RenderMedia : public RenderImage { public: - RenderMedia(HTMLMediaElement*); + explicit RenderMedia(HTMLMediaElement*); RenderMedia(HTMLMediaElement*, const IntSize& intrinsicSize); virtual ~RenderMedia(); @@ -68,7 +68,7 @@ private: inline RenderMedia* toRenderMedia(RenderObject* object) { - ASSERT(!object || object->isMedia()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isMedia()); return static_cast<RenderMedia*>(object); } diff --git a/Source/WebCore/rendering/RenderMediaControlElements.cpp b/Source/WebCore/rendering/RenderMediaControlElements.cpp new file mode 100644 index 000000000..215b103ce --- /dev/null +++ b/Source/WebCore/rendering/RenderMediaControlElements.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. + * Copyright (C) 2012 Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(VIDEO) +#include "RenderMediaControlElements.h" + +#include "RenderTheme.h" +#include "RenderView.h" + +namespace WebCore { + +RenderMediaVolumeSliderContainer::RenderMediaVolumeSliderContainer(Element* element) + : RenderBlock(element) +{ +} + +void RenderMediaVolumeSliderContainer::layout() +{ + RenderBlock::layout(); + + if (style()->display() == NONE || !nextSibling() || !nextSibling()->isBox()) + return; + + RenderBox* buttonBox = toRenderBox(nextSibling()); + int absoluteOffsetTop = buttonBox->localToAbsolute(FloatPoint(0, -size().height())).y(); + + LayoutStateDisabler layoutStateDisabler(view()); + + // If the slider would be rendered outside the page, it should be moved below the controls. + if (UNLIKELY(absoluteOffsetTop < 0)) + setY(buttonBox->offsetTop() + theme()->volumeSliderOffsetFromMuteButton(buttonBox, pixelSnappedSize()).y()); +} + +// ---------------------------- + +RenderMediaControlTimelineContainer::RenderMediaControlTimelineContainer(Element* element) + : RenderFlexibleBox(element) +{ +} + +// We want the timeline slider to be at least 100 pixels wide. +// FIXME: Eliminate hard-coded widths altogether. +static const int minWidthToDisplayTimeDisplays = 45 + 100 + 45; + +void RenderMediaControlTimelineContainer::layout() +{ + RenderFlexibleBox::layout(); + + LayoutStateDisabler layoutStateDisabler(view()); + MediaControlTimelineContainerElement* container = static_cast<MediaControlTimelineContainerElement*>(node()); + container->setTimeDisplaysHidden(width().toInt() < minWidthToDisplayTimeDisplays); +} + +// ---------------------------- + +#if ENABLE(VIDEO_TRACK) + +RenderTextTrackContainerElement::RenderTextTrackContainerElement(Element* element) + : RenderBlock(element) +{ +} + +void RenderTextTrackContainerElement::layout() +{ + RenderBlock::layout(); + if (style()->display() == NONE) + return; + + ASSERT(mediaControlElementType(node()) == MediaTextTrackDisplayContainer); + + LayoutStateDisabler layoutStateDisabler(view()); + static_cast<MediaControlTextTrackContainerElement*>(node())->updateSizes(); +} + +#endif // ENABLE(VIDEO_TRACK) + +} // namespace WebCore + +#endif // ENABLE(VIDEO) + diff --git a/Source/WebCore/rendering/RenderMediaControlsChromium.h b/Source/WebCore/rendering/RenderMediaControlElements.h index 6015cb1fc..a3df364d2 100644 --- a/Source/WebCore/rendering/RenderMediaControlsChromium.h +++ b/Source/WebCore/rendering/RenderMediaControlElements.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 2009 Apple Inc. - * Copyright (C) 2009 Google Inc. + * Copyright (C) 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. + * Copyright (C) 2012 Google Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -25,28 +25,52 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef RenderMediaControlsChromium_h -#define RenderMediaControlsChromium_h +#ifndef RenderMediaControlElements_h +#define RenderMediaControlElements_h + +#if ENABLE(VIDEO) #include "MediaControlElements.h" +#include "RenderBlock.h" +#include "RenderFlexibleBox.h" namespace WebCore { -struct PaintInfo; +class RenderMediaVolumeSliderContainer : public RenderBlock { +public: + RenderMediaVolumeSliderContainer(Element*); + +private: + virtual void layout(); +}; + +// ---------------------------- + +class RenderMediaControlTimelineContainer : public RenderFlexibleBox { +public: + RenderMediaControlTimelineContainer(Element*); + +private: + virtual void layout(); +}; + +// ---------------------------- -class HTMLMediaElement; -class IntRect; -class RenderObject; +#if ENABLE(VIDEO_TRACK) -class RenderMediaControlsChromium { +class RenderTextTrackContainerElement : public RenderBlock { public: - static bool paintMediaControlsPart(MediaControlElementType, RenderObject*, const PaintInfo&, const IntRect&); - static void adjustMediaSliderThumbSize(RenderStyle*); - static String formatMediaControlsTime(float time); - static String formatMediaControlsCurrentTime(float currentTime, float duration); - static String formatMediaControlsRemainingTime(float currentTime, float duration); + RenderTextTrackContainerElement(Element*); + +private: + virtual void layout(); }; +#endif // ENABLE(VIDEO_TRACK) + } // namespace WebCore -#endif // RenderMediaControlsChromium_h +#endif // ENABLE(VIDEO) + +#endif // RenderMediaControlElements_h + diff --git a/Source/WebCore/rendering/RenderMediaControls.cpp b/Source/WebCore/rendering/RenderMediaControls.cpp index f17647b0b..1582fa5df 100644 --- a/Source/WebCore/rendering/RenderMediaControls.cpp +++ b/Source/WebCore/rendering/RenderMediaControls.cpp @@ -215,7 +215,7 @@ IntPoint RenderMediaControls::volumeSliderOffsetFromMuteButton(RenderBox* muteBu float zoomLevel = muteButtonBox->style()->effectiveZoom(); int y = yOffset * zoomLevel + muteButtonBox->pixelSnappedOffsetHeight() - size.height(); - FloatPoint absPoint = muteButtonBox->localToAbsolute(FloatPoint(muteButtonBox->pixelSnappedOffsetLeft(), y), IsFixed | UseTransforms | SnapOffsetForTransforms); + FloatPoint absPoint = muteButtonBox->localToAbsolute(FloatPoint(muteButtonBox->pixelSnappedOffsetLeft(), y), IsFixed | UseTransforms); if (absPoint.y() < 0) y = muteButtonBox->pixelSnappedHeight(); return IntPoint(xOffset * zoomLevel, y); diff --git a/Source/WebCore/rendering/RenderMediaControlsChromium.cpp b/Source/WebCore/rendering/RenderMediaControlsChromium.cpp deleted file mode 100644 index 0451fbdd2..000000000 --- a/Source/WebCore/rendering/RenderMediaControlsChromium.cpp +++ /dev/null @@ -1,463 +0,0 @@ -/* - * Copyright (C) 2009 Apple Inc. - * Copyright (C) 2009 Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "RenderMediaControlsChromium.h" - -#include "Gradient.h" -#include "GraphicsContext.h" -#include "HTMLMediaElement.h" -#include "HTMLNames.h" -#include "PaintInfo.h" -#include "TimeRanges.h" - -namespace WebCore { - -#if ENABLE(VIDEO) - -typedef WTF::HashMap<const char*, Image*> MediaControlImageMap; -static MediaControlImageMap* gMediaControlImageMap = 0; - -static Image* platformResource(const char* name) -{ - if (!gMediaControlImageMap) - gMediaControlImageMap = new MediaControlImageMap(); - if (Image* image = gMediaControlImageMap->get(name)) - return image; - if (Image* image = Image::loadPlatformResource(name).leakRef()) { - gMediaControlImageMap->set(name, image); - return image; - } - ASSERT_NOT_REACHED(); - return 0; -} - -static bool hasSource(const HTMLMediaElement* mediaElement) -{ - return mediaElement->networkState() != HTMLMediaElement::NETWORK_EMPTY - && mediaElement->networkState() != HTMLMediaElement::NETWORK_NO_SOURCE; -} - -static bool paintMediaButton(GraphicsContext* context, const IntRect& rect, Image* image) -{ - context->drawImage(image, ColorSpaceDeviceRGB, rect); - return true; -} - -static bool paintMediaMuteButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) -{ - HTMLMediaElement* mediaElement = toParentMediaElement(object); - if (!mediaElement) - return false; - - static Image* soundLevel3 = platformResource("mediaplayerSoundLevel3"); - static Image* soundLevel2 = platformResource("mediaplayerSoundLevel2"); - static Image* soundLevel1 = platformResource("mediaplayerSoundLevel1"); - static Image* soundLevel0 = platformResource("mediaplayerSoundLevel0"); - static Image* soundDisabled = platformResource("mediaplayerSoundDisabled"); - - if (!hasSource(mediaElement) || !mediaElement->hasAudio()) - return paintMediaButton(paintInfo.context, rect, soundDisabled); - - if (mediaElement->muted() || mediaElement->volume() <= 0) - return paintMediaButton(paintInfo.context, rect, soundLevel0); - - if (mediaElement->volume() <= 0.33) - return paintMediaButton(paintInfo.context, rect, soundLevel1); - - if (mediaElement->volume() <= 0.66) - return paintMediaButton(paintInfo.context, rect, soundLevel2); - - return paintMediaButton(paintInfo.context, rect, soundLevel3); -} - -static bool paintMediaPlayButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) -{ - HTMLMediaElement* mediaElement = toParentMediaElement(object); - if (!mediaElement) - return false; - - static Image* mediaPlay = platformResource("mediaplayerPlay"); - static Image* mediaPause = platformResource("mediaplayerPause"); - static Image* mediaPlayDisabled = platformResource("mediaplayerPlayDisabled"); - - if (!hasSource(mediaElement)) - return paintMediaButton(paintInfo.context, rect, mediaPlayDisabled); - - return paintMediaButton(paintInfo.context, rect, mediaElement->canPlay() ? mediaPlay : mediaPause); -} - -static bool paintMediaOverlayPlayButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) -{ - HTMLMediaElement* mediaElement = toParentMediaElement(object); - if (!mediaElement) - return false; - - if (!hasSource(mediaElement) || !mediaElement->canPlay()) - return false; - - static Image* mediaOverlayPlay = platformResource("mediaplayerOverlayPlay"); - return paintMediaButton(paintInfo.context, rect, mediaOverlayPlay); -} - -static Image* getMediaSliderThumb() -{ - static Image* mediaSliderThumb = platformResource("mediaplayerSliderThumb"); - return mediaSliderThumb; -} - -static void paintRoundedSliderBackground(const IntRect& rect, const RenderStyle* style, GraphicsContext* context) -{ - int borderRadius = rect.height() / 2; - IntSize radii(borderRadius, borderRadius); - Color sliderBackgroundColor = Color(11, 11, 11); - context->save(); - context->fillRoundedRect(rect, radii, radii, radii, radii, sliderBackgroundColor, ColorSpaceDeviceRGB); - context->restore(); -} - -static void paintSliderRangeHighlight(const IntRect& rect, const RenderStyle* style, GraphicsContext* context, int startPosition, int endPosition, Color startColor, Color endColor) -{ - // Calculate border radius; need to avoid being smaller than half the slider height - // because of https://bugs.webkit.org/show_bug.cgi?id=30143. - int borderRadius = rect.height() / 2; - IntSize radii(borderRadius, borderRadius); - - // Calculate highlight rectangle and edge dimensions. - int startOffset = startPosition; - int endOffset = rect.width() - endPosition; - int rangeWidth = endPosition - startPosition; - - if (rangeWidth <= 0) - return; - - // Make sure the range width is bigger than border radius at the edges to retain rounded corners. - if (startOffset < borderRadius && rangeWidth < borderRadius) - rangeWidth = borderRadius; - if (endOffset < borderRadius && rangeWidth < borderRadius) { - startPosition -= borderRadius - rangeWidth; - rangeWidth = borderRadius; - } - - // Set rectangle to highlight range. - IntRect highlightRect = rect; - highlightRect.move(startOffset, 0); - highlightRect.setWidth(rangeWidth); - - // Don't bother drawing an empty area. - if (highlightRect.isEmpty()) - return; - - // Calculate white-grey gradient. - IntPoint sliderTopLeft = highlightRect.location(); - IntPoint sliderBottomLeft = sliderTopLeft; - sliderBottomLeft.move(0, highlightRect.height()); - RefPtr<Gradient> gradient = Gradient::create(sliderTopLeft, sliderBottomLeft); - gradient->addColorStop(0.0, startColor); - gradient->addColorStop(1.0, endColor); - - // Fill highlight rectangle with gradient, potentially rounded if on left or right edge. - context->save(); - context->setFillGradient(gradient); - - if (startOffset < borderRadius && endOffset < borderRadius) - context->fillRoundedRect(highlightRect, radii, radii, radii, radii, startColor, ColorSpaceDeviceRGB); - else if (startOffset < borderRadius) - context->fillRoundedRect(highlightRect, radii, IntSize(0, 0), radii, IntSize(0, 0), startColor, ColorSpaceDeviceRGB); - else if (endOffset < borderRadius) - context->fillRoundedRect(highlightRect, IntSize(0, 0), radii, IntSize(0, 0), radii, startColor, ColorSpaceDeviceRGB); - else - context->fillRect(highlightRect); - - context->restore(); -} - -const int mediaSliderThumbWidth = 32; - -static bool paintMediaSlider(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) -{ - HTMLMediaElement* mediaElement = toParentMediaElement(object); - if (!mediaElement) - return false; - - RenderStyle* style = object->style(); - GraphicsContext* context = paintInfo.context; - - paintRoundedSliderBackground(rect, style, context); - - // Draw the buffered range. Since the element may have multiple buffered ranges and it'd be - // distracting/'busy' to show all of them, show only the buffered range containing the current play head. - RefPtr<TimeRanges> bufferedTimeRanges = mediaElement->buffered(); - float duration = mediaElement->duration(); - float currentTime = mediaElement->currentTime(); - if (isnan(duration) || isinf(duration) || !duration || isnan(currentTime)) - return true; - - for (unsigned i = 0; i < bufferedTimeRanges->length(); ++i) { - float start = bufferedTimeRanges->start(i, ASSERT_NO_EXCEPTION); - float end = bufferedTimeRanges->end(i, ASSERT_NO_EXCEPTION); - if (isnan(start) || isnan(end) || start > currentTime || end < currentTime) - continue; - int startPosition = int(start * rect.width() / duration); - int currentPosition = int(currentTime * rect.width() / duration); - int endPosition = int(end * rect.width() / duration); - - // Add half the thumb width proportionally adjusted to the current painting position. - int thumbCenter = mediaSliderThumbWidth / 2; - int addWidth = thumbCenter * (1.0 - 2.0 * currentPosition / rect.width()); - currentPosition += addWidth; - - // Draw white-ish highlight before current time. - Color startColor = Color(195, 195, 195); - Color endColor = Color(217, 217, 217); - if (currentPosition > startPosition) - paintSliderRangeHighlight(rect, style, context, startPosition, currentPosition, startColor, endColor); - - // Draw grey-ish highlight after current time. - startColor = Color(60, 60, 60); - endColor = Color(76, 76, 76); - - if (endPosition > currentPosition) - paintSliderRangeHighlight(rect, style, context, currentPosition, endPosition, startColor, endColor); - - return true; - } - - return true; -} - -static bool paintMediaSliderThumb(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) -{ - ASSERT(object->node()); - HTMLMediaElement* mediaElement = toParentMediaElement(object->node()->shadowHost()); - if (!mediaElement) - return false; - - if (!hasSource(mediaElement)) - return true; - - Image* mediaSliderThumb = getMediaSliderThumb(); - return paintMediaButton(paintInfo.context, rect, mediaSliderThumb); -} - -const int mediaVolumeSliderThumbWidth = 24; - -static bool paintMediaVolumeSlider(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) -{ - HTMLMediaElement* mediaElement = toParentMediaElement(object); - if (!mediaElement) - return false; - - GraphicsContext* context = paintInfo.context; - RenderStyle* style = object->style(); - - paintRoundedSliderBackground(rect, style, context); - - // Calculate volume position for white background rectangle. - float volume = mediaElement->volume(); - if (isnan(volume) || volume < 0) - return true; - if (volume > 1) - volume = 1; - if (!hasSource(mediaElement) || !mediaElement->hasAudio() || mediaElement->muted()) - volume = 0; - - // Calculate the position relative to the center of the thumb. - float fillWidth = 0; - if (volume > 0) { - float thumbCenter = mediaVolumeSliderThumbWidth / 2; - float zoomLevel = style->effectiveZoom(); - float positionWidth = volume * (rect.width() - (zoomLevel * thumbCenter)); - fillWidth = positionWidth + (zoomLevel * thumbCenter / 2); - } - - Color startColor = Color(195, 195, 195); - Color endColor = Color(217, 217, 217); - - paintSliderRangeHighlight(rect, style, context, 0.0, fillWidth, startColor, endColor); - - return true; -} - -static bool paintMediaVolumeSliderThumb(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) -{ - ASSERT(object->node()); - HTMLMediaElement* mediaElement = toParentMediaElement(object->node()->shadowHost()); - if (!mediaElement) - return false; - - if (!hasSource(mediaElement) || !mediaElement->hasAudio()) - return true; - - static Image* mediaVolumeSliderThumb = platformResource("mediaplayerVolumeSliderThumb"); - return paintMediaButton(paintInfo.context, rect, mediaVolumeSliderThumb); -} - -static bool paintMediaFullscreenButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) -{ - HTMLMediaElement* mediaElement = toParentMediaElement(object); - if (!mediaElement) - return false; - - static Image* mediaFullscreenButton = platformResource("mediaplayerFullscreen"); - return paintMediaButton(paintInfo.context, rect, mediaFullscreenButton); -} - -static bool paintMediaClosedCaptionsButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) -{ - HTMLMediaElement* mediaElement = toParentMediaElement(object); - if (!mediaElement) - return false; - - static Image* mediaClosedCaptionButton = platformResource("mediaplayerClosedCaption"); - static Image* mediaClosedCaptionButtonDisabled = platformResource("mediaplayerClosedCaptionDisabled"); - - if (mediaElement->webkitClosedCaptionsVisible()) - return paintMediaButton(paintInfo.context, rect, mediaClosedCaptionButton); - - return paintMediaButton(paintInfo.context, rect, mediaClosedCaptionButtonDisabled); -} - - -bool RenderMediaControlsChromium::paintMediaControlsPart(MediaControlElementType part, RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) -{ - switch (part) { - case MediaMuteButton: - case MediaUnMuteButton: - return paintMediaMuteButton(object, paintInfo, rect); - case MediaPauseButton: - case MediaPlayButton: - return paintMediaPlayButton(object, paintInfo, rect); - case MediaShowClosedCaptionsButton: - return paintMediaClosedCaptionsButton(object, paintInfo, rect); - case MediaSlider: - return paintMediaSlider(object, paintInfo, rect); - case MediaSliderThumb: - return paintMediaSliderThumb(object, paintInfo, rect); - case MediaVolumeSlider: - return paintMediaVolumeSlider(object, paintInfo, rect); - case MediaVolumeSliderThumb: - return paintMediaVolumeSliderThumb(object, paintInfo, rect); - case MediaEnterFullscreenButton: - case MediaExitFullscreenButton: - return paintMediaFullscreenButton(object, paintInfo, rect); - case MediaOverlayPlayButton: - return paintMediaOverlayPlayButton(object, paintInfo, rect); - case MediaVolumeSliderMuteButton: - case MediaSeekBackButton: - case MediaSeekForwardButton: - case MediaVolumeSliderContainer: - case MediaTimelineContainer: - case MediaCurrentTimeDisplay: - case MediaTimeRemainingDisplay: - case MediaControlsPanel: - case MediaRewindButton: - case MediaReturnToRealtimeButton: - case MediaStatusDisplay: - case MediaHideClosedCaptionsButton: - case MediaTextTrackDisplayContainer: - case MediaTextTrackDisplay: - case MediaFullScreenVolumeSlider: - case MediaFullScreenVolumeSliderThumb: - case MediaClosedCaptionsContainer: - case MediaClosedCaptionsTrackList: - ASSERT_NOT_REACHED(); - break; - } - return false; -} - -const int mediaSliderThumbHeight = 24; -const int mediaVolumeSliderThumbHeight = 24; - -void RenderMediaControlsChromium::adjustMediaSliderThumbSize(RenderStyle* style) -{ - static Image* mediaSliderThumb = platformResource("mediaplayerSliderThumb"); - static Image* mediaVolumeSliderThumb = platformResource("mediaplayerVolumeSliderThumb"); - int width = 0; - int height = 0; - - Image* thumbImage = 0; - if (style->appearance() == MediaSliderThumbPart) { - thumbImage = mediaSliderThumb; - width = mediaSliderThumbWidth; - height = mediaSliderThumbHeight; - } else if (style->appearance() == MediaVolumeSliderThumbPart) { - thumbImage = mediaVolumeSliderThumb; - width = mediaVolumeSliderThumbWidth; - height = mediaVolumeSliderThumbHeight; - } - - float zoomLevel = style->effectiveZoom(); - if (thumbImage) { - style->setWidth(Length(static_cast<int>(width * zoomLevel), Fixed)); - style->setHeight(Length(static_cast<int>(height * zoomLevel), Fixed)); - } -} - -static String formatChromiumMediaControlsTime(float time, float duration) -{ - if (!isfinite(time)) - time = 0; - if (!isfinite(duration)) - duration = 0; - int seconds = static_cast<int>(fabsf(time)); - int hours = seconds / (60 * 60); - int minutes = (seconds / 60) % 60; - seconds %= 60; - - // duration defines the format of how the time is rendered - int durationSecs = static_cast<int>(fabsf(duration)); - int durationHours = durationSecs / (60 * 60); - int durationMins = (durationSecs / 60) % 60; - - if (durationHours || hours) - return String::format("%s%01d:%02d:%02d", (time < 0 ? "-" : ""), hours, minutes, seconds); - if (durationMins > 9) - return String::format("%s%02d:%02d", (time < 0 ? "-" : ""), minutes, seconds); - - return String::format("%s%01d:%02d", (time < 0 ? "-" : ""), minutes, seconds); -} - -String RenderMediaControlsChromium::formatMediaControlsTime(float time) -{ - return formatChromiumMediaControlsTime(time, time); -} - -String RenderMediaControlsChromium::formatMediaControlsCurrentTime(float currentTime, float duration) -{ - return formatChromiumMediaControlsTime(currentTime, duration); -} - -String RenderMediaControlsChromium::formatMediaControlsRemainingTime(float currentTime, float duration) -{ - return formatChromiumMediaControlsTime(currentTime - duration, duration); -} - -#endif // #if ENABLE(VIDEO) - -} // namespace WebCore diff --git a/Source/WebCore/rendering/RenderMenuList.cpp b/Source/WebCore/rendering/RenderMenuList.cpp index b721742e1..47a74fce8 100644 --- a/Source/WebCore/rendering/RenderMenuList.cpp +++ b/Source/WebCore/rendering/RenderMenuList.cpp @@ -54,10 +54,10 @@ namespace WebCore { using namespace HTMLNames; RenderMenuList::RenderMenuList(Element* element) - : RenderDeprecatedFlexibleBox(element) + : RenderFlexibleBox(element) , m_buttonText(0) , m_innerBlock(0) - , m_optionsChanged(true) + , m_needsOptionsWidthUpdate(true) , m_optionsWidth(0) , m_lastActiveIndex(-1) , m_popupIsVisible(false) @@ -91,26 +91,38 @@ void RenderMenuList::createInnerBlock() ASSERT(!firstChild()); m_innerBlock = createAnonymousBlock(); adjustInnerStyle(); - RenderDeprecatedFlexibleBox::addChild(m_innerBlock); + RenderFlexibleBox::addChild(m_innerBlock); } void RenderMenuList::adjustInnerStyle() { RenderStyle* innerStyle = m_innerBlock->style(); - innerStyle->setBoxFlex(1); - + innerStyle->setFlexGrow(1); + innerStyle->setFlexShrink(1); + // min-width: 0; is needed for correct shrinking. + // FIXME: Remove this line when https://bugs.webkit.org/show_bug.cgi?id=111790 is fixed. + innerStyle->setMinWidth(Length(0, Fixed)); + // Use margin:auto instead of align-items:center to get safe centering, i.e. + // when the content overflows, treat it the same as align-items: flex-start. + // But we only do that for the cases where html.css would otherwise use center. + if (style()->alignItems() == AlignCenter) { + innerStyle->setMarginTop(Length()); + innerStyle->setMarginBottom(Length()); + innerStyle->setAlignSelf(AlignFlexStart); + } + innerStyle->setPaddingLeft(Length(theme()->popupInternalPaddingLeft(style()), Fixed)); innerStyle->setPaddingRight(Length(theme()->popupInternalPaddingRight(style()), Fixed)); innerStyle->setPaddingTop(Length(theme()->popupInternalPaddingTop(style()), Fixed)); innerStyle->setPaddingBottom(Length(theme()->popupInternalPaddingBottom(style()), Fixed)); - if (document()->page()->chrome()->selectItemWritingDirectionIsNatural()) { + if (document()->page()->chrome().selectItemWritingDirectionIsNatural()) { // Items in the popup will not respect the CSS text-align and direction properties, // so we must adjust our own style to match. innerStyle->setTextAlign(LEFT); TextDirection direction = (m_buttonText && m_buttonText->text()->defaultWritingDirection() == WTF::Unicode::RightToLeft) ? RTL : LTR; innerStyle->setDirection(direction); - } else if (m_optionStyle && document()->page()->chrome()->selectItemAlignmentFollowsMenuWritingDirection()) { + } else if (m_optionStyle && document()->page()->chrome().selectItemAlignmentFollowsMenuWritingDirection()) { if ((m_optionStyle->direction() != innerStyle->direction() || m_optionStyle->unicodeBidi() != innerStyle->unicodeBidi())) m_innerBlock->setNeedsLayoutAndPrefWidthsRecalc(); innerStyle->setTextAlign(style()->isLeftToRightDirection() ? LEFT : RIGHT); @@ -130,14 +142,14 @@ void RenderMenuList::addChild(RenderObject* newChild, RenderObject* beforeChild) m_innerBlock->addChild(newChild, beforeChild); ASSERT(m_innerBlock == firstChild()); - if (AXObjectCache::accessibilityEnabled()) - document()->axObjectCache()->childrenChanged(this); + if (AXObjectCache* cache = document()->existingAXObjectCache()) + cache->childrenChanged(this); } void RenderMenuList::removeChild(RenderObject* oldChild) { if (oldChild == m_innerBlock || !m_innerBlock) { - RenderDeprecatedFlexibleBox::removeChild(oldChild); + RenderFlexibleBox::removeChild(oldChild); m_innerBlock = 0; } else m_innerBlock->removeChild(oldChild); @@ -153,8 +165,10 @@ void RenderMenuList::styleDidChange(StyleDifference diff, const RenderStyle* old adjustInnerStyle(); bool fontChanged = !oldStyle || oldStyle->font() != style()->font(); - if (fontChanged) + if (fontChanged) { updateOptionsWidth(); + m_needsOptionsWidthUpdate = false; + } } void RenderMenuList::updateOptionsWidth() @@ -166,7 +180,7 @@ void RenderMenuList::updateOptionsWidth() for (int i = 0; i < size; ++i) { HTMLElement* element = listItems[i]; - if (!element->hasTagName(optionTag)) + if (!isHTMLOptionElement(element)) continue; String text = toHTMLOptionElement(element)->textIndentedToRespectGroupLabel(); @@ -194,9 +208,9 @@ void RenderMenuList::updateOptionsWidth() void RenderMenuList::updateFromElement() { - if (m_optionsChanged) { + if (m_needsOptionsWidthUpdate) { updateOptionsWidth(); - m_optionsChanged = false; + m_needsOptionsWidthUpdate = false; } if (m_popupIsVisible) @@ -215,7 +229,7 @@ void RenderMenuList::setTextFromOption(int optionIndex) String text = emptyString(); if (i >= 0 && i < size) { Element* element = listItems[i]; - if (element->hasTagName(optionTag)) { + if (isHTMLOptionElement(element)) { text = toHTMLOptionElement(element)->textIndentedToRespectGroupLabel(); m_optionStyle = element->renderStyle(); } @@ -272,6 +286,13 @@ LayoutRect RenderMenuList::controlClipRect(const LayoutPoint& additionalOffset) return intersection(outerBox, innerBox); } +void RenderMenuList::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const +{ + maxLogicalWidth = max(m_optionsWidth, theme()->minimumMenuListSize(style())) + m_innerBlock->paddingLeft() + m_innerBlock->paddingRight(); + if (!style()->width().isPercent()) + minLogicalWidth = maxLogicalWidth; +} + void RenderMenuList::computePreferredLogicalWidths() { m_minPreferredLogicalWidth = 0; @@ -280,15 +301,12 @@ void RenderMenuList::computePreferredLogicalWidths() if (style()->width().isFixed() && style()->width().value() > 0) m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = adjustContentBoxLogicalWidthForBoxSizing(style()->width().value()); else - m_maxPreferredLogicalWidth = max(m_optionsWidth, theme()->minimumMenuListSize(style())) + m_innerBlock->paddingLeft() + m_innerBlock->paddingRight(); + computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth); if (style()->minWidth().isFixed() && style()->minWidth().value() > 0) { m_maxPreferredLogicalWidth = max(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style()->minWidth().value())); m_minPreferredLogicalWidth = max(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style()->minWidth().value())); - } else if (style()->width().isPercent() || (style()->width().isAuto() && style()->height().isPercent())) - m_minPreferredLogicalWidth = 0; - else - m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth; + } if (style()->maxWidth().isFixed()) { m_maxPreferredLogicalWidth = min(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style()->maxWidth().value())); @@ -307,7 +325,7 @@ void RenderMenuList::showPopup() if (m_popupIsVisible) return; - if (document()->page()->chrome()->hasOpenedPopup()) + if (document()->page()->chrome().hasOpenedPopup()) return; // Create m_innerBlock here so it ends up as the first child. @@ -315,12 +333,12 @@ void RenderMenuList::showPopup() // inside the showPopup call and it would fail. createInnerBlock(); if (!m_popup) - m_popup = document()->page()->chrome()->createPopupMenu(this); + m_popup = document()->page()->chrome().createPopupMenu(this); m_popupIsVisible = true; // Compute the top left taking transforms into account, but use // the actual width of the element to size the popup. - FloatPoint absTopLeft = localToAbsolute(FloatPoint(), UseTransforms | SnapOffsetForTransforms); + FloatPoint absTopLeft = localToAbsolute(FloatPoint(), UseTransforms); IntRect absBounds = absoluteBoundingBoxRectIgnoringTransforms(); absBounds.setLocation(roundedIntPoint(absTopLeft)); HTMLSelectElement* select = selectElement(); @@ -337,7 +355,7 @@ void RenderMenuList::valueChanged(unsigned listIndex, bool fireOnChange) { // Check to ensure a page navigation has not occurred while // the popup was up. - Document* doc = static_cast<Element*>(node())->document(); + Document* doc = toElement(node())->document(); if (!doc || doc != doc->frame()->document()) return; @@ -362,7 +380,7 @@ void RenderMenuList::didSetSelectedIndex(int listIndex) void RenderMenuList::didUpdateActiveOption(int optionIndex) { - if (!AXObjectCache::accessibilityEnabled()) + if (!AXObjectCache::accessibilityEnabled() || !document()->existingAXObjectCache()) return; if (m_lastActiveIndex == optionIndex) @@ -389,9 +407,9 @@ String RenderMenuList::itemText(unsigned listIndex) const String itemString; Element* element = listItems[listIndex]; - if (element->hasTagName(optgroupTag)) - itemString = static_cast<const HTMLOptGroupElement*>(element)->groupLabelText(); - else if (element->hasTagName(optionTag)) + if (isHTMLOptGroupElement(element)) + itemString = toHTMLOptGroupElement(element)->groupLabelText(); + else if (isHTMLOptionElement(element)) itemString = toHTMLOptionElement(element)->textIndentedToRespectGroupLabel(); applyTextTransform(style(), itemString, ' '); @@ -431,18 +449,18 @@ bool RenderMenuList::itemIsEnabled(unsigned listIndex) const if (listIndex >= listItems.size()) return false; HTMLElement* element = listItems[listIndex]; - if (!element->hasTagName(optionTag)) + if (!isHTMLOptionElement(element)) return false; bool groupEnabled = true; if (Element* parentElement = element->parentElement()) { - if (parentElement->hasTagName(optgroupTag)) - groupEnabled = !static_cast<HTMLOptGroupElement*>(parentElement)->disabled(); + if (isHTMLOptGroupElement(parentElement)) + groupEnabled = !parentElement->isDisabledFormControl(); } if (!groupEnabled) return false; - return element->isEnabledFormControl(); + return !element->isDisabledFormControl(); } PopupMenuStyle RenderMenuList::itemStyle(unsigned listIndex) const @@ -459,33 +477,46 @@ PopupMenuStyle RenderMenuList::itemStyle(unsigned listIndex) const listIndex = 0; } HTMLElement* element = listItems[listIndex]; - + + Color itemBackgroundColor; + bool itemHasCustomBackgroundColor; + getItemBackgroundColor(listIndex, itemBackgroundColor, itemHasCustomBackgroundColor); + RenderStyle* style = element->renderStyle() ? element->renderStyle() : element->computedStyle(); - return style ? PopupMenuStyle(style->visitedDependentColor(CSSPropertyColor), itemBackgroundColor(listIndex), style->font(), style->visibility() == VISIBLE, - style->display() == NONE, style->textIndent(), style->direction(), isOverride(style->unicodeBidi())) : menuStyle(); + return style ? PopupMenuStyle(style->visitedDependentColor(CSSPropertyColor), itemBackgroundColor, style->font(), style->visibility() == VISIBLE, + style->display() == NONE, style->textIndent(), style->direction(), isOverride(style->unicodeBidi()), + itemHasCustomBackgroundColor ? PopupMenuStyle::CustomBackgroundColor : PopupMenuStyle::DefaultBackgroundColor) : menuStyle(); } -Color RenderMenuList::itemBackgroundColor(unsigned listIndex) const +void RenderMenuList::getItemBackgroundColor(unsigned listIndex, Color& itemBackgroundColor, bool& itemHasCustomBackgroundColor) const { const Vector<HTMLElement*>& listItems = selectElement()->listItems(); - if (listIndex >= listItems.size()) - return style()->visitedDependentColor(CSSPropertyBackgroundColor); + if (listIndex >= listItems.size()) { + itemBackgroundColor = style()->visitedDependentColor(CSSPropertyBackgroundColor); + itemHasCustomBackgroundColor = false; + return; + } HTMLElement* element = listItems[listIndex]; Color backgroundColor; if (element->renderStyle()) backgroundColor = element->renderStyle()->visitedDependentColor(CSSPropertyBackgroundColor); + itemHasCustomBackgroundColor = backgroundColor.isValid() && backgroundColor.alpha(); // If the item has an opaque background color, return that. - if (!backgroundColor.hasAlpha()) - return backgroundColor; + if (!backgroundColor.hasAlpha()) { + itemBackgroundColor = backgroundColor; + return; + } // Otherwise, the item's background is overlayed on top of the menu background. backgroundColor = style()->visitedDependentColor(CSSPropertyBackgroundColor).blend(backgroundColor); - if (!backgroundColor.hasAlpha()) - return backgroundColor; + if (!backgroundColor.hasAlpha()) { + itemBackgroundColor = backgroundColor; + return; + } // If the menu background is not opaque, then add an opaque white background behind. - return Color(Color::white).blend(backgroundColor); + itemBackgroundColor = Color(Color::white).blend(backgroundColor); } PopupMenuStyle RenderMenuList::menuStyle() const @@ -567,7 +598,7 @@ bool RenderMenuList::itemIsSeparator(unsigned listIndex) const bool RenderMenuList::itemIsLabel(unsigned listIndex) const { const Vector<HTMLElement*>& listItems = selectElement()->listItems(); - return listIndex < listItems.size() && listItems[listIndex]->hasTagName(optgroupTag); + return listIndex < listItems.size() && isHTMLOptGroupElement(listItems[listIndex]); } bool RenderMenuList::itemIsSelected(unsigned listIndex) const @@ -576,7 +607,7 @@ bool RenderMenuList::itemIsSelected(unsigned listIndex) const if (listIndex >= listItems.size()) return false; HTMLElement* element = listItems[listIndex]; - return element->hasTagName(optionTag) && toHTMLOptionElement(element)->selected(); + return isHTMLOptionElement(element) && toHTMLOptionElement(element)->selected(); } void RenderMenuList::setTextFromItem(unsigned listIndex) @@ -586,7 +617,7 @@ void RenderMenuList::setTextFromItem(unsigned listIndex) FontSelector* RenderMenuList::fontSelector() const { - return document()->styleResolver()->fontSelector(); + return document()->ensureStyleResolver()->fontSelector(); } } diff --git a/Source/WebCore/rendering/RenderMenuList.h b/Source/WebCore/rendering/RenderMenuList.h index df42f1696..7287ac300 100644 --- a/Source/WebCore/rendering/RenderMenuList.h +++ b/Source/WebCore/rendering/RenderMenuList.h @@ -27,7 +27,7 @@ #include "LayoutRect.h" #include "PopupMenu.h" #include "PopupMenuClient.h" -#include "RenderDeprecatedFlexibleBox.h" +#include "RenderFlexibleBox.h" #if PLATFORM(MAC) #define POPUP_MENU_PULLS_DOWN 0 @@ -40,7 +40,7 @@ namespace WebCore { class HTMLSelectElement; class RenderText; -class RenderMenuList : public RenderDeprecatedFlexibleBox, private PopupMenuClient { +class RenderMenuList : public RenderFlexibleBox, private PopupMenuClient { public: RenderMenuList(Element*); @@ -51,7 +51,7 @@ public: void showPopup(); void hidePopup(); - void setOptionsChanged(bool changed) { m_optionsChanged = changed; } + void setOptionsChanged(bool changed) { m_needsOptionsWidthUpdate = changed; } void didSetSelectedIndex(int listIndex); @@ -75,7 +75,8 @@ private: virtual const char* renderName() const { return "RenderMenuList"; } - virtual void computePreferredLogicalWidths(); + virtual void computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const OVERRIDE; + virtual void computePreferredLogicalWidths() OVERRIDE; virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); @@ -114,7 +115,16 @@ private: virtual bool hasLineIfEmpty() const { return true; } - Color itemBackgroundColor(unsigned listIndex) const; + // Flexbox defines baselines differently than regular blocks. + // For backwards compatibility, menulists need to do the regular block behavior. + virtual int baselinePosition(FontBaseline baseline, bool firstLine, LineDirectionMode direction, LinePositionMode position) const OVERRIDE + { + return RenderBlock::baselinePosition(baseline, firstLine, direction, position); + } + virtual int firstLineBoxBaseline() const OVERRIDE { return RenderBlock::firstLineBoxBaseline(); } + virtual int inlineBlockBaseline(LineDirectionMode direction) const OVERRIDE { return RenderBlock::inlineBlockBaseline(direction); } + + void getItemBackgroundColor(unsigned listIndex, Color&, bool& itemHasCustomBackgroundColor) const; void createInnerBlock(); void adjustInnerStyle(); @@ -127,7 +137,7 @@ private: RenderText* m_buttonText; RenderBlock* m_innerBlock; - bool m_optionsChanged; + bool m_needsOptionsWidthUpdate; int m_optionsWidth; int m_lastActiveIndex; @@ -140,7 +150,7 @@ private: inline RenderMenuList* toRenderMenuList(RenderObject* object) { - ASSERT(!object || object->isMenuList()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isMenuList()); return static_cast<RenderMenuList*>(object); } diff --git a/Source/WebCore/rendering/RenderMeter.cpp b/Source/WebCore/rendering/RenderMeter.cpp index a4b3eca35..a2ea96890 100644 --- a/Source/WebCore/rendering/RenderMeter.cpp +++ b/Source/WebCore/rendering/RenderMeter.cpp @@ -19,9 +19,9 @@ */ #include "config.h" +#if ENABLE(METER_ELEMENT) #include "RenderMeter.h" -#if ENABLE(METER_ELEMENT) #include "HTMLMeterElement.h" #include "HTMLNames.h" #include "RenderTheme.h" @@ -73,11 +73,6 @@ void RenderMeter::computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logi computedValues.m_extent = isHorizontalWritingMode() ? frameSize.height() : frameSize.width(); } -double RenderMeter::valueRatio() const -{ - return meterElement()->valueRatio(); -} - void RenderMeter::updateFromElement() { repaint(); diff --git a/Source/WebCore/rendering/RenderMeter.h b/Source/WebCore/rendering/RenderMeter.h index b7c3ecee7..bc09d3fd6 100644 --- a/Source/WebCore/rendering/RenderMeter.h +++ b/Source/WebCore/rendering/RenderMeter.h @@ -32,26 +32,24 @@ class HTMLMeterElement; class RenderMeter : public RenderBlock { public: - RenderMeter(HTMLElement*); + explicit RenderMeter(HTMLElement*); virtual ~RenderMeter(); HTMLMeterElement* meterElement() const; virtual void updateFromElement(); -private: +private: virtual void updateLogicalWidth() OVERRIDE; virtual void computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues&) const OVERRIDE; virtual const char* renderName() const { return "RenderMeter"; } virtual bool isMeter() const { return true; } virtual bool requiresForcedStyleRecalcPropagation() const { return true; } - - double valueRatio() const; }; inline RenderMeter* toRenderMeter(RenderObject* object) { - ASSERT(!object || object->isMeter()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isMeter()); return static_cast<RenderMeter*>(object); } diff --git a/Source/WebCore/rendering/RenderMultiColumnBlock.cpp b/Source/WebCore/rendering/RenderMultiColumnBlock.cpp index ba3ccd9b2..33d5c873b 100644 --- a/Source/WebCore/rendering/RenderMultiColumnBlock.cpp +++ b/Source/WebCore/rendering/RenderMultiColumnBlock.cpp @@ -25,21 +25,31 @@ #include "config.h" #include "RenderMultiColumnBlock.h" + #include "RenderMultiColumnFlowThread.h" #include "RenderMultiColumnSet.h" +#include "RenderView.h" #include "StyleInheritedData.h" using namespace std; namespace WebCore { -RenderMultiColumnBlock::RenderMultiColumnBlock(Node* node) - : RenderBlock(node) +RenderMultiColumnBlock::RenderMultiColumnBlock(Element* element) + : RenderBlock(element) , m_flowThread(0) , m_columnCount(1) , m_columnWidth(0) - , m_columnHeight(0) + , m_columnHeightAvailable(0) + , m_inBalancingPass(false) +{ +} + +void RenderMultiColumnBlock::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) { + RenderBlock::styleDidChange(diff, oldStyle); + for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) + child->setStyle(RenderStyle::createAnonymousStyleWithDisplay(style(), BLOCK)); } void RenderMultiColumnBlock::computeColumnCountAndWidth() @@ -82,76 +92,104 @@ void RenderMultiColumnBlock::checkForPaginationLogicalHeightChange(LayoutUnit& / { // We don't actually update any of the variables. We just subclassed to adjust our column height. updateLogicalHeight(); - LayoutUnit newContentLogicalHeight = contentLogicalHeight(); - if (newContentLogicalHeight > 0) { - // The regions will be invalidated when we lay them out and they change size to - // the new column height. - if (columnHeight() != newContentLogicalHeight) - setColumnHeight(newContentLogicalHeight); - } + m_columnHeightAvailable = max<LayoutUnit>(contentLogicalHeight(), 0); setLogicalHeight(0); - - // Set up our column sets. - ensureColumnSets(); } -bool RenderMultiColumnBlock::relayoutForPagination(bool, LayoutUnit, LayoutStateMaintainer&) +bool RenderMultiColumnBlock::relayoutForPagination(bool, LayoutUnit, LayoutStateMaintainer& statePusher) { - // FIXME: Implement. - return false; + if (m_inBalancingPass || !requiresBalancing()) + return false; + m_inBalancingPass = true; // Prevent re-entering this method (and recursion into layout). + + bool needsRelayout; + bool neededRelayout = false; + bool firstPass = true; + do { + // Column heights may change here because of balancing. We may have to do multiple layout + // passes, depending on how the contents is fitted to the changed column heights. In most + // cases, laying out again twice or even just once will suffice. Sometimes we need more + // passes than that, though, but the number of retries should not exceed the number of + // columns, unless we have a bug. + needsRelayout = false; + for (RenderBox* childBox = firstChildBox(); childBox; childBox = childBox->nextSiblingBox()) + if (childBox != m_flowThread && childBox->isRenderMultiColumnSet()) { + RenderMultiColumnSet* multicolSet = toRenderMultiColumnSet(childBox); + if (multicolSet->calculateBalancedHeight(firstPass)) { + multicolSet->setChildNeedsLayout(true, MarkOnlyThis); + needsRelayout = true; + } + } + + if (needsRelayout) { + // Layout again. Column balancing resulted in a new height. + neededRelayout = true; + m_flowThread->setChildNeedsLayout(true, MarkOnlyThis); + setChildNeedsLayout(true, MarkOnlyThis); + if (firstPass) + statePusher.pop(); + layoutBlock(false); + } + firstPass = false; + } while (needsRelayout); + m_inBalancingPass = false; + return neededRelayout; } void RenderMultiColumnBlock::addChild(RenderObject* newChild, RenderObject* beforeChild) { if (!m_flowThread) { - m_flowThread = new (renderArena()) RenderMultiColumnFlowThread(document()); + m_flowThread = RenderMultiColumnFlowThread::createAnonymous(document()); m_flowThread->setStyle(RenderStyle::createAnonymousStyleWithDisplay(style(), BLOCK)); - RenderBlock::addChild(m_flowThread); // Always put the flow thread at the end. + RenderBlock::addChild(m_flowThread); } - - // Column sets are siblings of the flow thread. All children designed to be in the columns, however, are part - // of the flow thread itself. - if (newChild->isRenderMultiColumnSet()) - RenderBlock::addChild(newChild, beforeChild); - else - m_flowThread->addChild(newChild, beforeChild); + m_flowThread->addChild(newChild, beforeChild); } - -void RenderMultiColumnBlock::ensureColumnSets() + +RenderObject* RenderMultiColumnBlock::layoutSpecialExcludedChild(bool relayoutChildren) { - // This function ensures we have the correct column set information before we get into layout. - // For a simple multi-column layout in continuous media, only one column set child is required. - // Once a column is nested inside an enclosing pagination context, the number of column sets - // required becomes 2n-1, where n is the total number of nested pagination contexts. For example: - // - // Column layout with no enclosing pagination model = 2 * 1 - 1 = 1 column set. - // Columns inside pages = 2 * 2 - 1 = 3 column sets (bottom of first page, all the subsequent pages, then the last page). - // Columns inside columns inside pages = 2 * 3 - 1 = 5 column sets. - // - // In addition, column spans will force a column set to "split" into before/after sets around the spanning region. - // - // Finally, we will need to deal with columns inside regions. If regions have variable widths, then there will need - // to be unique column sets created inside any region whose width is different from its surrounding regions. This is - // actually pretty similar to the spanning case, in that we break up the column sets whenever the width varies. - // - // FIXME: For now just make one column set. This matches the old multi-column code. - // Right now our goal is just feature parity with the old multi-column code so that we can switch over to the - // new code as soon as possible. - if (flowThread() && !firstChild()->isRenderMultiColumnSet()) { - RenderMultiColumnSet* columnSet = new (renderArena()) RenderMultiColumnSet(document(), flowThread()); - columnSet->setStyle(RenderStyle::createAnonymousStyleWithDisplay(style(), BLOCK)); - RenderBlock::addChild(columnSet, firstChild()); + if (!m_flowThread) + return 0; + + // Update the dimensions of our regions before we lay out the flow thread. + // FIXME: Eventually this is going to get way more complicated, and we will be destroying regions + // instead of trying to keep them around. + bool shouldInvalidateRegions = false; + for (RenderBox* childBox = firstChildBox(); childBox; childBox = childBox->nextSiblingBox()) { + if (childBox == m_flowThread) + continue; + + if (relayoutChildren || childBox->needsLayout()) { + if (!m_inBalancingPass && childBox->isRenderMultiColumnSet()) + toRenderMultiColumnSet(childBox)->prepareForLayout(); + shouldInvalidateRegions = true; + } } + + if (shouldInvalidateRegions) + m_flowThread->invalidateRegions(); + + if (relayoutChildren) + m_flowThread->setChildNeedsLayout(true, MarkOnlyThis); + + setLogicalTopForChild(m_flowThread, borderAndPaddingBefore()); + m_flowThread->layoutIfNeeded(); + determineLogicalLeftPositionForChild(m_flowThread); + + return m_flowThread; } const char* RenderMultiColumnBlock::renderName() const -{ +{ if (isFloating()) return "RenderMultiColumnBlock (floating)"; if (isOutOfFlowPositioned()) return "RenderMultiColumnBlock (positioned)"; if (isAnonymousBlock()) return "RenderMultiColumnBlock (anonymous)"; + // FIXME: Temporary hack while the new generated content system is being implemented. + if (isPseudoElement()) + return "RenderMultiColumnBlock (generated)"; if (isAnonymous()) return "RenderMultiColumnBlock (generated)"; if (isRelPositioned()) diff --git a/Source/WebCore/rendering/RenderMultiColumnBlock.h b/Source/WebCore/rendering/RenderMultiColumnBlock.h index 58850c334..f4408ae98 100644 --- a/Source/WebCore/rendering/RenderMultiColumnBlock.h +++ b/Source/WebCore/rendering/RenderMultiColumnBlock.h @@ -35,21 +35,26 @@ class RenderMultiColumnFlowThread; class RenderMultiColumnBlock : public RenderBlock { public: - RenderMultiColumnBlock(Node*); - - LayoutUnit columnHeight() const { return m_columnHeight; } - void setColumnHeight(LayoutUnit columnHeight) { m_columnHeight = columnHeight; } + RenderMultiColumnBlock(Element*); + + LayoutUnit columnHeightAvailable() const { return m_columnHeightAvailable; } LayoutUnit columnWidth() const { return m_columnWidth; } unsigned columnCount() const { return m_columnCount; } RenderMultiColumnFlowThread* flowThread() const { return m_flowThread; } + bool requiresBalancing() const { return !m_columnHeightAvailable; } + private: virtual bool isRenderMultiColumnBlock() const { return true; } virtual const char* renderName() const; + virtual RenderObject* layoutSpecialExcludedChild(bool relayoutChildren) OVERRIDE; + + virtual void styleDidChange(StyleDifference, const RenderStyle*) OVERRIDE; + virtual bool updateLogicalWidthAndColumnWidth() OVERRIDE; virtual void checkForPaginationLogicalHeightChange(LayoutUnit& pageLogicalHeight, bool& pageLogicalHeightChanged, bool& hasSpecifiedPageLogicalHeight) OVERRIDE; virtual bool relayoutForPagination(bool hasSpecifiedPageLogicalHeight, LayoutUnit pageLogicalHeight, LayoutStateMaintainer&) OVERRIDE; @@ -64,18 +69,19 @@ private: unsigned m_columnCount; // The default column count/width that are based off our containing block width. These values represent only the default, LayoutUnit m_columnWidth; // since a multi-column block that is split across variable width pages or regions will have different column counts and widths in each. // These values will be cached (eventually) for multi-column blocks. - LayoutUnit m_columnHeight; // The current column height. + LayoutUnit m_columnHeightAvailable; // Total height available to columns, or 0 if auto. + bool m_inBalancingPass; // Set when relayouting for column balancing. }; inline RenderMultiColumnBlock* toRenderMultiColumnBlock(RenderObject* object) { - ASSERT(!object || object->isRenderMultiColumnBlock()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isRenderMultiColumnBlock()); return static_cast<RenderMultiColumnBlock*>(object); } inline const RenderMultiColumnBlock* toRenderMultiColumnBlock(const RenderObject* object) { - ASSERT(!object || object->isRenderMultiColumnBlock()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isRenderMultiColumnBlock()); return static_cast<const RenderMultiColumnBlock*>(object); } diff --git a/Source/WebCore/rendering/RenderMultiColumnFlowThread.cpp b/Source/WebCore/rendering/RenderMultiColumnFlowThread.cpp index 562506842..dfa017dcd 100644 --- a/Source/WebCore/rendering/RenderMultiColumnFlowThread.cpp +++ b/Source/WebCore/rendering/RenderMultiColumnFlowThread.cpp @@ -26,17 +26,27 @@ #include "config.h" #include "RenderMultiColumnFlowThread.h" +#include "RenderMultiColumnBlock.h" +#include "RenderMultiColumnSet.h" + namespace WebCore { -RenderMultiColumnFlowThread::RenderMultiColumnFlowThread(Node* node) - : RenderFlowThread(node) +RenderMultiColumnFlowThread::RenderMultiColumnFlowThread() { + setFlowThreadState(InsideInFlowThread); } RenderMultiColumnFlowThread::~RenderMultiColumnFlowThread() { } +RenderMultiColumnFlowThread* RenderMultiColumnFlowThread::createAnonymous(Document* document) +{ + RenderMultiColumnFlowThread* renderer = new (document->renderArena()) RenderMultiColumnFlowThread(); + renderer->setDocumentForAnonymous(document); + return renderer; +} + const char* RenderMultiColumnFlowThread::renderName() const { return "RenderMultiColumnFlowThread"; @@ -49,4 +59,61 @@ void RenderMultiColumnFlowThread::computeLogicalHeight(LayoutUnit logicalHeight, computedValues.m_position = logicalTop; } +LayoutUnit RenderMultiColumnFlowThread::initialLogicalWidth() const +{ + RenderMultiColumnBlock* parentBlock = toRenderMultiColumnBlock(parent()); + return parentBlock->columnWidth(); +} + +void RenderMultiColumnFlowThread::autoGenerateRegionsToBlockOffset(LayoutUnit /*offset*/) +{ + // This function ensures we have the correct column set information at all times. + // For a simple multi-column layout in continuous media, only one column set child is required. + // Once a column is nested inside an enclosing pagination context, the number of column sets + // required becomes 2n-1, where n is the total number of nested pagination contexts. For example: + // + // Column layout with no enclosing pagination model = 2 * 1 - 1 = 1 column set. + // Columns inside pages = 2 * 2 - 1 = 3 column sets (bottom of first page, all the subsequent pages, then the last page). + // Columns inside columns inside pages = 2 * 3 - 1 = 5 column sets. + // + // In addition, column spans will force a column set to "split" into before/after sets around the spanning element. + // + // Finally, we will need to deal with columns inside regions. If regions have variable widths, then there will need + // to be unique column sets created inside any region whose width is different from its surrounding regions. This is + // actually pretty similar to the spanning case, in that we break up the column sets whenever the width varies. + // + // FIXME: For now just make one column set. This matches the old multi-column code. + // Right now our goal is just feature parity with the old multi-column code so that we can switch over to the + // new code as soon as possible. + RenderMultiColumnSet* firstSet = toRenderMultiColumnSet(firstRegion()); + if (firstSet) + return; + + invalidateRegions(); + + RenderMultiColumnBlock* parentBlock = toRenderMultiColumnBlock(parent()); + firstSet = RenderMultiColumnSet::createAnonymous(this); + firstSet->setStyle(RenderStyle::createAnonymousStyleWithDisplay(parentBlock->style(), BLOCK)); + parentBlock->RenderBlock::addChild(firstSet); + + // Even though we aren't placed yet, we can go ahead and set up our size. At this point we're + // typically in the middle of laying out the thread, attempting to paginate, and we need to do + // some rudimentary "layout" of the set now, so that pagination will work. + firstSet->prepareForLayout(); + + validateRegions(); +} + +void RenderMultiColumnFlowThread::setPageBreak(LayoutUnit offset, LayoutUnit spaceShortage) +{ + if (RenderMultiColumnSet* multicolSet = toRenderMultiColumnSet(regionAtBlockOffset(offset))) + multicolSet->recordSpaceShortage(spaceShortage); +} + +void RenderMultiColumnFlowThread::updateMinimumPageHeight(LayoutUnit offset, LayoutUnit minHeight) +{ + if (RenderMultiColumnSet* multicolSet = toRenderMultiColumnSet(regionAtBlockOffset(offset))) + multicolSet->updateMinimumColumnHeight(minHeight); +} + } diff --git a/Source/WebCore/rendering/RenderMultiColumnFlowThread.h b/Source/WebCore/rendering/RenderMultiColumnFlowThread.h index 595222784..dfa9280f4 100644 --- a/Source/WebCore/rendering/RenderMultiColumnFlowThread.h +++ b/Source/WebCore/rendering/RenderMultiColumnFlowThread.h @@ -33,13 +33,19 @@ namespace WebCore { class RenderMultiColumnFlowThread : public RenderFlowThread { public: - RenderMultiColumnFlowThread(Node*); ~RenderMultiColumnFlowThread(); + static RenderMultiColumnFlowThread* createAnonymous(Document*); + private: + RenderMultiColumnFlowThread(); + virtual const char* renderName() const OVERRIDE; - virtual void computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues&) const OVERRIDE; + virtual void autoGenerateRegionsToBlockOffset(LayoutUnit) OVERRIDE; + virtual LayoutUnit initialLogicalWidth() const OVERRIDE; + virtual void setPageBreak(LayoutUnit offset, LayoutUnit spaceShortage) OVERRIDE; + virtual void updateMinimumPageHeight(LayoutUnit offset, LayoutUnit minHeight) OVERRIDE; }; } // namespace WebCore diff --git a/Source/WebCore/rendering/RenderMultiColumnSet.cpp b/Source/WebCore/rendering/RenderMultiColumnSet.cpp index 21bf10fe7..9642b5394 100644 --- a/Source/WebCore/rendering/RenderMultiColumnSet.cpp +++ b/Source/WebCore/rendering/RenderMultiColumnSet.cpp @@ -26,21 +26,22 @@ #include "config.h" #include "RenderMultiColumnSet.h" -#include "HitTestResult.h" #include "PaintInfo.h" -#include "RenderMultiColumnFlowThread.h" +#include "RenderLayer.h" #include "RenderMultiColumnBlock.h" +#include "RenderMultiColumnFlowThread.h" -using std::min; -using std::max; +using namespace std; namespace WebCore { -RenderMultiColumnSet::RenderMultiColumnSet(Node* node, RenderFlowThread* flowThread) - : RenderRegionSet(node, flowThread) +RenderMultiColumnSet::RenderMultiColumnSet(RenderFlowThread* flowThread) + : RenderRegionSet(0, flowThread) , m_computedColumnCount(1) , m_computedColumnWidth(0) , m_computedColumnHeight(0) + , m_maxColumnHeight(LayoutUnit::max()) + , m_minSpaceShortage(LayoutUnit::max()) , m_minimumColumnHeight(0) , m_forcedBreaksCount(0) , m_maximumDistanceBetweenForcedBreaks(0) @@ -48,69 +49,181 @@ RenderMultiColumnSet::RenderMultiColumnSet(Node* node, RenderFlowThread* flowThr { } +RenderMultiColumnSet* RenderMultiColumnSet::createAnonymous(RenderFlowThread* flowThread) +{ + Document* document = flowThread->document(); + RenderMultiColumnSet* renderer = new (document->renderArena()) RenderMultiColumnSet(flowThread); + renderer->setDocumentForAnonymous(document); + return renderer; +} + +LayoutUnit RenderMultiColumnSet::heightAdjustedForSetOffset(LayoutUnit height) const +{ + RenderMultiColumnBlock* multicolBlock = toRenderMultiColumnBlock(parent()); + LayoutUnit contentLogicalTop = logicalTop() - multicolBlock->borderAndPaddingBefore(); + + height -= contentLogicalTop; + return max(height, LayoutUnit(1)); // Let's avoid zero height, as that would probably cause an infinite amount of columns to be created. +} + LayoutUnit RenderMultiColumnSet::pageLogicalTopForOffset(LayoutUnit offset) const { LayoutUnit portionLogicalTop = (isHorizontalWritingMode() ? flowThreadPortionRect().y() : flowThreadPortionRect().x()); - unsigned columnIndex = (offset - portionLogicalTop) / computedColumnHeight(); + unsigned columnIndex = columnIndexAtOffset(offset, AssumeNewColumns); return portionLogicalTop + columnIndex * computedColumnHeight(); } +void RenderMultiColumnSet::setAndConstrainColumnHeight(LayoutUnit newHeight) +{ + m_computedColumnHeight = newHeight; + if (m_computedColumnHeight > m_maxColumnHeight) + m_computedColumnHeight = m_maxColumnHeight; + // FIXME: the height may also be affected by the enclosing pagination context, if any. +} + +bool RenderMultiColumnSet::calculateBalancedHeight(bool initial) +{ + ASSERT(toRenderMultiColumnBlock(parent())->requiresBalancing()); + LayoutUnit oldColumnHeight = m_computedColumnHeight; + LayoutUnit currentMinSpaceShortage = m_minSpaceShortage; + m_minSpaceShortage = LayoutUnit::max(); + + if (initial) { + // Start with the lowest imaginable column height. + LayoutUnit logicalHeightGuess = ceilf(float(flowThread()->logicalHeight()) / float(m_computedColumnCount)); + logicalHeightGuess = max(logicalHeightGuess, m_minimumColumnHeight); + setAndConstrainColumnHeight(logicalHeightGuess); + + // The multicol container now typically needs at least one more layout pass with a new + // column height, but if height was specified, we only need to do this if we found that we + // might need less space than that. On the other hand, if we determined that the columns + // need to be as tall as the specified height of the container, we have already laid it out + // correctly, and there's no need for another pass. + return m_computedColumnHeight != oldColumnHeight; + } + + if (columnCount() <= computedColumnCount()) + // With the current column height, the content fits without creating overflowing columns. We're done. + return false; + + // If the initial guessed column height wasn't enough, stretch it now. Stretch by the lowest + // amount of space shortage found during layout. + + ASSERT(currentMinSpaceShortage != LayoutUnit::max()); // If this can actually happen, we probably have a bug. + if (currentMinSpaceShortage == LayoutUnit::max()) + return false; // So bail out rather than looping infinitely. + + setAndConstrainColumnHeight(m_computedColumnHeight + currentMinSpaceShortage); + + // If we reach the maximum column height (typically set by the height or max-height property), + // we may not be allowed to stretch further. Return true only if stretching + // succeeded. Otherwise, we're done. + ASSERT(m_computedColumnHeight >= oldColumnHeight); // We shouldn't be able to shrink the height! + return m_computedColumnHeight > oldColumnHeight; +} + +void RenderMultiColumnSet::recordSpaceShortage(LayoutUnit spaceShortage) +{ + if (spaceShortage >= m_minSpaceShortage) + return; + + // The space shortage is what we use as our stretch amount. We need a positive number here in + // order to get anywhere. + ASSERT(spaceShortage > 0); + + m_minSpaceShortage = spaceShortage; +} + void RenderMultiColumnSet::updateLogicalWidth() { - // Our logical width starts off matching the column block itself. - // This width will be fixed up after the flow thread lays out once it is determined exactly how many - // columns we ended up holding. + RenderMultiColumnBlock* parentBlock = toRenderMultiColumnBlock(parent()); + setComputedColumnWidthAndCount(parentBlock->columnWidth(), parentBlock->columnCount()); // FIXME: This will eventually vary if we are contained inside regions. + // FIXME: When we add regions support, we'll start it off at the width of the multi-column // block in that particular region. setLogicalWidth(parentBox()->contentLogicalWidth()); - - RenderMultiColumnBlock* parentBlock = toRenderMultiColumnBlock(parent()); - setComputedColumnWidthAndCount(parentBlock->columnWidth(), parentBlock->columnCount()); // FIXME: This will eventually vary if we are contained inside regions. + + // If we overflow, increase our logical width. + unsigned colCount = columnCount(); + LayoutUnit colGap = columnGap(); + LayoutUnit minimumContentLogicalWidth = colCount * computedColumnWidth() + (colCount - 1) * colGap; + LayoutUnit currentContentLogicalWidth = contentLogicalWidth(); + LayoutUnit delta = max(LayoutUnit(), minimumContentLogicalWidth - currentContentLogicalWidth); + if (!delta) + return; + + // Increase our logical width by the delta. + setLogicalWidth(logicalWidth() + delta); } -void RenderMultiColumnSet::updateLogicalHeight() +void RenderMultiColumnSet::prepareForLayout() { - // FIXME: This is the only class that overrides updateLogicalHeight. If we didn't have to set computedColumnHeight, - // we could remove this and make updateLogicalHeight non-virtual. https://bugs.webkit.org/show_bug.cgi?id=96804 - // Make sure our column height is up to date. - LogicalExtentComputedValues computedValues; - computeLogicalHeight(0, 0, computedValues); - setComputedColumnHeight(computedValues.m_extent); // FIXME: Once we make more than one column set, this will become variable. - - // Our logical height is always just the height of our columns. - setLogicalHeight(computedColumnHeight()); + RenderMultiColumnBlock* multicolBlock = toRenderMultiColumnBlock(parent()); + RenderStyle* multicolStyle = multicolBlock->style(); + + // Set box logical top. + ASSERT(!previousSiblingBox() || !previousSiblingBox()->isRenderMultiColumnSet()); // FIXME: multiple set not implemented; need to examine previous set to calculate the correct logical top. + setLogicalTop(multicolBlock->borderAndPaddingBefore()); + + // Set box width. + updateLogicalWidth(); + + if (multicolBlock->requiresBalancing()) { + // Set maximum column height. We will not stretch beyond this. + m_maxColumnHeight = LayoutUnit::max(); + if (!multicolStyle->logicalHeight().isAuto()) + m_maxColumnHeight = multicolBlock->computeContentLogicalHeight(multicolStyle->logicalHeight()); + if (!multicolStyle->logicalMaxHeight().isUndefined()) { + LayoutUnit logicalMaxHeight = multicolBlock->computeContentLogicalHeight(multicolStyle->logicalMaxHeight()); + if (m_maxColumnHeight > logicalMaxHeight) + m_maxColumnHeight = logicalMaxHeight; + } + m_maxColumnHeight = heightAdjustedForSetOffset(m_maxColumnHeight); + m_computedColumnHeight = 0; // Restart balancing. + } else + setAndConstrainColumnHeight(heightAdjustedForSetOffset(multicolBlock->columnHeightAvailable())); + + // Nuke previously stored minimum column height. Contents may have changed for all we know. + m_minimumColumnHeight = 0; } -void RenderMultiColumnSet::computeLogicalHeight(LayoutUnit, LayoutUnit, LogicalExtentComputedValues& computedValues) const +void RenderMultiColumnSet::computeLogicalHeight(LayoutUnit, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const { - RenderMultiColumnBlock* parentBlock = toRenderMultiColumnBlock(parent()); - computedValues.m_extent = parentBlock->columnHeight(); + computedValues.m_extent = m_computedColumnHeight; + computedValues.m_position = logicalTop; } LayoutUnit RenderMultiColumnSet::columnGap() const { - if (style()->hasNormalColumnGap()) - return style()->fontDescription().computedPixelSize(); // "1em" is recommended as the normal gap setting. Matches <p> margins. - return static_cast<int>(style()->columnGap()); + // FIXME: Eventually we will cache the column gap when the widths of columns start varying, but for now we just + // go to the parent block to get the gap. + RenderMultiColumnBlock* parentBlock = toRenderMultiColumnBlock(parent()); + if (parentBlock->style()->hasNormalColumnGap()) + return parentBlock->style()->fontDescription().computedPixelSize(); // "1em" is recommended as the normal gap setting. Matches <p> margins. + return parentBlock->style()->columnGap(); } unsigned RenderMultiColumnSet::columnCount() const { + // We must always return a value of 1 or greater. Column count = 0 is a meaningless situation, + // and will confuse and cause problems in other parts of the code. if (!computedColumnHeight()) - return 0; - - // Our region rect determines our column count. We have as many columns as needed to fit all the content. + return 1; + + // Our portion rect determines our column count. We have as many columns as needed to fit all the content. LayoutUnit logicalHeightInColumns = flowThread()->isHorizontalWritingMode() ? flowThreadPortionRect().height() : flowThreadPortionRect().width(); - return ceil(static_cast<float>(logicalHeightInColumns) / computedColumnHeight()); + unsigned count = ceil(static_cast<float>(logicalHeightInColumns) / computedColumnHeight()); + ASSERT(count >= 1); + return count; } LayoutRect RenderMultiColumnSet::columnRectAt(unsigned index) const { LayoutUnit colLogicalWidth = computedColumnWidth(); LayoutUnit colLogicalHeight = computedColumnHeight(); - LayoutUnit colLogicalTop = borderBefore() + paddingBefore(); + LayoutUnit colLogicalTop = borderAndPaddingBefore(); LayoutUnit colLogicalLeft = borderAndPaddingLogicalLeft(); - int colGap = columnGap(); + LayoutUnit colGap = columnGap(); if (style()->isLeftToRightDirection()) colLogicalLeft += index * (colLogicalWidth + colGap); else @@ -121,18 +234,22 @@ LayoutRect RenderMultiColumnSet::columnRectAt(unsigned index) const return LayoutRect(colLogicalTop, colLogicalLeft, colLogicalHeight, colLogicalWidth); } -unsigned RenderMultiColumnSet::columnIndexAtOffset(LayoutUnit offset) const +unsigned RenderMultiColumnSet::columnIndexAtOffset(LayoutUnit offset, ColumnIndexCalculationMode mode) const { LayoutRect portionRect(flowThreadPortionRect()); - LayoutUnit flowThreadLogicalTop = isHorizontalWritingMode() ? portionRect.y() : portionRect.x(); - LayoutUnit flowThreadLogicalBottom = isHorizontalWritingMode() ? portionRect.maxY() : portionRect.maxX(); - + // Handle the offset being out of range. + LayoutUnit flowThreadLogicalTop = isHorizontalWritingMode() ? portionRect.y() : portionRect.x(); if (offset < flowThreadLogicalTop) return 0; - if (offset >= flowThreadLogicalBottom) - return columnCount() - 1; - + // If we're laying out right now, we cannot constrain against some logical bottom, since it + // isn't known yet. Otherwise, just return the last column if we're past the logical bottom. + if (mode == ClampToExistingColumns) { + LayoutUnit flowThreadLogicalBottom = isHorizontalWritingMode() ? portionRect.maxY() : portionRect.maxX(); + if (offset >= flowThreadLogicalBottom) + return columnCount() - 1; + } + // Just divide by the column height to determine the correct column. return static_cast<float>(offset - flowThreadLogicalTop) / computedColumnHeight(); } @@ -147,7 +264,7 @@ LayoutRect RenderMultiColumnSet::flowThreadPortionRectAt(unsigned index) const return portionRect; } -LayoutRect RenderMultiColumnSet::flowThreadPortionOverflowRect(const LayoutRect& portionRect, unsigned index, unsigned colCount, int colGap) const +LayoutRect RenderMultiColumnSet::flowThreadPortionOverflowRect(const LayoutRect& portionRect, unsigned index, unsigned colCount, LayoutUnit colGap) const { // This function determines the portion of the flow thread that paints for the column. Along the inline axis, columns are // unclipped at outside edges (i.e., the first and last column in the set), and they clip to half the column @@ -161,16 +278,18 @@ LayoutRect RenderMultiColumnSet::flowThreadPortionOverflowRect(const LayoutRect& // This problem applies to regions and pages as well and is not unique to columns. bool isFirstColumn = !index; bool isLastColumn = index == colCount - 1; + bool isLeftmostColumn = style()->isLeftToRightDirection() ? isFirstColumn : isLastColumn; + bool isRightmostColumn = style()->isLeftToRightDirection() ? isLastColumn : isFirstColumn; LayoutRect overflowRect(portionRect); if (isHorizontalWritingMode()) { - if (isFirstColumn) { + if (isLeftmostColumn) { // Shift to the logical left overflow of the flow thread to make sure it's all covered. overflowRect.shiftXEdgeTo(min(flowThread()->visualOverflowRect().x(), portionRect.x())); } else { // Expand into half of the logical left column gap. overflowRect.shiftXEdgeTo(portionRect.x() - colGap / 2); } - if (isLastColumn) { + if (isRightmostColumn) { // Shift to the logical right overflow of the flow thread to ensure content can spill out of the column. overflowRect.shiftMaxXEdgeTo(max(flowThread()->visualOverflowRect().maxX(), portionRect.maxX())); } else { @@ -178,14 +297,14 @@ LayoutRect RenderMultiColumnSet::flowThreadPortionOverflowRect(const LayoutRect& overflowRect.shiftMaxXEdgeTo(portionRect.maxX() + colGap / 2); } } else { - if (isFirstColumn) { + if (isLeftmostColumn) { // Shift to the logical left overflow of the flow thread to make sure it's all covered. overflowRect.shiftYEdgeTo(min(flowThread()->visualOverflowRect().y(), portionRect.y())); } else { // Expand into half of the logical left column gap. overflowRect.shiftYEdgeTo(portionRect.y() - colGap / 2); } - if (isLastColumn) { + if (isRightmostColumn) { // Shift to the logical right overflow of the flow thread to ensure content can spill out of the column. overflowRect.shiftMaxYEdgeTo(max(flowThread()->visualOverflowRect().maxY(), portionRect.maxY())); } else { @@ -196,18 +315,22 @@ LayoutRect RenderMultiColumnSet::flowThreadPortionOverflowRect(const LayoutRect& return overflowRectForFlowThreadPortion(overflowRect, isFirstRegion() && isFirstColumn, isLastRegion() && isLastColumn); } -void RenderMultiColumnSet::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOffset) +void RenderMultiColumnSet::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { - // FIXME: RenderRegions are replaced elements right now and so they only paint in the foreground phase. + if (style()->visibility() != VISIBLE) + return; + + RenderBlock::paintObject(paintInfo, paintOffset); + + // FIXME: Right now we're only painting in the foreground phase. // Columns should technically respect phases and allow for background/float/foreground overlap etc., just like - // RenderBlocks do. We can't correct this, however, until RenderRegions are changed to actually be - // RenderBlocks. Note this is a pretty minor issue, since the old column implementation clipped columns + // RenderBlocks do. Note this is a pretty minor issue, since the old column implementation clipped columns // anyway, thus making it impossible for them to overlap one another. It's also really unlikely that the columns // would overlap another block. - setRegionObjectsRegionStyle(); + if (!m_flowThread || !isValid() || (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseSelection)) + return; + paintColumnRules(paintInfo, paintOffset); - paintColumnContents(paintInfo, paintOffset); - restoreRegionObjectsOriginalStyle(); } void RenderMultiColumnSet::paintColumnRules(PaintInfo& paintInfo, const LayoutPoint& paintOffset) @@ -221,7 +344,7 @@ void RenderMultiColumnSet::paintColumnRules(PaintInfo& paintInfo, const LayoutPo EBorderStyle ruleStyle = blockStyle->columnRuleStyle(); LayoutUnit ruleThickness = blockStyle->columnRuleWidth(); LayoutUnit colGap = columnGap(); - bool renderRule = ruleStyle > BHIDDEN && !ruleTransparent && ruleThickness <= colGap; + bool renderRule = ruleStyle > BHIDDEN && !ruleTransparent; if (!renderRule) return; @@ -249,7 +372,7 @@ void RenderMultiColumnSet::paintColumnRules(PaintInfo& paintInfo, const LayoutPo ruleLogicalLeft -= (inlineDirectionSize + colGap / 2); currLogicalLeftOffset -= (inlineDirectionSize + colGap); } - + // Now paint the column rule. if (i < colCount - 1) { LayoutUnit ruleLeft = isHorizontalWritingMode() ? paintOffset.x() + ruleLogicalLeft - ruleThickness / 2 + ruleAdd : paintOffset.x() + borderLeft() + paddingLeft(); @@ -264,76 +387,6 @@ void RenderMultiColumnSet::paintColumnRules(PaintInfo& paintInfo, const LayoutPo } } -void RenderMultiColumnSet::paintColumnContents(PaintInfo& paintInfo, const LayoutPoint& paintOffset) -{ - // For each rectangle, set it as the region rectangle and then let flow thread painting do the rest. - // We make multiple calls to paintFlowThreadPortionInRegion, changing the rectangles each time. - unsigned colCount = columnCount(); - if (!colCount) - return; - - LayoutUnit colGap = columnGap(); - for (unsigned i = 0; i < colCount; i++) { - // First we get the column rect, which is in our local coordinate space, and we make it physical and apply - // the paint offset to it. That gives us the physical location that we want to paint the column at. - LayoutRect colRect = columnRectAt(i); - flipForWritingMode(colRect); - colRect.moveBy(paintOffset); - - // Next we get the portion of the flow thread that corresponds to this column. - LayoutRect flowThreadPortion = flowThreadPortionRectAt(i); - - // Now get the overflow rect that corresponds to the column. - LayoutRect flowThreadOverflowPortion = flowThreadPortionOverflowRect(flowThreadPortion, i, colCount, colGap); - - // Do the paint with the computed rects. - flowThread()->paintFlowThreadPortionInRegion(paintInfo, this, flowThreadPortion, flowThreadOverflowPortion, colRect.location()); - } -} - -bool RenderMultiColumnSet::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction action) -{ - LayoutPoint adjustedLocation = accumulatedOffset + location(); - - // Check our bounds next. For this purpose always assume that we can only be hit in the - // foreground phase (which is true for replaced elements like images). - // FIXME: Once we support overflow, we need to intersect with that and not with the bounds rect. - LayoutRect boundsRect = borderBoxRectInRegion(locationInContainer.region()); - boundsRect.moveBy(adjustedLocation); - if (!visibleToHitTesting() || action != HitTestForeground || !locationInContainer.intersects(boundsRect)) - return false; - - // The point is in one specific column. Since columns can't overlap, we don't ever have to test - // multiple columns. Put the - - // FIXME: It would be nice to jump right to the specific column by just doing math on the point. Since columns - // can't overlap, we shouldn't have to walk every column like this. The old column code walked all the columns, though, - // so this is no worse. We'd have to watch out for rect-based hit testing, though, which actually could overlap - // multiple columns. - LayoutUnit colGap = columnGap(); - unsigned colCount = columnCount(); - for (unsigned i = 0; i < colCount; i++) { - // First we get the column rect, which is in our local coordinate space, and we make it physical and apply - // the hit test offset to it. That gives us the physical location that we want to paint the column at. - LayoutRect colRect = columnRectAt(i); - flipForWritingMode(colRect); - colRect.moveBy(adjustedLocation); - - // Next we get the portion of the flow thread that corresponds to this column. - LayoutRect flowThreadPortion = flowThreadPortionRectAt(i); - - // Now get the overflow rect that corresponds to the column. - LayoutRect flowThreadOverflowPortion = flowThreadPortionOverflowRect(flowThreadPortion, i, colCount, colGap); - - // Do the hit test with the computed rects. - if (flowThread()->hitTestFlowThreadPortionInRegion(this, flowThreadPortion, flowThreadOverflowPortion, request, result, locationInContainer, colRect.location())) - return true; - } - - updateHitTestResult(result, locationInContainer.point() - toLayoutSize(adjustedLocation)); - return !result.addNodeToRectBasedTestResult(node(), request, locationInContainer, boundsRect); -} - void RenderMultiColumnSet::repaintFlowThreadContent(const LayoutRect& repaintRect, bool immediate) const { // Figure out the start and end columns and only check within that range so that we don't walk the @@ -372,6 +425,88 @@ void RenderMultiColumnSet::repaintFlowThreadContent(const LayoutRect& repaintRec } } +void RenderMultiColumnSet::collectLayerFragments(LayerFragments& fragments, const LayoutRect& layerBoundingBox, const LayoutRect& dirtyRect) +{ + // Put the layer bounds into flow thread-local coordinates by flipping it first. + LayoutRect layerBoundsInFlowThread(layerBoundingBox); + flowThread()->flipForWritingMode(layerBoundsInFlowThread); + + // Do the same for the dirty rect. + LayoutRect dirtyRectInFlowThread(dirtyRect); + flowThread()->flipForWritingMode(dirtyRectInFlowThread); + + // Now we can compare with the flow thread portions owned by each column. First let's + // see if the rect intersects our flow thread portion at all. + LayoutRect clippedRect(layerBoundsInFlowThread); + clippedRect.intersect(RenderRegion::flowThreadPortionOverflowRect()); + if (clippedRect.isEmpty()) + return; + + // Now we know we intersect at least one column. Let's figure out the logical top and logical + // bottom of the area we're checking. + LayoutUnit layerLogicalTop = isHorizontalWritingMode() ? layerBoundsInFlowThread.y() : layerBoundsInFlowThread.x(); + LayoutUnit layerLogicalBottom = (isHorizontalWritingMode() ? layerBoundsInFlowThread.maxY() : layerBoundsInFlowThread.maxX()) - 1; + + // Figure out the start and end columns and only check within that range so that we don't walk the + // entire column set. + unsigned startColumn = columnIndexAtOffset(layerLogicalTop); + unsigned endColumn = columnIndexAtOffset(layerLogicalBottom); + + LayoutUnit colLogicalWidth = computedColumnWidth(); + LayoutUnit colGap = columnGap(); + unsigned colCount = columnCount(); + + for (unsigned i = startColumn; i <= endColumn; i++) { + // Get the portion of the flow thread that corresponds to this column. + LayoutRect flowThreadPortion = flowThreadPortionRectAt(i); + + // Now get the overflow rect that corresponds to the column. + LayoutRect flowThreadOverflowPortion = flowThreadPortionOverflowRect(flowThreadPortion, i, colCount, colGap); + + // In order to create a fragment we must intersect the portion painted by this column. + LayoutRect clippedRect(layerBoundsInFlowThread); + clippedRect.intersect(flowThreadOverflowPortion); + if (clippedRect.isEmpty()) + continue; + + // We also need to intersect the dirty rect. We have to apply a translation and shift based off + // our column index. + LayoutPoint translationOffset; + LayoutUnit inlineOffset = i * (colLogicalWidth + colGap); + if (!style()->isLeftToRightDirection()) + inlineOffset = -inlineOffset; + translationOffset.setX(inlineOffset); + LayoutUnit blockOffset = isHorizontalWritingMode() ? -flowThreadPortion.y() : -flowThreadPortion.x(); + if (isFlippedBlocksWritingMode(style()->writingMode())) + blockOffset = -blockOffset; + translationOffset.setY(blockOffset); + if (!isHorizontalWritingMode()) + translationOffset = translationOffset.transposedPoint(); + // FIXME: The translation needs to include the multicolumn set's content offset within the + // multicolumn block as well. This won't be an issue until we start creating multiple multicolumn sets. + + // Shift the dirty rect to be in flow thread coordinates with this translation applied. + LayoutRect translatedDirtyRect(dirtyRectInFlowThread); + translatedDirtyRect.moveBy(-translationOffset); + + // See if we intersect the dirty rect. + clippedRect = layerBoundsInFlowThread; + clippedRect.intersect(translatedDirtyRect); + if (clippedRect.isEmpty()) + continue; + + // Something does need to paint in this column. Make a fragment now and supply the physical translation + // offset and the clip rect for the column with that offset applied. + LayerFragment fragment; + fragment.paginationOffset = translationOffset; + + LayoutRect flippedFlowThreadOverflowPortion(flowThreadOverflowPortion); + flipForWritingMode(flippedFlowThreadOverflowPortion); + fragment.paginationClip = flippedFlowThreadOverflowPortion; + fragments.append(fragment); + } +} + const char* RenderMultiColumnSet::renderName() const { return "RenderMultiColumnSet"; diff --git a/Source/WebCore/rendering/RenderMultiColumnSet.h b/Source/WebCore/rendering/RenderMultiColumnSet.h index 88653035c..5f597f7ee 100644 --- a/Source/WebCore/rendering/RenderMultiColumnSet.h +++ b/Source/WebCore/rendering/RenderMultiColumnSet.h @@ -43,8 +43,8 @@ namespace WebCore { // come before and after the span. class RenderMultiColumnSet : public RenderRegionSet { public: - RenderMultiColumnSet(Node*, RenderFlowThread*); - + static RenderMultiColumnSet* createAnonymous(RenderFlowThread*); + virtual bool isRenderMultiColumnSet() const OVERRIDE { return true; } unsigned computedColumnCount() const { return m_computedColumnCount; } @@ -56,10 +56,8 @@ public: m_computedColumnWidth = width; m_computedColumnCount = count; } - void setComputedColumnHeight(LayoutUnit height) - { - m_computedColumnHeight = height; - } + + LayoutUnit heightAdjustedForSetOffset(LayoutUnit height) const; void updateMinimumColumnHeight(LayoutUnit height) { m_minimumColumnHeight = std::max(height, m_minimumColumnHeight); } LayoutUnit minimumColumnHeight() const { return m_minimumColumnHeight; } @@ -84,13 +82,27 @@ public: m_forcedBreakOffset = offsetFromFirstPage; } -private: + // Calculate the column height when contents are supposed to be balanced. If 'initial' is set, + // guess an initial column height; otherwise, stretch the column height a tad. Return true if + // column height changed and another layout pass is required. + bool calculateBalancedHeight(bool initial); + + // Record space shortage (the amount of space that would have been enough to prevent some + // element from being moved to the next column) at a column break. The smallest amount of space + // shortage we find is the amount with which we will stretch the column height, if it turns out + // after layout that the columns weren't tall enough. + void recordSpaceShortage(LayoutUnit spaceShortage); + virtual void updateLogicalWidth() OVERRIDE; - virtual void updateLogicalHeight() OVERRIDE; + + void prepareForLayout(); + +private: + RenderMultiColumnSet(RenderFlowThread*); + virtual void computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues&) const OVERRIDE; - virtual void paintReplaced(PaintInfo&, const LayoutPoint& paintOffset) OVERRIDE; - virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, const HitTestLocation&, const LayoutPoint& accumulatedOffset, HitTestAction) OVERRIDE; + virtual void paintObject(PaintInfo&, const LayoutPoint& paintOffset) OVERRIDE; virtual LayoutUnit pageLogicalWidth() const OVERRIDE { return m_computedColumnWidth; } virtual LayoutUnit pageLogicalHeight() const OVERRIDE { return m_computedColumnHeight; } @@ -99,34 +111,62 @@ private: // FIXME: This will change once we have column sets constrained by enclosing pages, etc. virtual LayoutUnit logicalHeightOfAllFlowThreadContent() const OVERRIDE { return m_computedColumnHeight; } + + // FIXME: For now we return false, but it's likely we will leverage the auto height region code to do column + // balancing. That's why we have an override of this function that is distinct from RenderRegionSet's override. + virtual bool shouldHaveAutoLogicalHeight() const OVERRIDE { return false; } virtual void repaintFlowThreadContent(const LayoutRect& repaintRect, bool immediate) const OVERRIDE; + virtual void collectLayerFragments(LayerFragments&, const LayoutRect& layerBoundingBox, const LayoutRect& dirtyRect) OVERRIDE; + virtual const char* renderName() const; void paintColumnRules(PaintInfo&, const LayoutPoint& paintOffset); - void paintColumnContents(PaintInfo&, const LayoutPoint& paintOffset); LayoutUnit columnGap() const; LayoutRect columnRectAt(unsigned index) const; unsigned columnCount() const; LayoutRect flowThreadPortionRectAt(unsigned index) const; - LayoutRect flowThreadPortionOverflowRect(const LayoutRect& flowThreadPortion, unsigned index, unsigned colCount, int colGap) const; - - unsigned columnIndexAtOffset(LayoutUnit) const; - + LayoutRect flowThreadPortionOverflowRect(const LayoutRect& flowThreadPortion, unsigned index, unsigned colCount, LayoutUnit colGap) const; + + enum ColumnIndexCalculationMode { + ClampToExistingColumns, // Stay within the range of already existing columns. + AssumeNewColumns // Allow column indices outside the range of already existing columns. + }; + unsigned columnIndexAtOffset(LayoutUnit, ColumnIndexCalculationMode = ClampToExistingColumns) const; + + void setAndConstrainColumnHeight(LayoutUnit); + unsigned m_computedColumnCount; LayoutUnit m_computedColumnWidth; LayoutUnit m_computedColumnHeight; // The following variables are used when balancing the column set. + LayoutUnit m_maxColumnHeight; // Maximum column height allowed. + LayoutUnit m_minSpaceShortage; // The smallest amout of space shortage that caused a column break. LayoutUnit m_minimumColumnHeight; unsigned m_forcedBreaksCount; // FIXME: We will ultimately need to cache more information to balance around forced breaks properly. LayoutUnit m_maximumDistanceBetweenForcedBreaks; LayoutUnit m_forcedBreakOffset; }; +inline RenderMultiColumnSet* toRenderMultiColumnSet(RenderObject* object) +{ + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isRenderMultiColumnSet()); + return static_cast<RenderMultiColumnSet*>(object); +} + +inline const RenderMultiColumnSet* toRenderMultiColumnSet(const RenderObject* object) +{ + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isRenderMultiColumnSet()); + return static_cast<const RenderMultiColumnSet*>(object); +} + +// This will catch anyone doing an unnecessary cast. +void toRenderMultiColumnSet(const RenderMultiColumnSet*); + } // namespace WebCore #endif // RenderMultiColumnSet_h diff --git a/Source/WebCore/rendering/RenderNamedFlowThread.cpp b/Source/WebCore/rendering/RenderNamedFlowThread.cpp index d4b9ae5f8..89e4a3156 100644 --- a/Source/WebCore/rendering/RenderNamedFlowThread.cpp +++ b/Source/WebCore/rendering/RenderNamedFlowThread.cpp @@ -26,10 +26,14 @@ #include "config.h" #include "RenderNamedFlowThread.h" +#include "ExceptionCodePlaceholder.h" #include "FlowThreadController.h" #include "InlineTextBox.h" #include "InspectorInstrumentation.h" +#include "NodeRenderingContext.h" +#include "NodeTraversal.h" #include "Position.h" +#include "Range.h" #include "RenderInline.h" #include "RenderRegion.h" #include "RenderText.h" @@ -39,10 +43,19 @@ namespace WebCore { -RenderNamedFlowThread::RenderNamedFlowThread(Node* node, PassRefPtr<WebKitNamedFlow> namedFlow) - : RenderFlowThread(node) +RenderNamedFlowThread* RenderNamedFlowThread::createAnonymous(Document* document, PassRefPtr<WebKitNamedFlow> namedFlow) +{ + ASSERT(document->cssRegionsEnabled()); + RenderNamedFlowThread* renderer = new (document->renderArena()) RenderNamedFlowThread(namedFlow); + renderer->setDocumentForAnonymous(document); + return renderer; +} + +RenderNamedFlowThread::RenderNamedFlowThread(PassRefPtr<WebKitNamedFlow> namedFlow) + : m_overset(true) , m_namedFlow(namedFlow) , m_regionLayoutUpdateEventTimer(this, &RenderNamedFlowThread::regionLayoutUpdateEventTimerFired) + , m_regionOversetChangeEventTimer(this, &RenderNamedFlowThread::regionOversetChangeEventTimerFired) { } @@ -220,27 +233,34 @@ static void addRegionToList(RenderRegionList& regionList, RenderRegion* renderRe } } -void RenderNamedFlowThread::addRegionToThread(RenderRegion* renderRegion) +void RenderNamedFlowThread::addRegionToNamedFlowThread(RenderRegion* renderRegion) { ASSERT(renderRegion); - - resetMarkForDestruction(); - ASSERT(!renderRegion->isValid()); - if (renderRegion->parentNamedFlowThread()) { - if (renderRegion->parentNamedFlowThread()->dependsOn(this)) { - // The order of invalid regions is irrelevant. - m_invalidRegionList.add(renderRegion); - // Register ourself to get a notification when the state changes. - renderRegion->parentNamedFlowThread()->m_observerThreadsSet.add(this); - return; - } + if (renderRegion->parentNamedFlowThread()) addDependencyOnFlowThread(renderRegion->parentNamedFlowThread()); - } renderRegion->setIsValid(true); addRegionToList(m_regionList, renderRegion); +} + +void RenderNamedFlowThread::addRegionToThread(RenderRegion* renderRegion) +{ + ASSERT(renderRegion); + ASSERT(!renderRegion->isValid()); + + resetMarkForDestruction(); + + if (renderRegion->parentNamedFlowThread() && renderRegion->parentNamedFlowThread()->dependsOn(this)) { + // The order of invalid regions is irrelevant. + m_invalidRegionList.add(renderRegion); + // Register ourself to get a notification when the state changes. + renderRegion->parentNamedFlowThread()->m_observerThreadsSet.add(this); + return; + } + + addRegionToNamedFlowThread(renderRegion); invalidateRegions(); } @@ -248,7 +268,6 @@ void RenderNamedFlowThread::addRegionToThread(RenderRegion* renderRegion) void RenderNamedFlowThread::removeRegionFromThread(RenderRegion* renderRegion) { ASSERT(renderRegion); - m_regionRangeMap.clear(); if (renderRegion->parentNamedFlowThread()) { if (!renderRegion->isValid()) { @@ -275,6 +294,54 @@ void RenderNamedFlowThread::removeRegionFromThread(RenderRegion* renderRegion) invalidateRegions(); } +void RenderNamedFlowThread::computeOversetStateForRegions(LayoutUnit oldClientAfterEdge) +{ + LayoutUnit height = oldClientAfterEdge; + + // FIXME: the visual overflow of middle region (if it is the last one to contain any content in a render flow thread) + // might not be taken into account because the render flow thread height is greater that that regions height + its visual overflow + // because of how computeLogicalHeight is implemented for RenderNamedFlowThread (as a sum of all regions height). + // This means that the middle region will be marked as fit (even if it has visual overflow flowing into the next region) + if (hasRenderOverflow() + && ( (isHorizontalWritingMode() && visualOverflowRect().maxY() > clientBoxRect().maxY()) + || (!isHorizontalWritingMode() && visualOverflowRect().maxX() > clientBoxRect().maxX()))) + height = isHorizontalWritingMode() ? visualOverflowRect().maxY() : visualOverflowRect().maxX(); + + RenderRegion* lastReg = lastRegion(); + for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { + RenderRegion* region = *iter; + LayoutUnit flowMin = height - (isHorizontalWritingMode() ? region->flowThreadPortionRect().y() : region->flowThreadPortionRect().x()); + LayoutUnit flowMax = height - (isHorizontalWritingMode() ? region->flowThreadPortionRect().maxY() : region->flowThreadPortionRect().maxX()); + RegionOversetState previousState = region->regionOversetState(); + RegionOversetState state = RegionFit; + if (flowMin <= 0) + state = RegionEmpty; + if (flowMax > 0 && region == lastReg) + state = RegionOverset; + region->setRegionOversetState(state); + // determine whether the NamedFlow object should dispatch a regionLayoutUpdate event + // FIXME: currently it cannot determine whether a region whose regionOverset state remained either "fit" or "overset" has actually + // changed, so it just assumes that the NamedFlow should dispatch the event + if (previousState != state + || state == RegionFit + || state == RegionOverset) + setDispatchRegionLayoutUpdateEvent(true); + + if (previousState != state) + setDispatchRegionOversetChangeEvent(true); + } + + // If the number of regions has changed since we last computed the overset property, schedule the regionOversetChange event. + if (previousRegionCountChanged()) { + setDispatchRegionOversetChangeEvent(true); + updatePreviousRegionCount(); + } + + // With the regions overflow state computed we can also set the overset flag for the named flow. + // If there are no valid regions in the chain, overset is true. + m_overset = lastReg ? lastReg->regionOversetState() == RegionOverset : true; +} + void RenderNamedFlowThread::checkInvalidRegions() { Vector<RenderRegion*> newValidRegions; @@ -292,9 +359,7 @@ void RenderNamedFlowThread::checkInvalidRegions() RenderRegion* region = *iter; m_invalidRegionList.remove(region); region->parentNamedFlowThread()->m_observerThreadsSet.remove(this); - region->setIsValid(true); - addDependencyOnFlowThread(region->parentNamedFlowThread()); - addRegionToList(m_regionList, region); + addRegionToNamedFlowThread(region); } if (!newValidRegions.isEmpty()) @@ -387,6 +452,22 @@ const AtomicString& RenderNamedFlowThread::flowThreadName() const return m_namedFlow->name(); } +bool RenderNamedFlowThread::isChildAllowed(RenderObject* child, RenderStyle* style) const +{ + ASSERT(child); + ASSERT(style); + + if (!child->node()) + return true; + + ASSERT(child->node()->isElementNode()); + RenderObject* parentRenderer = NodeRenderingContext(child->node()).parentRenderer(); + if (!parentRenderer) + return true; + + return parentRenderer->isChildAllowed(child, style); +} + void RenderNamedFlowThread::dispatchRegionLayoutUpdateEvent() { RenderFlowThread::dispatchRegionLayoutUpdateEvent(); @@ -396,6 +477,15 @@ void RenderNamedFlowThread::dispatchRegionLayoutUpdateEvent() m_regionLayoutUpdateEventTimer.startOneShot(0); } +void RenderNamedFlowThread::dispatchRegionOversetChangeEvent() +{ + RenderFlowThread::dispatchRegionOversetChangeEvent(); + InspectorInstrumentation::didChangeRegionOverset(document(), m_namedFlow.get()); + + if (!m_regionOversetChangeEventTimer.isActive() && m_namedFlow->hasEventListeners()) + m_regionOversetChangeEventTimer.startOneShot(0); +} + void RenderNamedFlowThread::regionLayoutUpdateEventTimerFired(Timer<RenderNamedFlowThread>*) { ASSERT(m_namedFlow); @@ -403,6 +493,13 @@ void RenderNamedFlowThread::regionLayoutUpdateEventTimerFired(Timer<RenderNamedF m_namedFlow->dispatchRegionLayoutUpdateEvent(); } +void RenderNamedFlowThread::regionOversetChangeEventTimerFired(Timer<RenderNamedFlowThread>*) +{ + ASSERT(m_namedFlow); + + m_namedFlow->dispatchRegionOversetChangeEvent(); +} + void RenderNamedFlowThread::setMarkForDestruction() { if (m_namedFlow->flowState() == WebKitNamedFlow::FlowStateNull) @@ -476,7 +573,6 @@ void RenderNamedFlowThread::getRanges(Vector<RefPtr<Range> >& rangeObjects, cons if (!contentNode->renderer()) continue; - ExceptionCode ignoredException; RefPtr<Range> range = Range::create(contentNode->document()); bool foundStartPosition = false; bool startsAboveRegion = true; @@ -484,7 +580,7 @@ void RenderNamedFlowThread::getRanges(Vector<RefPtr<Range> >& rangeObjects, cons bool skipOverOutsideNodes = false; Node* lastEndNode = 0; - for (Node* node = contentNode; node; node = node->traverseNextNode(contentNode)) { + for (Node* node = contentNode; node; node = NodeTraversal::next(node, contentNode)) { RenderObject* renderer = node->renderer(); if (!renderer) continue; @@ -515,16 +611,16 @@ void RenderNamedFlowThread::getRanges(Vector<RefPtr<Range> >& rangeObjects, cons if (!boxIntersectsRegion(logicalTopForRenderer, logicalBottomForRenderer, logicalTopForRegion, logicalBottomForRegion)) { if (foundStartPosition) { if (!startsAboveRegion) { - if (range->intersectsNode(node, ignoredException)) - range->setEndBefore(node, ignoredException); - rangeObjects.append(range->cloneRange(ignoredException)); + if (range->intersectsNode(node, IGNORE_EXCEPTION)) + range->setEndBefore(node, IGNORE_EXCEPTION); + rangeObjects.append(range->cloneRange(IGNORE_EXCEPTION)); range = Range::create(contentNode->document()); startsAboveRegion = true; } else skipOverOutsideNodes = true; } if (skipOverOutsideNodes) - range->setStartAfter(node, ignoredException); + range->setStartAfter(node, IGNORE_EXCEPTION); foundStartPosition = false; continue; } @@ -551,7 +647,7 @@ void RenderNamedFlowThread::getRanges(Vector<RefPtr<Range> >& rangeObjects, cons // the range is closed. if (startsAboveRegion) { startsAboveRegion = false; - range->setStartBefore(node, ignoredException); + range->setStartBefore(node, IGNORE_EXCEPTION); } } skipOverOutsideNodes = false; @@ -585,7 +681,7 @@ void RenderNamedFlowThread::getRanges(Vector<RefPtr<Range> >& rangeObjects, cons // for elements that ends inside the region, set the end position to be after them // allow this end position to be changed only by other elements that are not descendants of the current end node if (endsBelowRegion || (!endsBelowRegion && !node->isDescendantOf(lastEndNode))) { - range->setEndAfter(node, ignoredException); + range->setEndAfter(node, IGNORE_EXCEPTION); endsBelowRegion = false; lastEndNode = node; } diff --git a/Source/WebCore/rendering/RenderNamedFlowThread.h b/Source/WebCore/rendering/RenderNamedFlowThread.h index 2bd438bbc..c0c493ec8 100644 --- a/Source/WebCore/rendering/RenderNamedFlowThread.h +++ b/Source/WebCore/rendering/RenderNamedFlowThread.h @@ -45,9 +45,10 @@ typedef ListHashSet<Node*> NamedFlowContentNodes; class RenderNamedFlowThread : public RenderFlowThread { public: - RenderNamedFlowThread(Node*, PassRefPtr<WebKitNamedFlow>); virtual ~RenderNamedFlowThread(); + static RenderNamedFlowThread* createAnonymous(Document*, PassRefPtr<WebKitNamedFlow>); + const AtomicString& flowThreadName() const; const RenderRegionList& invalidRenderRegionList() const { return m_invalidRegionList; } @@ -67,6 +68,9 @@ public: virtual void addRegionToThread(RenderRegion*) OVERRIDE; virtual void removeRegionFromThread(RenderRegion*) OVERRIDE; + bool overset() const { return m_overset; } + void computeOversetStateForRegions(LayoutUnit oldClientAfterEdge); + void registerNamedFlowContentNode(Node*); void unregisterNamedFlowContentNode(Node*); const NamedFlowContentNodes& contentNodes() const { return m_contentNodes; } @@ -79,17 +83,26 @@ protected: void resetMarkForDestruction(); private: + RenderNamedFlowThread(PassRefPtr<WebKitNamedFlow>); + virtual const char* renderName() const OVERRIDE; virtual bool isRenderNamedFlowThread() const OVERRIDE { return true; } + virtual bool isChildAllowed(RenderObject*, RenderStyle*) const OVERRIDE; virtual void dispatchRegionLayoutUpdateEvent() OVERRIDE; + virtual void dispatchRegionOversetChangeEvent() OVERRIDE; bool dependsOn(RenderNamedFlowThread* otherRenderFlowThread) const; void addDependencyOnFlowThread(RenderNamedFlowThread*); void removeDependencyOnFlowThread(RenderNamedFlowThread*); + + void addRegionToNamedFlowThread(RenderRegion*); + void checkInvalidRegions(); + bool canBeDestroyed() const { return m_invalidRegionList.isEmpty() && m_regionList.isEmpty() && m_contentNodes.isEmpty(); } void regionLayoutUpdateEventTimerFired(Timer<RenderNamedFlowThread>*); + void regionOversetChangeEventTimerFired(Timer<RenderNamedFlowThread>*); void clearContentNodes(); private: @@ -111,21 +124,24 @@ private: RenderRegionList m_invalidRegionList; + bool m_overset : 1; + // The DOM Object that represents a named flow. RefPtr<WebKitNamedFlow> m_namedFlow; Timer<RenderNamedFlowThread> m_regionLayoutUpdateEventTimer; + Timer<RenderNamedFlowThread> m_regionOversetChangeEventTimer; }; inline RenderNamedFlowThread* toRenderNamedFlowThread(RenderObject* object) { - ASSERT(!object || object->isRenderNamedFlowThread()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isRenderNamedFlowThread()); return static_cast<RenderNamedFlowThread*>(object); } inline const RenderNamedFlowThread* toRenderNamedFlowThread(const RenderObject* object) { - ASSERT(!object || object->isRenderNamedFlowThread()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isRenderNamedFlowThread()); return static_cast<const RenderNamedFlowThread*>(object); } diff --git a/Source/WebCore/rendering/RenderObject.cpp b/Source/WebCore/rendering/RenderObject.cpp index 17bdafb54..b93764828 100644 --- a/Source/WebCore/rendering/RenderObject.cpp +++ b/Source/WebCore/rendering/RenderObject.cpp @@ -28,19 +28,23 @@ #include "RenderObject.h" #include "AXObjectCache.h" -#include "Chrome.h" +#include "AnimationController.h" #include "ContentData.h" #include "CursorList.h" -#include "DashArray.h" -#include "EditingBoundary.h" +#include "EventHandler.h" #include "FloatQuad.h" #include "FlowThreadController.h" #include "Frame.h" +#include "FrameSelection.h" #include "FrameView.h" #include "GraphicsContext.h" +#include "HTMLAnchorElement.h" #include "HTMLElement.h" +#include "HTMLImageElement.h" #include "HTMLNames.h" +#include "HTMLTableElement.h" #include "HitTestResult.h" +#include "LogicalSelectionOffsetCaches.h" #include "Page.h" #include "RenderArena.h" #include "RenderCounter.h" @@ -52,6 +56,7 @@ #include "RenderImageResourceStyleImage.h" #include "RenderInline.h" #include "RenderLayer.h" +#include "RenderLayerBacking.h" #include "RenderListItem.h" #include "RenderMultiColumnBlock.h" #include "RenderNamedFlowThread.h" @@ -70,9 +75,8 @@ #include "TransformState.h" #include "htmlediting.h" #include <algorithm> -#include <stdio.h> #include <wtf/RefCountedLeakCounter.h> -#include <wtf/UnusedParam.h> +#include <wtf/StackStats.h> #if USE(ACCELERATED_COMPOSITING) #include "RenderLayerCompositor.h" @@ -92,11 +96,11 @@ using namespace HTMLNames; #ifndef NDEBUG static void* baseOfRenderObjectBeingDeleted; -RenderObject::SetLayoutNeededForbiddenScope::SetLayoutNeededForbiddenScope(RenderObject* renderObject) +RenderObject::SetLayoutNeededForbiddenScope::SetLayoutNeededForbiddenScope(RenderObject* renderObject, bool isForbidden) : m_renderObject(renderObject) , m_preexistingForbidden(m_renderObject->isSetNeedsLayoutForbidden()) { - m_renderObject->setNeedsLayoutIsForbidden(true); + m_renderObject->setNeedsLayoutIsForbidden(isForbidden); } RenderObject::SetLayoutNeededForbiddenScope::~SetLayoutNeededForbiddenScope() @@ -116,7 +120,30 @@ struct SameSizeAsRenderObject { COMPILE_ASSERT(sizeof(RenderObject) == sizeof(SameSizeAsRenderObject), RenderObject_should_stay_small); +// On low-powered/mobile devices, preventing blitting on a scroll can cause noticeable delays +// when scrolling a page with a fixed background image. As an optimization, assuming there are +// no fixed positoned elements on the page, we can acclerate scrolling (via blitting) if we +// ignore the CSS property "background-attachment: fixed". +static bool shouldRepaintFixedBackgroundsOnScroll(FrameView* frameView) +{ +#if !ENABLE(FAST_MOBILE_SCROLLING) && !PLATFORM(QT) + UNUSED_PARAM(frameView); +#endif + + bool repaintFixedBackgroundsOnScroll = true; +#if ENABLE(FAST_MOBILE_SCROLLING) +#if PLATFORM(QT) + if (frameView->delegatesScrolling()) + repaintFixedBackgroundsOnScroll = false; +#else + repaintFixedBackgroundsOnScroll = false; +#endif +#endif + return repaintFixedBackgroundsOnScroll; +} + bool RenderObject::s_affectsParentBlock = false; +bool RenderObject::s_noLongerAffectsParentBlock = false; RenderObjectAncestorLineboxDirtySet* RenderObject::s_ancestorLineboxDirtySet = 0; @@ -133,17 +160,17 @@ void RenderObject::operator delete(void* ptr, size_t sz) *(size_t *)ptr = sz; } -RenderObject* RenderObject::createObject(Node* node, RenderStyle* style) +RenderObject* RenderObject::createObject(Element* element, RenderStyle* style) { - Document* doc = node->document(); + Document* doc = element->document(); RenderArena* arena = doc->renderArena(); // Minimal support for content properties replacing an entire element. // Works only if we have exactly one piece of content and it's a URL. // Otherwise acts as if we didn't support this feature. const ContentData* contentData = style->contentData(); - if (contentData && !contentData->next() && contentData->isImage() && doc != node) { - RenderImage* image = new (arena) RenderImage(node); + if (contentData && !contentData->next() && contentData->isImage() && !element->isPseudoElement()) { + RenderImage* image = new (arena) RenderImage(element); // RenderImageResourceStyleImage requires a style being present on the image but we don't want to // trigger a style change now as the node is not fully attached. Moving this code to style change // doesn't make sense as it should be run once at renderer creation. @@ -157,56 +184,56 @@ RenderObject* RenderObject::createObject(Node* node, RenderStyle* style) return image; } - if (node->hasTagName(rubyTag)) { + if (element->hasTagName(rubyTag)) { if (style->display() == INLINE) - return new (arena) RenderRubyAsInline(node); + return new (arena) RenderRubyAsInline(element); else if (style->display() == BLOCK) - return new (arena) RenderRubyAsBlock(node); + return new (arena) RenderRubyAsBlock(element); } // treat <rt> as ruby text ONLY if it still has its default treatment of block - if (node->hasTagName(rtTag) && style->display() == BLOCK) - return new (arena) RenderRubyText(node); + if (element->hasTagName(rtTag) && style->display() == BLOCK) + return new (arena) RenderRubyText(element); if (doc->cssRegionsEnabled() && style->isDisplayRegionType() && !style->regionThread().isEmpty() && doc->renderView()) - return new (arena) RenderRegion(node, 0); + return new (arena) RenderRegion(element, 0); switch (style->display()) { case NONE: return 0; case INLINE: - return new (arena) RenderInline(node); + return new (arena) RenderInline(element); case BLOCK: case INLINE_BLOCK: case RUN_IN: case COMPACT: if ((!style->hasAutoColumnCount() || !style->hasAutoColumnWidth()) && doc->regionBasedColumnsEnabled()) - return new (arena) RenderMultiColumnBlock(node); - return new (arena) RenderBlock(node); + return new (arena) RenderMultiColumnBlock(element); + return new (arena) RenderBlock(element); case LIST_ITEM: - return new (arena) RenderListItem(node); + return new (arena) RenderListItem(element); case TABLE: case INLINE_TABLE: - return new (arena) RenderTable(node); + return new (arena) RenderTable(element); case TABLE_ROW_GROUP: case TABLE_HEADER_GROUP: case TABLE_FOOTER_GROUP: - return new (arena) RenderTableSection(node); + return new (arena) RenderTableSection(element); case TABLE_ROW: - return new (arena) RenderTableRow(node); + return new (arena) RenderTableRow(element); case TABLE_COLUMN_GROUP: case TABLE_COLUMN: - return new (arena) RenderTableCol(node); + return new (arena) RenderTableCol(element); case TABLE_CELL: - return new (arena) RenderTableCell(node); + return new (arena) RenderTableCell(element); case TABLE_CAPTION: - return new (arena) RenderTableCaption(node); + return new (arena) RenderTableCaption(element); case BOX: case INLINE_BOX: - return new (arena) RenderDeprecatedFlexibleBox(node); + return new (arena) RenderDeprecatedFlexibleBox(element); case FLEX: case INLINE_FLEX: - return new (arena) RenderFlexibleBox(node); + return new (arena) RenderFlexibleBox(element); case GRID: case INLINE_GRID: - return new (arena) RenderGrid(node); + return new (arena) RenderGrid(element); } return 0; @@ -230,7 +257,6 @@ RenderObject::RenderObject(Node* node) #ifndef NDEBUG renderObjectCounter.increment(); #endif - ASSERT(node); } RenderObject::~RenderObject() @@ -277,6 +303,19 @@ bool RenderObject::isHTMLMarquee() const return node() && node()->renderer() == this && node()->hasTagName(marqueeTag); } +void RenderObject::setFlowThreadStateIncludingDescendants(FlowThreadState state) +{ + setFlowThreadState(state); + + for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { + // If the child is a fragmentation context it already updated the descendants flag accordingly. + if (child->isRenderFlowThread()) + continue; + ASSERT(state != child->flowThreadState()); + child->setFlowThreadStateIncludingDescendants(state); + } +} + void RenderObject::addChild(RenderObject* newChild, RenderObject* beforeChild) { RenderObjectChildList* children = virtualChildren(); @@ -444,7 +483,7 @@ static void addLayers(RenderObject* obj, RenderLayer* parentLayer, RenderObject* { if (obj->hasLayer()) { if (!beforeChild && newObject) { - // We need to figure out the layer that follows newObject. We only do + // We need to figure out the layer that follows newObject. We only do // this the first time we find a child layer, and then we update the // pointer values for newObject and beforeChild used by everyone else. beforeChild = newObject->parent()->findNextLayer(parentLayer, newObject); @@ -503,7 +542,7 @@ void RenderObject::moveLayers(RenderLayer* oldParent, RenderLayer* newParent) RenderLayer* RenderObject::findNextLayer(RenderLayer* parentLayer, RenderObject* startPoint, bool checkParent) { - // Error check the parent layer passed in. If it's null, we can't find anything. + // Error check the parent layer passed in. If it's null, we can't find anything. if (!parentLayer) return 0; @@ -523,7 +562,7 @@ RenderLayer* RenderObject::findNextLayer(RenderLayer* parentLayer, RenderObject* } } - // Step 3: If our layer is the desired parent layer, then we're finished. We didn't + // Step 3: If our layer is the desired parent layer, then we're finished. We didn't // find anything. if (parentLayer == ourLayer) return 0; @@ -584,11 +623,10 @@ RenderBoxModelObject* RenderObject::enclosingBoxModelObject() const return 0; } -RenderFlowThread* RenderObject::enclosingRenderFlowThread() const -{ - if (!inRenderFlowThread()) - return 0; - +RenderFlowThread* RenderObject::locateFlowThreadContainingBlock() const +{ + ASSERT(flowThreadState() != NotInsideFlowThread); + // See if we have the thread cached because we're in the middle of layout. RenderFlowThread* flowThread = view()->flowThreadController()->currentRenderFlowThread(); if (flowThread) @@ -599,7 +637,7 @@ RenderFlowThread* RenderObject::enclosingRenderFlowThread() const while (curr) { if (curr->isRenderFlowThread()) return toRenderFlowThread(curr); - curr = curr->parent(); + curr = curr->containingBlock(); } return 0; } @@ -645,6 +683,7 @@ static inline bool objectIsRelayoutBoundary(const RenderObject* object) void RenderObject::markContainingBlocksForLayout(bool scheduleRelayout, RenderObject* newRoot) { ASSERT(!scheduleRelayout || !newRoot); + ASSERT(!isSetNeedsLayoutForbidden()); RenderObject* object = container(); RenderObject* last = this; @@ -652,6 +691,11 @@ void RenderObject::markContainingBlocksForLayout(bool scheduleRelayout, RenderOb bool simplifiedNormalFlowLayout = needsSimplifiedNormalFlowLayout() && !selfNeedsLayout() && !normalChildNeedsLayout(); while (object) { +#ifndef NDEBUG + // FIXME: Remove this once we remove the special cases for counters, quotes and mathml + // calling setNeedsLayout during preferred width computation. + SetLayoutNeededForbiddenScope layoutForbiddenScope(object, isSetNeedsLayoutForbidden()); +#endif // Don't mark the outermost object of an unrooted subtree. That object will be // marked when the subtree is added to the document. RenderObject* container = object->container(); @@ -750,56 +794,13 @@ RenderBlock* RenderObject::containingBlock() const RenderObject* o = parent(); if (!o && isRenderScrollbarPart()) o = toRenderScrollbarPart(this)->rendererOwningScrollbar(); - if (!isText() && m_style->position() == FixedPosition) { - while (o) { - if (o->isRenderView()) - break; - if (o->hasTransform() && o->isRenderBlock()) - break; - // The render flow thread is the top most containing block - // for the fixed positioned elements. - if (o->isRenderFlowThread()) - break; -#if ENABLE(SVG) - // foreignObject is the containing block for its contents. - if (o->isSVGForeignObject()) - break; -#endif - o = o->parent(); - } - ASSERT(!o->isAnonymousBlock()); - } else if (!isText() && m_style->position() == AbsolutePosition) { - while (o) { - // For relpositioned inlines, we return the nearest non-anonymous enclosing block. We don't try - // to return the inline itself. This allows us to avoid having a positioned objects - // list in all RenderInlines and lets us return a strongly-typed RenderBlock* result - // from this method. The container() method can actually be used to obtain the - // inline directly. - if (!o->style()->position() == StaticPosition && !(o->isInline() && !o->isReplaced())) - break; - if (o->isRenderView()) - break; - if (o->hasTransform() && o->isRenderBlock()) - break; - if (o->style()->hasInFlowPosition() && o->isInline() && !o->isReplaced()) { - o = o->containingBlock(); - break; - } -#if ENABLE(SVG) - if (o->isSVGForeignObject()) //foreignObject is the containing block for contents inside it - break; -#endif - - o = o->parent(); - } - - while (o && o->isAnonymousBlock()) - o = o->containingBlock(); - } else { - while (o && ((o->isInline() && !o->isReplaced()) || !o->isRenderBlock())) - o = o->parent(); - } + if (!isText() && m_style->position() == FixedPosition) + o = containingBlockForFixedPosition(o); + else if (!isText() && m_style->position() == AbsolutePosition) + o = containingBlockForAbsolutePosition(o); + else + o = containingBlockForObjectInFlow(o); if (!o || !o->isRenderBlock()) return 0; // This can still happen in case of an orphaned tree @@ -821,10 +822,19 @@ static bool mustRepaintFillLayers(const RenderObject* renderer, const FillLayer* if (!layer->xPosition().isZero() || !layer->yPosition().isZero()) return true; - if (layer->size().type == SizeLength) { - if (layer->size().size.width().isPercent() || layer->size().size.height().isPercent()) + EFillSizeType sizeType = layer->sizeType(); + + if (sizeType == Contain || sizeType == Cover) + return true; + + if (sizeType == SizeLength) { + LengthSize size = layer->sizeLength(); + if (size.width().isPercent() || size.height().isPercent()) return true; - } else if (layer->size().type == Contain || layer->size().type == Cover || img->usesImageContainerSize()) + // If the image has neither an intrinsic width nor an intrinsic height, its size is determined as for 'contain'. + if ((size.width().isAuto() || size.height().isAuto()) && img->isGeneratedImage()) + return true; + } else if (img->usesImageContainerSize()) return true; return false; @@ -885,14 +895,13 @@ void RenderObject::drawLineForBoxSide(GraphicsContext* graphicsContext, int x1, return; case DOTTED: case DASHED: { - graphicsContext->setStrokeColor(color, m_style->colorSpace()); - graphicsContext->setStrokeThickness(thickness); - StrokeStyle oldStrokeStyle = graphicsContext->strokeStyle(); - graphicsContext->setStrokeStyle(style == DASHED ? DashedStroke : DottedStroke); - if (thickness > 0) { bool wasAntialiased = graphicsContext->shouldAntialias(); + StrokeStyle oldStrokeStyle = graphicsContext->strokeStyle(); graphicsContext->setShouldAntialias(antialias); + graphicsContext->setStrokeColor(color, m_style->colorSpace()); + graphicsContext->setStrokeThickness(thickness); + graphicsContext->setStrokeStyle(style == DASHED ? DashedStroke : DottedStroke); switch (side) { case BSBottom: @@ -1086,14 +1095,14 @@ void RenderObject::drawLineForBoxSide(GraphicsContext* graphicsContext, int x1, } } -void RenderObject::paintFocusRing(GraphicsContext* context, const LayoutPoint& paintOffset, RenderStyle* style) +void RenderObject::paintFocusRing(PaintInfo& paintInfo, const LayoutPoint& paintOffset, RenderStyle* style) { Vector<IntRect> focusRingRects; - addFocusRingRects(focusRingRects, paintOffset); + addFocusRingRects(focusRingRects, paintOffset, paintInfo.paintContainer); if (style->outlineStyleIsAuto()) - context->drawFocusRing(focusRingRects, style->outlineWidth(), style->outlineOffset(), style->visitedDependentColor(CSSPropertyOutlineColor)); + paintInfo.context->drawFocusRing(focusRingRects, style->outlineWidth(), style->outlineOffset(), style->visitedDependentColor(CSSPropertyOutlineColor)); else - addPDFURLRect(context, unionRect(focusRingRects)); + addPDFURLRect(paintInfo.context, unionRect(focusRingRects)); } void RenderObject::addPDFURLRect(GraphicsContext* context, const LayoutRect& rect) @@ -1103,29 +1112,26 @@ void RenderObject::addPDFURLRect(GraphicsContext* context, const LayoutRect& rec Node* n = node(); if (!n || !n->isLink() || !n->isElementNode()) return; - const AtomicString& href = static_cast<Element*>(n)->getAttribute(hrefAttr); + const AtomicString& href = toElement(n)->getAttribute(hrefAttr); if (href.isNull()) return; context->setURLForRect(n->document()->completeURL(href), pixelSnappedIntRect(rect)); } -void RenderObject::paintOutline(GraphicsContext* graphicsContext, const LayoutRect& paintRect) +void RenderObject::paintOutline(PaintInfo& paintInfo, const LayoutRect& paintRect) { if (!hasOutline()) return; RenderStyle* styleToUse = style(); LayoutUnit outlineWidth = styleToUse->outlineWidth(); - EBorderStyle outlineStyle = styleToUse->outlineStyle(); - - Color outlineColor = styleToUse->visitedDependentColor(CSSPropertyOutlineColor); int outlineOffset = styleToUse->outlineOffset(); if (styleToUse->outlineStyleIsAuto() || hasOutlineAnnotation()) { if (!theme()->supportsFocusRing(styleToUse)) { // Only paint the focus ring by hand if the theme isn't able to draw the focus ring. - paintFocusRing(graphicsContext, paintRect.location(), styleToUse); + paintFocusRing(paintInfo, paintRect.location(), styleToUse); } } @@ -1142,6 +1148,10 @@ void RenderObject::paintOutline(GraphicsContext* graphicsContext, const LayoutRe if (outer.isEmpty()) return; + EBorderStyle outlineStyle = styleToUse->outlineStyle(); + Color outlineColor = styleToUse->visitedDependentColor(CSSPropertyOutlineColor); + + GraphicsContext* graphicsContext = paintInfo.context; bool useTransparencyLayer = outlineColor.hasAlpha(); if (useTransparencyLayer) { if (outlineStyle == SOLID) { @@ -1214,11 +1224,11 @@ void RenderObject::absoluteFocusRingQuads(Vector<FloatQuad>& quads) // descendants. FloatPoint absolutePoint = localToAbsolute(); addFocusRingRects(rects, flooredLayoutPoint(absolutePoint)); - size_t count = rects.size(); + size_t count = rects.size(); for (size_t i = 0; i < count; ++i) { IntRect rect = rects[i]; rect.move(-absolutePoint.x(), -absolutePoint.y()); - quads.append(localToAbsoluteQuad(FloatQuad(rect), SnapOffsetForTransforms)); + quads.append(localToAbsoluteQuad(FloatQuad(rect))); } } @@ -1233,9 +1243,12 @@ FloatRect RenderObject::absoluteBoundingBoxRectForRange(const Range* range) Vector<FloatQuad> quads; range->textQuads(quads); - FloatRect result; - for (size_t i = 0; i < quads.size(); ++i) - result.unite(quads[i].boundingBox()); + if (quads.isEmpty()) + return FloatRect(); + + FloatRect result = quads[0].boundingBox(); + for (size_t i = 1; i < quads.size(); ++i) + result.uniteEvenIfEmpty(quads[i].boundingBox()); return result; } @@ -1248,6 +1261,7 @@ void RenderObject::addAbsoluteRectForLayer(LayoutRect& result) current->addAbsoluteRectForLayer(result); } +// FIXME: change this to use the subtreePaint terminology LayoutRect RenderObject::paintingRootRect(LayoutRect& topLevelRect) { LayoutRect result = absoluteBoundingBoxRectIgnoringTransforms(); @@ -1292,11 +1306,16 @@ RenderLayerModelObject* RenderObject::containerForRepaint() const // If we have a flow thread, then we need to do individual repaints within the RenderRegions instead. // Return the flow thread as a repaint container in order to create a chokepoint that allows us to change // repainting to do individual region repaints. - if (inRenderFlowThread()) { - RenderFlowThread* parentRenderFlowThread = enclosingRenderFlowThread(); + RenderFlowThread* parentRenderFlowThread = flowThreadContainingBlock(); + if (parentRenderFlowThread) { + // The ancestor document will do the reparenting when the repaint propagates further up. + // We're just a seamless child document, and we don't need to do the hacking. + if (parentRenderFlowThread && parentRenderFlowThread->document() != document()) + return repaintContainer; // If we have already found a repaint container then we will repaint into that container only if it is part of the same // flow thread. Otherwise we will need to catch the repaint call and send it to the flow thread. - if (!(repaintContainer && repaintContainer->inRenderFlowThread() && repaintContainer->enclosingRenderFlowThread() == parentRenderFlowThread)) + RenderFlowThread* repaintContainerFlowThread = repaintContainer ? repaintContainer->flowThreadContainingBlock() : 0; + if (!repaintContainerFlowThread || repaintContainerFlowThread != parentRenderFlowThread) repaintContainer = parentRenderFlowThread; } return repaintContainer; @@ -1327,10 +1346,7 @@ void RenderObject::repaintUsingContainer(const RenderLayerModelObject* repaintCo ASSERT(repaintContainer == v); bool viewHasCompositedLayer = v->hasLayer() && v->layer()->isComposited(); if (!viewHasCompositedLayer || v->layer()->backing()->paintsIntoWindow()) { - LayoutRect repaintRectangle = r; - if (viewHasCompositedLayer && v->layer()->transform()) - repaintRectangle = enclosingIntRect(v->layer()->transform()->mapRect(r)); - v->repaintViewRectangle(repaintRectangle, immediate); + v->repaintViewRectangle(viewHasCompositedLayer && v->layer()->transform() ? v->layer()->transform()->mapRect(r) : r, immediate); return; } } @@ -1496,14 +1512,6 @@ bool RenderObject::repaintAfterLayoutIfNeeded(const RenderLayerModelObject* repa return false; } -void RenderObject::repaintDuringLayoutIfMoved(const LayoutRect&) -{ -} - -void RenderObject::repaintOverhangingFloats(bool) -{ -} - bool RenderObject::checkForRepaintDuringLayout() const { return !document()->view()->needsFullRepaint() && !hasLayer() && everHadLayout(); @@ -1623,13 +1631,15 @@ Color RenderObject::selectionBackgroundColor() const { Color color; if (style()->userSelect() != SELECT_NONE) { - RefPtr<RenderStyle> pseudoStyle = getUncachedPseudoStyle(SELECTION); - if (pseudoStyle && pseudoStyle->visitedDependentColor(CSSPropertyBackgroundColor).isValid()) - color = pseudoStyle->visitedDependentColor(CSSPropertyBackgroundColor).blendWithWhite(); - else - color = frame()->selection()->isFocusedAndActive() ? - theme()->activeSelectionBackgroundColor() : - theme()->inactiveSelectionBackgroundColor(); + if (frame()->selection()->shouldShowBlockCursor() && frame()->selection()->isCaret()) + color = style()->visitedDependentColor(CSSPropertyColor).blendWithWhite(); + else { + RefPtr<RenderStyle> pseudoStyle = getUncachedPseudoStyle(PseudoStyleRequest(SELECTION)); + if (pseudoStyle && pseudoStyle->visitedDependentColor(CSSPropertyBackgroundColor).isValid()) + color = pseudoStyle->visitedDependentColor(CSSPropertyBackgroundColor).blendWithWhite(); + else + color = frame()->selection()->isFocusedAndActive() ? theme()->activeSelectionBackgroundColor() : theme()->inactiveSelectionBackgroundColor(); + } } return color; @@ -1644,7 +1654,7 @@ Color RenderObject::selectionColor(int colorProperty) const || (frame()->view()->paintBehavior() & PaintBehaviorSelectionOnly)) return color; - if (RefPtr<RenderStyle> pseudoStyle = getUncachedPseudoStyle(SELECTION)) { + if (RefPtr<RenderStyle> pseudoStyle = getUncachedPseudoStyle(PseudoStyleRequest(SELECTION))) { color = pseudoStyle->visitedDependentColor(colorProperty); if (!color.isValid()) color = pseudoStyle->visitedDependentColor(CSSPropertyColor); @@ -1690,6 +1700,30 @@ void RenderObject::handleDynamicFloatPositionChange() } } +void RenderObject::removeAnonymousWrappersForInlinesIfNecessary() +{ + // We have changed to floated or out-of-flow positioning so maybe all our parent's + // children can be inline now. Bail if there are any block children left on the line, + // otherwise we can proceed to stripping solitary anonymous wrappers from the inlines. + // FIXME: We should also handle split inlines here - we exclude them at the moment by returning + // if we find a continuation. + RenderObject* curr = parent()->firstChild(); + while (curr && ((curr->isAnonymousBlock() && !toRenderBlock(curr)->isAnonymousBlockContinuation()) || curr->style()->isFloating() || curr->style()->hasOutOfFlowPosition())) + curr = curr->nextSibling(); + + if (curr) + return; + + curr = parent()->firstChild(); + RenderBlock* parentBlock = toRenderBlock(parent()); + while (curr) { + RenderObject* next = curr->nextSibling(); + if (curr->isAnonymousBlock()) + parentBlock->collapseAnonymousBoxChild(parentBlock, toRenderBlock(curr)); + curr = next; + } +} + void RenderObject::setAnimatableStyle(PassRefPtr<RenderStyle> style) { if (!isText() && style) @@ -1756,6 +1790,37 @@ StyleDifference RenderObject::adjustStyleDifference(StyleDifference diff, unsign return diff; } +void RenderObject::setPseudoStyle(PassRefPtr<RenderStyle> pseudoStyle) +{ + ASSERT(pseudoStyle->styleType() == BEFORE || pseudoStyle->styleType() == AFTER); + + // Images are special and must inherit the pseudoStyle so the width and height of + // the pseudo element doesn't change the size of the image. In all other cases we + // can just share the style. + if (isImage()) { + RefPtr<RenderStyle> style = RenderStyle::create(); + style->inheritFrom(pseudoStyle.get()); + setStyle(style.release()); + return; + } + + setStyle(pseudoStyle); +} + +inline bool RenderObject::hasImmediateNonWhitespaceTextChild() const +{ + for (const RenderObject* r = firstChild(); r; r = r->nextSibling()) { + if (r->isText() && !toRenderText(r)->isAllCollapsibleWhitespace()) + return true; + } + return false; +} + +inline bool RenderObject::shouldRepaintForStyleDifference(StyleDifference diff) const +{ + return diff == StyleDifferenceRepaint || (diff == StyleDifferenceRepaintIfText && hasImmediateNonWhitespaceTextChild()); +} + void RenderObject::setStyle(PassRefPtr<RenderStyle> style) { if (m_style == style) { @@ -1809,21 +1874,26 @@ void RenderObject::setStyle(PassRefPtr<RenderStyle> style) if (updatedDiff == StyleDifferenceLayout) setNeedsLayoutAndPrefWidthsRecalc(); else if (updatedDiff == StyleDifferenceLayoutPositionedMovementOnly) - setNeedsPositionedMovementLayout(); + setNeedsPositionedMovementLayout(oldStyle.get()); else if (updatedDiff == StyleDifferenceSimplifiedLayoutAndPositionedMovement) { - setNeedsPositionedMovementLayout(); + setNeedsPositionedMovementLayout(oldStyle.get()); setNeedsSimplifiedNormalFlowLayout(); } else if (updatedDiff == StyleDifferenceSimplifiedLayout) setNeedsSimplifiedNormalFlowLayout(); } - - if (updatedDiff == StyleDifferenceRepaintLayer || updatedDiff == StyleDifferenceRepaint) { + + if (updatedDiff == StyleDifferenceRepaintLayer || shouldRepaintForStyleDifference(updatedDiff)) { // Do a repaint with the new style now, e.g., for example if we go from // not having an outline to having an outline. repaint(); } } +static inline bool rendererHasBackground(const RenderObject* renderer) +{ + return renderer && renderer->hasBackground(); +} + void RenderObject::styleWillChange(StyleDifference diff, const RenderStyle* newStyle) { if (m_style) { @@ -1837,8 +1907,10 @@ void RenderObject::styleWillChange(StyleDifference diff, const RenderStyle* newS if (visibilityChanged) document()->setAnnotatedRegionsDirty(true); #endif - if (visibilityChanged && AXObjectCache::accessibilityEnabled()) - document()->axObjectCache()->childrenChanged(parent()); + if (visibilityChanged) { + if (AXObjectCache* cache = document()->existingAXObjectCache()) + cache->childrenChanged(parent()); + } // Keep layer hierarchy visibility bits up to date if visibility changes. if (m_style->visibility() != newStyle->visibility()) { @@ -1854,7 +1926,7 @@ void RenderObject::styleWillChange(StyleDifference diff, const RenderStyle* newS } } - if (m_parent && (diff == StyleDifferenceRepaint || newStyle->outlineSize() < m_style->outlineSize())) + if (m_parent && (newStyle->outlineSize() < m_style->outlineSize() || shouldRepaintForStyleDifference(diff))) repaint(); if (isFloating() && (m_style->floating() != newStyle->floating())) // For changes in float styles, we need to conceivably remove ourselves @@ -1869,41 +1941,48 @@ void RenderObject::styleWillChange(StyleDifference diff, const RenderStyle* newS && (!newStyle->isFloating() && !newStyle->hasOutOfFlowPosition()) && parent() && (parent()->isBlockFlow() || parent()->isRenderInline()); + s_noLongerAffectsParentBlock = ((!isFloating() && newStyle->isFloating()) || (!isOutOfFlowPositioned() && newStyle->hasOutOfFlowPosition())) + && parent() && parent()->isRenderBlock(); + // reset style flags if (diff == StyleDifferenceLayout || diff == StyleDifferenceLayoutPositionedMovementOnly) { setFloating(false); - setPositioned(false); - setRelPositioned(false); - setStickyPositioned(false); + clearPositionedState(); } setHorizontalWritingMode(true); - setPaintBackground(false); + setHasBoxDecorations(false); setHasOverflowClip(false); setHasTransform(false); setHasReflection(false); - } else + } else { s_affectsParentBlock = false; + s_noLongerAffectsParentBlock = false; + } - if (view()->frameView()) { - bool shouldBlitOnFixedBackgroundImage = false; -#if ENABLE(FAST_MOBILE_SCROLLING) - // On low-powered/mobile devices, preventing blitting on a scroll can cause noticeable delays - // when scrolling a page with a fixed background image. As an optimization, assuming there are - // no fixed positoned elements on the page, we can acclerate scrolling (via blitting) if we - // ignore the CSS property "background-attachment: fixed". -#if PLATFORM(QT) - if (view()->frameView()->delegatesScrolling()) -#endif - shouldBlitOnFixedBackgroundImage = true; -#endif + if (FrameView* frameView = view()->frameView()) { + bool repaintFixedBackgroundsOnScroll = shouldRepaintFixedBackgroundsOnScroll(frameView); - bool newStyleSlowScroll = newStyle && !shouldBlitOnFixedBackgroundImage && newStyle->hasFixedBackgroundImage(); - bool oldStyleSlowScroll = m_style && !shouldBlitOnFixedBackgroundImage && m_style->hasFixedBackgroundImage(); + bool newStyleSlowScroll = newStyle && repaintFixedBackgroundsOnScroll && newStyle->hasFixedBackgroundImage(); + bool oldStyleSlowScroll = m_style && repaintFixedBackgroundsOnScroll && m_style->hasFixedBackgroundImage(); + +#if USE(ACCELERATED_COMPOSITING) + bool drawsRootBackground = isRoot() || (isBody() && !rendererHasBackground(document()->documentElement()->renderer())); + if (drawsRootBackground && repaintFixedBackgroundsOnScroll) { + if (view()->compositor()->supportsFixedRootBackgroundCompositing()) { + if (newStyleSlowScroll && newStyle->hasEntirelyFixedBackground()) + newStyleSlowScroll = false; + + if (oldStyleSlowScroll && m_style->hasEntirelyFixedBackground()) + oldStyleSlowScroll = false; + } + } +#endif if (oldStyleSlowScroll != newStyleSlowScroll) { if (oldStyleSlowScroll) - view()->frameView()->removeSlowRepaintObject(); + frameView->removeSlowRepaintObject(this); + if (newStyleSlowScroll) - view()->frameView()->addSlowRepaintObject(); + frameView->addSlowRepaintObject(this); } } } @@ -1924,6 +2003,8 @@ void RenderObject::styleDidChange(StyleDifference diff, const RenderStyle* oldSt if (s_affectsParentBlock) handleDynamicFloatPositionChange(); + if (s_noLongerAffectsParentBlock) + removeAnonymousWrappersForInlinesIfNecessary(); #if ENABLE(SVG) SVGRenderSupport::styleChanged(this); #endif @@ -1947,17 +2028,17 @@ void RenderObject::styleDidChange(StyleDifference diff, const RenderStyle* oldSt else setNeedsSimplifiedNormalFlowLayout(); } else if (diff == StyleDifferenceSimplifiedLayoutAndPositionedMovement) { - setNeedsPositionedMovementLayout(); + setNeedsPositionedMovementLayout(oldStyle); setNeedsSimplifiedNormalFlowLayout(); } else if (diff == StyleDifferenceLayoutPositionedMovementOnly) - setNeedsPositionedMovementLayout(); + setNeedsPositionedMovementLayout(oldStyle); // Don't check for repaint here; we need to wait until the layer has been // updated by subclasses before we know if we have to repaint (in setStyle()). if (oldStyle && !areCursorsEqual(oldStyle, style())) { if (Frame* frame = this->frame()) - frame->eventHandler()->dispatchFakeMouseMoveEventSoon(); + frame->eventHandler()->scheduleCursorUpdate(); } } @@ -2044,6 +2125,14 @@ FloatPoint RenderObject::absoluteToLocal(const FloatPoint& containerPoint, MapCo return transformState.lastPlanarPoint(); } +FloatQuad RenderObject::absoluteToLocalQuad(const FloatQuad& quad, MapCoordinatesFlags mode) const +{ + TransformState transformState(TransformState::UnapplyInverseTransformDirection, quad.boundingBox().center(), quad); + mapAbsoluteToLocalPoint(mode, transformState); + transformState.flatten(); + return transformState.lastPlanarQuad(); +} + void RenderObject::mapLocalToContainer(const RenderLayerModelObject* repaintContainer, TransformState& transformState, MapCoordinatesFlags mode, bool* wasFixed) const { if (repaintContainer == this) @@ -2170,7 +2259,7 @@ LayoutSize RenderObject::offsetFromContainer(RenderObject* o, const LayoutPoint& offset -= toRenderBox(o)->scrolledContentOffset(); if (offsetDependsOnPoint) - *offsetDependsOnPoint = hasColumns(); + *offsetDependsOnPoint = hasColumns() || o->isRenderFlowThread(); return offset; } @@ -2240,11 +2329,11 @@ RespectImageOrientationEnum RenderObject::shouldRespectImageOrientation() const // Respect the image's orientation if it's being used as a full-page image or it's // an <img> and the setting to respect it everywhere is set. return -#if USE(CG) || PLATFORM(CHROMIUM) || USE(CAIRO) +#if USE(CG) || USE(CAIRO) || PLATFORM(BLACKBERRY) // This can only be enabled for ports which honor the orientation flag in their drawing code. document()->isImageDocument() || #endif - (document()->settings() && document()->settings()->shouldRespectImageOrientation() && node() && (node()->hasTagName(HTMLNames::imgTag) || node()->hasTagName(HTMLNames::webkitInnerImageTag))) ? RespectImageOrientation : DoNotRespectImageOrientation; + (document()->settings() && document()->settings()->shouldRespectImageOrientation() && node() && isHTMLImageElement(node())) ? RespectImageOrientation : DoNotRespectImageOrientation; } bool RenderObject::hasOutlineAnnotation() const @@ -2252,6 +2341,11 @@ bool RenderObject::hasOutlineAnnotation() const return node() && node()->isLink() && document()->printing(); } +bool RenderObject::hasEntirelyFixedBackground() const +{ + return m_style->hasEntirelyFixedBackground(); +} + RenderObject* RenderObject::container(const RenderLayerModelObject* repaintContainer, bool* repaintContainerSkipped) const { if (repaintContainerSkipped) @@ -2289,7 +2383,7 @@ RenderObject* RenderObject::container(const RenderLayerModelObject* repaintConta #endif // The render flow thread is the top most containing block // for the fixed positioned elements. - if (o->isRenderFlowThread()) + if (o->isOutOfFlowRenderFlowThread()) break; if (repaintContainerSkipped && o == repaintContainer) @@ -2353,14 +2447,22 @@ void RenderObject::willBeDestroyed() if (frame() && frame()->eventHandler()->autoscrollRenderer() == this) frame()->eventHandler()->stopAutoscrollTimer(true); - if (AXObjectCache::accessibilityEnabled()) { - document()->axObjectCache()->childrenChanged(this->parent()); - document()->axObjectCache()->remove(this); - } animation()->cancelAnimations(this); + // For accessibility management, notify the parent of the imminent change to its child set. + // We do it now, before remove(), while the parent pointer is still available. + if (AXObjectCache* cache = document()->existingAXObjectCache()) + cache->childrenChanged(this->parent()); + remove(); + ASSERT(documentBeingDestroyed() || !frame()->view()->hasSlowRepaintObject(this)); + + // The remove() call above may invoke axObjectCache()->childrenChanged() on the parent, which may require the AX render + // object for this renderer. So we remove the AX render object now, after the renderer is removed. + if (AXObjectCache* cache = document()->existingAXObjectCache()) + cache->remove(this); + #ifndef NDEBUG if (!documentBeingDestroyed() && view() && view()->hasRenderNamedFlowThreads()) { // After remove, the object and the associated information should not be in any flow thread. @@ -2425,6 +2527,14 @@ void RenderObject::willBeRemovedFromTree() { // FIXME: We should ASSERT(isRooted()) but we have some out-of-order removals which would need to be fixed first. + if (!isText()) { + if (FrameView* frameView = view()->frameView()) { + bool repaintFixedBackgroundsOnScroll = shouldRepaintFixedBackgroundsOnScroll(frameView); + if (repaintFixedBackgroundsOnScroll && m_style && m_style->hasFixedBackgroundImage()) + frameView->removeSlowRepaintObject(this); + } + } + // If we remove a visible child from an invisible parent, we don't know the layer visibility any more. RenderLayer* layer = 0; if (parent()->style()->visibility() != VISIBLE && style()->visibility() == VISIBLE && !hasLayer()) { @@ -2442,8 +2552,7 @@ void RenderObject::willBeRemovedFromTree() if (isOutOfFlowPositioned() && parent()->childrenInline()) parent()->dirtyLinesFromChangedChild(this); - if (inRenderFlowThread()) - removeFromRenderFlowThread(); + removeFromRenderFlowThread(); if (RenderNamedFlowThread* containerFlowThread = parent()->renderNamedFlowThreadWrapper()) containerFlowThread->removeFlowChild(this); @@ -2456,13 +2565,14 @@ void RenderObject::willBeRemovedFromTree() void RenderObject::removeFromRenderFlowThread() { - RenderFlowThread* renderFlowThread = enclosingRenderFlowThread(); - ASSERT(renderFlowThread); - // Sometimes we remove the element from the flow, but it's not destroyed at that time. + if (flowThreadState() == NotInsideFlowThread) + return; + + // Sometimes we remove the element from the flow, but it's not destroyed at that time. // It's only until later when we actually destroy it and remove all the children from it. // Currently, that happens for firstLetter elements and list markers. // Pass in the flow thread so that we don't have to look it up for all the children. - removeFromRenderFlowThreadRecursive(renderFlowThread); + removeFromRenderFlowThreadRecursive(flowThreadContainingBlock()); } void RenderObject::removeFromRenderFlowThreadRecursive(RenderFlowThread* renderFlowThread) @@ -2471,8 +2581,13 @@ void RenderObject::removeFromRenderFlowThreadRecursive(RenderFlowThread* renderF for (RenderObject* child = children->firstChild(); child; child = child->nextSibling()) child->removeFromRenderFlowThreadRecursive(renderFlowThread); } - renderFlowThread->removeFlowChildInfo(this); - setInRenderFlowThread(false); + + RenderFlowThread* localFlowThread = renderFlowThread; + if (flowThreadState() == InsideInFlowThread) + localFlowThread = flowThreadContainingBlock(); // We have to ask. We can't just assume we are in the same flow thread. + if (localFlowThread) + localFlowThread->removeFlowChildInfo(this); + setFlowThreadState(NotInsideFlowThread); } void RenderObject::destroyAndCleanupAnonymousWrappers() @@ -2485,9 +2600,9 @@ void RenderObject::destroyAndCleanupAnonymousWrappers() RenderObject* destroyRoot = this; for (RenderObject* destroyRootParent = destroyRoot->parent(); destroyRootParent && destroyRootParent->isAnonymous(); destroyRoot = destroyRootParent, destroyRootParent = destroyRootParent->parent()) { - // Currently we only remove anonymous cells' wrapper but we should remove all unneeded + // Currently we only remove anonymous cells' and table sections' wrappers but we should remove all unneeded // wrappers. See http://webkit.org/b/52123 as an example where this is needed. - if (!destroyRootParent->isTableCell()) + if (!destroyRootParent->isTableCell() && !destroyRootParent->isTableSection()) break; if (destroyRootParent->firstChild() != this || destroyRootParent->lastChild() != this) @@ -2586,11 +2701,19 @@ void RenderObject::updateHitTestResult(HitTestResult& result, const LayoutPoint& if (result.innerNode()) return; - Node* n = node(); - if (n) { - result.setInnerNode(n); + Node* node = this->node(); + + // If we hit the anonymous renderers inside generated content we should + // actually hit the generated content so walk up to the PseudoElement. + if (!node && parent() && parent()->isBeforeOrAfterContent()) { + for (RenderObject* renderer = parent(); renderer && !node; renderer = renderer->parent()) + node = renderer->node(); + } + + if (node) { + result.setInnerNode(node); if (!result.innerNonSharedNode()) - result.setInnerNonSharedNode(n); + result.setInnerNonSharedNode(node); result.setLocalPoint(point); } } @@ -2644,7 +2767,7 @@ static PassRefPtr<RenderStyle> firstLineStyleForCachedUncachedType(StyleCacheSta if (RenderBlock* firstLineBlock = rendererForFirstLineStyle->firstLineBlock()) { if (type == Cached) return firstLineBlock->getCachedPseudoStyle(FIRST_LINE, style); - return firstLineBlock->getUncachedPseudoStyle(FIRST_LINE, style, firstLineBlock == renderer ? style : 0); + return firstLineBlock->getUncachedPseudoStyle(PseudoStyleRequest(FIRST_LINE), style, firstLineBlock == renderer ? style : 0); } } else if (!rendererForFirstLineStyle->isAnonymous() && rendererForFirstLineStyle->isRenderInline()) { RenderStyle* parentStyle = rendererForFirstLineStyle->parent()->firstLineStyle(); @@ -2654,7 +2777,7 @@ static PassRefPtr<RenderStyle> firstLineStyleForCachedUncachedType(StyleCacheSta rendererForFirstLineStyle->style()->setHasPseudoStyle(FIRST_LINE_INHERITED); return rendererForFirstLineStyle->getCachedPseudoStyle(FIRST_LINE_INHERITED, parentStyle); } - return rendererForFirstLineStyle->getUncachedPseudoStyle(FIRST_LINE_INHERITED, parentStyle, style); + return rendererForFirstLineStyle->getUncachedPseudoStyle(PseudoStyleRequest(FIRST_LINE_INHERITED), parentStyle, style); } } return 0; @@ -2689,15 +2812,15 @@ RenderStyle* RenderObject::getCachedPseudoStyle(PseudoId pseudo, RenderStyle* pa if (cachedStyle) return cachedStyle; - RefPtr<RenderStyle> result = getUncachedPseudoStyle(pseudo, parentStyle); + RefPtr<RenderStyle> result = getUncachedPseudoStyle(PseudoStyleRequest(pseudo), parentStyle); if (result) return style()->addCachedPseudoStyle(result.release()); return 0; } -PassRefPtr<RenderStyle> RenderObject::getUncachedPseudoStyle(PseudoId pseudo, RenderStyle* parentStyle, RenderStyle* ownStyle) const +PassRefPtr<RenderStyle> RenderObject::getUncachedPseudoStyle(const PseudoStyleRequest& pseudoStyleRequest, RenderStyle* parentStyle, RenderStyle* ownStyle) const { - if (pseudo < FIRST_INTERNAL_PSEUDOID && !ownStyle && !style()->hasPseudoStyle(pseudo)) + if (pseudoStyleRequest.pseudoId < FIRST_INTERNAL_PSEUDOID && !ownStyle && !style()->hasPseudoStyle(pseudoStyleRequest.pseudoId)) return 0; if (!parentStyle) { @@ -2713,17 +2836,24 @@ PassRefPtr<RenderStyle> RenderObject::getUncachedPseudoStyle(PseudoId pseudo, Re return 0; Element* element = toElement(n); - if (pseudo == FIRST_LINE_INHERITED) { - RefPtr<RenderStyle> result = document()->styleResolver()->styleForElement(element, parentStyle, DisallowStyleSharing); + if (pseudoStyleRequest.pseudoId == FIRST_LINE_INHERITED) { + RefPtr<RenderStyle> result = document()->ensureStyleResolver()->styleForElement(element, parentStyle, DisallowStyleSharing); result->setStyleType(FIRST_LINE_INHERITED); return result.release(); } - return document()->styleResolver()->pseudoStyleForElement(pseudo, element, parentStyle); + + return document()->ensureStyleResolver()->pseudoStyleForElement(element, pseudoStyleRequest, parentStyle); } static Color decorationColor(RenderStyle* style) { Color result; +#if ENABLE(CSS3_TEXT) + // Check for text decoration color first. + result = style->visitedDependentColor(CSSPropertyWebkitTextDecorationColor); + if (result.isValid()) + return result; +#endif // CSS3_TEXT if (style->textStrokeWidth() > 0) { // Prefer stroke color if possible but not if it's fully transparent. result = style->visitedDependentColor(CSSPropertyWebkitTextStrokeColor); @@ -2740,21 +2870,25 @@ void RenderObject::getTextDecorationColors(int decorations, Color& underline, Co { RenderObject* curr = this; RenderStyle* styleToUse = 0; + TextDecoration currDecs = TextDecorationNone; + Color resultColor; do { styleToUse = curr->style(firstlineStyle); - int currDecs = styleToUse->textDecoration(); + currDecs = styleToUse->textDecoration(); + resultColor = decorationColor(styleToUse); + // Parameter 'decorations' is cast as an int to enable the bitwise operations below. if (currDecs) { - if (currDecs & UNDERLINE) { - decorations &= ~UNDERLINE; - underline = decorationColor(styleToUse); + if (currDecs & TextDecorationUnderline) { + decorations &= ~TextDecorationUnderline; + underline = resultColor; } - if (currDecs & OVERLINE) { - decorations &= ~OVERLINE; - overline = decorationColor(styleToUse); + if (currDecs & TextDecorationOverline) { + decorations &= ~TextDecorationOverline; + overline = resultColor; } - if (currDecs & LINE_THROUGH) { - decorations &= ~LINE_THROUGH; - linethrough = decorationColor(styleToUse); + if (currDecs & TextDecorationLineThrough) { + decorations &= ~TextDecorationLineThrough; + linethrough = resultColor; } } if (curr->isRubyText()) @@ -2762,18 +2896,18 @@ void RenderObject::getTextDecorationColors(int decorations, Color& underline, Co curr = curr->parent(); if (curr && curr->isAnonymousBlock() && toRenderBlock(curr)->continuation()) curr = toRenderBlock(curr)->continuation(); - } while (curr && decorations && (!quirksMode || !curr->node() || - (!curr->node()->hasTagName(aTag) && !curr->node()->hasTagName(fontTag)))); + } while (curr && decorations && (!quirksMode || !curr->node() || (!isHTMLAnchorElement(curr->node()) && !curr->node()->hasTagName(fontTag)))); // If we bailed out, use the element we bailed out at (typically a <font> or <a> element). if (decorations && curr) { styleToUse = curr->style(firstlineStyle); - if (decorations & UNDERLINE) - underline = decorationColor(styleToUse); - if (decorations & OVERLINE) - overline = decorationColor(styleToUse); - if (decorations & LINE_THROUGH) - linethrough = decorationColor(styleToUse); + resultColor = decorationColor(styleToUse); + if (decorations & TextDecorationUnderline) + underline = resultColor; + if (decorations & TextDecorationOverline) + overline = resultColor; + if (decorations & TextDecorationLineThrough) + linethrough = resultColor; } } @@ -2910,6 +3044,33 @@ void RenderObject::imageChanged(CachedImage* image, const IntRect* rect) { imageChanged(static_cast<WrappedImagePtr>(image), rect); } + +RenderObject* RenderObject::hoverAncestor() const +{ + // When searching for the hover ancestor and encountering a named flow thread, + // the search will continue with the DOM ancestor of the top-most element + // in the named flow thread. + // See https://bugs.webkit.org/show_bug.cgi?id=111749 + RenderObject* hoverAncestor = parent(); + + // Skip anonymous blocks directly flowed into flow threads as it would + // prevent us from continuing the search on the DOM tree when reaching the named flow thread. + if (hoverAncestor && hoverAncestor->isAnonymousBlock() && hoverAncestor->parent() && hoverAncestor->parent()->isRenderNamedFlowThread()) + hoverAncestor = hoverAncestor->parent(); + + if (hoverAncestor && hoverAncestor->isRenderNamedFlowThread()) { + hoverAncestor = 0; + + Node* node = this->node(); + if (node) { + Node* domAncestorNode = node->parentNode(); + if (domAncestorNode) + hoverAncestor = domAncestorNode->renderer(); + } + } + + return hoverAncestor; +} RenderBoxModelObject* RenderObject::offsetParent() const { @@ -2935,24 +3096,29 @@ RenderBoxModelObject* RenderObject::offsetParent() const bool skipTables = isPositioned(); float currZoom = style()->effectiveZoom(); RenderObject* curr = parent(); - while (curr && (!curr->node() || (!curr->isPositioned() && !curr->isBody()))) { + while (curr && (!curr->node() || (!curr->isPositioned() && !curr->isBody())) && !curr->isRenderNamedFlowThread()) { Node* element = curr->node(); - if (!skipTables && element && (element->hasTagName(tableTag) || element->hasTagName(tdTag) || element->hasTagName(thTag))) + if (!skipTables && element && (isHTMLTableElement(element) || element->hasTagName(tdTag) || element->hasTagName(thTag))) break; - + float newZoom = curr->style()->effectiveZoom(); if (currZoom != newZoom) break; currZoom = newZoom; curr = curr->parent(); } + + // CSS regions specification says that region flows should return the body element as their offsetParent. + if (curr && curr->isRenderNamedFlowThread()) + curr = document()->body() ? document()->body()->renderer() : 0; + return curr && curr->isBoxModelObject() ? toRenderBoxModelObject(curr) : 0; } VisiblePosition RenderObject::createVisiblePosition(int offset, EAffinity affinity) { // If this is a non-anonymous renderer in an editable area, then it's simple. - if (Node* node = this->node()) { + if (Node* node = nonPseudoNode()) { if (!node->rendererIsEditable()) { // If it can be found, we prefer a visually equivalent position that is editable. Position position = createLegacyEditingPosition(node, offset); @@ -2978,7 +3144,7 @@ VisiblePosition RenderObject::createVisiblePosition(int offset, EAffinity affini // Find non-anonymous content after. RenderObject* renderer = child; while ((renderer = renderer->nextInPreOrder(parent))) { - if (Node* node = renderer->node()) + if (Node* node = renderer->nonPseudoNode()) return VisiblePosition(firstPositionInOrBeforeNode(node), DOWNSTREAM); } @@ -2987,12 +3153,12 @@ VisiblePosition RenderObject::createVisiblePosition(int offset, EAffinity affini while ((renderer = renderer->previousInPreOrder())) { if (renderer == parent) break; - if (Node* node = renderer->node()) + if (Node* node = renderer->nonPseudoNode()) return VisiblePosition(lastPositionInOrAfterNode(node), DOWNSTREAM); } // Use the parent itself unless it too is anonymous. - if (Node* node = parent->node()) + if (Node* node = parent->nonPseudoNode()) return VisiblePosition(firstPositionInOrBeforeNode(node), DOWNSTREAM); // Repeat at the next level up. diff --git a/Source/WebCore/rendering/RenderObject.h b/Source/WebCore/rendering/RenderObject.h index 258276b36..ac83e84f4 100644 --- a/Source/WebCore/rendering/RenderObject.h +++ b/Source/WebCore/rendering/RenderObject.h @@ -37,10 +37,7 @@ #include "ScrollBehavior.h" #include "StyleInheritedData.h" #include "TextAffinity.h" -#include "TransformationMatrix.h" #include <wtf/HashSet.h> -#include <wtf/StackStats.h> -#include <wtf/UnusedParam.h> namespace WebCore { @@ -51,10 +48,9 @@ class Document; class HitTestLocation; class HitTestResult; class InlineBox; -class InlineFlowBox; -class OverlapTestRequestClient; class Path; class Position; +class PseudoStyleRequest; class RenderBoxModelObject; class RenderInline; class RenderBlock; @@ -63,7 +59,6 @@ class RenderGeometryMap; class RenderLayer; class RenderLayerModelObject; class RenderNamedFlowThread; -class RenderTable; class RenderTheme; class TransformState; class VisiblePosition; @@ -106,16 +101,10 @@ enum MarkingBehavior { MarkContainingBlockChain, }; -enum PlaceGeneratedRunInFlag { - PlaceGeneratedRunIn, - DoNotPlaceGeneratedRunIn -}; - enum MapCoordinatesMode { IsFixed = 1 << 0, UseTransforms = 1 << 1, - ApplyContainerFlip = 1 << 2, - SnapOffsetForTransforms = 1 << 3 + ApplyContainerFlip = 1 << 2 }; typedef unsigned MapCoordinatesFlags; @@ -161,7 +150,7 @@ class RenderObject : public CachedImageClient { public: // Anonymous objects should pass the document as their node, and they will then automatically be // marked as anonymous in the constructor. - RenderObject(Node*); + explicit RenderObject(Node*); virtual ~RenderObject(); RenderTheme* theme() const; @@ -191,22 +180,6 @@ public: return children->lastChild(); return 0; } - RenderObject* beforePseudoElementRenderer() const - { - if (const RenderObjectChildList* children = virtualChildren()) - return children->beforePseudoElementRenderer(this); - return 0; - } - - // This function only returns the renderer of the "after" pseudoElement if it is a child of - // this renderer. If "continuations" exist, the function returns 0 even if the element that - // generated this renderer has an "after" pseudo-element. - RenderObject* afterPseudoElementRenderer() const - { - if (const RenderObjectChildList* children = virtualChildren()) - return children->afterPseudoElementRenderer(this); - return 0; - } virtual RenderObjectChildList* virtualChildren() { return 0; } virtual const RenderObjectChildList* virtualChildren() const { return 0; } @@ -238,8 +211,14 @@ public: RenderBox* enclosingBox() const; RenderBoxModelObject* enclosingBoxModelObject() const; - // Function to return our enclosing flow thread if we are contained inside one. - RenderFlowThread* enclosingRenderFlowThread() const; + // Function to return our enclosing flow thread if we are contained inside one. This + // function follows the containing block chain. + RenderFlowThread* flowThreadContainingBlock() const + { + if (flowThreadState() == NotInsideFlowThread) + return 0; + return locateFlowThreadContainingBlock(); + } RenderNamedFlowThread* renderNamedFlowThreadWrapper() const; @@ -248,13 +227,11 @@ public: #ifndef NDEBUG void setHasAXObject(bool flag) { m_hasAXObject = flag; } bool hasAXObject() const { return m_hasAXObject; } - bool isSetNeedsLayoutForbidden() const { return m_setNeedsLayoutForbidden; } - void setNeedsLayoutIsForbidden(bool flag) { m_setNeedsLayoutForbidden = flag; } // Helper class forbidding calls to setNeedsLayout() during its lifetime. class SetLayoutNeededForbiddenScope { public: - explicit SetLayoutNeededForbiddenScope(RenderObject*); + explicit SetLayoutNeededForbiddenScope(RenderObject*, bool isForbidden = true); ~SetLayoutNeededForbiddenScope(); private: RenderObject* m_renderObject; @@ -270,6 +247,7 @@ public: // again. We have to make sure the render tree updates as needed to accommodate the new // normal flow object. void handleDynamicFloatPositionChange(); + void removeAnonymousWrappersForInlinesIfNecessary(); // RenderObject tree manipulation ////////////////////////////////////////// @@ -290,13 +268,22 @@ protected: void setParent(RenderObject* parent) { m_parent = parent; - if (parent && parent->inRenderFlowThread()) - setInRenderFlowThread(true); - else if (!parent && inRenderFlowThread()) - setInRenderFlowThread(false); + + // Only update if our flow thread state is different from our new parent and if we're not a RenderFlowThread. + // A RenderFlowThread is always considered to be inside itself, so it never has to change its state + // in response to parent changes. + FlowThreadState newState = parent ? parent->flowThreadState() : NotInsideFlowThread; + if (newState != flowThreadState() && !isRenderFlowThread()) + setFlowThreadStateIncludingDescendants(newState); } + ////////////////////////////////////////// private: +#ifndef NDEBUG + bool isSetNeedsLayoutForbidden() const { return m_setNeedsLayoutForbidden; } + void setNeedsLayoutIsForbidden(bool flag) { m_setNeedsLayoutForbidden = flag; } +#endif + void addAbsoluteRectForLayer(LayoutRect& result); void setLayerNeedsFullRepaint(); void setLayerNeedsFullRepaintForPositionedMovementLayout(); @@ -314,7 +301,7 @@ public: void showRenderTreeAndMark(const RenderObject* markedObject1 = 0, const char* markedLabel1 = 0, const RenderObject* markedObject2 = 0, const char* markedLabel2 = 0, int depth = 0) const; #endif - static RenderObject* createObject(Node*, RenderStyle*); + static RenderObject* createObject(Element*, RenderStyle*); // Overloaded new operator. Derived classes must override operator new // in order to allocate out of the RenderArena. @@ -330,6 +317,8 @@ private: public: RenderArena* renderArena() const { return document()->renderArena(); } + bool isPseudoElement() const { return node() && node()->isPseudoElement(); } + virtual bool isBR() const { return false; } virtual bool isBlockFlow() const { return false; } virtual bool isBoxModelObject() const { return false; } @@ -363,6 +352,7 @@ public: virtual bool isProgress() const { return false; } #endif virtual bool isRenderBlock() const { return false; } + virtual bool isRenderSVGBlock() const { return false; }; virtual bool isRenderButton() const { return false; } virtual bool isRenderIFrame() const { return false; } virtual bool isRenderImage() const { return false; } @@ -396,14 +386,17 @@ public: virtual bool isRenderFullScreenPlaceholder() const { return false; } #endif + virtual bool isRenderGrid() const { return false; } + virtual bool isRenderFlowThread() const { return false; } virtual bool isRenderNamedFlowThread() const { return false; } - + bool isInFlowRenderFlowThread() const { return isRenderFlowThread() && !isOutOfFlowPositioned(); } + bool isOutOfFlowRenderFlowThread() const { return isRenderFlowThread() && isOutOfFlowPositioned(); } + virtual bool isRenderMultiColumnBlock() const { return false; } virtual bool isRenderMultiColumnSet() const { return false; } virtual bool isRenderScrollbarPart() const { return false; } - bool canHaveRegionStyle() const { return isRenderBlock() && !isAnonymous() && !isRenderFlowThread(); } bool isRoot() const { return document()->documentElement() == m_node; } bool isBody() const; @@ -447,8 +440,16 @@ public: } } - bool inRenderFlowThread() const { return m_bitfields.inRenderFlowThread(); } - void setInRenderFlowThread(bool b = true) { m_bitfields.setInRenderFlowThread(b); } + enum FlowThreadState { + NotInsideFlowThread = 0, + InsideOutOfFlowThread = 1, + InsideInFlowThread = 2, + }; + + void setFlowThreadStateIncludingDescendants(FlowThreadState); + + FlowThreadState flowThreadState() const { return m_bitfields.flowThreadState(); } + void setFlowThreadState(FlowThreadState state) { m_bitfields.setFlowThreadState(state); } virtual bool requiresForcedStyleRecalcPropagation() const { return false; } @@ -484,6 +485,7 @@ public: // to inherit from RenderSVGObject -> RenderObject (some need RenderBlock inheritance for instance) virtual void setNeedsTransformUpdate() { } virtual void setNeedsBoundariesUpdate(); + virtual bool needsBoundariesUpdate() { return false; } // Per SVG 1.1 objectBoundingBox ignores clipping, masking, filter effects, opacity and stroke-width. // This is used for all computation of objectBoundingBox relative units and by SVGLocatable::getBBox(). @@ -512,15 +514,15 @@ public: virtual bool nodeAtFloatPoint(const HitTestRequest&, HitTestResult&, const FloatPoint& pointInParent, HitTestAction); #endif + bool hasAspectRatio() const { return isReplaced() && (isImage() || isVideo() || isCanvas()); } bool isAnonymous() const { return m_bitfields.isAnonymous(); } - void setIsAnonymous(bool b) { m_bitfields.setIsAnonymous(b); } bool isAnonymousBlock() const { // This function is kept in sync with anonymous block creation conditions in // RenderBlock::createAnonymousBlock(). This includes creating an anonymous // RenderBlock having a BLOCK or BOX display. Other classes such as RenderTextFragment // are not RenderBlocks and will return false. See https://bugs.webkit.org/show_bug.cgi?id=56709. - return isAnonymous() && (style()->display() == BLOCK || style()->display() == BOX) && style()->styleType() == NOPSEUDO && isRenderBlock() && !isListMarker() + return isAnonymous() && (style()->display() == BLOCK || style()->display() == BOX) && style()->styleType() == NOPSEUDO && isRenderBlock() && !isListMarker() && !isRenderFlowThread() #if ENABLE(FULLSCREEN_API) && !isRenderFullScreen() && !isRenderFullScreenPlaceholder() @@ -539,11 +541,11 @@ public: bool isFloating() const { return m_bitfields.floating(); } - bool isOutOfFlowPositioned() const { return m_bitfields.positioned(); } // absolute or fixed positioning - bool isInFlowPositioned() const { return m_bitfields.relPositioned() || m_bitfields.stickyPositioned(); } // relative or sticky positioning - bool isRelPositioned() const { return m_bitfields.relPositioned(); } // relative positioning - bool isStickyPositioned() const { return m_bitfields.stickyPositioned(); } - bool isPositioned() const { return m_bitfields.positioned() || m_bitfields.relPositioned() || m_bitfields.stickyPositioned(); } + bool isOutOfFlowPositioned() const { return m_bitfields.isOutOfFlowPositioned(); } // absolute or fixed positioning + bool isInFlowPositioned() const { return m_bitfields.isRelPositioned() || m_bitfields.isStickyPositioned(); } // relative or sticky positioning + bool isRelPositioned() const { return m_bitfields.isRelPositioned(); } // relative positioning + bool isStickyPositioned() const { return m_bitfields.isStickyPositioned(); } + bool isPositioned() const { return m_bitfields.isPositioned(); } bool isText() const { return m_bitfields.isText(); } bool isBox() const { return m_bitfields.isBox(); } @@ -554,11 +556,20 @@ public: bool isHorizontalWritingMode() const { return m_bitfields.horizontalWritingMode(); } bool hasLayer() const { return m_bitfields.hasLayer(); } - - bool hasBoxDecorations() const { return m_bitfields.paintBackground(); } + + enum BoxDecorationState { + NoBoxDecorations, + HasBoxDecorationsAndBackgroundObscurationStatusInvalid, + HasBoxDecorationsAndBackgroundIsKnownToBeObscured, + HasBoxDecorationsAndBackgroundMayBeVisible, + }; + bool hasBoxDecorations() const { return m_bitfields.boxDecorationState() != NoBoxDecorations; } + bool backgroundIsKnownToBeObscured(); bool borderImageIsLoadedAndCanBeRendered() const; bool mustRepaintBackgroundOrBorder() const; bool hasBackground() const { return style()->hasBackground(); } + bool hasEntirelyFixedBackground() const; + bool needsLayout() const { return m_bitfields.needsLayout() || m_bitfields.normalChildNeedsLayout() || m_bitfields.posChildNeedsLayout() @@ -607,7 +618,7 @@ public: // The pseudo element style can be cached or uncached. Use the cached method if the pseudo element doesn't respect // any pseudo classes (and therefore has no concept of changing state). RenderStyle* getCachedPseudoStyle(PseudoId, RenderStyle* parentStyle = 0) const; - PassRefPtr<RenderStyle> getUncachedPseudoStyle(PseudoId, RenderStyle* parentStyle = 0, RenderStyle* ownStyle = 0) const; + PassRefPtr<RenderStyle> getUncachedPseudoStyle(const PseudoStyleRequest&, RenderStyle* parentStyle = 0, RenderStyle* ownStyle = 0) const; virtual void updateDragState(bool dragOn); @@ -617,12 +628,15 @@ public: bool isRooted(RenderView** = 0) const; Node* node() const { return isAnonymous() ? 0 : m_node; } + Node* nonPseudoNode() const { return isPseudoElement() ? 0 : node(); } + + // FIXME: Why does RenderWidget need this? + void clearNode() { m_node = 0; } // Returns the styled node that caused the generation of this renderer. // This is the same as node() except for renderers of :before and :after // pseudo elements for which their parent node is returned. - Node* generatingNode() const { return m_node == document() ? 0 : m_node; } - void setNode(Node* node) { m_node = node; } + Node* generatingNode() const { return isPseudoElement() ? node()->parentOrShadowHostNode() : node(); } Document* document() const { return m_node->document(); } Frame* frame() const { return document()->frame(); } @@ -635,15 +649,14 @@ public: // is true if the renderer returned is an ancestor of repaintContainer. RenderObject* container(const RenderLayerModelObject* repaintContainer = 0, bool* repaintContainerSkipped = 0) const; - virtual RenderObject* hoverAncestor() const { return parent(); } + virtual RenderObject* hoverAncestor() const; - // IE Extension that can be called on any RenderObject. See the implementation for the details. RenderBoxModelObject* offsetParent() const; void markContainingBlocksForLayout(bool scheduleRelayout = true, RenderObject* newRoot = 0); void setNeedsLayout(bool needsLayout, MarkingBehavior = MarkContainingBlockChain); void setChildNeedsLayout(bool childNeedsLayout, MarkingBehavior = MarkContainingBlockChain); - void setNeedsPositionedMovementLayout(); + void setNeedsPositionedMovementLayout(const RenderStyle* oldStyle); void setNeedsSimplifiedNormalFlowLayout(); void setPreferredLogicalWidthsDirty(bool, MarkingBehavior = MarkContainingBlockChain); void invalidateContainerPreferredLogicalWidths(); @@ -654,12 +667,20 @@ public: setPreferredLogicalWidthsDirty(true); } - void setPositioned(bool b = true) { m_bitfields.setPositioned(b); } - void setRelPositioned(bool b = true) { m_bitfields.setRelPositioned(b); } - void setStickyPositioned(bool b = true) { m_bitfields.setStickyPositioned(b); } + void setPositionState(EPosition position) + { + ASSERT((position != AbsolutePosition && position != FixedPosition) || isBox()); + m_bitfields.setPositionedState(position); + } + void clearPositionedState() { m_bitfields.clearPositionedState(); } + void setFloating(bool b = true) { m_bitfields.setFloating(b); } void setInline(bool b = true) { m_bitfields.setIsInline(b); } - void setHasBoxDecorations(bool b = true) { m_bitfields.setPaintBackground(b); } + + void setHasBoxDecorations(bool = true); + void invalidateBackgroundObscurationStatus(); + virtual bool computeBackgroundIsKnownToBeObscured() { return false; } + void setIsText() { m_bitfields.setIsText(true); } void setIsBox() { m_bitfields.setIsBox(true); } void setReplaced(bool b = true) { m_bitfields.setIsReplaced(b); } @@ -711,6 +732,9 @@ public: // Set the style of the object and update the state of the object accordingly. virtual void setStyle(PassRefPtr<RenderStyle>); + // Set the style of the object if it's generated content. + void setPseudoStyle(PassRefPtr<RenderStyle>); + // Updates only the local style ptr of the object. Does not update the state of the object, // and so only should be called when the style is known not to have changed (or from setStyle). void setStyleInternal(PassRefPtr<RenderStyle> style) { m_style = style; } @@ -718,6 +742,15 @@ public: // returns the containing block level element for this element. RenderBlock* containingBlock() const; + bool canContainFixedPositionObjects() const + { + return isRenderView() || (hasTransform() && isRenderBlock()) +#if ENABLE(SVG) + || isSVGForeignObject() +#endif + || isOutOfFlowRenderFlowThread(); + } + // Convert the given local point to absolute coordinates // FIXME: Temporary. If UseTransforms is true, take transforms into account. Eventually localToAbsolute() will always be transform-aware. FloatPoint localToAbsolute(const FloatPoint& localPoint = FloatPoint(), MapCoordinatesFlags = 0) const; @@ -728,6 +761,8 @@ public: { return localToContainerQuad(quad, 0, mode, wasFixed); } + // Convert an absolute quad to local coordinates. + FloatQuad absoluteToLocalQuad(const FloatQuad&, MapCoordinatesFlags mode = 0) const; // Convert a local quad into the coordinate system of container, taking transforms into account. FloatQuad localToContainerQuad(const FloatQuad&, const RenderLayerModelObject* repaintContainer, MapCoordinatesFlags = 0, bool* wasFixed = 0) const; @@ -792,12 +827,6 @@ public: // Repaint only if our old bounds and new bounds are different. The caller may pass in newBounds and newOutlineBox if they are known. bool repaintAfterLayoutIfNeeded(const RenderLayerModelObject* repaintContainer, const LayoutRect& oldBounds, const LayoutRect& oldOutlineBox, const LayoutRect* newBoundsPtr = 0, const LayoutRect* newOutlineBoxPtr = 0); - // Repaint only if the object moved. - virtual void repaintDuringLayoutIfMoved(const LayoutRect&); - - // Called to repaint a block's floats. - virtual void repaintOverhangingFloats(bool paintAllDescendants = false); - bool checkForRepaintDuringLayout() const; // Returns the rect that should be repainted whenever this object changes. The rect is in the view's @@ -835,6 +864,7 @@ public: virtual unsigned int length() const { return 1; } bool isFloatingOrOutOfFlowPositioned() const { return (isFloating() || isOutOfFlowPositioned()); } + bool isFloatingWithShapeOutside() const { return isBox() && isFloating() && style()->shapeOutside(); } bool isTransparent() const { return style()->opacity() < 1.0f; } float opacity() const { return style()->opacity(); } @@ -883,11 +913,6 @@ public: */ virtual LayoutRect localCaretRect(InlineBox*, int caretOffset, LayoutUnit* extraWidthToEndOfLine = 0); - bool isMarginBeforeQuirk() const { return m_bitfields.marginBeforeQuirk(); } - bool isMarginAfterQuirk() const { return m_bitfields.marginAfterQuirk(); } - void setMarginBeforeQuirk(bool b = true) { m_bitfields.setMarginBeforeQuirk(b); } - void setMarginAfterQuirk(bool b = true) { m_bitfields.setMarginAfterQuirk(b); } - // When performing a global document tear-down, the renderer of the document is cleared. We use this // as a hook to detect the case of document destruction and don't waste time doing unnecessary work. bool documentBeingDestroyed() const; @@ -943,7 +968,7 @@ public: // return true if this object requires a new stacking context bool createsGroup() const { return isTransparent() || hasMask() || hasFilter() || hasBlendMode(); } - virtual void addFocusRingRects(Vector<IntRect>&, const LayoutPoint&) { }; + virtual void addFocusRingRects(Vector<IntRect>&, const LayoutPoint& /* additionalOffset */, const RenderLayerModelObject* /* paintContainer */ = 0) { }; LayoutRect absoluteOutlineBounds() const { @@ -967,8 +992,8 @@ protected: void drawLineForBoxSide(GraphicsContext*, int x1, int y1, int x2, int y2, BoxSide, Color, EBorderStyle, int adjbw1, int adjbw2, bool antialias = false); - void paintFocusRing(GraphicsContext*, const LayoutPoint&, RenderStyle*); - void paintOutline(GraphicsContext*, const LayoutRect&); + void paintFocusRing(PaintInfo&, const LayoutPoint&, RenderStyle*); + void paintOutline(PaintInfo&, const LayoutRect&); void addPDFURLRect(GraphicsContext*, const LayoutRect&); virtual LayoutRect viewRect() const; @@ -984,10 +1009,16 @@ protected: virtual void insertedIntoTree(); virtual void willBeRemovedFromTree(); + void setDocumentForAnonymous(Document* document) { ASSERT(isAnonymous()); m_node = document; } + private: + RenderFlowThread* locateFlowThreadContainingBlock() const; void removeFromRenderFlowThread(); void removeFromRenderFlowThreadRecursive(RenderFlowThread*); + bool shouldRepaintForStyleDifference(StyleDifference) const; + bool hasImmediateNonWhitespaceTextChild() const; + RenderStyle* cachedFirstLineStyle() const; StyleDifference adjustStyleDifference(StyleDifference, unsigned contextSensitiveProperties) const; @@ -1020,6 +1051,13 @@ private: void set##Name(bool name) { m_##name = name; }\ class RenderObjectBitfields { + enum PositionedState { + IsStaticallyPositioned = 0, + IsRelativelyPositioned = 1, + IsOutOfFlowPositioned = 2, + IsStickyPositioned = 3 + }; + public: RenderObjectBitfields(Node* node) : m_needsLayout(false) @@ -1029,11 +1067,7 @@ private: , m_needsSimplifiedNormalFlowLayout(false) , m_preferredLogicalWidthsDirty(false) , m_floating(false) - , m_positioned(false) - , m_relPositioned(false) - , m_stickyPositioned(false) - , m_paintBackground(false) - , m_isAnonymous(node == node->document()) + , m_isAnonymous(!node) , m_isText(false) , m_isBox(false) , m_isInline(true) @@ -1046,16 +1080,16 @@ private: , m_hasReflection(false) , m_hasCounterNodeMap(false) , m_everHadLayout(false) - , m_inRenderFlowThread(false) , m_childrenInline(false) - , m_marginBeforeQuirk(false) - , m_marginAfterQuirk(false) , m_hasColumns(false) + , m_positionedState(IsStaticallyPositioned) , m_selectionState(SelectionNone) + , m_flowThreadState(NotInsideFlowThread) + , m_boxDecorationState(NoBoxDecorations) { } - // 32 bits have been used here. THERE ARE NO FREE BITS AVAILABLE. + // 31 bits have been used here. There is one bit available. ADD_BOOLEAN_BITFIELD(needsLayout, NeedsLayout); ADD_BOOLEAN_BITFIELD(needsPositionedMovementLayout, NeedsPositionedMovementLayout); ADD_BOOLEAN_BITFIELD(normalChildNeedsLayout, NormalChildNeedsLayout); @@ -1064,12 +1098,6 @@ private: ADD_BOOLEAN_BITFIELD(preferredLogicalWidthsDirty, PreferredLogicalWidthsDirty); ADD_BOOLEAN_BITFIELD(floating, Floating); - ADD_BOOLEAN_BITFIELD(positioned, Positioned); - ADD_BOOLEAN_BITFIELD(relPositioned, RelPositioned); - ADD_BOOLEAN_BITFIELD(stickyPositioned, StickyPositioned); - ADD_BOOLEAN_BITFIELD(paintBackground, PaintBackground); // if the box has something to paint in the - // background painting phase (background, border, etc) - ADD_BOOLEAN_BITFIELD(isAnonymous, IsAnonymous); ADD_BOOLEAN_BITFIELD(isText, IsText); ADD_BOOLEAN_BITFIELD(isBox, IsBox); @@ -1086,22 +1114,37 @@ private: ADD_BOOLEAN_BITFIELD(hasCounterNodeMap, HasCounterNodeMap); ADD_BOOLEAN_BITFIELD(everHadLayout, EverHadLayout); - // These bitfields are moved here from subclasses to pack them together. - // from RenderFlowThread - ADD_BOOLEAN_BITFIELD(inRenderFlowThread, InRenderFlowThread); - // from RenderBlock ADD_BOOLEAN_BITFIELD(childrenInline, ChildrenInline); - ADD_BOOLEAN_BITFIELD(marginBeforeQuirk, MarginBeforeQuirk); - ADD_BOOLEAN_BITFIELD(marginAfterQuirk, MarginAfterQuirk); ADD_BOOLEAN_BITFIELD(hasColumns, HasColumns); private: + unsigned m_positionedState : 2; // PositionedState unsigned m_selectionState : 3; // SelectionState + unsigned m_flowThreadState : 2; // FlowThreadState + unsigned m_boxDecorationState : 2; // BoxDecorationState public: + bool isOutOfFlowPositioned() const { return m_positionedState == IsOutOfFlowPositioned; } + bool isRelPositioned() const { return m_positionedState == IsRelativelyPositioned; } + bool isStickyPositioned() const { return m_positionedState == IsStickyPositioned; } + bool isPositioned() const { return m_positionedState != IsStaticallyPositioned; } + + void setPositionedState(int positionState) + { + // This mask maps FixedPosition and AbsolutePosition to IsOutOfFlowPositioned, saving one bit. + m_positionedState = static_cast<PositionedState>(positionState & 0x3); + } + void clearPositionedState() { m_positionedState = StaticPosition; } + ALWAYS_INLINE SelectionState selectionState() const { return static_cast<SelectionState>(m_selectionState); } ALWAYS_INLINE void setSelectionState(SelectionState selectionState) { m_selectionState = selectionState; } + + ALWAYS_INLINE FlowThreadState flowThreadState() const { return static_cast<FlowThreadState>(m_flowThreadState); } + ALWAYS_INLINE void setFlowThreadState(FlowThreadState flowThreadState) { m_flowThreadState = flowThreadState; } + + ALWAYS_INLINE BoxDecorationState boxDecorationState() const { return static_cast<BoxDecorationState>(m_boxDecorationState); } + ALWAYS_INLINE void setBoxDecorationState(BoxDecorationState boxDecorationState) { m_boxDecorationState = boxDecorationState; } }; #undef ADD_BOOLEAN_BITFIELD @@ -1112,13 +1155,13 @@ private: void setNormalChildNeedsLayout(bool b) { m_bitfields.setNormalChildNeedsLayout(b); } void setPosChildNeedsLayout(bool b) { m_bitfields.setPosChildNeedsLayout(b); } void setNeedsSimplifiedNormalFlowLayout(bool b) { m_bitfields.setNeedsSimplifiedNormalFlowLayout(b); } - void setPaintBackground(bool b) { m_bitfields.setPaintBackground(b); } void setIsDragging(bool b) { m_bitfields.setIsDragging(b); } void setEverHadLayout(bool b) { m_bitfields.setEverHadLayout(b); } private: // Store state between styleWillChange and styleDidChange static bool s_affectsParentBlock; + static bool s_noLongerAffectsParentBlock; }; inline bool RenderObject::documentBeingDestroyed() const @@ -1192,15 +1235,19 @@ inline void RenderObject::setChildNeedsLayout(bool childNeedsLayout, MarkingBeha } } -inline void RenderObject::setNeedsPositionedMovementLayout() +inline void RenderObject::setNeedsPositionedMovementLayout(const RenderStyle* oldStyle) { bool alreadyNeededLayout = needsPositionedMovementLayout(); setNeedsPositionedMovementLayout(true); ASSERT(!isSetNeedsLayoutForbidden()); if (!alreadyNeededLayout) { markContainingBlocksForLayout(); - if (hasLayer()) - setLayerNeedsFullRepaintForPositionedMovementLayout(); + if (hasLayer()) { + if (oldStyle && m_style->diffRequiresRepaint(oldStyle)) + setLayerNeedsFullRepaint(); + else + setLayerNeedsFullRepaintForPositionedMovementLayout(); + } } } @@ -1248,15 +1295,31 @@ inline void RenderObject::setSelectionStateIfNeeded(SelectionState state) setSelectionState(state); } -inline void makeMatrixRenderable(TransformationMatrix& matrix, bool has3DRendering) +inline void RenderObject::setHasBoxDecorations(bool b) { -#if !ENABLE(3D_RENDERING) - UNUSED_PARAM(has3DRendering); - matrix.makeAffine(); -#else - if (!has3DRendering) - matrix.makeAffine(); -#endif + if (!b) { + m_bitfields.setBoxDecorationState(NoBoxDecorations); + return; + } + if (hasBoxDecorations()) + return; + m_bitfields.setBoxDecorationState(HasBoxDecorationsAndBackgroundObscurationStatusInvalid); +} + +inline void RenderObject::invalidateBackgroundObscurationStatus() +{ + if (!hasBoxDecorations()) + return; + m_bitfields.setBoxDecorationState(HasBoxDecorationsAndBackgroundObscurationStatusInvalid); +} + +inline bool RenderObject::backgroundIsKnownToBeObscured() +{ + if (m_bitfields.boxDecorationState() == HasBoxDecorationsAndBackgroundObscurationStatusInvalid) { + BoxDecorationState boxDecorationState = computeBackgroundIsKnownToBeObscured() ? HasBoxDecorationsAndBackgroundIsKnownToBeObscured : HasBoxDecorationsAndBackgroundMayBeVisible; + m_bitfields.setBoxDecorationState(boxDecorationState); + } + return m_bitfields.boxDecorationState() == HasBoxDecorationsAndBackgroundIsKnownToBeObscured; } inline int adjustForAbsoluteZoom(int value, RenderObject* renderer) @@ -1264,6 +1327,13 @@ inline int adjustForAbsoluteZoom(int value, RenderObject* renderer) return adjustForAbsoluteZoom(value, renderer->style()); } +#if ENABLE(SUBPIXEL_LAYOUT) +inline LayoutUnit adjustLayoutUnitForAbsoluteZoom(LayoutUnit value, RenderObject* renderer) +{ + return adjustLayoutUnitForAbsoluteZoom(value, renderer->style()); +} +#endif + inline void adjustFloatQuadForAbsoluteZoom(FloatQuad& quad, RenderObject* renderer) { float zoom = renderer->style()->effectiveZoom(); diff --git a/Source/WebCore/rendering/RenderObjectChildList.cpp b/Source/WebCore/rendering/RenderObjectChildList.cpp index e2a95a207..9445df88f 100644 --- a/Source/WebCore/rendering/RenderObjectChildList.cpp +++ b/Source/WebCore/rendering/RenderObjectChildList.cpp @@ -28,20 +28,13 @@ #include "RenderObjectChildList.h" #include "AXObjectCache.h" -#include "ContentData.h" -#include "RenderBlock.h" #include "RenderCounter.h" -#include "RenderLayer.h" -#include "RenderListItem.h" -#include "RenderNamedFlowThread.h" -#include "RenderRegion.h" +#include "RenderObject.h" #include "RenderStyle.h" #include "RenderView.h" namespace WebCore { -bool RenderObjectChildList::s_enableUpdateBeforeAfterContent = true; - void RenderObjectChildList::destroyLeftoverChildren() { while (firstChild()) { @@ -113,346 +106,64 @@ RenderObject* RenderObjectChildList::removeChildNode(RenderObject* owner, Render // rendererRemovedFromTree walks the whole subtree. We can improve performance // by skipping this step when destroying the entire tree. - if (!owner->documentBeingDestroyed()) { + if (!owner->documentBeingDestroyed()) RenderCounter::rendererRemovedFromTree(oldChild); - } - if (AXObjectCache::accessibilityEnabled()) - owner->document()->axObjectCache()->childrenChanged(owner); + if (AXObjectCache* cache = owner->document()->existingAXObjectCache()) + cache->childrenChanged(owner); return oldChild; } -void RenderObjectChildList::appendChildNode(RenderObject* owner, RenderObject* newChild, bool notifyRenderer) +void RenderObjectChildList::insertChildNode(RenderObject* owner, RenderObject* newChild, RenderObject* beforeChild, bool notifyRenderer) { - ASSERT(newChild->parent() == 0); + ASSERT(!newChild->parent()); ASSERT(!owner->isBlockFlow() || (!newChild->isTableSection() && !newChild->isTableRow() && !newChild->isTableCell())); - newChild->setParent(owner); - RenderObject* lChild = lastChild(); - - if (lChild) { - newChild->setPreviousSibling(lChild); - lChild->setNextSibling(newChild); - } else - setFirstChild(newChild); - - setLastChild(newChild); - - if (!owner->documentBeingDestroyed() && notifyRenderer) - newChild->insertedIntoTree(); - - if (!owner->documentBeingDestroyed()) { - RenderCounter::rendererSubtreeAttached(newChild); - } - newChild->setNeedsLayoutAndPrefWidthsRecalc(); // Goes up the containing block hierarchy. - if (!owner->normalChildNeedsLayout()) - owner->setChildNeedsLayout(true); // We may supply the static position for an absolute positioned child. - - if (AXObjectCache::accessibilityEnabled()) - owner->document()->axObjectCache()->childrenChanged(owner); -} - -void RenderObjectChildList::insertChildNode(RenderObject* owner, RenderObject* child, RenderObject* beforeChild, bool notifyRenderer) -{ - if (!beforeChild) { - appendChildNode(owner, child, notifyRenderer); - return; - } - - ASSERT(!child->parent()); - while (beforeChild->parent() != owner && beforeChild->parent()->isAnonymousBlock()) + while (beforeChild && beforeChild->parent() && beforeChild->parent() != owner) beforeChild = beforeChild->parent(); - ASSERT(beforeChild->parent() == owner); - - ASSERT(!owner->isBlockFlow() || (!child->isTableSection() && !child->isTableRow() && !child->isTableCell())); - - if (beforeChild == firstChild()) - setFirstChild(child); - - RenderObject* prev = beforeChild->previousSibling(); - child->setNextSibling(beforeChild); - beforeChild->setPreviousSibling(child); - if (prev) - prev->setNextSibling(child); - child->setPreviousSibling(prev); - - child->setParent(owner); - - if (!owner->documentBeingDestroyed() && notifyRenderer) - child->insertedIntoTree(); - - if (!owner->documentBeingDestroyed()) { - RenderCounter::rendererSubtreeAttached(child); - } - child->setNeedsLayoutAndPrefWidthsRecalc(); - if (!owner->normalChildNeedsLayout()) - owner->setChildNeedsLayout(true); // We may supply the static position for an absolute positioned child. - - if (AXObjectCache::accessibilityEnabled()) - owner->document()->axObjectCache()->childrenChanged(owner); -} - -static RenderObject* findBeforeAfterParent(RenderObject* object) -{ - // Only table parts and flex-boxes need to search for the :before or :after parent - // FIXME: We could likely get away without this check and always look for the right parent. - if (!(object->isTable() || object->isTableSection() || object->isTableRow() || object->isFlexibleBoxIncludingDeprecated())) - return object; - - // If there is a :first-letter style applied on the :before or :after content, - // then we want the parent of the first-letter block - RenderObject* beforeAfterParent = object; - while (beforeAfterParent && !(beforeAfterParent->isText() || beforeAfterParent->isImage()) - && (beforeAfterParent->style()->styleType() != FIRST_LETTER)) - beforeAfterParent = beforeAfterParent->firstChild(); - - return beforeAfterParent ? beforeAfterParent->parent() : 0; -} - -RenderObject* RenderObjectChildList::beforePseudoElementRenderer(const RenderObject* owner) const -{ - // An anonymous (generated) inline run-in that has PseudoId BEFORE must come from a grandparent. - // Therefore we should skip these generated run-ins when checking our immediate children. - // If we don't find our :before child immediately, then we should check if we own a - // generated inline run-in in the next level of children. - RenderObject* first = const_cast<RenderObject*>(owner); - do { - first = first->firstChild(); - // Skip list markers and generated run-ins. - while (first && (first->isListMarker() || (first->isRenderInline() && first->isRunIn()))) - first = first->nextInPreOrderAfterChildren(owner); - } while (first && first->isAnonymous() && first->style()->styleType() == NOPSEUDO); - - if (!first) - return 0; - - if (first->isBeforeContent()) - return first; - - // Check for a possible generated run-in, using run-in positioning rules. - first = owner->firstChild(); - if (!first->isRenderBlock()) - return 0; - - first = first->firstChild(); - // We still need to skip any list markers that could exist before the run-in. - while (first && first->isListMarker()) - first = first->nextSibling(); - if (first && first->isBeforeContent() && first->isRenderInline() && first->isRunIn()) - return first; - - return 0; -} - -RenderObject* RenderObjectChildList::afterPseudoElementRenderer(const RenderObject* owner) const -{ - RenderObject* last = const_cast<RenderObject*>(owner); - do { - last = last->lastChild(); - } while (last && last->isAnonymous() && last->style()->styleType() == NOPSEUDO && !last->isListMarker()); - if (last && !last->isAfterContent()) - return 0; - return last; -} -void RenderObjectChildList::updateBeforeAfterStyle(RenderObject* child, PseudoId type, RenderStyle* pseudoElementStyle) -{ - if (!child || child->style()->styleType() != type) - return; - - // We have generated content present still. We want to walk this content and update our - // style information with the new pseudo-element style. - child->setStyle(pseudoElementStyle); - - RenderObject* beforeAfterParent = findBeforeAfterParent(child); - if (!beforeAfterParent) - return; - - // When beforeAfterParent is not equal to child (e.g. in tables), - // we need to create new styles inheriting from pseudoElementStyle - // on all the intermediate parents (leaving their display same). - if (beforeAfterParent != child) { - RenderObject* curr = beforeAfterParent; - while (curr && curr != child) { - ASSERT(curr->isAnonymous()); - RefPtr<RenderStyle> newStyle = RenderStyle::create(); - newStyle->inheritFrom(pseudoElementStyle); - newStyle->setDisplay(curr->style()->display()); - newStyle->setStyleType(curr->style()->styleType()); - curr->setStyle(newStyle); - curr = curr->parent(); - } - } - - // Note that if we ever support additional types of generated content (which should be way off - // in the future), this code will need to be patched. - for (RenderObject* genChild = beforeAfterParent->firstChild(); genChild; genChild = genChild->nextSibling()) { - if (genChild->isText()) - // Generated text content is a child whose style also needs to be set to the pseudo-element style. - genChild->setStyle(pseudoElementStyle); - else if (genChild->isImage()) { - // Images get an empty style that inherits from the pseudo. - RefPtr<RenderStyle> style = RenderStyle::create(); - style->inheritFrom(pseudoElementStyle); - genChild->setStyle(style.release()); - } else { - // RenderListItem may insert a list marker here. We do not need to care about this case. - // Otherwise, genChild must be a first-letter container. updateFirstLetter() will take care of it. - ASSERT(genChild->isListMarker() || genChild->style()->styleType() == FIRST_LETTER); - } - } -} - -static RenderObject* ensureBeforeAfterContainer(RenderObject* owner, PseudoId type, RenderStyle* pseudoElementStyle, Node* generatingNode, RenderObject* insertBefore) -{ - // Make a generated box that might be any display type now that we are able to drill down into children - // to find the original content properly. - RenderObject* generatedContentContainer = RenderObject::createObject(owner->document(), pseudoElementStyle); - ASSERT(generatingNode); // The styled object cannot be anonymous or else it could not have ':before' or ':after' pseudo elements. - generatedContentContainer->setNode(generatingNode); // This allows access to the generatingNode. - generatedContentContainer->setStyle(pseudoElementStyle); - if (!owner->isChildAllowed(generatedContentContainer, pseudoElementStyle)) { - // The generated content container is not allowed here -> abort. - generatedContentContainer->destroy(); - return 0; - } - - // When we don't have a first child and are part of a continuation chain, - // insertBefore is incorrectly set to zero above, which causes the :before - // child to end up at the end of continuation chain. - // See https://bugs.webkit.org/show_bug.cgi?id=78380. - if (!insertBefore && type == BEFORE && owner->virtualContinuation()) - owner->addChildIgnoringContinuation(generatedContentContainer, 0); - else - owner->addChild(generatedContentContainer, insertBefore); - - return generatedContentContainer; -} - -void RenderObjectChildList::updateBeforeAfterContent(RenderObject* owner, PseudoId type, const RenderObject* styledObject) -{ - // Double check that the document did in fact use generated content rules. Otherwise we should not have been called. - ASSERT(owner->document()->styleSheetCollection()->usesBeforeAfterRules()); - - // In CSS2, before/after pseudo-content cannot nest. Check this first. - if (owner->style()->styleType() == BEFORE || owner->style()->styleType() == AFTER) - return; - if (!s_enableUpdateBeforeAfterContent) - return; - - if (!styledObject) - styledObject = owner; - - RenderStyle* pseudoElementStyle = styledObject->getCachedPseudoStyle(type); - RenderObject* child; - switch (type) { - case BEFORE: - child = beforePseudoElementRenderer(owner); - break; - case AFTER: - child = afterPseudoElementRenderer(owner); - break; - default: + // This should never happen, but if it does prevent render tree corruption + // where child->parent() ends up being owner but child->nextSibling()->parent() + // is not owner. + if (beforeChild && beforeChild->parent() != owner) { ASSERT_NOT_REACHED(); return; } - // Whether or not we currently have generated content attached. - bool oldContentPresent = child; - - // Whether or not we now want generated content. - bool newContentWanted = pseudoElementStyle && pseudoElementStyle->display() != NONE; - - // For <q><p/></q>, if this object is the inline continuation of the <q>, we only want to generate - // :after content and not :before content. - if (newContentWanted && type == BEFORE && owner->isElementContinuation()) - newContentWanted = false; - - // Similarly, if we're the beginning of a <q>, and there's an inline continuation for our object, - // then we don't generate the :after content. - if (newContentWanted && type == AFTER && owner->virtualContinuation()) - newContentWanted = false; - - // If we don't want generated content any longer, or if we have generated content, but it's no longer - // identical to the new content data we want to build render objects for, then we nuke all - // of the old generated content. - if (oldContentPresent && (!newContentWanted || Node::diff(child->style(), pseudoElementStyle, owner->document()) == Node::Detach)) { - // Nuke the child. - if (child->style()->styleType() == type) { - oldContentPresent = false; - child->destroy(); - child = (type == BEFORE) ? owner->virtualChildren()->firstChild() : owner->virtualChildren()->lastChild(); - } - } - - // If we have no pseudo-element style or if the pseudo-element style's display type is NONE, then we - // have no generated content and can now return. - if (!newContentWanted) - return; + newChild->setParent(owner); - if (owner->isRenderInline() && !pseudoElementStyle->isDisplayInlineType() && !pseudoElementStyle->isFloating() && - !pseudoElementStyle->hasOutOfFlowPosition()) - // According to the CSS2 spec (the end of section 12.1), the only allowed - // display values for the pseudo style are NONE and INLINE for inline flows. - // FIXME: CSS2.1 lifted this restriction, but block display types will crash. - // For now we at least relax the restriction to allow all inline types like inline-block - // and inline-table. - pseudoElementStyle->setDisplay(INLINE); + if (firstChild() == beforeChild) + setFirstChild(newChild); - if (oldContentPresent) { - updateBeforeAfterStyle(child, type, pseudoElementStyle); - return; // We've updated the generated content. That's all we needed to do. + if (beforeChild) { + RenderObject* previousSibling = beforeChild->previousSibling(); + if (previousSibling) + previousSibling->setNextSibling(newChild); + newChild->setPreviousSibling(previousSibling); + newChild->setNextSibling(beforeChild); + beforeChild->setPreviousSibling(newChild); + } else { + if (lastChild()) + lastChild()->setNextSibling(newChild); + newChild->setPreviousSibling(lastChild()); + setLastChild(newChild); } - - RenderObject* insertBefore = (type == BEFORE) ? owner->virtualChildren()->firstChild() : 0; - if (insertBefore && insertBefore->isAnonymousBlock() && insertBefore->childrenInline() && !insertBefore->isEmpty()) { - // We are going to add the "before" element. We have to check whether the "insertBefore" element - // is an anonymous block with inline children. If it is, then we should insert the "before" element - // before the first inline child of the anonymous block, otherwise we will end up with the "before" - // element in a different block. We do this only when the anonymous block has children, otherwise - // we end up with the before element in a wrong block. - insertBefore = insertBefore->firstChild(); - } - - // Nothing goes before the intruded run-in, not even generated content. - if (insertBefore && insertBefore->isRunIn() && owner->isRenderBlock() - && toRenderBlock(owner)->runInIsPlacedIntoSiblingBlock(insertBefore)) - insertBefore = insertBefore->nextSibling(); - - // Generated content consists of a single container that houses multiple children (specified - // by the content property). This generated content container gets the pseudo-element style set on it. - // For pseudo-elements that are regions, the container is the RenderRegion. - RenderObject* generatedContentContainer = 0; - if (!pseudoElementStyle->regionThread().isEmpty()) - generatedContentContainer = ensureBeforeAfterContainer(owner, type, pseudoElementStyle, styledObject->node(), insertBefore); - else { - // Walk our list of generated content and create render objects for each. - for (const ContentData* content = pseudoElementStyle->contentData(); content; content = content->next()) { - RenderObject* renderer = content->createRenderer(owner->document(), pseudoElementStyle); + if (!owner->documentBeingDestroyed() && notifyRenderer) + newChild->insertedIntoTree(); - if (!generatedContentContainer) { - generatedContentContainer = ensureBeforeAfterContainer(owner, type, pseudoElementStyle, styledObject->node(), insertBefore); - if (!generatedContentContainer) { - renderer->destroy(); - return; - } - } - if (generatedContentContainer->isChildAllowed(renderer, pseudoElementStyle)) - generatedContentContainer->addChild(renderer); - else - renderer->destroy(); - } + if (!owner->documentBeingDestroyed()) { + RenderCounter::rendererSubtreeAttached(newChild); } - if (!generatedContentContainer) - return; + newChild->setNeedsLayoutAndPrefWidthsRecalc(); + owner->setPreferredLogicalWidthsDirty(true); + if (!owner->normalChildNeedsLayout()) + owner->setChildNeedsLayout(true); // We may supply the static position for an absolute positioned child. - // Handle placement of run-ins. We do the run-in placement at the end since generatedContentContainer can get destroyed. - RenderObject* generatedContentContainerImmediateParent = generatedContentContainer->parent(); - if (generatedContentContainerImmediateParent->isRenderBlock()) - toRenderBlock(generatedContentContainerImmediateParent)->placeRunInIfNeeded(generatedContentContainer, PlaceGeneratedRunIn); + if (AXObjectCache* cache = owner->document()->axObjectCache()) + cache->childrenChanged(owner); } } // namespace WebCore diff --git a/Source/WebCore/rendering/RenderObjectChildList.h b/Source/WebCore/rendering/RenderObjectChildList.h index 3f606db8d..b2804ec56 100644 --- a/Source/WebCore/rendering/RenderObjectChildList.h +++ b/Source/WebCore/rendering/RenderObjectChildList.h @@ -26,13 +26,11 @@ #ifndef RenderObjectChildList_h #define RenderObjectChildList_h -#include "RenderStyleConstants.h" #include <wtf/Forward.h> namespace WebCore { class RenderObject; -class RenderStyle; class RenderObjectChildList { public: @@ -44,28 +42,22 @@ public: RenderObject* firstChild() const { return m_firstChild; } RenderObject* lastChild() const { return m_lastChild; } - + // FIXME: Temporary while RenderBox still exists. Eventually this will just happen during insert/append/remove methods on the child list, and nobody // will need to manipulate firstChild or lastChild directly. void setFirstChild(RenderObject* child) { m_firstChild = child; } void setLastChild(RenderObject* child) { m_lastChild = child; } - + void destroyLeftoverChildren(); RenderObject* removeChildNode(RenderObject* owner, RenderObject*, bool notifyRenderer = true); - void appendChildNode(RenderObject* owner, RenderObject*, bool notifyRenderer = true); - void insertChildNode(RenderObject* owner, RenderObject* child, RenderObject* before, bool notifyRenderer = true); - - void updateBeforeAfterContent(RenderObject* owner, PseudoId type, const RenderObject* styledObject = 0); - RenderObject* beforePseudoElementRenderer(const RenderObject* owner) const; - RenderObject* afterPseudoElementRenderer(const RenderObject* owner) const; - -public: - static bool s_enableUpdateBeforeAfterContent; + void insertChildNode(RenderObject* owner, RenderObject* newChild, RenderObject* beforeChild, bool notifyRenderer = true); + void appendChildNode(RenderObject* owner, RenderObject* newChild, bool notifyRenderer = true) + { + insertChildNode(owner, newChild, 0, notifyRenderer); + } private: - void updateBeforeAfterStyle(RenderObject* child, PseudoId type, RenderStyle* pseudoElementStyle); - RenderObject* m_firstChild; RenderObject* m_lastChild; }; diff --git a/Source/WebCore/rendering/RenderOverflow.h b/Source/WebCore/rendering/RenderOverflow.h index e6b85e9f4..2430b882d 100644 --- a/Source/WebCore/rendering/RenderOverflow.h +++ b/Source/WebCore/rendering/RenderOverflow.h @@ -48,16 +48,6 @@ public: const LayoutRect layoutOverflowRect() const { return m_layoutOverflow; } const LayoutRect visualOverflowRect() const { return m_visualOverflow; } - - void setMinYLayoutOverflow(LayoutUnit overflow) { m_layoutOverflow.setY(overflow); } - void setMaxYLayoutOverflow(LayoutUnit overflow) { m_layoutOverflow.setHeight(overflow - m_layoutOverflow.y()); } - void setMinXLayoutOverflow(LayoutUnit overflow) { m_layoutOverflow.setX(overflow); } - void setMaxXLayoutOverflow(LayoutUnit overflow) { m_layoutOverflow.setWidth(overflow - m_layoutOverflow.x()); } - - void setMinYVisualOverflow(LayoutUnit overflow) { m_visualOverflow.setY(overflow); } - void setMaxYVisualOverflow(LayoutUnit overflow) { m_visualOverflow.setHeight(overflow - m_layoutOverflow.y()); } - void setMinXVisualOverflow(LayoutUnit overflow) { m_visualOverflow.setX(overflow); } - void setMaxXVisualOverflow(LayoutUnit overflow) { m_visualOverflow.setWidth(overflow - m_layoutOverflow.x()); } void move(LayoutUnit dx, LayoutUnit dy); @@ -67,9 +57,14 @@ public: void setLayoutOverflow(const LayoutRect&); void setVisualOverflow(const LayoutRect&); + LayoutUnit layoutClientAfterEdge() const { return m_layoutClientAfterEdge; } + void setLayoutClientAfterEdge(LayoutUnit clientAfterEdge) { m_layoutClientAfterEdge = clientAfterEdge; } + private: LayoutRect m_layoutOverflow; LayoutRect m_visualOverflow; + + LayoutUnit m_layoutClientAfterEdge; }; inline void RenderOverflow::move(LayoutUnit dx, LayoutUnit dy) diff --git a/Source/WebCore/rendering/RenderPart.cpp b/Source/WebCore/rendering/RenderPart.cpp index 92fcf5d85..6a269efb1 100644 --- a/Source/WebCore/rendering/RenderPart.cpp +++ b/Source/WebCore/rendering/RenderPart.cpp @@ -28,7 +28,9 @@ #include "Frame.h" #include "FrameView.h" #include "HTMLFrameElementBase.h" +#include "HitTestResult.h" #include "PluginViewBase.h" +#include "RenderLayer.h" #include "RenderSVGRoot.h" #include "RenderView.h" @@ -78,13 +80,13 @@ bool RenderPart::requiresAcceleratedCompositing() const // renderer and the plugin has a layer, then we need a layer. Second, if this is // a renderer with a contentDocument and that document needs a layer, then we need // a layer. - if (widget() && widget()->isPluginViewBase() && static_cast<PluginViewBase*>(widget())->platformLayer()) + if (widget() && widget()->isPluginViewBase() && toPluginViewBase(widget())->platformLayer()) return true; if (!node() || !node()->isFrameOwnerElement()) return false; - HTMLFrameOwnerElement* element = static_cast<HTMLFrameOwnerElement*>(node()); + HTMLFrameOwnerElement* element = toFrameOwnerElement(node()); if (Document* contentDocument = element->contentDocument()) { if (RenderView* view = contentDocument->renderView()) return view->usesCompositing(); @@ -105,7 +107,36 @@ RenderBox* RenderPart::embeddedContentBox() const { if (!node() || !widget() || !widget()->isFrameView()) return 0; - return static_cast<FrameView*>(widget())->embeddedContentBox(); + return toFrameView(widget())->embeddedContentBox(); +} + +bool RenderPart::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction action) +{ + if (!widget() || !widget()->isFrameView() || !request.allowsChildFrameContent()) + return RenderWidget::nodeAtPoint(request, result, locationInContainer, accumulatedOffset, action); + + FrameView* childFrameView = toFrameView(widget()); + RenderView* childRoot = childFrameView->renderView(); + + if (childRoot) { + LayoutPoint adjustedLocation = accumulatedOffset + location(); + LayoutPoint contentOffset = LayoutPoint(borderLeft() + paddingLeft(), borderTop() + paddingTop()) - childFrameView->scrollOffset(); + HitTestLocation newHitTestLocation(locationInContainer, -adjustedLocation - contentOffset); + HitTestRequest newHitTestRequest(request.type() | HitTestRequest::ChildFrameHitTest); + HitTestResult childFrameResult(newHitTestLocation); + + bool isInsideChildFrame = childRoot->hitTest(newHitTestRequest, newHitTestLocation, childFrameResult); + + if (newHitTestLocation.isRectBasedTest()) + result.append(childFrameResult); + else if (isInsideChildFrame) + result = childFrameResult; + + if (isInsideChildFrame) + return true; + } + + return RenderWidget::nodeAtPoint(request, result, locationInContainer, accumulatedOffset, action); } } diff --git a/Source/WebCore/rendering/RenderPart.h b/Source/WebCore/rendering/RenderPart.h index dffbbd54c..538b0f973 100644 --- a/Source/WebCore/rendering/RenderPart.h +++ b/Source/WebCore/rendering/RenderPart.h @@ -30,7 +30,7 @@ namespace WebCore { // Renderer for frames via RenderFrameBase, and plug-ins via RenderEmbeddedObject. class RenderPart : public RenderWidget { public: - RenderPart(Element*); + explicit RenderPart(Element*); virtual ~RenderPart(); virtual void setWidget(PassRefPtr<Widget>); @@ -43,6 +43,8 @@ public: virtual bool needsPreferredWidthsRecalculation() const; virtual RenderBox* embeddedContentBox() const; + virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction) OVERRIDE; + protected: #if USE(ACCELERATED_COMPOSITING) virtual bool requiresLayer() const; @@ -55,7 +57,7 @@ private: inline RenderPart* toRenderPart(RenderObject* object) { - ASSERT(!object || object->isRenderPart()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isRenderPart()); return static_cast<RenderPart*>(object); } diff --git a/Source/WebCore/rendering/RenderProgress.cpp b/Source/WebCore/rendering/RenderProgress.cpp index 7243e96dc..ad1b338ad 100644 --- a/Source/WebCore/rendering/RenderProgress.cpp +++ b/Source/WebCore/rendering/RenderProgress.cpp @@ -19,9 +19,9 @@ */ #include "config.h" +#if ENABLE(PROGRESS_ELEMENT) #include "RenderProgress.h" -#if ENABLE(PROGRESS_ELEMENT) #include "HTMLNames.h" #include "HTMLProgressElement.h" #include "PaintInfo.h" @@ -56,6 +56,7 @@ void RenderProgress::updateFromElement() m_position = element->position(); updateAnimationState(); + repaint(); RenderBlock::updateFromElement(); } @@ -91,7 +92,6 @@ void RenderProgress::updateAnimationState() if (animating == m_animating) return; - repaint(); m_animating = animating; if (m_animating) { m_animationStartTime = currentTime(); diff --git a/Source/WebCore/rendering/RenderProgress.h b/Source/WebCore/rendering/RenderProgress.h index 6835e48c8..1428d838b 100644 --- a/Source/WebCore/rendering/RenderProgress.h +++ b/Source/WebCore/rendering/RenderProgress.h @@ -30,7 +30,7 @@ class HTMLProgressElement; class RenderProgress : public RenderBlock { public: - RenderProgress(HTMLElement*); + explicit RenderProgress(HTMLElement*); virtual ~RenderProgress(); double position() const { return m_position; } @@ -61,7 +61,7 @@ private: inline RenderProgress* toRenderProgress(RenderObject* object) { - ASSERT(!object || object->isProgress()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isProgress()); return static_cast<RenderProgress*>(object); } diff --git a/Source/WebCore/rendering/RenderQuote.cpp b/Source/WebCore/rendering/RenderQuote.cpp index da16df5aa..48f1f097e 100644 --- a/Source/WebCore/rendering/RenderQuote.cpp +++ b/Source/WebCore/rendering/RenderQuote.cpp @@ -1,6 +1,7 @@ -/** +/* * Copyright (C) 2011 Nokia Inc. All rights reserved. * Copyright (C) 2012 Google Inc. All rights reserved. + * Copyright (C) 2013 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -22,27 +23,27 @@ #include "config.h" #include "RenderQuote.h" -#include "RenderView.h" -#include <wtf/text/AtomicString.h> +#include "QuotesData.h" -#define U(x) ((const UChar*)L##x) +using namespace WTF::Unicode; namespace WebCore { RenderQuote::RenderQuote(Document* node, QuoteType quote) : RenderText(node, StringImpl::empty()) , m_type(quote) - , m_depth(0) + , m_depth(-1) , m_next(0) , m_previous(0) - , m_attached(false) + , m_isAttached(false) { } RenderQuote::~RenderQuote() { - ASSERT(!m_attached); - ASSERT(!m_next && !m_previous); + ASSERT(!m_isAttached); + ASSERT(!m_next); + ASSERT(!m_previous); } void RenderQuote::willBeDestroyed() @@ -54,252 +55,332 @@ void RenderQuote::willBeDestroyed() void RenderQuote::willBeRemovedFromTree() { RenderText::willBeRemovedFromTree(); - detachQuote(); } -typedef HashMap<AtomicString, const QuotesData*, CaseFoldingHash> QuotesMap; +void RenderQuote::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) +{ + RenderText::styleDidChange(diff, oldStyle); + setText(originalText()); +} + +const unsigned maxDistinctQuoteCharacters = 16; + +#if !ASSERT_DISABLED -static const QuotesMap& quotesDataLanguageMap() +static void checkNumberOfDistinctQuoteCharacters(UChar character) { - DEFINE_STATIC_LOCAL(QuotesMap, staticQuotesMap, ()); - if (staticQuotesMap.size()) - return staticQuotesMap; + ASSERT(character); + static UChar distinctQuoteCharacters[maxDistinctQuoteCharacters]; + for (unsigned i = 0; i < maxDistinctQuoteCharacters; ++i) { + if (distinctQuoteCharacters[i] == character) + return; + if (!distinctQuoteCharacters[i]) { + distinctQuoteCharacters[i] = character; + return; + } + } + ASSERT_NOT_REACHED(); +} - // Table of quotes from http://www.whatwg.org/specs/web-apps/current-work/multipage/rendering.html#quotes - #define QUOTES_LANG(lang, o1, c1, o2, c2) staticQuotesMap.set(lang, QuotesData::create(U(o1), U(c1), U(o2), U(c2)).leakRef()) - QUOTES_LANG("af", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("agq", "\x201e", "\x201d", "\x201a", "\x2019"); - QUOTES_LANG("ak", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("am", "\x00ab", "\x00bb", "\x2039", "\x203a"); - QUOTES_LANG("ar", "\x201d", "\x201c", "\x2019", "\x2018"); - QUOTES_LANG("asa", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("az-Cyrl", "\x00ab", "\x00bb", "\x2039", "\x203a"); - QUOTES_LANG("bas", "\x00ab", "\x00bb", "\x201e", "\x201c"); - QUOTES_LANG("bem", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("bez", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("bg", "\x201e", "\x201c", "\x201a", "\x2018"); - QUOTES_LANG("bm", "\x00ab", "\x00bb", "\x201c", "\x201d"); - QUOTES_LANG("bn", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("br", "\x00ab", "\x00bb", "\x2039", "\x203a"); - QUOTES_LANG("brx", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("bs-Cyrl", "\x201e", "\x201c", "\x201a", "\x2018"); - QUOTES_LANG("ca", "\x201c", "\x201d", "\x00ab", "\x00bb"); - QUOTES_LANG("cgg", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("chr", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("cs", "\x201e", "\x201c", "\x201a", "\x2018"); - QUOTES_LANG("da", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("dav", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("de", "\x201e", "\x201c", "\x201a", "\x2018"); - QUOTES_LANG("de-CH", "\x00ab", "\x00bb", "\x2039", "\x203a"); - QUOTES_LANG("dje", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("dua", "\x00ab", "\x00bb", "\x2018", "\x2019"); - QUOTES_LANG("dyo", "\x00ab", "\x00bb", "\x201c", "\x201d"); - QUOTES_LANG("dz", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("ebu", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("ee", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("el", "\x00ab", "\x00bb", "\x201c", "\x201d"); - QUOTES_LANG("en", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("en-GB", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("es", "\x201c", "\x201d", "\x00ab", "\x00bb"); - QUOTES_LANG("et", "\x201e", "\x201c", "\x201a", "\x2018"); - QUOTES_LANG("eu", "\x201c", "\x201d", "\x00ab", "\x00bb"); - QUOTES_LANG("ewo", "\x00ab", "\x00bb", "\x201c", "\x201d"); - QUOTES_LANG("fa", "\x00ab", "\x00bb", "\x2039", "\x203a"); - QUOTES_LANG("ff", "\x201e", "\x201d", "\x201a", "\x2019"); - QUOTES_LANG("fi", "\x201d", "\x201d", "\x2019", "\x2019"); - QUOTES_LANG("fr", "\x00ab", "\x00bb", "\x00ab", "\x00bb"); - QUOTES_LANG("fr-CA", "\x00ab", "\x00bb", "\x2039", "\x203a"); - QUOTES_LANG("fr-CH", "\x00ab", "\x00bb", "\x2039", "\x203a"); - QUOTES_LANG("gsw", "\x00ab", "\x00bb", "\x2039", "\x203a"); - QUOTES_LANG("gu", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("guz", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("ha", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("he", "\x0022", "\x0022", "\x0027", "\x0027"); - QUOTES_LANG("hi", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("hr", "\x201e", "\x201c", "\x201a", "\x2018"); - QUOTES_LANG("hu", "\x201e", "\x201d", "\x00bb", "\x00ab"); - QUOTES_LANG("id", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("ig", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("it", "\x00ab", "\x00bb", "\x201c", "\x201d"); - QUOTES_LANG("ja", "\x300c", "\x300d", "\x300e", "\x300f"); - QUOTES_LANG("jgo", "\x00ab", "\x00bb", "\x2039", "\x203a"); - QUOTES_LANG("jmc", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("kab", "\x00ab", "\x00bb", "\x201c", "\x201d"); - QUOTES_LANG("kam", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("kde", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("kea", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("khq", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("ki", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("kkj", "\x00ab", "\x00bb", "\x2039", "\x203a"); - QUOTES_LANG("kln", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("km", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("kn", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("ko", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("ksb", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("ksf", "\x00ab", "\x00bb", "\x2018", "\x2019"); - QUOTES_LANG("lag", "\x201d", "\x201d", "\x2019", "\x2019"); - QUOTES_LANG("lg", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("ln", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("lo", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("lt", "\x201e", "\x201c", "\x201e", "\x201c"); - QUOTES_LANG("lu", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("luo", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("luy", "\x201e", "\x201c", "\x201a", "\x2018"); - QUOTES_LANG("lv", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("mas", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("mer", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("mfe", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("mg", "\x00ab", "\x00bb", "\x201c", "\x201d"); - QUOTES_LANG("mgo", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("mk", "\x201e", "\x201c", "\x201a", "\x2018"); - QUOTES_LANG("ml", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("mr", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("ms", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("mua", "\x00ab", "\x00bb", "\x201c", "\x201d"); - QUOTES_LANG("my", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("naq", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("nb", "\x00ab", "\x00bb", "\x2018", "\x2019"); - QUOTES_LANG("nd", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("nl", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("nmg", "\x201e", "\x201d", "\x00ab", "\x00bb"); - QUOTES_LANG("nn", "\x00ab", "\x00bb", "\x2018", "\x2019"); - QUOTES_LANG("nnh", "\x00ab", "\x00bb", "\x201c", "\x201d"); - QUOTES_LANG("nus", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("nyn", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("pl", "\x201e", "\x201d", "\x00ab", "\x00bb"); - QUOTES_LANG("pt", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("pt-PT", "\x00ab", "\x00bb", "\x201c", "\x201d"); - QUOTES_LANG("rn", "\x201d", "\x201d", "\x2019", "\x2019"); - QUOTES_LANG("ro", "\x201e", "\x201d", "\x00ab", "\x00bb"); - QUOTES_LANG("rof", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("ru", "\x00ab", "\x00bb", "\x201e", "\x201c"); - QUOTES_LANG("rw", "\x00ab", "\x00bb", "\x2018", "\x2019"); - QUOTES_LANG("rwk", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("saq", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("sbp", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("seh", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("ses", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("sg", "\x00ab", "\x00bb", "\x201c", "\x201d"); - QUOTES_LANG("shi", "\x00ab", "\x00bb", "\x201e", "\x201d"); - QUOTES_LANG("shi-Tfng", "\x00ab", "\x00bb", "\x201e", "\x201d"); - QUOTES_LANG("si", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("sk", "\x201e", "\x201c", "\x201a", "\x2018"); - QUOTES_LANG("sl", "\x201e", "\x201c", "\x201a", "\x2018"); - QUOTES_LANG("sn", "\x201d", "\x201d", "\x2019", "\x2019"); - QUOTES_LANG("so", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("sq", "\x201e", "\x201c", "\x201a", "\x2018"); - QUOTES_LANG("sr", "\x201e", "\x201c", "\x201a", "\x2018"); - QUOTES_LANG("sr-Latn", "\x201e", "\x201c", "\x201a", "\x2018"); - QUOTES_LANG("sv", "\x201d", "\x201d", "\x2019", "\x2019"); - QUOTES_LANG("sw", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("swc", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("ta", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("te", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("teo", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("th", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("ti-ER", "\x2018", "\x2019", "\x201c", "\x201d"); - QUOTES_LANG("to", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("tr", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("twq", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("tzm", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("uk", "\x00ab", "\x00bb", "\x201e", "\x201c"); - QUOTES_LANG("ur", "\x201d", "\x201c", "\x2019", "\x2018"); - QUOTES_LANG("vai", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("vai-Latn", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("vi", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("vun", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("xh", "\x2018", "\x2019", "\x201c", "\x201d"); - QUOTES_LANG("xog", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("yav", "\x00ab", "\x00bb", "\x00ab", "\x00bb"); - QUOTES_LANG("yo", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("zh", "\x201c", "\x201d", "\x2018", "\x2019"); - QUOTES_LANG("zh-Hant", "\x300c", "\x300d", "\x300e", "\x300f"); - QUOTES_LANG("zu", "\x201c", "\x201d", "\x2018", "\x2019"); - #undef QUOTES_LANG - - return staticQuotesMap; +#endif + +struct QuotesForLanguage { + const char* language; + UChar open1; + UChar close1; + UChar open2; + UChar close2; +}; + +static int quoteTableLanguageComparisonFunction(const void* a, const void* b) +{ + return strcmp(static_cast<const QuotesForLanguage*>(a)->language, + static_cast<const QuotesForLanguage*>(b)->language); } -static const QuotesData* basicQuotesData() +static const QuotesForLanguage* quotesForLanguage(const String& language) { - // FIXME: The default quotes should be the fancy quotes for "en". - static const QuotesData* staticBasicQuotes = QuotesData::create(U("\""), U("\""), U("'"), U("'")).leakRef(); - return staticBasicQuotes; + // Table of quotes from http://www.whatwg.org/specs/web-apps/current-work/multipage/rendering.html#quotes + static const QuotesForLanguage quoteTable[] = { + { "af", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "agq", 0x201e, 0x201d, 0x201a, 0x2019 }, + { "ak", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "am", 0x00ab, 0x00bb, 0x2039, 0x203a }, + { "ar", 0x201d, 0x201c, 0x2019, 0x2018 }, + { "asa", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "az-cyrl", 0x00ab, 0x00bb, 0x2039, 0x203a }, + { "bas", 0x00ab, 0x00bb, 0x201e, 0x201c }, + { "bem", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "bez", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "bg", 0x201e, 0x201c, 0x201a, 0x2018 }, + { "bm", 0x00ab, 0x00bb, 0x201c, 0x201d }, + { "bn", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "br", 0x00ab, 0x00bb, 0x2039, 0x203a }, + { "brx", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "bs-cyrl", 0x201e, 0x201c, 0x201a, 0x2018 }, + { "ca", 0x201c, 0x201d, 0x00ab, 0x00bb }, + { "cgg", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "chr", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "cs", 0x201e, 0x201c, 0x201a, 0x2018 }, + { "da", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "dav", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "de", 0x201e, 0x201c, 0x201a, 0x2018 }, + { "de-ch", 0x00ab, 0x00bb, 0x2039, 0x203a }, + { "dje", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "dua", 0x00ab, 0x00bb, 0x2018, 0x2019 }, + { "dyo", 0x00ab, 0x00bb, 0x201c, 0x201d }, + { "dz", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "ebu", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "ee", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "el", 0x00ab, 0x00bb, 0x201c, 0x201d }, + { "en", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "en-gb", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "es", 0x201c, 0x201d, 0x00ab, 0x00bb }, + { "et", 0x201e, 0x201c, 0x201a, 0x2018 }, + { "eu", 0x201c, 0x201d, 0x00ab, 0x00bb }, + { "ewo", 0x00ab, 0x00bb, 0x201c, 0x201d }, + { "fa", 0x00ab, 0x00bb, 0x2039, 0x203a }, + { "ff", 0x201e, 0x201d, 0x201a, 0x2019 }, + { "fi", 0x201d, 0x201d, 0x2019, 0x2019 }, + { "fr", 0x00ab, 0x00bb, 0x00ab, 0x00bb }, + { "fr-ca", 0x00ab, 0x00bb, 0x2039, 0x203a }, + { "fr-ch", 0x00ab, 0x00bb, 0x2039, 0x203a }, + { "gsw", 0x00ab, 0x00bb, 0x2039, 0x203a }, + { "gu", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "guz", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "ha", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "he", 0x0022, 0x0022, 0x0027, 0x0027 }, + { "hi", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "hr", 0x201e, 0x201c, 0x201a, 0x2018 }, + { "hu", 0x201e, 0x201d, 0x00bb, 0x00ab }, + { "id", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "ig", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "it", 0x00ab, 0x00bb, 0x201c, 0x201d }, + { "ja", 0x300c, 0x300d, 0x300e, 0x300f }, + { "jgo", 0x00ab, 0x00bb, 0x2039, 0x203a }, + { "jmc", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "kab", 0x00ab, 0x00bb, 0x201c, 0x201d }, + { "kam", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "kde", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "kea", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "khq", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "ki", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "kkj", 0x00ab, 0x00bb, 0x2039, 0x203a }, + { "kln", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "km", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "kn", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "ko", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "ksb", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "ksf", 0x00ab, 0x00bb, 0x2018, 0x2019 }, + { "lag", 0x201d, 0x201d, 0x2019, 0x2019 }, + { "lg", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "ln", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "lo", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "lt", 0x201e, 0x201c, 0x201e, 0x201c }, + { "lu", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "luo", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "luy", 0x201e, 0x201c, 0x201a, 0x2018 }, + { "lv", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "mas", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "mer", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "mfe", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "mg", 0x00ab, 0x00bb, 0x201c, 0x201d }, + { "mgo", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "mk", 0x201e, 0x201c, 0x201a, 0x2018 }, + { "ml", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "mr", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "ms", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "mua", 0x00ab, 0x00bb, 0x201c, 0x201d }, + { "my", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "naq", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "nb", 0x00ab, 0x00bb, 0x2018, 0x2019 }, + { "nd", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "nl", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "nmg", 0x201e, 0x201d, 0x00ab, 0x00bb }, + { "nn", 0x00ab, 0x00bb, 0x2018, 0x2019 }, + { "nnh", 0x00ab, 0x00bb, 0x201c, 0x201d }, + { "nus", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "nyn", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "pl", 0x201e, 0x201d, 0x00ab, 0x00bb }, + { "pt", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "pt-pt", 0x00ab, 0x00bb, 0x201c, 0x201d }, + { "rn", 0x201d, 0x201d, 0x2019, 0x2019 }, + { "ro", 0x201e, 0x201d, 0x00ab, 0x00bb }, + { "rof", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "ru", 0x00ab, 0x00bb, 0x201e, 0x201c }, + { "rw", 0x00ab, 0x00bb, 0x2018, 0x2019 }, + { "rwk", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "saq", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "sbp", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "seh", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "ses", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "sg", 0x00ab, 0x00bb, 0x201c, 0x201d }, + { "shi", 0x00ab, 0x00bb, 0x201e, 0x201d }, + { "shi-tfng", 0x00ab, 0x00bb, 0x201e, 0x201d }, + { "si", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "sk", 0x201e, 0x201c, 0x201a, 0x2018 }, + { "sl", 0x201e, 0x201c, 0x201a, 0x2018 }, + { "sn", 0x201d, 0x201d, 0x2019, 0x2019 }, + { "so", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "sq", 0x201e, 0x201c, 0x201a, 0x2018 }, + { "sr", 0x201e, 0x201c, 0x201a, 0x2018 }, + { "sr-latn", 0x201e, 0x201c, 0x201a, 0x2018 }, + { "sv", 0x201d, 0x201d, 0x2019, 0x2019 }, + { "sw", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "swc", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "ta", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "te", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "teo", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "th", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "ti-er", 0x2018, 0x2019, 0x201c, 0x201d }, + { "to", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "tr", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "twq", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "tzm", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "uk", 0x00ab, 0x00bb, 0x201e, 0x201c }, + { "ur", 0x201d, 0x201c, 0x2019, 0x2018 }, + { "vai", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "vai-latn", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "vi", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "vun", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "xh", 0x2018, 0x2019, 0x201c, 0x201d }, + { "xog", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "yav", 0x00ab, 0x00bb, 0x00ab, 0x00bb }, + { "yo", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "zh", 0x201c, 0x201d, 0x2018, 0x2019 }, + { "zh-hant", 0x300c, 0x300d, 0x300e, 0x300f }, + { "zu", 0x201c, 0x201d, 0x2018, 0x2019 }, + }; + + const unsigned maxLanguageLength = 8; + +#if !ASSERT_DISABLED + // One time check that the table meets the constraints that the code below relies on. + + static bool didOneTimeCheck = false; + if (!didOneTimeCheck) { + didOneTimeCheck = true; + + checkNumberOfDistinctQuoteCharacters(quotationMark); + checkNumberOfDistinctQuoteCharacters(apostrophe); + + for (unsigned i = 0; i < WTF_ARRAY_LENGTH(quoteTable); ++i) { + ASSERT(strlen(quoteTable[i].language) <= maxLanguageLength); + + if (i) + ASSERT(strcmp(quoteTable[i - 1].language, quoteTable[i].language) < 0); + + for (unsigned j = 0; UChar character = quoteTable[i].language[j]; ++j) + ASSERT(isASCIILower(character) || character == '-'); + + checkNumberOfDistinctQuoteCharacters(quoteTable[i].open1); + checkNumberOfDistinctQuoteCharacters(quoteTable[i].close1); + checkNumberOfDistinctQuoteCharacters(quoteTable[i].open2); + checkNumberOfDistinctQuoteCharacters(quoteTable[i].close2); + } + } +#endif + + unsigned length = language.length(); + if (!length || length > maxLanguageLength) + return 0; + + char languageKeyBuffer[maxLanguageLength + 1]; + for (unsigned i = 0; i < length; ++i) { + UChar character = toASCIILower(language[i]); + if (!(isASCIILower(character) || character == '-')) + return 0; + languageKeyBuffer[i] = static_cast<char>(character); + } + languageKeyBuffer[length] = 0; + + QuotesForLanguage languageKey = { languageKeyBuffer, 0, 0, 0, 0 }; + + return static_cast<const QuotesForLanguage*>(bsearch(&languageKey, + quoteTable, WTF_ARRAY_LENGTH(quoteTable), sizeof(quoteTable[0]), quoteTableLanguageComparisonFunction)); } -PassRefPtr<StringImpl> RenderQuote::originalText() const +static StringImpl* stringForQuoteCharacter(UChar character) { - switch (m_type) { - case NO_OPEN_QUOTE: - case NO_CLOSE_QUOTE: - return StringImpl::empty(); - case CLOSE_QUOTE: - return quotesData()->getCloseQuote(m_depth - 1).impl(); - case OPEN_QUOTE: - return quotesData()->getOpenQuote(m_depth).impl(); + // Use linear search because there is a small number of distinct characters, thus binary search is unneeded. + ASSERT(character); + struct StringForCharacter { + UChar character; + StringImpl* string; + }; + static StringForCharacter strings[maxDistinctQuoteCharacters]; + for (unsigned i = 0; i < maxDistinctQuoteCharacters; ++i) { + if (strings[i].character == character) + return strings[i].string; + if (!strings[i].character) { + strings[i].character = character; + strings[i].string = StringImpl::create8BitIfPossible(&character, 1).leakRef(); + return strings[i].string; + } } ASSERT_NOT_REACHED(); return StringImpl::empty(); } -void RenderQuote::updateText() +static inline StringImpl* quotationMarkString() { - computePreferredLogicalWidths(0); + static StringImpl* quotationMarkString = stringForQuoteCharacter(quotationMark); + return quotationMarkString; } -void RenderQuote::computePreferredLogicalWidths(float lead) +static inline StringImpl* apostropheString() { - if (!m_attached) - attachQuote(); - setTextInternal(originalText()); - RenderText::computePreferredLogicalWidths(lead); + static StringImpl* apostropheString = stringForQuoteCharacter(apostrophe); + return apostropheString; } -const QuotesData* RenderQuote::quotesData() const +PassRefPtr<StringImpl> RenderQuote::originalText() const { - if (QuotesData* customQuotes = style()->quotes()) - return customQuotes; - - AtomicString language = style()->locale(); - if (language.isNull()) - return basicQuotesData(); - const QuotesData* quotes = quotesDataLanguageMap().get(language); - if (!quotes) - return basicQuotesData(); - return quotes; + if (m_depth < 0) + return StringImpl::empty(); + bool isOpenQuote = false; + switch (m_type) { + case NO_OPEN_QUOTE: + case NO_CLOSE_QUOTE: + return StringImpl::empty(); + case OPEN_QUOTE: + isOpenQuote = true; + // fall through + case CLOSE_QUOTE: + if (const QuotesData* quotes = style()->quotes()) + return isOpenQuote ? quotes->openQuote(m_depth).impl() : quotes->closeQuote(m_depth).impl(); + if (const QuotesForLanguage* quotes = quotesForLanguage(style()->locale())) + return stringForQuoteCharacter(isOpenQuote ? (m_depth ? quotes->open2 : quotes->open1) : (m_depth ? quotes->close2 : quotes->close1)); + // FIXME: Should the default be the quotes for "en" rather than straight quotes? + return m_depth ? apostropheString() : quotationMarkString(); + } + ASSERT_NOT_REACHED(); + return StringImpl::empty(); } void RenderQuote::attachQuote() { ASSERT(view()); - ASSERT(!m_attached); - ASSERT(!m_next && !m_previous); + ASSERT(!m_isAttached); + ASSERT(!m_next); + ASSERT(!m_previous); + ASSERT(isRooted()); - // FIXME: Don't set pref widths dirty during layout. See updateDepth() for - // more detail. - if (!isRooted()) { - setNeedsLayoutAndPrefWidthsRecalc(); - return; - } - - if (!view()->renderQuoteHead()) { - view()->setRenderQuoteHead(this); - m_attached = true; - return; - } - - for (RenderObject* predecessor = previousInPreOrder(); predecessor; predecessor = predecessor->previousInPreOrder()) { - // Skip unattached predecessors to avoid having stale m_previous pointers - // if the previous node is never attached and is then destroyed. - if (!predecessor->isQuote() || !toRenderQuote(predecessor)->isAttached()) - continue; - m_previous = toRenderQuote(predecessor); - m_next = m_previous->m_next; - m_previous->m_next = this; - if (m_next) - m_next->m_previous = this; - break; + // Optimize case where this is the first quote in a RenderView by not searching for predecessors in that case. + if (view()->renderQuoteHead()) { + for (RenderObject* predecessor = previousInPreOrder(); predecessor; predecessor = predecessor->previousInPreOrder()) { + // Skip unattached predecessors to avoid having stale m_previous pointers + // if the previous node is never attached and is then destroyed. + if (!predecessor->isQuote() || !toRenderQuote(predecessor)->m_isAttached) + continue; + m_previous = toRenderQuote(predecessor); + m_next = m_previous->m_next; + m_previous->m_next = this; + if (m_next) + m_next->m_previous = this; + break; + } } if (!m_previous) { @@ -308,22 +389,23 @@ void RenderQuote::attachQuote() if (m_next) m_next->m_previous = this; } - m_attached = true; + + m_isAttached = true; for (RenderQuote* quote = this; quote; quote = quote->m_next) quote->updateDepth(); - ASSERT(!m_next || m_next->m_attached); + ASSERT(!m_next || m_next->m_isAttached); ASSERT(!m_next || m_next->m_previous == this); - ASSERT(!m_previous || m_previous->m_attached); + ASSERT(!m_previous || m_previous->m_isAttached); ASSERT(!m_previous || m_previous->m_next == this); } void RenderQuote::detachQuote() { - ASSERT(!m_next || m_next->m_attached); - ASSERT(!m_previous || m_previous->m_attached); - if (!m_attached) + ASSERT(!m_next || m_next->m_isAttached); + ASSERT(!m_previous || m_previous->m_isAttached); + if (!m_isAttached) return; if (m_previous) m_previous->m_next = m_next; @@ -335,37 +417,42 @@ void RenderQuote::detachQuote() for (RenderQuote* quote = m_next; quote; quote = quote->m_next) quote->updateDepth(); } - m_attached = false; + m_isAttached = false; m_next = 0; m_previous = 0; - m_depth = 0; } void RenderQuote::updateDepth() { - ASSERT(m_attached); - int oldDepth = m_depth; - m_depth = 0; + ASSERT(m_isAttached); + int depth = 0; if (m_previous) { - m_depth = m_previous->m_depth; + depth = m_previous->m_depth; + if (depth < 0) + depth = 0; switch (m_previous->m_type) { case OPEN_QUOTE: case NO_OPEN_QUOTE: - m_depth++; + depth++; break; case CLOSE_QUOTE: case NO_CLOSE_QUOTE: - if (m_depth) - m_depth--; break; } } - // FIXME: Don't call setNeedsLayout or dirty our preferred widths during layout. - // This is likely to fail anyway as one of our ancestor will call setNeedsLayout(false), - // preventing the future layout to occur on |this|. The solution is to move that to a - // pre-layout phase. - if (oldDepth != m_depth) - setNeedsLayoutAndPrefWidthsRecalc(); + switch (m_type) { + case OPEN_QUOTE: + case NO_OPEN_QUOTE: + break; + case CLOSE_QUOTE: + case NO_CLOSE_QUOTE: + depth--; + break; + } + if (m_depth == depth) + return; + m_depth = depth; + setText(originalText()); } } // namespace WebCore diff --git a/Source/WebCore/rendering/RenderQuote.h b/Source/WebCore/rendering/RenderQuote.h index 947f3a6f5..55b9d44db 100644 --- a/Source/WebCore/rendering/RenderQuote.h +++ b/Source/WebCore/rendering/RenderQuote.h @@ -1,6 +1,7 @@ /* * Copyright (C) 2011 Nokia Inc. All rights reserved. * Copyright (C) 2012 Google Inc. All rights reserved. + * Copyright (C) 2013 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -22,52 +23,39 @@ #ifndef RenderQuote_h #define RenderQuote_h -#include "QuotesData.h" -#include "RenderStyle.h" -#include "RenderStyleConstants.h" #include "RenderText.h" namespace WebCore { -class Document; - -class RenderQuote : public RenderText { +class RenderQuote FINAL : public RenderText { public: - RenderQuote(Document*, const QuoteType); + RenderQuote(Document*, QuoteType); virtual ~RenderQuote(); + void attachQuote(); - void detachQuote(); private: + void detachQuote(); + virtual void willBeDestroyed() OVERRIDE; - virtual const char* renderName() const OVERRIDE { return "RenderQuote"; }; + virtual const char* renderName() const OVERRIDE { return "RenderQuote"; } virtual bool isQuote() const OVERRIDE { return true; }; virtual PassRefPtr<StringImpl> originalText() const OVERRIDE; - - virtual void updateText() OVERRIDE; - virtual void computePreferredLogicalWidths(float leadWidth) OVERRIDE; - - // We don't override insertedIntoTree to call attachQuote() as it would be attached - // too early and get the wrong depth since generated content is inserted into anonymous - // renderers before going into the main render tree. Once we can ensure that insertIntoTree, - // is called on an attached tree, we should override it here. - + virtual void styleDidChange(StyleDifference, const RenderStyle*) OVERRIDE; virtual void willBeRemovedFromTree() OVERRIDE; - const QuotesData* quotesData() const; void updateDepth(); - bool isAttached() { return m_attached; } QuoteType m_type; int m_depth; RenderQuote* m_next; RenderQuote* m_previous; - bool m_attached; + bool m_isAttached; }; inline RenderQuote* toRenderQuote(RenderObject* object) { - ASSERT(!object || object->isQuote()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isQuote()); return static_cast<RenderQuote*>(object); } diff --git a/Source/WebCore/rendering/RenderRegion.cpp b/Source/WebCore/rendering/RenderRegion.cpp index ce0b9d4f9..acd7f5ccf 100644 --- a/Source/WebCore/rendering/RenderRegion.cpp +++ b/Source/WebCore/rendering/RenderRegion.cpp @@ -34,42 +34,63 @@ #include "GraphicsContext.h" #include "HitTestResult.h" #include "IntRect.h" +#include "LayoutRepainter.h" #include "PaintInfo.h" #include "Range.h" #include "RenderBoxRegionInfo.h" #include "RenderNamedFlowThread.h" #include "RenderView.h" #include "StyleResolver.h" +#include <wtf/StackStats.h> + +using namespace std; namespace WebCore { -RenderRegion::RenderRegion(Node* node, RenderFlowThread* flowThread) - : RenderReplaced(node, IntSize()) +RenderRegion::RenderRegion(Element* element, RenderFlowThread* flowThread) + : RenderBlock(element) , m_flowThread(flowThread) , m_parentNamedFlowThread(0) , m_isValid(false) , m_hasCustomRegionStyle(false) , m_hasAutoLogicalHeight(false) - , m_regionState(RegionUndefined) + , m_hasComputedAutoHeight(false) + , m_computedAutoHeight(0) { } LayoutUnit RenderRegion::pageLogicalWidth() const { + ASSERT(m_flowThread); return m_flowThread->isHorizontalWritingMode() ? contentWidth() : contentHeight(); } LayoutUnit RenderRegion::pageLogicalHeight() const { - if (hasOverrideHeight() && view()->normalLayoutPhase()) - return overrideLogicalContentHeight() + borderAndPaddingLogicalHeight(); + ASSERT(m_flowThread); + if (hasComputedAutoHeight() && !m_flowThread->inConstrainedLayoutPhase()) { + ASSERT(hasAutoLogicalHeight()); + return computedAutoHeight(); + } return m_flowThread->isHorizontalWritingMode() ? contentHeight() : contentWidth(); } +// This method returns the maximum page size of a region with auto-height. This is the initial +// height value for auto-height regions in the first layout phase of the parent named flow. +LayoutUnit RenderRegion::maxPageLogicalHeight() const +{ + ASSERT(m_flowThread); + ASSERT(hasAutoLogicalHeight() && !m_flowThread->inConstrainedLayoutPhase()); + return style()->logicalMaxHeight().isUndefined() ? RenderFlowThread::maxLogicalHeight() : computeReplacedLogicalHeightUsing(style()->logicalMaxHeight()); +} + LayoutUnit RenderRegion::logicalHeightOfAllFlowThreadContent() const { - if (hasOverrideHeight() && view()->normalLayoutPhase()) - return overrideLogicalContentHeight() + borderAndPaddingLogicalHeight(); + ASSERT(m_flowThread); + if (hasComputedAutoHeight() && !m_flowThread->inConstrainedLayoutPhase()) { + ASSERT(hasAutoLogicalHeight()); + return computedAutoHeight(); + } return m_flowThread->isHorizontalWritingMode() ? contentHeight() : contentWidth(); } @@ -78,14 +99,16 @@ LayoutRect RenderRegion::flowThreadPortionOverflowRect() const return overflowRectForFlowThreadPortion(flowThreadPortionRect(), isFirstRegion(), isLastRegion()); } -LayoutRect RenderRegion::overflowRectForFlowThreadPortion(LayoutRect flowThreadPortionRect, bool isFirstPortion, bool isLastPortion) const +LayoutRect RenderRegion::overflowRectForFlowThreadPortion(const LayoutRect& flowThreadPortionRect, bool isFirstPortion, bool isLastPortion) const { + ASSERT(isValid()); + // FIXME: Would like to just use hasOverflowClip() but we aren't a block yet. When RenderRegion is eliminated and // folded into RenderBlock, switch to hasOverflowClip(). bool clipX = style()->overflowX() != OVISIBLE; bool clipY = style()->overflowY() != OVISIBLE; - bool isLastRegionWithRegionOverflowBreak = (isLastPortion && (style()->regionOverflow() == BreakRegionOverflow)); - if ((clipX && clipY) || !isValid() || !m_flowThread || isLastRegionWithRegionOverflowBreak) + bool isLastRegionWithRegionFragmentBreak = (isLastPortion && (style()->regionFragment() == BreakRegionFragment)); + if ((clipX && clipY) || isLastRegionWithRegionFragmentBreak) return flowThreadPortionRect; LayoutRect flowThreadOverflow = m_flowThread->visualOverflowRect(); @@ -96,20 +119,41 @@ LayoutRect RenderRegion::overflowRectForFlowThreadPortion(LayoutRect flowThreadP if (m_flowThread->isHorizontalWritingMode()) { LayoutUnit minY = isFirstPortion ? (flowThreadOverflow.y() - outlineSize) : flowThreadPortionRect.y(); LayoutUnit maxY = isLastPortion ? max(flowThreadPortionRect.maxY(), flowThreadOverflow.maxY()) + outlineSize : flowThreadPortionRect.maxY(); - LayoutUnit minX = clipX ? flowThreadPortionRect.x() : (flowThreadOverflow.x() - outlineSize); - LayoutUnit maxX = clipX ? flowThreadPortionRect.maxX() : (flowThreadOverflow.maxX() + outlineSize); + LayoutUnit minX = clipX ? flowThreadPortionRect.x() : min(flowThreadPortionRect.x(), flowThreadOverflow.x() - outlineSize); + LayoutUnit maxX = clipX ? flowThreadPortionRect.maxX() : max(flowThreadPortionRect.maxX(), (flowThreadOverflow.maxX() + outlineSize)); clipRect = LayoutRect(minX, minY, maxX - minX, maxY - minY); } else { LayoutUnit minX = isFirstPortion ? (flowThreadOverflow.x() - outlineSize) : flowThreadPortionRect.x(); LayoutUnit maxX = isLastPortion ? max(flowThreadPortionRect.maxX(), flowThreadOverflow.maxX()) + outlineSize : flowThreadPortionRect.maxX(); - LayoutUnit minY = clipY ? flowThreadPortionRect.y() : (flowThreadOverflow.y() - outlineSize); - LayoutUnit maxY = clipY ? flowThreadPortionRect.maxY() : (flowThreadOverflow.maxY() + outlineSize); + LayoutUnit minY = clipY ? flowThreadPortionRect.y() : min(flowThreadPortionRect.y(), (flowThreadOverflow.y() - outlineSize)); + LayoutUnit maxY = clipY ? flowThreadPortionRect.maxY() : max(flowThreadPortionRect.y(), (flowThreadOverflow.maxY() + outlineSize)); clipRect = LayoutRect(minX, minY, maxX - minX, maxY - minY); } return clipRect; } +RegionOversetState RenderRegion::regionOversetState() const +{ + ASSERT(node()); + + if (!isValid()) + return RegionUndefined; + + if (Element* element = toElement(node())) + return element->regionOversetState(); + + return RegionUndefined; +} + +void RenderRegion::setRegionOversetState(RegionOversetState state) +{ + ASSERT(node()); + + if (Element* element = toElement(node())) + element->setRegionOversetState(state); +} + LayoutUnit RenderRegion::pageLogicalTopForOffset(LayoutUnit /* offset */) const { return flowThread()->isHorizontalWritingMode() ? flowThreadPortionRect().y() : flowThreadPortionRect().x(); @@ -117,46 +161,60 @@ LayoutUnit RenderRegion::pageLogicalTopForOffset(LayoutUnit /* offset */) const bool RenderRegion::isFirstRegion() const { - ASSERT(isValid() && m_flowThread); + ASSERT(isValid()); + return m_flowThread->firstRegion() == this; } bool RenderRegion::isLastRegion() const { - ASSERT(isValid() && m_flowThread); + ASSERT(isValid()); + return m_flowThread->lastRegion() == this; } -void RenderRegion::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOffset) +static bool shouldPaintRegionContentsInPhase(PaintPhase phase) +{ + return phase == PaintPhaseBlockBackground + || phase == PaintPhaseChildBlockBackground + || phase == PaintPhaseSelection + || phase == PaintPhaseTextClip; +} + +void RenderRegion::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { - // Delegate painting of content in region to RenderFlowThread. - if (!m_flowThread || !isValid()) + if (style()->visibility() != VISIBLE) return; + RenderBlock::paintObject(paintInfo, paintOffset); + + if (!isValid()) + return; + + // We do not want to paint a region's contents multiple times (for each paint phase of the region object). + // Thus, we only paint the region's contents in certain phases. + if (!shouldPaintRegionContentsInPhase(paintInfo.phase)) + return; + + // Delegate the painting of a region's contents to RenderFlowThread. + // RenderFlowThread is a self painting layer because it's a positioned object. + // RenderFlowThread paints its children, the collected objects. setRegionObjectsRegionStyle(); m_flowThread->paintFlowThreadPortionInRegion(paintInfo, this, flowThreadPortionRect(), flowThreadPortionOverflowRect(), LayoutPoint(paintOffset.x() + borderLeft() + paddingLeft(), paintOffset.y() + borderTop() + paddingTop())); restoreRegionObjectsOriginalStyle(); } // Hit Testing -bool RenderRegion::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction action) +bool RenderRegion::hitTestContents(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction action) { - if (!isValid()) + if (!isValid() || action != HitTestForeground) return false; - LayoutPoint adjustedLocation = accumulatedOffset + location(); - - // Check our bounds next. For this purpose always assume that we can only be hit in the - // foreground phase (which is true for replaced elements like images). - // FIXME: Once we support overflow, we need to intersect with that and not with the bounds rect. LayoutRect boundsRect = borderBoxRectInRegion(locationInContainer.region()); - boundsRect.moveBy(adjustedLocation); - if (visibleToHitTesting() && action == HitTestForeground && locationInContainer.intersects(boundsRect)) { - // Check the contents of the RenderFlowThread. - if (m_flowThread && m_flowThread->hitTestFlowThreadPortionInRegion(this, flowThreadPortionRect(), flowThreadPortionOverflowRect(), request, result, locationInContainer, LayoutPoint(adjustedLocation.x() + borderLeft() + paddingLeft(), adjustedLocation.y() + borderTop() + paddingTop()))) - return true; - updateHitTestResult(result, locationInContainer.point() - toLayoutSize(adjustedLocation)); - if (!result.addNodeToRectBasedTestResult(generatingNode(), request, locationInContainer, boundsRect)) + boundsRect.moveBy(accumulatedOffset); + if (visibleToHitTesting() && locationInContainer.intersects(boundsRect)) { + if (m_flowThread->hitTestFlowThreadPortionInRegion(this, flowThreadPortionRect(), flowThreadPortionOverflowRect(), request, result, + locationInContainer, LayoutPoint(accumulatedOffset.x() + borderLeft() + paddingLeft(), accumulatedOffset.y() + borderTop() + paddingTop()))) return true; } @@ -170,13 +228,28 @@ void RenderRegion::checkRegionStyle() // FIXME: Region styling doesn't work for pseudo elements. if (node()) { - Element* regionElement = static_cast<Element*>(node()); - customRegionStyle = view()->document()->styleResolver()->checkRegionStyle(regionElement); + Element* regionElement = toElement(node()); + customRegionStyle = view()->document()->ensureStyleResolver()->checkRegionStyle(regionElement); } setHasCustomRegionStyle(customRegionStyle); m_flowThread->checkRegionsWithStyling(); } +void RenderRegion::incrementAutoLogicalHeightCount() +{ + ASSERT(isValid()); + ASSERT(m_hasAutoLogicalHeight); + + m_flowThread->incrementAutoLogicalHeightRegions(); +} + +void RenderRegion::decrementAutoLogicalHeightCount() +{ + ASSERT(isValid()); + + m_flowThread->decrementAutoLogicalHeightRegions(); +} + void RenderRegion::updateRegionHasAutoLogicalHeightFlag() { ASSERT(m_flowThread); @@ -188,17 +261,24 @@ void RenderRegion::updateRegionHasAutoLogicalHeightFlag() m_hasAutoLogicalHeight = shouldHaveAutoLogicalHeight(); if (m_hasAutoLogicalHeight != didHaveAutoLogicalHeight) { if (m_hasAutoLogicalHeight) - view()->flowThreadController()->incrementAutoLogicalHeightRegions(); + incrementAutoLogicalHeightCount(); else { - clearOverrideLogicalContentHeight(); - view()->flowThreadController()->decrementAutoLogicalHeightRegions(); + clearComputedAutoHeight(); + decrementAutoLogicalHeightCount(); } } } +bool RenderRegion::shouldHaveAutoLogicalHeight() const +{ + bool hasSpecifiedEndpointsForHeight = style()->logicalTop().isSpecified() && style()->logicalBottom().isSpecified(); + bool hasAnchoredEndpointsForHeight = isOutOfFlowPositioned() && hasSpecifiedEndpointsForHeight; + return style()->logicalHeight().isAuto() && !hasAnchoredEndpointsForHeight; +} + void RenderRegion::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) { - RenderReplaced::styleDidChange(diff, oldStyle); + RenderBlock::styleDidChange(diff, oldStyle); // If the region is not attached to any thread, there is no need to check // whether the region has region styling since no content will be displayed @@ -212,15 +292,24 @@ void RenderRegion::styleDidChange(StyleDifference diff, const RenderStyle* oldSt updateRegionHasAutoLogicalHeightFlag(); } -void RenderRegion::layout() +void RenderRegion::layoutBlock(bool relayoutChildren, LayoutUnit) { StackStats::LayoutCheckPoint layoutCheckPoint; - RenderReplaced::layout(); - if (m_flowThread && isValid()) { + RenderBlock::layoutBlock(relayoutChildren); + + if (isValid()) { LayoutRect oldRegionRect(flowThreadPortionRect()); if (!isHorizontalWritingMode()) oldRegionRect = oldRegionRect.transposedRect(); - if (oldRegionRect.width() != pageLogicalWidth() || oldRegionRect.height() != pageLogicalHeight()) + + if (hasAutoLogicalHeight() && !m_flowThread->inConstrainedLayoutPhase()) { + m_flowThread->invalidateRegions(); + clearComputedAutoHeight(); + return; + } + + if (!isRenderRegionSet() && (oldRegionRect.width() != pageLogicalWidth() || oldRegionRect.height() != pageLogicalHeight())) + // This can happen even if we are in the inConstrainedLayoutPhase and it will trigger a pathological layout of the flow thread. m_flowThread->invalidateRegions(); } @@ -243,6 +332,8 @@ void RenderRegion::repaintFlowThreadContent(const LayoutRect& repaintRect, bool void RenderRegion::repaintFlowThreadContentRectangle(const LayoutRect& repaintRect, bool immediate, const LayoutRect& flowThreadPortionRect, const LayoutRect& flowThreadPortionOverflowRect, const LayoutPoint& regionLocation) const { + ASSERT(isValid()); + // We only have to issue a repaint in this region if the region rect intersects the repaint rect. LayoutRect flippedFlowThreadPortionRect(flowThreadPortionRect); LayoutRect flippedFlowThreadPortionOverflowRect(flowThreadPortionOverflowRect); @@ -303,6 +394,7 @@ void RenderRegion::attachRegion() if (!m_flowThread) return; + // Only after adding the region to the thread, the region is marked to be valid. m_flowThread->addRegionToThread(this); // The region just got attached to the flow thread, lets check whether @@ -314,34 +406,29 @@ void RenderRegion::attachRegion() m_hasAutoLogicalHeight = shouldHaveAutoLogicalHeight(); if (hasAutoLogicalHeight()) - view()->flowThreadController()->incrementAutoLogicalHeightRegions(); + incrementAutoLogicalHeightCount(); } void RenderRegion::detachRegion() { if (m_flowThread) { m_flowThread->removeRegionFromThread(this); - if (hasAutoLogicalHeight()) { - ASSERT(isValid()); - view()->flowThreadController()->decrementAutoLogicalHeightRegions(); - } + if (hasAutoLogicalHeight()) + decrementAutoLogicalHeightCount(); } m_flowThread = 0; } RenderBoxRegionInfo* RenderRegion::renderBoxRegionInfo(const RenderBox* box) const { - if (!m_isValid || !m_flowThread) - return 0; + ASSERT(isValid()); return m_renderBoxRegionInfo.get(box); } RenderBoxRegionInfo* RenderRegion::setRenderBoxRegionInfo(const RenderBox* box, LayoutUnit logicalLeftInset, LayoutUnit logicalRightInset, bool containingBlockChainIsInset) { - ASSERT(m_isValid && m_flowThread); - if (!m_isValid || !m_flowThread) - return 0; + ASSERT(isValid()); OwnPtr<RenderBoxRegionInfo>& boxInfo = m_renderBoxRegionInfo.add(box, nullptr).iterator->value; if (boxInfo) @@ -369,15 +456,13 @@ void RenderRegion::deleteAllRenderBoxRegionInfo() LayoutUnit RenderRegion::logicalTopOfFlowThreadContentRect(const LayoutRect& rect) const { - if (!m_isValid || !flowThread()) - return 0; + ASSERT(isValid()); return flowThread()->isHorizontalWritingMode() ? rect.y() : rect.x(); } LayoutUnit RenderRegion::logicalBottomOfFlowThreadContentRect(const LayoutRect& rect) const { - if (!m_isValid || !flowThread()) - return 0; + ASSERT(isValid()); return flowThread()->isHorizontalWritingMode() ? rect.maxY() : rect.maxX(); } @@ -453,14 +538,14 @@ void RenderRegion::restoreRegionObjectsOriginalStyle() void RenderRegion::insertedIntoTree() { - RenderReplaced::insertedIntoTree(); + RenderBlock::insertedIntoTree(); attachRegion(); } void RenderRegion::willBeRemovedFromTree() { - RenderReplaced::willBeRemovedFromTree(); + RenderBlock::willBeRemovedFromTree(); detachRegion(); } @@ -475,7 +560,7 @@ PassRefPtr<RenderStyle> RenderRegion::computeStyleInRegion(const RenderObject* o // FIXME: Region styling fails for pseudo-elements because the renderers don't have a node. Element* element = toElement(object->node()); - RefPtr<RenderStyle> renderObjectRegionStyle = object->view()->document()->styleResolver()->styleForElement(element, 0, DisallowStyleSharing, MatchAllRules, this); + RefPtr<RenderStyle> renderObjectRegionStyle = object->view()->document()->ensureStyleResolver()->styleForElement(element, 0, DisallowStyleSharing, MatchAllRules, this); return renderObjectRegionStyle.release(); } @@ -492,7 +577,7 @@ void RenderRegion::computeChildrenStyleInRegion(const RenderObject* object) childStyleInRegion = it->value.style; objectRegionStyleCached = true; } else { - if (child->isAnonymous()) + if (child->isAnonymous() || child->isInFlowRenderFlowThread()) childStyleInRegion = RenderStyle::createAnonymousStyleWithDisplay(object->style(), child->style()->display()); else if (child->isText()) childStyleInRegion = RenderStyle::clone(object->style()); @@ -508,9 +593,7 @@ void RenderRegion::computeChildrenStyleInRegion(const RenderObject* object) void RenderRegion::setObjectStyleInRegion(RenderObject* object, PassRefPtr<RenderStyle> styleInRegion, bool objectRegionStyleCached) { - ASSERT(object->inRenderFlowThread()); - if (!object->inRenderFlowThread()) - return; + ASSERT(object->flowThreadContainingBlock()); RefPtr<RenderStyle> objectOriginalStyle = object->style(); object->setStyleInternal(styleInRegion); @@ -540,44 +623,50 @@ void RenderRegion::clearObjectStyleInRegion(const RenderObject* object) clearObjectStyleInRegion(child); } -// FIXME: when RenderRegion will inherit from RenderBlock instead of RenderReplaced, -// we should overwrite computePreferredLogicalWidths ( see https://bugs.webkit.org/show_bug.cgi?id=74132 ) -LayoutUnit RenderRegion::minPreferredLogicalWidth() const +void RenderRegion::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const { - if (!m_flowThread || !m_isValid) - return RenderReplaced::minPreferredLogicalWidth(); - - // FIXME: Currently, the code handles only the <length> case for min-width. It should also support other values, like percentage, calc - // or viewport relative. - RenderStyle* styleToUse = style(); - LayoutUnit minPreferredLogicalWidth = m_flowThread->minPreferredLogicalWidth(); - - if (styleToUse->logicalMinWidth().isFixed() && styleToUse->logicalMinWidth().value() > 0) - minPreferredLogicalWidth = std::max(minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMinWidth().value())); - - if (styleToUse->logicalMaxWidth().isFixed()) - minPreferredLogicalWidth = std::min(minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMaxWidth().value())); + if (!isValid()) { + RenderBlock::computeIntrinsicLogicalWidths(minLogicalWidth, maxLogicalWidth); + return; + } - return minPreferredLogicalWidth + borderAndPaddingLogicalWidth(); + minLogicalWidth = m_flowThread->minPreferredLogicalWidth(); + maxLogicalWidth = m_flowThread->maxPreferredLogicalWidth(); } -LayoutUnit RenderRegion::maxPreferredLogicalWidth() const +void RenderRegion::computePreferredLogicalWidths() { - if (!m_flowThread || !m_isValid) - return RenderReplaced::maxPreferredLogicalWidth(); + ASSERT(preferredLogicalWidthsDirty()); + + if (!isValid()) { + RenderBlock::computePreferredLogicalWidths(); + return; + } + + // FIXME: Currently, the code handles only the <length> case for min-width/max-width. + // It should also support other values, like percentage, calc or viewport relative. + m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = 0; - // FIXME: Currently, the code handles only the <length> case for max-width. It should also support other values, like percentage, calc - // or viewport relative. RenderStyle* styleToUse = style(); - LayoutUnit maxPreferredLogicalWidth = m_flowThread->maxPreferredLogicalWidth(); + if (styleToUse->logicalWidth().isFixed() && styleToUse->logicalWidth().value() > 0) + m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalWidth().value()); + else + computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth); - if (styleToUse->logicalMinWidth().isFixed() && styleToUse->logicalMinWidth().value() > 0) - maxPreferredLogicalWidth = std::max(maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMinWidth().value())); + if (styleToUse->logicalMinWidth().isFixed() && styleToUse->logicalMinWidth().value() > 0) { + m_maxPreferredLogicalWidth = std::max(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMinWidth().value())); + m_minPreferredLogicalWidth = std::max(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMinWidth().value())); + } - if (styleToUse->logicalMaxWidth().isFixed()) - maxPreferredLogicalWidth = std::min(maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMaxWidth().value())); + if (styleToUse->logicalMaxWidth().isFixed()) { + m_maxPreferredLogicalWidth = std::min(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMaxWidth().value())); + m_minPreferredLogicalWidth = std::min(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMaxWidth().value())); + } - return maxPreferredLogicalWidth + borderAndPaddingLogicalWidth(); + LayoutUnit borderAndPadding = borderAndPaddingLogicalWidth(); + m_minPreferredLogicalWidth += borderAndPadding; + m_maxPreferredLogicalWidth += borderAndPadding; + setPreferredLogicalWidthsDirty(false); } void RenderRegion::getRanges(Vector<RefPtr<Range> >& rangeObjects) const @@ -588,33 +677,32 @@ void RenderRegion::getRanges(Vector<RefPtr<Range> >& rangeObjects) const void RenderRegion::updateLogicalHeight() { - RenderReplaced::updateLogicalHeight(); + RenderBlock::updateLogicalHeight(); if (!hasAutoLogicalHeight()) return; - // We want to update the logical height based on the computed override logical - // content height only if the view is in the layout phase - // in which all the auto logical height regions have their override logical height set. - if (view()->normalLayoutPhase()) + // We want to update the logical height based on the computed auto-height + // only if the view is in the layout phase in which all the + // auto logical height regions have a computed auto-height. + if (!m_flowThread->inConstrainedLayoutPhase()) return; // There may be regions with auto logical height that during the prerequisite layout phase // did not have the chance to layout flow thread content. Because of that, these regions do not - // have an overrideLogicalContentHeight computed and they will not be able to fragment any flow + // have a computedAutoHeight and they will not be able to fragment any flow // thread content. - if (!hasOverrideHeight()) + if (!hasComputedAutoHeight()) return; - LayoutUnit newLogicalHeight = overrideLogicalContentHeight() + borderAndPaddingLogicalHeight(); + LayoutUnit newLogicalHeight = computedAutoHeight() + borderAndPaddingLogicalHeight(); ASSERT(newLogicalHeight < LayoutUnit::max() / 2); - if (newLogicalHeight > logicalHeight()) + if (newLogicalHeight > logicalHeight()) { setLogicalHeight(newLogicalHeight); -} - -bool RenderRegion::needsOverrideLogicalContentHeightComputation() const -{ - return hasAutoLogicalHeight() && view()->normalLayoutPhase() && !hasOverrideHeight(); + // Recalculate position of the render block after new logical height is set. + // (needed in absolute positioning case with bottom alignment for example) + RenderBlock::updateLogicalHeight(); + } } } // namespace WebCore diff --git a/Source/WebCore/rendering/RenderRegion.h b/Source/WebCore/rendering/RenderRegion.h index 5617ae802..770816231 100644 --- a/Source/WebCore/rendering/RenderRegion.h +++ b/Source/WebCore/rendering/RenderRegion.h @@ -30,24 +30,25 @@ #ifndef RenderRegion_h #define RenderRegion_h -#include "RenderReplaced.h" +#include "RenderBlock.h" #include "StyleInheritedData.h" namespace WebCore { +struct LayerFragment; +typedef Vector<LayerFragment, 1> LayerFragments; class RenderBox; class RenderBoxRegionInfo; class RenderFlowThread; class RenderNamedFlowThread; -class RenderRegion : public RenderReplaced { +class RenderRegion : public RenderBlock { public: - explicit RenderRegion(Node*, RenderFlowThread*); + explicit RenderRegion(Element*, RenderFlowThread*); virtual bool isRenderRegion() const { return true; } - virtual void paintReplaced(PaintInfo&, const LayoutPoint&); - virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction) OVERRIDE; + virtual bool hitTestContents(const HitTestRequest&, HitTestResult&, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction) OVERRIDE; virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); @@ -68,8 +69,6 @@ public: bool hasCustomRegionStyle() const { return m_hasCustomRegionStyle; } void setHasCustomRegionStyle(bool hasCustomRegionStyle) { m_hasCustomRegionStyle = hasCustomRegionStyle; } - virtual void layout(); - RenderBoxRegionInfo* renderBoxRegionInfo(const RenderBox*) const; RenderBoxRegionInfo* setRenderBoxRegionInfo(const RenderBox*, LayoutUnit logicalLeftInset, LayoutUnit logicalRightInset, bool containingBlockChainIsInset); @@ -83,25 +82,16 @@ public: void clearObjectStyleInRegion(const RenderObject*); - enum RegionState { - RegionUndefined, - RegionEmpty, - RegionFit, - RegionOverset - }; + RegionOversetState regionOversetState() const; + void setRegionOversetState(RegionOversetState); - RegionState regionState() const { return isValid() ? m_regionState : RegionUndefined; } - void setRegionState(RegionState regionState) { m_regionState = regionState; } - // These methods represent the width and height of a "page" and for a RenderRegion they are just the // content width and content height of a region. For RenderRegionSets, however, they will be the width and // height of a single column or page in the set. virtual LayoutUnit pageLogicalWidth() const; virtual LayoutUnit pageLogicalHeight() const; + LayoutUnit maxPageLogicalHeight() const; - virtual LayoutUnit minPreferredLogicalWidth() const OVERRIDE; - virtual LayoutUnit maxPreferredLogicalWidth() const OVERRIDE; - LayoutUnit logicalTopOfFlowThreadContentRect(const LayoutRect&) const; LayoutUnit logicalBottomOfFlowThreadContentRect(const LayoutRect&) const; LayoutUnit logicalTopForFlowThreadContent() const { return logicalTopOfFlowThreadContentRect(flowThreadPortionRect()); }; @@ -116,7 +106,21 @@ public: bool hasAutoLogicalHeight() const { return m_hasAutoLogicalHeight; } - bool needsOverrideLogicalContentHeightComputation() const; + LayoutUnit computedAutoHeight() const { return m_computedAutoHeight; } + + void setComputedAutoHeight(LayoutUnit computedAutoHeight) + { + m_hasComputedAutoHeight = true; + m_computedAutoHeight = computedAutoHeight; + } + + void clearComputedAutoHeight() + { + m_hasComputedAutoHeight = false; + m_computedAutoHeight = 0; + } + + bool hasComputedAutoHeight() const { return m_hasComputedAutoHeight; } virtual void updateLogicalHeight() OVERRIDE; @@ -132,43 +136,45 @@ public: virtual void repaintFlowThreadContent(const LayoutRect& repaintRect, bool immediate) const; + virtual void collectLayerFragments(LayerFragments&, const LayoutRect&, const LayoutRect&) { } + protected: void setRegionObjectsRegionStyle(); void restoreRegionObjectsOriginalStyle(); - LayoutRect overflowRectForFlowThreadPortion(LayoutRect flowThreadPortionRect, bool isFirstPortion, bool isLastPortion) const; + virtual void computePreferredLogicalWidths() OVERRIDE; + virtual void computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const OVERRIDE; + + LayoutRect overflowRectForFlowThreadPortion(const LayoutRect& flowThreadPortionRect, bool isFirstPortion, bool isLastPortion) const; void repaintFlowThreadContentRectangle(const LayoutRect& repaintRect, bool immediate, const LayoutRect& flowThreadPortionRect, const LayoutRect& flowThreadPortionOverflowRect, const LayoutPoint& regionLocation) const; + virtual bool shouldHaveAutoLogicalHeight() const; + private: virtual const char* renderName() const { return "RenderRegion"; } - // FIXME: these functions should be revisited once RenderRegion inherits from RenderBlock - // instead of RenderReplaced (see https://bugs.webkit.org/show_bug.cgi?id=74132 ) - // When width is auto, use normal block/box sizing code except when inline. - virtual bool isInlineBlockOrInlineTable() const OVERRIDE { return isInline() && !shouldComputeSizeAsReplaced(); } - virtual bool shouldComputeSizeAsReplaced() const OVERRIDE { return !style()->logicalWidth().isAuto() && !style()->logicalHeight().isAuto(); } - - bool shouldHaveAutoLogicalHeight() const - { - bool hasSpecifiedEndpointsForHeight = style()->logicalTop().isSpecified() && style()->logicalBottom().isSpecified(); - bool hasAnchoredEndpointsForHeight = isOutOfFlowPositioned() && hasSpecifiedEndpointsForHeight; - return style()->logicalHeight().isAuto() && !hasAnchoredEndpointsForHeight; - } + virtual bool canHaveChildren() const OVERRIDE { return false; } + virtual bool canHaveGeneratedChildren() const OVERRIDE { return true; } virtual void insertedIntoTree() OVERRIDE; virtual void willBeRemovedFromTree() OVERRIDE; + virtual void layoutBlock(bool relayoutChildren, LayoutUnit pageLogicalHeight = 0) OVERRIDE; + virtual void paintObject(PaintInfo&, const LayoutPoint&) OVERRIDE; + virtual void installFlowThread(); PassRefPtr<RenderStyle> computeStyleInRegion(const RenderObject*); void computeChildrenStyleInRegion(const RenderObject*); void setObjectStyleInRegion(RenderObject*, PassRefPtr<RenderStyle>, bool objectRegionStyleCached); - void printRegionObjectsStyles(); void checkRegionStyle(); void updateRegionHasAutoLogicalHeightFlag(); + void incrementAutoLogicalHeightCount(); + void decrementAutoLogicalHeightCount(); + protected: RenderFlowThread* m_flowThread; @@ -202,18 +208,20 @@ private: bool m_isValid : 1; bool m_hasCustomRegionStyle : 1; bool m_hasAutoLogicalHeight : 1; - RegionState m_regionState; + bool m_hasComputedAutoHeight : 1; + + LayoutUnit m_computedAutoHeight; }; inline RenderRegion* toRenderRegion(RenderObject* object) { - ASSERT(!object || object->isRenderRegion()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isRenderRegion()); return static_cast<RenderRegion*>(object); } inline const RenderRegion* toRenderRegion(const RenderObject* object) { - ASSERT(!object || object->isRenderRegion()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isRenderRegion()); return static_cast<const RenderRegion*>(object); } diff --git a/Source/WebCore/rendering/RenderRegionSet.cpp b/Source/WebCore/rendering/RenderRegionSet.cpp index 25b5cfa9c..e205bcb1e 100644 --- a/Source/WebCore/rendering/RenderRegionSet.cpp +++ b/Source/WebCore/rendering/RenderRegionSet.cpp @@ -30,8 +30,8 @@ namespace WebCore { -RenderRegionSet::RenderRegionSet(Node* node, RenderFlowThread* flowThread) - : RenderRegion(node, flowThread) +RenderRegionSet::RenderRegionSet(Element* element, RenderFlowThread* flowThread) + : RenderRegion(element, flowThread) { } diff --git a/Source/WebCore/rendering/RenderRegionSet.h b/Source/WebCore/rendering/RenderRegionSet.h index e91eceecc..aa8969d67 100644 --- a/Source/WebCore/rendering/RenderRegionSet.h +++ b/Source/WebCore/rendering/RenderRegionSet.h @@ -47,8 +47,11 @@ class RenderFlowThread; class RenderRegionSet : public RenderRegion { public: - RenderRegionSet(Node*, RenderFlowThread*); + RenderRegionSet(Element*, RenderFlowThread*); +protected: + virtual bool shouldHaveAutoLogicalHeight() const OVERRIDE { return false; } + private: virtual void installFlowThread() OVERRIDE; diff --git a/Source/WebCore/rendering/RenderReplaced.cpp b/Source/WebCore/rendering/RenderReplaced.cpp index 47c239ab5..1df9e19c5 100644 --- a/Source/WebCore/rendering/RenderReplaced.cpp +++ b/Source/WebCore/rendering/RenderReplaced.cpp @@ -33,6 +33,7 @@ #include "RenderTheme.h" #include "RenderView.h" #include "VisiblePosition.h" +#include <wtf/StackStats.h> using namespace std; @@ -41,15 +42,15 @@ namespace WebCore { const int cDefaultWidth = 300; const int cDefaultHeight = 150; -RenderReplaced::RenderReplaced(Node* node) - : RenderBox(node) +RenderReplaced::RenderReplaced(Element* element) + : RenderBox(element) , m_intrinsicSize(cDefaultWidth, cDefaultHeight) { setReplaced(true); } -RenderReplaced::RenderReplaced(Node* node, const LayoutSize& intrinsicSize) - : RenderBox(node) +RenderReplaced::RenderReplaced(Element* element, const LayoutSize& intrinsicSize) + : RenderBox(element) , m_intrinsicSize(intrinsicSize) { setReplaced(true); @@ -92,11 +93,12 @@ void RenderReplaced::layout() m_overflow.clear(); addVisualEffectOverflow(); updateLayerTransform(); - + invalidateBackgroundObscurationStatus(); + repainter.repaintAfterLayout(); setNeedsLayout(false); } - + void RenderReplaced::intrinsicSizeChanged() { int scaledWidth = static_cast<int>(cDefaultWidth * style()->effectiveZoom()); @@ -122,7 +124,7 @@ void RenderReplaced::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) LayoutRect paintRect = LayoutRect(adjustedPaintOffset, size()); if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && style()->outlineWidth()) - paintOutline(paintInfo.context, paintRect); + paintOutline(paintInfo, paintRect); if (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseSelection && !canHaveChildren()) return; @@ -232,33 +234,13 @@ bool RenderReplaced::hasReplacedLogicalWidth() const return firstContainingBlockWithLogicalWidth(this); } -static inline bool hasAutoHeightOrContainingBlockWithAutoHeight(const RenderReplaced* replaced) -{ - Length logicalHeightLength = replaced->style()->logicalHeight(); - if (logicalHeightLength.isAuto()) - return true; - - // For percentage heights: The percentage is calculated with respect to the height of the generated box's - // containing block. If the height of the containing block is not specified explicitly (i.e., it depends - // on content height), and this element is not absolutely positioned, the value computes to 'auto'. - if (!logicalHeightLength.isPercent() || replaced->isOutOfFlowPositioned() || replaced->document()->inQuirksMode()) - return false; - - for (RenderBlock* cb = replaced->containingBlock(); !cb->isRenderView(); cb = cb->containingBlock()) { - if (cb->isTableCell() || (!cb->style()->logicalHeight().isAuto() || (!cb->style()->top().isAuto() && !cb->style()->bottom().isAuto()))) - return false; - } - - return true; -} - bool RenderReplaced::hasReplacedLogicalHeight() const { if (style()->logicalHeight().isAuto()) return false; if (style()->logicalHeight().isSpecified()) { - if (hasAutoHeightOrContainingBlockWithAutoHeight(this)) + if (hasAutoHeightOrContainingBlockWithAutoHeight()) return false; return true; } @@ -266,12 +248,6 @@ bool RenderReplaced::hasReplacedLogicalHeight() const return false; } -static inline bool rendererHasAspectRatio(const RenderObject* renderer) -{ - ASSERT(renderer); - return renderer->isImage() || renderer->isCanvas() || renderer->isVideo(); -} - void RenderReplaced::computeAspectRatioInformationForRenderBox(RenderBox* contentRenderer, FloatSize& constrainedSize, double& intrinsicRatio, bool& isPercentageIntrinsicSize) const { FloatSize intrinsicSize; @@ -284,7 +260,7 @@ void RenderReplaced::computeAspectRatioInformationForRenderBox(RenderBox* conten if (!isPercentageIntrinsicSize) intrinsicSize.scale(style()->effectiveZoom()); - if (rendererHasAspectRatio(this) && isPercentageIntrinsicSize) + if (hasAspectRatio() && isPercentageIntrinsicSize) intrinsicRatio = 1; // Update our intrinsic size to match what the content renderer has computed, so that when we @@ -330,16 +306,16 @@ void RenderReplaced::computeIntrinsicRatioInformation(FloatSize& intrinsicSize, intrinsicSize = FloatSize(intrinsicLogicalWidth(), intrinsicLogicalHeight()); // Figure out if we need to compute an intrinsic ratio. - if (intrinsicSize.isEmpty() || !rendererHasAspectRatio(this)) + if (intrinsicSize.isEmpty() || !hasAspectRatio()) return; intrinsicRatio = intrinsicSize.width() / intrinsicSize.height(); } -LayoutUnit RenderReplaced::computeReplacedLogicalWidth(bool includeMaxWidth) const +LayoutUnit RenderReplaced::computeReplacedLogicalWidth(ShouldComputePreferred shouldComputePreferred) const { - if (style()->logicalWidth().isSpecified()) - return computeReplacedLogicalWidthRespectingMinMaxWidth(computeReplacedLogicalWidthUsing(MainOrPreferredSize, style()->logicalWidth()), includeMaxWidth); + if (style()->logicalWidth().isSpecified() || style()->logicalWidth().isIntrinsic()) + return computeReplacedLogicalWidthRespectingMinMaxWidth(computeReplacedLogicalWidthUsing(style()->logicalWidth()), shouldComputePreferred); RenderBox* contentRenderer = embeddedContentBox(); @@ -355,7 +331,7 @@ LayoutUnit RenderReplaced::computeReplacedLogicalWidth(bool includeMaxWidth) con // If 'height' and 'width' both have computed values of 'auto' and the element also has an intrinsic width, then that intrinsic width is the used value of 'width'. if (heightIsAuto && hasIntrinsicWidth) - return computeReplacedLogicalWidthRespectingMinMaxWidth(constrainedSize.width(), includeMaxWidth); + return computeReplacedLogicalWidthRespectingMinMaxWidth(constrainedSize.width(), shouldComputePreferred); bool hasIntrinsicHeight = !isPercentageIntrinsicSize && constrainedSize.height() > 0; if (intrinsicRatio || isPercentageIntrinsicSize) { @@ -363,8 +339,8 @@ LayoutUnit RenderReplaced::computeReplacedLogicalWidth(bool includeMaxWidth) con // or if 'width' has a computed value of 'auto', 'height' has some other computed value, and the element does have an intrinsic ratio; then the used value // of 'width' is: (used height) * (intrinsic ratio) if (intrinsicRatio && ((heightIsAuto && !hasIntrinsicWidth && hasIntrinsicHeight) || !heightIsAuto)) { - LayoutUnit logicalHeight = computeReplacedLogicalHeightUsing(MainOrPreferredSize, style()->logicalHeight()); - return computeReplacedLogicalWidthRespectingMinMaxWidth(roundToInt(round(logicalHeight * intrinsicRatio))); + LayoutUnit logicalHeight = computeReplacedLogicalHeight(); + return computeReplacedLogicalWidthRespectingMinMaxWidth(roundToInt(round(logicalHeight * intrinsicRatio)), shouldComputePreferred); } // If 'height' and 'width' both have computed values of 'auto' and the element has an intrinsic ratio but no intrinsic height or width, then the used value of @@ -375,7 +351,7 @@ LayoutUnit RenderReplaced::computeReplacedLogicalWidth(bool includeMaxWidth) con // 'margin-left' + 'border-left-width' + 'padding-left' + 'width' + 'padding-right' + 'border-right-width' + 'margin-right' = width of containing block LayoutUnit logicalWidth; if (RenderBlock* blockWithWidth = firstContainingBlockWithLogicalWidth(this)) - logicalWidth = blockWithWidth->computeReplacedLogicalWidthRespectingMinMaxWidth(blockWithWidth->computeReplacedLogicalWidthUsing(MainOrPreferredSize, blockWithWidth->style()->logicalWidth()), false); + logicalWidth = blockWithWidth->computeReplacedLogicalWidthRespectingMinMaxWidth(blockWithWidth->computeReplacedLogicalWidthUsing(blockWithWidth->style()->logicalWidth()), shouldComputePreferred); else logicalWidth = containingBlock()->availableLogicalWidth(); @@ -385,13 +361,13 @@ LayoutUnit RenderReplaced::computeReplacedLogicalWidth(bool includeMaxWidth) con logicalWidth = max<LayoutUnit>(0, logicalWidth - (marginStart + marginEnd + (width() - clientWidth()))); if (isPercentageIntrinsicSize) logicalWidth = logicalWidth * constrainedSize.width() / 100; - return computeReplacedLogicalWidthRespectingMinMaxWidth(logicalWidth, includeMaxWidth); + return computeReplacedLogicalWidthRespectingMinMaxWidth(logicalWidth, shouldComputePreferred); } } // Otherwise, if 'width' has a computed value of 'auto', and the element has an intrinsic width, then that intrinsic width is the used value of 'width'. if (hasIntrinsicWidth) - return computeReplacedLogicalWidthRespectingMinMaxWidth(constrainedSize.width(), includeMaxWidth); + return computeReplacedLogicalWidthRespectingMinMaxWidth(constrainedSize.width(), shouldComputePreferred); // Otherwise, if 'width' has a computed value of 'auto', but none of the conditions above are met, then the used value of 'width' becomes 300px. If 300px is too // wide to fit the device, UAs should use the width of the largest rectangle that has a 2:1 ratio and fits the device instead. @@ -400,14 +376,14 @@ LayoutUnit RenderReplaced::computeReplacedLogicalWidth(bool includeMaxWidth) con // has no intrinsic size, which is wrong per CSS 2.1, but matches our behavior since a long time. } - return computeReplacedLogicalWidthRespectingMinMaxWidth(intrinsicLogicalWidth(), includeMaxWidth); + return computeReplacedLogicalWidthRespectingMinMaxWidth(intrinsicLogicalWidth(), shouldComputePreferred); } LayoutUnit RenderReplaced::computeReplacedLogicalHeight() const { // 10.5 Content height: the 'height' property: http://www.w3.org/TR/CSS21/visudet.html#propdef-height if (hasReplacedLogicalHeight()) - return computeReplacedLogicalHeightRespectingMinMaxHeight(computeReplacedLogicalHeightUsing(MainOrPreferredSize, style()->logicalHeight())); + return computeReplacedLogicalHeightRespectingMinMaxHeight(computeReplacedLogicalHeightUsing(style()->logicalHeight())); RenderBox* contentRenderer = embeddedContentBox(); @@ -438,34 +414,39 @@ LayoutUnit RenderReplaced::computeReplacedLogicalHeight() const return computeReplacedLogicalHeightRespectingMinMaxHeight(intrinsicLogicalHeight()); } -LayoutUnit RenderReplaced::computeMaxPreferredLogicalWidth() const +void RenderReplaced::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const { - Length logicalWidth = style()->logicalWidth(); - - // We cannot resolve any percent logical width here as the available logical - // width may not be set on our containing block. - if (logicalWidth.isPercent()) - return intrinsicLogicalWidth(); - - // FIXME: We shouldn't be calling a logical width computing function in preferred - // logical widths computation as the layout information is probably invalid. - return computeReplacedLogicalWidth(false); + minLogicalWidth = maxLogicalWidth = intrinsicLogicalWidth(); } void RenderReplaced::computePreferredLogicalWidths() { ASSERT(preferredLogicalWidthsDirty()); - LayoutUnit borderAndPadding = borderAndPaddingLogicalWidth(); - m_maxPreferredLogicalWidth = computeMaxPreferredLogicalWidth() + borderAndPadding; - - if (style()->maxWidth().isFixed()) - m_maxPreferredLogicalWidth = min<LayoutUnit>(m_maxPreferredLogicalWidth, style()->maxWidth().value() + (style()->boxSizing() == CONTENT_BOX ? borderAndPadding : LayoutUnit())); + // We cannot resolve any percent logical width here as the available logical + // width may not be set on our containing block. + if (style()->logicalWidth().isPercent()) + computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth); + else + m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = computeReplacedLogicalWidth(ComputePreferred); - if (hasRelativeDimensions()) + RenderStyle* styleToUse = style(); + if (styleToUse->logicalWidth().isPercent() || styleToUse->logicalMaxWidth().isPercent() || hasRelativeIntrinsicLogicalWidth()) m_minPreferredLogicalWidth = 0; - else - m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth; + + if (styleToUse->logicalMinWidth().isFixed() && styleToUse->logicalMinWidth().value() > 0) { + m_maxPreferredLogicalWidth = max(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMinWidth().value())); + m_minPreferredLogicalWidth = max(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMinWidth().value())); + } + + if (styleToUse->logicalMaxWidth().isFixed()) { + m_maxPreferredLogicalWidth = min(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMaxWidth().value())); + m_minPreferredLogicalWidth = min(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMaxWidth().value())); + } + + LayoutUnit borderAndPadding = borderAndPaddingLogicalWidth(); + m_minPreferredLogicalWidth += borderAndPadding; + m_maxPreferredLogicalWidth += borderAndPadding; setPreferredLogicalWidthsDirty(false); } diff --git a/Source/WebCore/rendering/RenderReplaced.h b/Source/WebCore/rendering/RenderReplaced.h index a6dee443e..ae85e44b7 100644 --- a/Source/WebCore/rendering/RenderReplaced.h +++ b/Source/WebCore/rendering/RenderReplaced.h @@ -28,11 +28,11 @@ namespace WebCore { class RenderReplaced : public RenderBox { public: - RenderReplaced(Node*); - RenderReplaced(Node*, const LayoutSize& intrinsicSize); + RenderReplaced(Element*); + RenderReplaced(Element*, const LayoutSize& intrinsicSize); virtual ~RenderReplaced(); - virtual LayoutUnit computeReplacedLogicalWidth(bool includeMaxWidth = true) const; + virtual LayoutUnit computeReplacedLogicalWidth(ShouldComputePreferred = ComputeActual) const OVERRIDE; virtual LayoutUnit computeReplacedLogicalHeight() const; bool hasReplacedLogicalWidth() const; @@ -46,6 +46,8 @@ protected: virtual LayoutSize intrinsicSize() const OVERRIDE { return m_intrinsicSize; } virtual void computeIntrinsicRatioInformation(FloatSize& intrinsicSize, double& intrinsicRatio, bool& isPercentageIntrinsicSize) const; + virtual void computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const OVERRIDE; + virtual LayoutUnit minimumReplacedHeight() const { return LayoutUnit(); } virtual void setSelectionState(SelectionState); @@ -56,6 +58,7 @@ protected: void setIntrinsicSize(const LayoutSize& intrinsicSize) { m_intrinsicSize = intrinsicSize; } virtual void intrinsicSizeChanged(); + virtual bool hasRelativeIntrinsicLogicalWidth() const { return false; } virtual void paint(PaintInfo&, const LayoutPoint&); bool shouldPaint(PaintInfo&, const LayoutPoint&); @@ -67,7 +70,6 @@ private: virtual bool canHaveChildren() const { return false; } - LayoutUnit computeMaxPreferredLogicalWidth() const; virtual void computePreferredLogicalWidths(); virtual void paintReplaced(PaintInfo&, const LayoutPoint&) { } diff --git a/Source/WebCore/rendering/RenderReplica.cpp b/Source/WebCore/rendering/RenderReplica.cpp index c10bdcaf3..e0f4ad245 100644 --- a/Source/WebCore/rendering/RenderReplica.cpp +++ b/Source/WebCore/rendering/RenderReplica.cpp @@ -30,11 +30,12 @@ #include "RenderReplica.h" #include "RenderLayer.h" +#include <wtf/StackStats.h> namespace WebCore { -RenderReplica::RenderReplica(Node* n) -: RenderBox(n) +RenderReplica::RenderReplica() + : RenderBox(0) { // This is a hack. Replicas are synthetic, and don't pick up the attributes of the // renderers being replicated, so they always report that they are inline, non-replaced. @@ -43,8 +44,16 @@ RenderReplica::RenderReplica(Node* n) setReplaced(true); } +RenderReplica* RenderReplica::createAnonymous(Document* document) +{ + RenderReplica* renderer = new (document->renderArena()) RenderReplica(); + renderer->setDocumentForAnonymous(document); + return renderer; +} + RenderReplica::~RenderReplica() -{} +{ +} void RenderReplica::layout() { diff --git a/Source/WebCore/rendering/RenderReplica.h b/Source/WebCore/rendering/RenderReplica.h index de694439e..4665e13f1 100644 --- a/Source/WebCore/rendering/RenderReplica.h +++ b/Source/WebCore/rendering/RenderReplica.h @@ -35,7 +35,8 @@ namespace WebCore { class RenderReplica : public RenderBox { public: - RenderReplica(Node*); + static RenderReplica* createAnonymous(Document*); + virtual ~RenderReplica(); virtual const char* renderName() const { return "RenderReplica"; } @@ -43,12 +44,14 @@ public: virtual bool requiresLayer() const { return true; } virtual void layout(); - virtual void computePreferredLogicalWidths(); virtual void paint(PaintInfo&, const LayoutPoint&); private: + RenderReplica(); + virtual bool isReplica() const { return true; } + virtual void computePreferredLogicalWidths(); }; diff --git a/Source/WebCore/rendering/RenderRuby.cpp b/Source/WebCore/rendering/RenderRuby.cpp index e7b859f70..076744e16 100644 --- a/Source/WebCore/rendering/RenderRuby.cpp +++ b/Source/WebCore/rendering/RenderRuby.cpp @@ -74,19 +74,19 @@ static inline bool isRubyAfterBlock(const RenderObject* object) static inline RenderBlock* rubyBeforeBlock(const RenderObject* ruby) { RenderObject* child = ruby->firstChild(); - return isRubyBeforeBlock(child) ? static_cast<RenderBlock*>(child) : 0; + return isRubyBeforeBlock(child) ? toRenderBlock(child) : 0; } static inline RenderBlock* rubyAfterBlock(const RenderObject* ruby) { RenderObject* child = ruby->lastChild(); - return isRubyAfterBlock(child) ? static_cast<RenderBlock*>(child) : 0; + return isRubyAfterBlock(child) ? toRenderBlock(child) : 0; } static RenderBlock* createAnonymousRubyInlineBlock(RenderObject* ruby) { RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyleWithDisplay(ruby->style(), INLINE_BLOCK); - RenderBlock* newBlock = new (ruby->renderArena()) RenderBlock(ruby->document() /* anonymous box */); + RenderBlock* newBlock = RenderBlock::createAnonymous(ruby->document()); newBlock->setStyle(newStyle.release()); return newBlock; } @@ -97,20 +97,20 @@ static RenderRubyRun* lastRubyRun(const RenderObject* ruby) if (child && !child->isRubyRun()) child = child->previousSibling(); ASSERT(!child || child->isRubyRun() || child->isBeforeContent() || child == rubyBeforeBlock(ruby)); - return child && child->isRubyRun() ? static_cast<RenderRubyRun*>(child) : 0; + return child && child->isRubyRun() ? toRenderRubyRun(child) : 0; } static inline RenderRubyRun* findRubyRunParent(RenderObject* child) { while (child && !child->isRubyRun()) child = child->parent(); - return static_cast<RenderRubyRun*>(child); + return toRenderRubyRun(child); } //=== ruby as inline object === -RenderRubyAsInline::RenderRubyAsInline(Node* node) - : RenderInline(node) +RenderRubyAsInline::RenderRubyAsInline(Element* element) + : RenderInline(element) { } @@ -184,7 +184,7 @@ void RenderRubyAsInline::addChild(RenderObject* child, RenderObject* beforeChild RenderRubyRun* lastRun = lastRubyRun(this); if (!lastRun || lastRun->hasRubyText()) { lastRun = RenderRubyRun::staticCreateRubyRun(this); - RenderInline::addChild(lastRun); + RenderInline::addChild(lastRun, beforeChild); } lastRun->addChild(child); } @@ -213,11 +213,10 @@ void RenderRubyAsInline::removeChild(RenderObject* child) run->removeChild(child); } - //=== ruby as block object === -RenderRubyAsBlock::RenderRubyAsBlock(Node* node) - : RenderBlock(node) +RenderRubyAsBlock::RenderRubyAsBlock(Element* element) + : RenderBlock(element) { } @@ -291,7 +290,7 @@ void RenderRubyAsBlock::addChild(RenderObject* child, RenderObject* beforeChild) RenderRubyRun* lastRun = lastRubyRun(this); if (!lastRun || lastRun->hasRubyText()) { lastRun = RenderRubyRun::staticCreateRubyRun(this); - RenderBlock::addChild(lastRun); + RenderBlock::addChild(lastRun, beforeChild); } lastRun->addChild(child); } diff --git a/Source/WebCore/rendering/RenderRuby.h b/Source/WebCore/rendering/RenderRuby.h index 2ab964ca0..2494639f9 100644 --- a/Source/WebCore/rendering/RenderRuby.h +++ b/Source/WebCore/rendering/RenderRuby.h @@ -53,7 +53,7 @@ namespace WebCore { // <ruby> when used as 'display:inline' class RenderRubyAsInline : public RenderInline { public: - RenderRubyAsInline(Node*); + RenderRubyAsInline(Element*); virtual ~RenderRubyAsInline(); virtual void addChild(RenderObject* child, RenderObject* beforeChild = 0); @@ -72,7 +72,7 @@ private: // <ruby> when used as 'display:block' or 'display:inline-block' class RenderRubyAsBlock : public RenderBlock { public: - RenderRubyAsBlock(Node*); + RenderRubyAsBlock(Element*); virtual ~RenderRubyAsBlock(); virtual void addChild(RenderObject* child, RenderObject* beforeChild = 0); diff --git a/Source/WebCore/rendering/RenderRubyBase.cpp b/Source/WebCore/rendering/RenderRubyBase.cpp index 87402f713..adc2f0d44 100644 --- a/Source/WebCore/rendering/RenderRubyBase.cpp +++ b/Source/WebCore/rendering/RenderRubyBase.cpp @@ -38,8 +38,8 @@ using namespace std; namespace WebCore { -RenderRubyBase::RenderRubyBase(Node* node) - : RenderBlock(node) +RenderRubyBase::RenderRubyBase() + : RenderBlock(0) { setInline(false); } @@ -48,6 +48,13 @@ RenderRubyBase::~RenderRubyBase() { } +RenderRubyBase* RenderRubyBase::createAnonymous(Document* document) +{ + RenderRubyBase* renderer = new (document->renderArena()) RenderRubyBase(); + renderer->setDocumentForAnonymous(document); + return renderer; +} + bool RenderRubyBase::isChildAllowed(RenderObject* child, RenderStyle*) const { return child->isInline(); diff --git a/Source/WebCore/rendering/RenderRubyBase.h b/Source/WebCore/rendering/RenderRubyBase.h index 993892005..3888606b4 100644 --- a/Source/WebCore/rendering/RenderRubyBase.h +++ b/Source/WebCore/rendering/RenderRubyBase.h @@ -39,8 +39,9 @@ class RenderRubyRun; class RenderRubyBase : public RenderBlock { public: - RenderRubyBase(Node*); virtual ~RenderRubyBase(); + + static RenderRubyBase* createAnonymous(Document*); virtual const char* renderName() const { return "RenderRubyBase (anonymous)"; } @@ -49,6 +50,8 @@ public: virtual bool isChildAllowed(RenderObject*, RenderStyle*) const; private: + RenderRubyBase(); + virtual ETextAlign textAlignmentForLine(bool endsWithSoftBreak) const; virtual void adjustInlineDirectionLineBounds(int expansionOpportunityCount, float& logicalLeft, float& logicalWidth) const; diff --git a/Source/WebCore/rendering/RenderRubyRun.cpp b/Source/WebCore/rendering/RenderRubyRun.cpp index 498d17d10..874821f1b 100644 --- a/Source/WebCore/rendering/RenderRubyRun.cpp +++ b/Source/WebCore/rendering/RenderRubyRun.cpp @@ -37,13 +37,14 @@ #include "RenderText.h" #include "RenderView.h" #include "StyleInheritedData.h" +#include <wtf/StackStats.h> using namespace std; namespace WebCore { -RenderRubyRun::RenderRubyRun(Node* node) - : RenderBlock(node) +RenderRubyRun::RenderRubyRun() + : RenderBlock(0) { setReplaced(true); setInline(true); @@ -199,17 +200,18 @@ void RenderRubyRun::removeChild(RenderObject* child) RenderRubyBase* RenderRubyRun::createRubyBase() const { - RenderRubyBase* rb = new (renderArena()) RenderRubyBase(document() /* anonymous */); + RenderRubyBase* renderer = RenderRubyBase::createAnonymous(document()); RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyleWithDisplay(style(), BLOCK); newStyle->setTextAlign(CENTER); // FIXME: use WEBKIT_CENTER? - rb->setStyle(newStyle.release()); - return rb; + renderer->setStyle(newStyle.release()); + return renderer; } RenderRubyRun* RenderRubyRun::staticCreateRubyRun(const RenderObject* parentRuby) { ASSERT(parentRuby && parentRuby->isRuby()); - RenderRubyRun* rr = new (parentRuby->renderArena()) RenderRubyRun(parentRuby->document() /* anonymous */); + RenderRubyRun* rr = new (parentRuby->renderArena()) RenderRubyRun(); + rr->setDocumentForAnonymous(parentRuby->document()); RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyleWithDisplay(parentRuby->style(), INLINE_BLOCK); rr->setStyle(newStyle.release()); return rr; @@ -232,11 +234,13 @@ void RenderRubyRun::layout() { RenderBlock::layout(); - // Place the RenderRubyText such that its bottom is flush with the lineTop of the first line of the RenderRubyBase. RenderRubyText* rt = rubyText(); if (!rt) return; + + rt->setLogicalLeft(0); + // Place the RenderRubyText such that its bottom is flush with the lineTop of the first line of the RenderRubyBase. LayoutUnit lastLineRubyTextBottom = rt->logicalHeight(); LayoutUnit firstLineRubyTextTop = 0; RootInlineBox* rootBox = rt->lastRootBox(); @@ -269,7 +273,6 @@ void RenderRubyRun::layout() } // Update our overflow to account for the new RenderRubyText position. - m_overflow.clear(); computeOverflow(clientLogicalBottom()); } diff --git a/Source/WebCore/rendering/RenderRubyRun.h b/Source/WebCore/rendering/RenderRubyRun.h index f65ad86c6..1e553a6a0 100644 --- a/Source/WebCore/rendering/RenderRubyRun.h +++ b/Source/WebCore/rendering/RenderRubyRun.h @@ -43,7 +43,6 @@ class RenderRubyText; class RenderRubyRun : public RenderBlock { public: - RenderRubyRun(Node*); virtual ~RenderRubyRun(); bool hasRubyText() const; @@ -71,6 +70,8 @@ protected: RenderRubyBase* createRubyBase() const; private: + RenderRubyRun(); + virtual bool isRubyRun() const { return true; } virtual const char* renderName() const { return "RenderRubyRun (anonymous)"; } virtual bool createsAnonymousWrapper() const { return true; } @@ -79,13 +80,13 @@ private: inline RenderRubyRun* toRenderRubyRun(RenderObject* object) { - ASSERT(!object || object->isRubyRun()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isRubyRun()); return static_cast<RenderRubyRun*>(object); } inline const RenderRubyRun* toRenderRubyRun(const RenderObject* object) { - ASSERT(!object || object->isBox()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isBox()); return static_cast<const RenderRubyRun*>(object); } diff --git a/Source/WebCore/rendering/RenderRubyText.cpp b/Source/WebCore/rendering/RenderRubyText.cpp index e9765f28d..497c8b71d 100644 --- a/Source/WebCore/rendering/RenderRubyText.cpp +++ b/Source/WebCore/rendering/RenderRubyText.cpp @@ -37,8 +37,8 @@ using namespace std; namespace WebCore { -RenderRubyText::RenderRubyText(Node* node) - : RenderBlock(node) +RenderRubyText::RenderRubyText(Element* element) + : RenderBlock(element) { } @@ -88,15 +88,4 @@ bool RenderRubyText::avoidsFloats() const return true; } -void RenderRubyText::updateBeforeAfterContent(PseudoId pseudoId) -{ - // RenderRubyText manages its own :before and :after content - // and is not handled by its anonymous wrappers RenderRubyRun - // and RenderRuby. This contrasts with other ruby children, which - // are enclosed in RenderRubyBase and hence they are able to - // update their :before, :after content (since RenderRubyBase - // is not a anonymous wrapper). - return children()->updateBeforeAfterContent(this, pseudoId); -} - } // namespace WebCore diff --git a/Source/WebCore/rendering/RenderRubyText.h b/Source/WebCore/rendering/RenderRubyText.h index f47304ccd..0b0bae582 100644 --- a/Source/WebCore/rendering/RenderRubyText.h +++ b/Source/WebCore/rendering/RenderRubyText.h @@ -37,7 +37,7 @@ namespace WebCore { class RenderRubyText : public RenderBlock { public: - RenderRubyText(Node*); + RenderRubyText(Element*); virtual ~RenderRubyText(); virtual const char* renderName() const { return "RenderRubyText"; } @@ -46,8 +46,6 @@ public: virtual bool isChildAllowed(RenderObject*, RenderStyle*) const; - virtual void updateBeforeAfterContent(PseudoId); - private: virtual bool avoidsFloats() const; diff --git a/Source/WebCore/rendering/RenderScrollbar.cpp b/Source/WebCore/rendering/RenderScrollbar.cpp index 216c5f8d8..0699502c6 100644 --- a/Source/WebCore/rendering/RenderScrollbar.cpp +++ b/Source/WebCore/rendering/RenderScrollbar.cpp @@ -32,6 +32,7 @@ #include "RenderScrollbarPart.h" #include "RenderScrollbarTheme.h" #include "StyleInheritedData.h" +#include "StyleResolver.h" namespace WebCore { @@ -145,30 +146,12 @@ void RenderScrollbar::setPressedPart(ScrollbarPart part) updateScrollbarPart(TrackBGPart); } -static ScrollbarPart s_styleResolvePart; -static RenderScrollbar* s_styleResolveScrollbar; - -RenderScrollbar* RenderScrollbar::scrollbarForStyleResolve() -{ - return s_styleResolveScrollbar; -} - -ScrollbarPart RenderScrollbar::partForStyleResolve() -{ - return s_styleResolvePart; -} - PassRefPtr<RenderStyle> RenderScrollbar::getScrollbarPseudoStyle(ScrollbarPart partType, PseudoId pseudoId) { if (!owningRenderer()) return 0; - s_styleResolvePart = partType; - s_styleResolveScrollbar = this; - RefPtr<RenderStyle> result = owningRenderer()->getUncachedPseudoStyle(pseudoId, owningRenderer()->style()); - s_styleResolvePart = NoPart; - s_styleResolveScrollbar = 0; - + RefPtr<RenderStyle> result = owningRenderer()->getUncachedPseudoStyle(PseudoStyleRequest(pseudoId, this, partType), owningRenderer()->style()); // Scrollbars for root frames should always have background color // unless explicitly specified as transparent. So we force it. // This is because WebKit assumes scrollbar to be always painted and missing background @@ -270,7 +253,7 @@ void RenderScrollbar::updateScrollbarPart(ScrollbarPart partType, bool destroy) RenderScrollbarPart* partRenderer = m_parts.get(partType); if (!partRenderer && needRenderer) { - partRenderer = new (owningRenderer()->renderArena()) RenderScrollbarPart(owningRenderer()->document(), this, partType); + partRenderer = RenderScrollbarPart::createAnonymous(owningRenderer()->document(), this, partType); m_parts.set(partType, partRenderer); } else if (partRenderer && !needRenderer) { m_parts.remove(partType); diff --git a/Source/WebCore/rendering/RenderScrollbar.h b/Source/WebCore/rendering/RenderScrollbar.h index 918eee0a9..685c0072f 100644 --- a/Source/WebCore/rendering/RenderScrollbar.h +++ b/Source/WebCore/rendering/RenderScrollbar.h @@ -47,9 +47,6 @@ public: static PassRefPtr<Scrollbar> createCustomScrollbar(ScrollableArea*, ScrollbarOrientation, Node*, Frame* owningFrame = 0); virtual ~RenderScrollbar(); - static ScrollbarPart partForStyleResolve(); - static RenderScrollbar* scrollbarForStyleResolve(); - RenderBox* owningRenderer() const; void paintPart(GraphicsContext*, ScrollbarPart, const IntRect&); @@ -92,7 +89,7 @@ private: inline RenderScrollbar* toRenderScrollbar(ScrollbarThemeClient* scrollbar) { - ASSERT(!scrollbar || scrollbar->isCustomScrollbar()); + ASSERT_WITH_SECURITY_IMPLICATION(!scrollbar || scrollbar->isCustomScrollbar()); return static_cast<RenderScrollbar*>(scrollbar); } diff --git a/Source/WebCore/rendering/RenderScrollbarPart.cpp b/Source/WebCore/rendering/RenderScrollbarPart.cpp index 3eeb1d89f..af8ac3c12 100644 --- a/Source/WebCore/rendering/RenderScrollbarPart.cpp +++ b/Source/WebCore/rendering/RenderScrollbarPart.cpp @@ -30,13 +30,14 @@ #include "RenderScrollbar.h" #include "RenderScrollbarTheme.h" #include "RenderView.h" +#include <wtf/StackStats.h> using namespace std; namespace WebCore { -RenderScrollbarPart::RenderScrollbarPart(Node* node, RenderScrollbar* scrollbar, ScrollbarPart part) - : RenderBlock(node) +RenderScrollbarPart::RenderScrollbarPart(RenderScrollbar* scrollbar, ScrollbarPart part) + : RenderBlock(0) , m_scrollbar(scrollbar) , m_part(part) { @@ -46,6 +47,13 @@ RenderScrollbarPart::~RenderScrollbarPart() { } +RenderScrollbarPart* RenderScrollbarPart::createAnonymous(Document* document, RenderScrollbar* scrollbar, ScrollbarPart part) +{ + RenderScrollbarPart* renderer = new (document->renderArena()) RenderScrollbarPart(scrollbar, part); + renderer->setDocumentForAnonymous(document); + return renderer; +} + void RenderScrollbarPart::layout() { StackStats::LayoutCheckPoint layoutCheckPoint; @@ -143,7 +151,7 @@ void RenderScrollbarPart::styleDidChange(StyleDifference diff, const RenderStyle { RenderBlock::styleDidChange(diff, oldStyle); setInline(false); - setPositioned(false); + clearPositionedState(); setFloating(false); setHasOverflowClip(false); if (oldStyle && m_scrollbar && m_part != NoPart && diff >= StyleDifferenceRepaint) @@ -177,7 +185,7 @@ void RenderScrollbarPart::paintIntoRect(GraphicsContext* graphicsContext, const return; // Now do the paint. - PaintInfo paintInfo(graphicsContext, pixelSnappedIntRect(rect), PaintPhaseBlockBackground, false, 0, 0, 0); + PaintInfo paintInfo(graphicsContext, pixelSnappedIntRect(rect), PaintPhaseBlockBackground, PaintBehaviorNormal); paint(paintInfo, paintOffset); paintInfo.phase = PaintPhaseChildBlockBackgrounds; paint(paintInfo, paintOffset); diff --git a/Source/WebCore/rendering/RenderScrollbarPart.h b/Source/WebCore/rendering/RenderScrollbarPart.h index 2d8a36c4a..75b7b5106 100644 --- a/Source/WebCore/rendering/RenderScrollbarPart.h +++ b/Source/WebCore/rendering/RenderScrollbarPart.h @@ -35,7 +35,8 @@ class RenderScrollbar; class RenderScrollbarPart : public RenderBlock { public: - RenderScrollbarPart(Node*, RenderScrollbar* = 0, ScrollbarPart = NoPart); + static RenderScrollbarPart* createAnonymous(Document*, RenderScrollbar* = 0, ScrollbarPart = NoPart); + virtual ~RenderScrollbarPart(); virtual const char* renderName() const { return "RenderScrollbarPart"; } @@ -43,7 +44,6 @@ public: virtual bool requiresLayer() const { return false; } virtual void layout(); - virtual void computePreferredLogicalWidths(); void paintIntoRect(GraphicsContext*, const LayoutPoint&, const LayoutRect&); @@ -62,6 +62,10 @@ protected: virtual void imageChanged(WrappedImagePtr, const IntRect* = 0); private: + RenderScrollbarPart(RenderScrollbar*, ScrollbarPart); + + virtual void computePreferredLogicalWidths(); + void layoutHorizontalPart(); void layoutVerticalPart(); @@ -74,13 +78,13 @@ private: inline RenderScrollbarPart* toRenderScrollbarPart(RenderObject* object) { - ASSERT(!object || object->isRenderScrollbarPart()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isRenderScrollbarPart()); return static_cast<RenderScrollbarPart*>(object); } inline const RenderScrollbarPart* toRenderScrollbarPart(const RenderObject* object) { - ASSERT(!object || object->isRenderScrollbarPart()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isRenderScrollbarPart()); return static_cast<const RenderScrollbarPart*>(object); } diff --git a/Source/WebCore/rendering/RenderSearchField.cpp b/Source/WebCore/rendering/RenderSearchField.cpp index 4ad458ab7..50d29a784 100644 --- a/Source/WebCore/rendering/RenderSearchField.cpp +++ b/Source/WebCore/rendering/RenderSearchField.cpp @@ -53,14 +53,14 @@ using namespace HTMLNames; // ---------------------------- -RenderSearchField::RenderSearchField(Node* node) - : RenderTextControlSingleLine(node) +RenderSearchField::RenderSearchField(Element* element) + : RenderTextControlSingleLine(element) , m_searchPopupIsVisible(false) , m_searchPopup(0) { - ASSERT(node->isHTMLElement()); - ASSERT(node->toInputElement()); - ASSERT(node->toInputElement()->isSearchField()); + ASSERT(element->isHTMLElement()); + ASSERT(element->toInputElement()); + ASSERT(element->toInputElement()->isSearchField()); } RenderSearchField::~RenderSearchField() @@ -107,7 +107,7 @@ void RenderSearchField::addSearchResult() const AtomicString& name = autosaveName(); if (!m_searchPopup) - m_searchPopup = document()->page()->chrome()->createSearchPopupMenu(this); + m_searchPopup = document()->page()->chrome().createSearchPopupMenu(this); m_searchPopup->saveRecentSearches(name, m_recentSearches); } @@ -118,7 +118,7 @@ void RenderSearchField::showPopup() return; if (!m_searchPopup) - m_searchPopup = document()->page()->chrome()->createSearchPopupMenu(this); + m_searchPopup = document()->page()->chrome().createSearchPopupMenu(this); if (!m_searchPopup->enabled()) return; @@ -147,19 +147,19 @@ void RenderSearchField::hidePopup() m_searchPopup->popupMenu()->hide(); } -LayoutUnit RenderSearchField::computeControlHeight(LayoutUnit lineHeight, LayoutUnit nonContentHeight) const +LayoutUnit RenderSearchField::computeControlLogicalHeight(LayoutUnit lineHeight, LayoutUnit nonContentHeight) const { HTMLElement* resultsButton = resultsButtonElement(); if (RenderBox* resultsRenderer = resultsButton ? resultsButton->renderBox() : 0) { resultsRenderer->updateLogicalHeight(); - nonContentHeight = max(nonContentHeight, resultsRenderer->borderAndPaddingHeight() + resultsRenderer->marginHeight()); - lineHeight = max(lineHeight, resultsRenderer->height()); + nonContentHeight = max(nonContentHeight, resultsRenderer->borderAndPaddingLogicalHeight() + resultsRenderer->marginLogicalHeight()); + lineHeight = max(lineHeight, resultsRenderer->logicalHeight()); } HTMLElement* cancelButton = cancelButtonElement(); if (RenderBox* cancelRenderer = cancelButton ? cancelButton->renderBox() : 0) { cancelRenderer->updateLogicalHeight(); - nonContentHeight = max(nonContentHeight, cancelRenderer->borderAndPaddingHeight() + cancelRenderer->marginHeight()); - lineHeight = max(lineHeight, cancelRenderer->height()); + nonContentHeight = max(nonContentHeight, cancelRenderer->borderAndPaddingLogicalHeight() + cancelRenderer->marginLogicalHeight()); + lineHeight = max(lineHeight, cancelRenderer->logicalHeight()); } return lineHeight + nonContentHeight; @@ -199,7 +199,7 @@ EVisibility RenderSearchField::visibilityForCancelButton() const const AtomicString& RenderSearchField::autosaveName() const { - return static_cast<Element*>(node())->getAttribute(autosaveAttr); + return toElement(node())->getAttribute(autosaveAttr); } // PopupMenuClient methods @@ -213,7 +213,7 @@ void RenderSearchField::valueChanged(unsigned listIndex, bool fireEvents) const AtomicString& name = autosaveName(); if (!name.isEmpty()) { if (!m_searchPopup) - m_searchPopup = document()->page()->chrome()->createSearchPopupMenu(this); + m_searchPopup = document()->page()->chrome().createSearchPopupMenu(this); m_searchPopup->saveRecentSearches(name, m_recentSearches); } } @@ -266,7 +266,7 @@ PopupMenuStyle RenderSearchField::itemStyle(unsigned) const PopupMenuStyle RenderSearchField::menuStyle() const { return PopupMenuStyle(style()->visitedDependentColor(CSSPropertyColor), style()->visitedDependentColor(CSSPropertyBackgroundColor), style()->font(), style()->visibility() == VISIBLE, - style()->display() == NONE, style()->textIndent(), style()->direction(), isOverride(style()->unicodeBidi())); + style()->display() == NONE, style()->textIndent(), style()->direction(), isOverride(style()->unicodeBidi()), PopupMenuStyle::CustomBackgroundColor); } int RenderSearchField::clientInsetLeft() const @@ -344,7 +344,7 @@ void RenderSearchField::setTextFromItem(unsigned listIndex) FontSelector* RenderSearchField::fontSelector() const { - return document()->styleResolver()->fontSelector(); + return document()->ensureStyleResolver()->fontSelector(); } HostWindow* RenderSearchField::hostWindow() const @@ -363,9 +363,9 @@ PassRefPtr<Scrollbar> RenderSearchField::createScrollbar(ScrollableArea* scrolla return widget.release(); } -LayoutUnit RenderSearchField::computeHeightLimit() const +LayoutUnit RenderSearchField::computeLogicalHeightLimit() const { - return height(); + return logicalHeight(); } void RenderSearchField::centerContainerIfNeeded(RenderBox* containerRenderer) const @@ -373,13 +373,13 @@ void RenderSearchField::centerContainerIfNeeded(RenderBox* containerRenderer) co if (!containerRenderer) return; - if (containerRenderer->height() <= contentHeight()) + if (containerRenderer->logicalHeight() <= contentLogicalHeight()) return; // A quirk for find-in-page box on Safari Windows. // http://webkit.org/b/63157 - LayoutUnit heightDiff = containerRenderer->height() - contentHeight(); - containerRenderer->setY(containerRenderer->y() - (heightDiff / 2 + layoutMod(heightDiff, 2))); + LayoutUnit logicalHeightDiff = containerRenderer->logicalHeight() - contentLogicalHeight(); + containerRenderer->setLogicalTop(containerRenderer->logicalTop() - (logicalHeightDiff / 2 + layoutMod(logicalHeightDiff, 2))); } } diff --git a/Source/WebCore/rendering/RenderSearchField.h b/Source/WebCore/rendering/RenderSearchField.h index 967335d34..b0ffaa8c3 100644 --- a/Source/WebCore/rendering/RenderSearchField.h +++ b/Source/WebCore/rendering/RenderSearchField.h @@ -33,7 +33,7 @@ class SearchPopupMenu; class RenderSearchField : public RenderTextControlSingleLine, private PopupMenuClient { public: - RenderSearchField(Node*); + RenderSearchField(Element*); virtual ~RenderSearchField(); void updateCancelButtonVisibility() const; @@ -47,8 +47,8 @@ public: private: virtual void centerContainerIfNeeded(RenderBox*) const OVERRIDE; - virtual LayoutUnit computeControlHeight(LayoutUnit lineHeight, LayoutUnit nonContentHeight) const OVERRIDE; - virtual LayoutUnit computeHeightLimit() const OVERRIDE; + virtual LayoutUnit computeControlLogicalHeight(LayoutUnit lineHeight, LayoutUnit nonContentHeight) const OVERRIDE; + virtual LayoutUnit computeLogicalHeightLimit() const OVERRIDE; virtual void updateFromElement() OVERRIDE; EVisibility visibilityForCancelButton() const; const AtomicString& autosaveName() const; @@ -92,7 +92,7 @@ private: inline RenderSearchField* toRenderSearchField(RenderObject* object) { - ASSERT(!object || object->isTextField()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isTextField()); return static_cast<RenderSearchField*>(object); } diff --git a/Source/WebCore/rendering/RenderSlider.cpp b/Source/WebCore/rendering/RenderSlider.cpp index d5b90cad6..55df4fdea 100644 --- a/Source/WebCore/rendering/RenderSlider.cpp +++ b/Source/WebCore/rendering/RenderSlider.cpp @@ -41,6 +41,7 @@ #include "StepRange.h" #include "StyleResolver.h" #include <wtf/MathExtras.h> +#include <wtf/StackStats.h> using std::min; @@ -70,6 +71,13 @@ int RenderSlider::baselinePosition(FontBaseline, bool /*firstLine*/, LineDirecti return height() + marginTop(); } +void RenderSlider::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const +{ + maxLogicalWidth = defaultTrackLength * style()->effectiveZoom(); + if (!style()->width().isPercent()) + minLogicalWidth = maxLogicalWidth; +} + void RenderSlider::computePreferredLogicalWidths() { m_minPreferredLogicalWidth = 0; @@ -78,16 +86,13 @@ void RenderSlider::computePreferredLogicalWidths() if (style()->width().isFixed() && style()->width().value() > 0) m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = adjustContentBoxLogicalWidthForBoxSizing(style()->width().value()); else - m_maxPreferredLogicalWidth = defaultTrackLength * style()->effectiveZoom(); + computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth); if (style()->minWidth().isFixed() && style()->minWidth().value() > 0) { m_maxPreferredLogicalWidth = max(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style()->minWidth().value())); m_minPreferredLogicalWidth = max(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style()->minWidth().value())); - } else if (style()->width().isPercent() || (style()->width().isAuto() && style()->height().isPercent())) - m_minPreferredLogicalWidth = 0; - else - m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth; - + } + if (style()->maxWidth().isFixed()) { m_maxPreferredLogicalWidth = min(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style()->maxWidth().value())); m_minPreferredLogicalWidth = min(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style()->maxWidth().value())); @@ -109,7 +114,7 @@ void RenderSlider::layout() if (thumbBox && thumbBox->isSliderThumb()) static_cast<RenderSliderThumb*>(thumbBox)->updateAppearance(style()); - RenderBlock::layout(); + RenderFlexibleBox::layout(); } bool RenderSlider::inDragMode() const diff --git a/Source/WebCore/rendering/RenderSlider.h b/Source/WebCore/rendering/RenderSlider.h index 252cd080c..8603a90e8 100644 --- a/Source/WebCore/rendering/RenderSlider.h +++ b/Source/WebCore/rendering/RenderSlider.h @@ -33,7 +33,7 @@ class RenderSlider : public RenderFlexibleBox { public: static const int defaultTrackLength; - RenderSlider(HTMLInputElement*); + explicit RenderSlider(HTMLInputElement*); virtual ~RenderSlider(); bool inDragMode() const; @@ -44,14 +44,15 @@ private: virtual bool canBeReplacedWithInlineRunIn() const OVERRIDE; virtual int baselinePosition(FontBaseline, bool firstLine, LineDirectionMode, LinePositionMode = PositionOnContainingLine) const; - virtual void computePreferredLogicalWidths(); + virtual void computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const OVERRIDE; + virtual void computePreferredLogicalWidths() OVERRIDE; virtual bool requiresForcedStyleRecalcPropagation() const { return true; } virtual void layout(); }; inline RenderSlider* toRenderSlider(RenderObject* object) { - ASSERT(!object || object->isSlider()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isSlider()); return static_cast<RenderSlider*>(object); } diff --git a/Source/WebCore/rendering/RenderSnapshottedPlugIn.cpp b/Source/WebCore/rendering/RenderSnapshottedPlugIn.cpp index 972b2bf90..075910abe 100644 --- a/Source/WebCore/rendering/RenderSnapshottedPlugIn.cpp +++ b/Source/WebCore/rendering/RenderSnapshottedPlugIn.cpp @@ -26,25 +26,30 @@ #include "config.h" #include "RenderSnapshottedPlugIn.h" +#include "CachedImage.h" +#include "Chrome.h" +#include "ChromeClient.h" #include "Cursor.h" +#include "Filter.h" #include "FrameLoaderClient.h" #include "FrameView.h" #include "Gradient.h" #include "HTMLPlugInImageElement.h" +#include "ImageBuffer.h" #include "MouseEvent.h" +#include "Page.h" #include "PaintInfo.h" #include "Path.h" +#include "PlatformMouseEvent.h" +#include "RenderView.h" +#include <wtf/StackStats.h> namespace WebCore { -static const int autoStartPlugInSizeThresholdWidth = 1; -static const int autoStartPlugInSizeThresholdHeight = 1; -static const int startButtonPadding = 10; - RenderSnapshottedPlugIn::RenderSnapshottedPlugIn(HTMLPlugInImageElement* element) : RenderEmbeddedObject(element) , m_snapshotResource(RenderImageResource::create()) - , m_isMouseInButtonRect(false) + , m_isPotentialMouseActivation(false) { m_snapshotResource->initialize(this); } @@ -57,7 +62,22 @@ RenderSnapshottedPlugIn::~RenderSnapshottedPlugIn() HTMLPlugInImageElement* RenderSnapshottedPlugIn::plugInImageElement() const { - return static_cast<HTMLPlugInImageElement*>(node()); + return toHTMLPlugInImageElement(node()); +} + +void RenderSnapshottedPlugIn::layout() +{ + StackStats::LayoutCheckPoint layoutCheckPoint; + LayoutSize oldSize = contentBoxRect().size(); + + RenderEmbeddedObject::layout(); + + LayoutSize newSize = contentBoxRect().size(); + if (newSize == oldSize) + return; + + if (document()->view()) + document()->view()->addWidgetToUpdate(this); } void RenderSnapshottedPlugIn::updateSnapshot(PassRefPtr<Image> image) @@ -72,37 +92,37 @@ void RenderSnapshottedPlugIn::updateSnapshot(PassRefPtr<Image> image) void RenderSnapshottedPlugIn::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { - if (plugInImageElement()->displayState() < HTMLPlugInElement::PlayingWithPendingMouseClick) { - RenderReplaced::paint(paintInfo, paintOffset); - return; + if (paintInfo.phase == PaintPhaseForeground && plugInImageElement()->displayState() < HTMLPlugInElement::Restarting) { + paintSnapshot(paintInfo, paintOffset); } - RenderEmbeddedObject::paint(paintInfo, paintOffset); -} + PaintPhase newPhase = (paintInfo.phase == PaintPhaseChildOutlines) ? PaintPhaseOutline : paintInfo.phase; + newPhase = (newPhase == PaintPhaseChildBlockBackgrounds) ? PaintPhaseChildBlockBackground : newPhase; -void RenderSnapshottedPlugIn::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOffset) -{ - if (plugInImageElement()->displayState() < HTMLPlugInElement::PlayingWithPendingMouseClick) { - paintReplacedSnapshot(paintInfo, paintOffset); - paintButton(paintInfo, paintOffset); - return; + PaintInfo paintInfoForChild(paintInfo); + paintInfoForChild.phase = newPhase; + paintInfoForChild.updateSubtreePaintRootForChildren(this); + + for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { + LayoutPoint childPoint = flipForWritingModeForChild(child, paintOffset); + if (!child->hasSelfPaintingLayer() && !child->isFloating()) + child->paint(paintInfoForChild, childPoint); } - RenderEmbeddedObject::paintReplaced(paintInfo, paintOffset); + RenderEmbeddedObject::paint(paintInfo, paintOffset); } -void RenderSnapshottedPlugIn::paintReplacedSnapshot(PaintInfo& paintInfo, const LayoutPoint& paintOffset) +void RenderSnapshottedPlugIn::paintSnapshot(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { - // This code should be similar to RenderImage::paintReplaced() and RenderImage::paintIntoRect(). + Image* image = m_snapshotResource->image().get(); + if (!image || image->isNull()) + return; + LayoutUnit cWidth = contentWidth(); LayoutUnit cHeight = contentHeight(); if (!cWidth || !cHeight) return; - RefPtr<Image> image = m_snapshotResource->image(); - if (!image || image->isNull()) - return; - GraphicsContext* context = paintInfo.context; #if PLATFORM(MAC) if (style()->highlight() != nullAtom && !context->paintingDisabled()) @@ -110,7 +130,7 @@ void RenderSnapshottedPlugIn::paintReplacedSnapshot(PaintInfo& paintInfo, const #endif LayoutSize contentSize(cWidth, cHeight); - LayoutPoint contentLocation = paintOffset; + LayoutPoint contentLocation = location() + paintOffset; contentLocation.move(borderLeft() + paddingLeft(), borderTop() + paddingTop()); LayoutRect rect(contentLocation, contentSize); @@ -118,48 +138,13 @@ void RenderSnapshottedPlugIn::paintReplacedSnapshot(PaintInfo& paintInfo, const if (alignedRect.width() <= 0 || alignedRect.height() <= 0) return; - bool useLowQualityScaling = shouldPaintAtLowQuality(context, image.get(), image.get(), alignedRect.size()); - context->drawImage(image.get(), style()->colorSpace(), alignedRect, CompositeSourceOver, shouldRespectImageOrientation(), useLowQualityScaling); -} - -static Image* startButtonImage() -{ - static Image* buttonImage = Image::loadPlatformResource("startButton").leakRef(); - return buttonImage; -} - -static Image* startButtonPressedImage() -{ - static Image* buttonImage = Image::loadPlatformResource("startButtonPressed").leakRef(); - return buttonImage; -} - -void RenderSnapshottedPlugIn::paintButton(PaintInfo& paintInfo, const LayoutPoint& paintOffset) -{ - LayoutRect contentRect = contentBoxRect(); - if (contentRect.isEmpty()) - return; - - Image* buttonImage = startButtonImage(); - if (plugInImageElement()->active()) { - if (m_isMouseInButtonRect) - buttonImage = startButtonPressedImage(); - } else if (!plugInImageElement()->hovered()) - return; - - LayoutPoint contentLocation = paintOffset + contentRect.maxXMaxYCorner() - buttonImage->size() - LayoutSize(startButtonPadding, startButtonPadding); - paintInfo.context->drawImage(buttonImage, ColorSpaceDeviceRGB, roundedIntPoint(contentLocation), buttonImage->rect()); -} - -void RenderSnapshottedPlugIn::repaintButton() -{ - // FIXME: This is unfortunate. We should just repaint the button. - repaint(); + bool useLowQualityScaling = shouldPaintAtLowQuality(context, image, image, alignedRect.size()); + context->drawImage(image, style()->colorSpace(), alignedRect, CompositeSourceOver, shouldRespectImageOrientation(), useLowQualityScaling); } CursorDirective RenderSnapshottedPlugIn::getCursor(const LayoutPoint& point, Cursor& overrideCursor) const { - if (plugInImageElement()->displayState() < HTMLPlugInElement::PlayingWithPendingMouseClick) { + if (plugInImageElement()->displayState() < HTMLPlugInElement::Restarting) { overrideCursor = handCursor(); return SetCursor; } @@ -173,43 +158,29 @@ void RenderSnapshottedPlugIn::handleEvent(Event* event) MouseEvent* mouseEvent = static_cast<MouseEvent*>(event); - if (event->type() == eventNames().clickEvent && mouseEvent->button() == LeftButton) { - if (m_isMouseInButtonRect) - plugInImageElement()->setDisplayState(HTMLPlugInElement::Playing); - else { - plugInImageElement()->setDisplayState(HTMLPlugInElement::PlayingWithPendingMouseClick); - plugInImageElement()->setPendingClickEvent(mouseEvent); - } - if (widget()) { - if (Frame* frame = document()->frame()) - frame->loader()->client()->recreatePlugin(widget()); - repaint(); - } - event->setDefaultHandled(); - } else if (event->type() == eventNames().mouseoverEvent || event->type() == eventNames().mouseoutEvent) - repaintButton(); - else if (event->type() == eventNames().mousedownEvent) { - bool isMouseInButtonRect = m_buttonRect.contains(IntPoint(mouseEvent->offsetX(), mouseEvent->offsetY())); - if (isMouseInButtonRect != m_isMouseInButtonRect) { - m_isMouseInButtonRect = isMouseInButtonRect; - repaintButton(); - } - } -} + // If we're a snapshotted plugin, we want to make sure we activate on + // clicks even if the page is preventing our default behaviour. Otherwise + // we can never restart. One we do restart, then the page will happily + // block the new plugin in the normal renderer. All this means we have to + // be on the lookout for a mouseup event that comes after a mousedown + // event. The code below is not completely foolproof, but the worst that + // could happen is that a snapshotted plugin restarts. -void RenderSnapshottedPlugIn::layout() -{ - RenderEmbeddedObject::layout(); - if (plugInImageElement()->displayState() < HTMLPlugInElement::Playing) { - LayoutRect rect = contentBoxRect(); - int width = rect.width(); - int height = rect.height(); - if (!width || !height || (width <= autoStartPlugInSizeThresholdWidth && height <= autoStartPlugInSizeThresholdHeight)) - plugInImageElement()->setDisplayState(HTMLPlugInElement::Playing); - } + if (event->type() == eventNames().mouseoutEvent) + m_isPotentialMouseActivation = false; + + if (mouseEvent->button() != LeftButton) + return; - LayoutSize buttonSize = startButtonImage()->size(); - m_buttonRect = LayoutRect(contentBoxRect().maxXMaxYCorner() - LayoutSize(startButtonPadding, startButtonPadding) - buttonSize, buttonSize); + if (event->type() == eventNames().clickEvent || (m_isPotentialMouseActivation && event->type() == eventNames().mouseupEvent)) { + m_isPotentialMouseActivation = false; + bool clickWasOnOverlay = plugInImageElement()->partOfSnapshotOverlay(event->target()->toNode()); + plugInImageElement()->userDidClickSnapshot(mouseEvent, !clickWasOnOverlay); + event->setDefaultHandled(); + } else if (event->type() == eventNames().mousedownEvent) { + m_isPotentialMouseActivation = true; + event->setDefaultHandled(); + } } } // namespace WebCore diff --git a/Source/WebCore/rendering/RenderSnapshottedPlugIn.h b/Source/WebCore/rendering/RenderSnapshottedPlugIn.h index 2a1f896c7..beae256fa 100644 --- a/Source/WebCore/rendering/RenderSnapshottedPlugIn.h +++ b/Source/WebCore/rendering/RenderSnapshottedPlugIn.h @@ -27,9 +27,8 @@ #define RenderSnapshottedPlugIn_h #include "RenderEmbeddedObject.h" - #include "RenderImageResource.h" -#include "RenderTheme.h" +#include "Timer.h" namespace WebCore { @@ -37,7 +36,7 @@ class HTMLPlugInImageElement; class RenderSnapshottedPlugIn : public RenderEmbeddedObject { public: - RenderSnapshottedPlugIn(HTMLPlugInImageElement*); + explicit RenderSnapshottedPlugIn(HTMLPlugInImageElement*); virtual ~RenderSnapshottedPlugIn(); void updateSnapshot(PassRefPtr<Image>); @@ -51,22 +50,20 @@ private: virtual CursorDirective getCursor(const LayoutPoint&, Cursor&) const OVERRIDE; virtual bool isSnapshottedPlugIn() const OVERRIDE { return true; } virtual void paint(PaintInfo&, const LayoutPoint&) OVERRIDE; - virtual void paintReplaced(PaintInfo&, const LayoutPoint&) OVERRIDE; + + virtual bool canHaveWidget() const OVERRIDE { return false; } - void paintReplacedSnapshot(PaintInfo&, const LayoutPoint&); - void paintButton(PaintInfo&, const LayoutPoint&); - void repaintButton(); + void paintSnapshot(PaintInfo&, const LayoutPoint&); virtual void layout() OVERRIDE; OwnPtr<RenderImageResource> m_snapshotResource; - LayoutRect m_buttonRect; - bool m_isMouseInButtonRect; + bool m_isPotentialMouseActivation; }; inline RenderSnapshottedPlugIn* toRenderSnapshottedPlugIn(RenderObject* object) { - ASSERT(!object || object->isSnapshottedPlugIn()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isSnapshottedPlugIn()); return static_cast<RenderSnapshottedPlugIn*>(object); } diff --git a/Source/WebCore/rendering/RenderTable.cpp b/Source/WebCore/rendering/RenderTable.cpp index 7d5cfe94f..3249afb8a 100644 --- a/Source/WebCore/rendering/RenderTable.cpp +++ b/Source/WebCore/rendering/RenderTable.cpp @@ -28,12 +28,12 @@ #include "AutoTableLayout.h" #include "CollapsedBorderValue.h" -#include "DeleteButtonController.h" #include "Document.h" #include "FixedTableLayout.h" #include "FrameView.h" #include "HitTestResult.h" #include "HTMLNames.h" +#include "HTMLTableElement.h" #include "LayoutRepainter.h" #include "RenderLayer.h" #include "RenderTableCaption.h" @@ -42,6 +42,7 @@ #include "RenderTableSection.h" #include "RenderView.h" #include "StyleInheritedData.h" +#include <wtf/StackStats.h> using namespace std; @@ -49,8 +50,8 @@ namespace WebCore { using namespace HTMLNames; -RenderTable::RenderTable(Node* node) - : RenderBlock(node) +RenderTable::RenderTable(Element* element) + : RenderBlock(element) , m_head(0) , m_foot(0) , m_firstBody(0) @@ -113,10 +114,6 @@ static inline void resetSectionPointerIfNotBefore(RenderTableSection*& ptr, Rend void RenderTable::addChild(RenderObject* child, RenderObject* beforeChild) { - // Make sure we don't append things after :after-generated content if we have it. - if (!beforeChild) - beforeChild = afterPseudoElementRenderer(); - bool wrapInAnonymousSection = !child->isOutOfFlowPositioned(); if (child->isTableCaption()) @@ -159,6 +156,9 @@ void RenderTable::addChild(RenderObject* child, RenderObject* beforeChild) else wrapInAnonymousSection = true; + if (child->isTableSection()) + setNeedsSectionRecalc(); + if (!wrapInAnonymousSection) { if (beforeChild && beforeChild->parent() != this) beforeChild = splitAnonymousBoxesAroundChild(beforeChild); @@ -255,7 +255,7 @@ void RenderTable::updateLogicalWidth() LayoutUnit containerWidthInInlineDirection = hasPerpendicularContainingBlock ? perpendicularContainingBlockLogicalHeight() : availableLogicalWidth; Length styleLogicalWidth = style()->logicalWidth(); - if (styleLogicalWidth.isSpecified() && styleLogicalWidth.isPositive()) + if ((styleLogicalWidth.isSpecified() && styleLogicalWidth.isPositive()) || styleLogicalWidth.isIntrinsic()) setLogicalWidth(convertStyleLogicalWidthToComputedWidth(styleLogicalWidth, containerWidthInInlineDirection)); else { // Subtract out any fixed margins from our available width for auto width tables. @@ -267,7 +267,7 @@ void RenderTable::updateLogicalWidth() LayoutUnit availableContentLogicalWidth = max<LayoutUnit>(0, containerWidthInInlineDirection - marginTotal); if (shrinkToAvoidFloats() && cb->containsFloats() && !hasPerpendicularContainingBlock) { // FIXME: Work with regions someday. - availableContentLogicalWidth = shrinkLogicalWidthToAvoidFloats(marginStart, marginEnd, cb, 0, 0); + availableContentLogicalWidth = shrinkLogicalWidthToAvoidFloats(marginStart, marginEnd, cb, 0); } // Ensure we aren't bigger than our available width. @@ -280,14 +280,14 @@ void RenderTable::updateLogicalWidth() // Ensure we aren't bigger than our max-width style. Length styleMaxLogicalWidth = style()->logicalMaxWidth(); - if (styleMaxLogicalWidth.isSpecified() && !styleMaxLogicalWidth.isNegative()) { + if ((styleMaxLogicalWidth.isSpecified() && !styleMaxLogicalWidth.isNegative()) || styleMaxLogicalWidth.isIntrinsic()) { LayoutUnit computedMaxLogicalWidth = convertStyleLogicalWidthToComputedWidth(styleMaxLogicalWidth, availableLogicalWidth); setLogicalWidth(min<int>(logicalWidth(), computedMaxLogicalWidth)); } // Ensure we aren't smaller than our min-width style. Length styleMinLogicalWidth = style()->logicalMinWidth(); - if (styleMinLogicalWidth.isSpecified() && !styleMinLogicalWidth.isNegative()) { + if ((styleMinLogicalWidth.isSpecified() && !styleMinLogicalWidth.isNegative()) || styleMinLogicalWidth.isIntrinsic()) { LayoutUnit computedMinLogicalWidth = convertStyleLogicalWidthToComputedWidth(styleMinLogicalWidth, availableLogicalWidth); setLogicalWidth(max<int>(logicalWidth(), computedMinLogicalWidth)); } @@ -298,7 +298,7 @@ void RenderTable::updateLogicalWidth() if (!hasPerpendicularContainingBlock) { LayoutUnit containerLogicalWidthForAutoMargins = availableLogicalWidth; if (avoidsFloats() && cb->containsFloats()) - containerLogicalWidthForAutoMargins = containingBlockAvailableLineWidthInRegion(0, 0); // FIXME: Work with regions someday. + containerLogicalWidthForAutoMargins = containingBlockAvailableLineWidthInRegion(0); // FIXME: Work with regions someday. ComputedMarginValues marginValues; bool hasInvertedDirection = cb->style()->isLeftToRightDirection() == style()->isLeftToRightDirection(); computeInlineDirectionMargins(cb, containerLogicalWidthForAutoMargins, logicalWidth(), @@ -315,14 +315,15 @@ void RenderTable::updateLogicalWidth() // This method takes a RenderStyle's logical width, min-width, or max-width length and computes its actual value. LayoutUnit RenderTable::convertStyleLogicalWidthToComputedWidth(const Length& styleLogicalWidth, LayoutUnit availableWidth) { + if (styleLogicalWidth.isIntrinsic()) + return computeIntrinsicLogicalWidthUsing(styleLogicalWidth, availableWidth, bordersPaddingAndSpacingInRowDirection()); + // HTML tables' width styles already include borders and paddings, but CSS tables' width styles do not. LayoutUnit borders = 0; - bool isCSSTable = !node() || !node()->hasTagName(tableTag); - if (isCSSTable && styleLogicalWidth.isSpecified() && styleLogicalWidth.isPositive()) { - recalcBordersInRowDirection(); - if (style()->boxSizing() == CONTENT_BOX) - borders = borderStart() + borderEnd() + (collapseBorders() ? LayoutUnit() : paddingStart() + paddingEnd()); - } + bool isCSSTable = !node() || !isHTMLTableElement(node()); + if (isCSSTable && styleLogicalWidth.isSpecified() && styleLogicalWidth.isPositive() && style()->boxSizing() == CONTENT_BOX) + borders = borderStart() + borderEnd() + (collapseBorders() ? LayoutUnit() : paddingStart() + paddingEnd()); + return minimumValueForLength(styleLogicalWidth, availableWidth, view()) + borders; } @@ -333,7 +334,7 @@ LayoutUnit RenderTable::convertStyleLogicalHeightToComputedHeight(const Length& // HTML tables size as though CSS height includes border/padding, CSS tables do not. LayoutUnit borders = LayoutUnit(); // FIXME: We cannot apply box-sizing: content-box on <table> which other browsers allow. - if ((node() && node()->hasTagName(tableTag)) || style()->boxSizing() == BORDER_BOX) { + if ((node() && isHTMLTableElement(node())) || style()->boxSizing() == BORDER_BOX) { LayoutUnit borderAndPaddingBefore = borderBefore() + (collapseBorders() ? LayoutUnit() : paddingBefore()); LayoutUnit borderAndPaddingAfter = borderAfter() + (collapseBorders() ? LayoutUnit() : paddingAfter()); borders = borderAndPaddingBefore + borderAndPaddingAfter; @@ -382,6 +383,14 @@ void RenderTable::distributeExtraLogicalHeight(int extraLogicalHeight) // ASSERT(!topSection() || !extraLogicalHeight); } +void RenderTable::simplifiedNormalFlowLayout() +{ + for (RenderTableSection* section = topSection(); section; section = sectionBelow(section)) { + section->layoutIfNeeded(); + section->computeOverflowFromCells(); + } +} + void RenderTable::layout() { StackStats::LayoutCheckPoint layoutCheckPoint; @@ -391,12 +400,14 @@ void RenderTable::layout() return; recalcSectionsIfNeeded(); + // FIXME: We should do this recalc lazily in borderStart/borderEnd so that we don't have to make sure + // to call this before we call borderStart/borderEnd to avoid getting a stale value. + recalcBordersInRowDirection(); LayoutRepainter repainter(*this, checkForRepaintDuringLayout()); - LayoutStateMaintainer statePusher(view(), this, locationOffset(), style()->isFlippedBlocksWritingMode()); + LayoutStateMaintainer statePusher(view(), this, locationOffset(), hasTransform() || hasReflection() || style()->isFlippedBlocksWritingMode()); setLogicalHeight(0); - m_overflow.clear(); initMaxMarginValues(); @@ -643,7 +654,7 @@ void RenderTable::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffs PaintInfo info(paintInfo); info.phase = paintPhase; - info.updatePaintingRootForChildren(this); + info.updateSubtreePaintRootForChildren(this); for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { if (child->isBox() && !toRenderBox(child)->hasSelfPaintingLayer() && (child->isTableSection() || child->isTableCaption())) { @@ -670,7 +681,7 @@ void RenderTable::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffs // Paint outline. if ((paintPhase == PaintPhaseOutline || paintPhase == PaintPhaseSelfOutline) && hasOutline() && style()->visibility() == VISIBLE) - paintOutline(paintInfo.context, LayoutRect(paintOffset, size())); + paintOutline(paintInfo, LayoutRect(paintOffset, size())); } void RenderTable::subtractCaptionRect(LayoutRect& rect) const @@ -698,9 +709,10 @@ void RenderTable::paintBoxDecorations(PaintInfo& paintInfo, const LayoutPoint& p LayoutRect rect(paintOffset, size()); subtractCaptionRect(rect); - if (!boxShadowShouldBeAppliedToBackground(determineBackgroundBleedAvoidance(paintInfo.context))) + BackgroundBleedAvoidance bleedAvoidance = determineBackgroundBleedAvoidance(paintInfo.context); + if (!boxShadowShouldBeAppliedToBackground(bleedAvoidance)) paintBoxShadow(paintInfo, rect, style(), Normal); - paintBackground(paintInfo, rect); + paintBackground(paintInfo, rect, bleedAvoidance); paintBoxShadow(paintInfo, rect, style(), Inset); if (style()->hasBorder() && !collapseBorders()) @@ -718,18 +730,49 @@ void RenderTable::paintMask(PaintInfo& paintInfo, const LayoutPoint& paintOffset paintMaskImages(paintInfo, rect); } +void RenderTable::computeIntrinsicLogicalWidths(LayoutUnit& minWidth, LayoutUnit& maxWidth) const +{ + recalcSectionsIfNeeded(); + // FIXME: Do the recalc in borderStart/borderEnd and make those const_cast this call. + // Then m_borderStart/m_borderEnd will be transparent a cache and it removes the possibility + // of reading out stale values. + const_cast<RenderTable*>(this)->recalcBordersInRowDirection(); + // FIXME: Restructure the table layout code so that we can make this method const. + const_cast<RenderTable*>(this)->m_tableLayout->computeIntrinsicLogicalWidths(minWidth, maxWidth); + + // FIXME: We should include captions widths here like we do in computePreferredLogicalWidths. +} + void RenderTable::computePreferredLogicalWidths() { ASSERT(preferredLogicalWidthsDirty()); - recalcSectionsIfNeeded(); - recalcBordersInRowDirection(); + computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth); + + int bordersPaddingAndSpacing = bordersPaddingAndSpacingInRowDirection(); + m_minPreferredLogicalWidth += bordersPaddingAndSpacing; + m_maxPreferredLogicalWidth += bordersPaddingAndSpacing; - m_tableLayout->computePreferredLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth); + m_tableLayout->applyPreferredLogicalWidthQuirks(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth); for (unsigned i = 0; i < m_captions.size(); i++) m_minPreferredLogicalWidth = max(m_minPreferredLogicalWidth, m_captions[i]->minPreferredLogicalWidth()); + RenderStyle* styleToUse = style(); + // FIXME: This should probably be checking for isSpecified since you should be able to use percentage, calc or viewport relative values for min-width. + if (styleToUse->logicalMinWidth().isFixed() && styleToUse->logicalMinWidth().value() > 0) { + m_maxPreferredLogicalWidth = std::max(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMinWidth().value())); + m_minPreferredLogicalWidth = std::max(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMinWidth().value())); + } + + // FIXME: This should probably be checking for isSpecified since you should be able to use percentage, calc or viewport relative values for maxWidth. + if (styleToUse->logicalMaxWidth().isFixed()) { + m_maxPreferredLogicalWidth = std::min(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMaxWidth().value())); + m_minPreferredLogicalWidth = std::min(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMaxWidth().value())); + } + + // FIXME: We should be adding borderAndPaddingLogicalWidth here, but m_tableLayout->computePreferredLogicalWidths already does, + // so a bunch of tests break doing this naively. setPreferredLogicalWidthsDirty(false); } @@ -762,7 +805,6 @@ void RenderTable::splitColumn(unsigned position, unsigned firstSpan) } m_columnPos.grow(numEffCols() + 1); - setNeedsLayoutAndPrefWidthsRecalc(); } void RenderTable::appendColumn(unsigned span) @@ -784,7 +826,6 @@ void RenderTable::appendColumn(unsigned span) } m_columnPos.grow(numEffCols() + 1); - setNeedsLayoutAndPrefWidthsRecalc(); } RenderTableCol* RenderTable::firstColumn() const @@ -1314,9 +1355,16 @@ int RenderTable::firstLineBoxBaseline() const return -1; } -LayoutRect RenderTable::overflowClipRect(const LayoutPoint& location, RenderRegion* region, OverlayScrollbarSizeRelevancy relevancy) +LayoutRect RenderTable::overflowClipRect(const LayoutPoint& location, RenderRegion* region, OverlayScrollbarSizeRelevancy relevancy, PaintPhase phase) { - LayoutRect rect = RenderBlock::overflowClipRect(location, region, relevancy); + LayoutRect rect; + // Don't clip out the table's side of the collapsed borders if we're in the paint phase that will ask the sections to paint them. + // Likewise, if we're self-painting we avoid clipping them out as the clip rect that will be passed down to child layers from RenderLayer will do that instead. + if (phase == PaintPhaseChildBlockBackgrounds || layer()->isSelfPaintingLayer()) { + rect = borderBoxRectInRegion(region); + rect.setLocation(location + rect.location()); + } else + rect = RenderBox::overflowClipRect(location, region, relevancy); // If we have a caption, expand the clip to include the caption. // FIXME: Technically this is wrong, but it's virtually impossible to fix this @@ -1368,7 +1416,8 @@ bool RenderTable::nodeAtPoint(const HitTestRequest& request, HitTestResult& resu RenderTable* RenderTable::createAnonymousWithParentRenderer(const RenderObject* parent) { RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyleWithDisplay(parent->style(), TABLE); - RenderTable* newTable = new (parent->renderArena()) RenderTable(parent->document() /* is anonymous */); + RenderTable* newTable = new (parent->renderArena()) RenderTable(0); + newTable->setDocumentForAnonymous(parent->document()); newTable->setStyle(newStyle.release()); return newTable; } diff --git a/Source/WebCore/rendering/RenderTable.h b/Source/WebCore/rendering/RenderTable.h index 55a450957..85a807a0f 100644 --- a/Source/WebCore/rendering/RenderTable.h +++ b/Source/WebCore/rendering/RenderTable.h @@ -42,7 +42,7 @@ enum SkipEmptySectionsValue { DoNotSkipEmptySections, SkipEmptySections }; class RenderTable : public RenderBlock { public: - explicit RenderTable(Node*); + explicit RenderTable(Element*); virtual ~RenderTable(); // Per CSS 3 writing-mode: "The first and second values of the 'border-spacing' property represent spacing between columns @@ -194,6 +194,10 @@ public: return 0; } + // Override paddingStart/End to return pixel values to match behavor of RenderTableCell. + virtual LayoutUnit paddingEnd() const OVERRIDE { return static_cast<int>(RenderBlock::paddingEnd()); } + virtual LayoutUnit paddingStart() const OVERRIDE { return static_cast<int>(RenderBlock::paddingStart()); } + LayoutUnit bordersPaddingAndSpacingInRowDirection() const { // 'border-spacing' only applies to separate borders (see 17.6.1 The separated borders model). @@ -260,6 +264,7 @@ public: protected: virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); + virtual void simplifiedNormalFlowLayout(); private: virtual const char* renderName() const { return "RenderTable"; } @@ -273,7 +278,8 @@ private: virtual void paintBoxDecorations(PaintInfo&, const LayoutPoint&); virtual void paintMask(PaintInfo&, const LayoutPoint&); virtual void layout(); - virtual void computePreferredLogicalWidths(); + virtual void computeIntrinsicLogicalWidths(LayoutUnit& minWidth, LayoutUnit& maxWidth) const OVERRIDE; + virtual void computePreferredLogicalWidths() OVERRIDE; virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction) OVERRIDE; virtual int baselinePosition(FontBaseline, bool firstLine, LineDirectionMode, LinePositionMode = PositionOnContainingLine) const OVERRIDE; @@ -293,7 +299,8 @@ private: LayoutUnit convertStyleLogicalWidthToComputedWidth(const Length& styleLogicalWidth, LayoutUnit availableWidth); LayoutUnit convertStyleLogicalHeightToComputedHeight(const Length& styleLogicalHeight); - virtual LayoutRect overflowClipRect(const LayoutPoint& location, RenderRegion*, OverlayScrollbarSizeRelevancy = IgnoreOverlayScrollbarSize); + virtual LayoutRect overflowClipRect(const LayoutPoint& location, RenderRegion*, OverlayScrollbarSizeRelevancy = IgnoreOverlayScrollbarSize, PaintPhase = PaintPhaseBlockBackground); + virtual LayoutRect overflowClipRectForChildLayers(const LayoutPoint& location, RenderRegion* region, OverlayScrollbarSizeRelevancy relevancy) { return RenderBox::overflowClipRect(location, region, relevancy); } virtual void addOverflowFromChildren(); @@ -344,13 +351,13 @@ inline RenderTableSection* RenderTable::topSection() const inline RenderTable* toRenderTable(RenderObject* object) { - ASSERT(!object || object->isTable()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isTable()); return static_cast<RenderTable*>(object); } inline const RenderTable* toRenderTable(const RenderObject* object) { - ASSERT(!object || object->isTable()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isTable()); return static_cast<const RenderTable*>(object); } diff --git a/Source/WebCore/rendering/RenderTableCaption.cpp b/Source/WebCore/rendering/RenderTableCaption.cpp index df5194c97..76486f975 100644 --- a/Source/WebCore/rendering/RenderTableCaption.cpp +++ b/Source/WebCore/rendering/RenderTableCaption.cpp @@ -24,8 +24,8 @@ namespace WebCore { -RenderTableCaption::RenderTableCaption(Node* node) - : RenderBlock(node) +RenderTableCaption::RenderTableCaption(Element* element) + : RenderBlock(element) { } diff --git a/Source/WebCore/rendering/RenderTableCaption.h b/Source/WebCore/rendering/RenderTableCaption.h index 91edb9f6e..3fb51db33 100644 --- a/Source/WebCore/rendering/RenderTableCaption.h +++ b/Source/WebCore/rendering/RenderTableCaption.h @@ -28,7 +28,7 @@ class RenderTable; class RenderTableCaption : public RenderBlock { public: - explicit RenderTableCaption(Node*); + explicit RenderTableCaption(Element*); virtual ~RenderTableCaption(); virtual LayoutUnit containingBlockLogicalWidthForContent() const OVERRIDE; @@ -43,13 +43,13 @@ private: inline RenderTableCaption* toRenderTableCaption(RenderObject* object) { - ASSERT(!object || object->isTableCaption()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isTableCaption()); return static_cast<RenderTableCaption*>(object); } inline const RenderTableCaption* toRenderTableCaption(const RenderObject* object) { - ASSERT(!object || object->isTableCaption()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isTableCaption()); return static_cast<const RenderTableCaption*>(object); } diff --git a/Source/WebCore/rendering/RenderTableCell.cpp b/Source/WebCore/rendering/RenderTableCell.cpp index aa3ad197f..9b0d57cd3 100644 --- a/Source/WebCore/rendering/RenderTableCell.cpp +++ b/Source/WebCore/rendering/RenderTableCell.cpp @@ -33,8 +33,9 @@ #include "PaintInfo.h" #include "RenderTableCol.h" #include "RenderView.h" -#include "StyleInheritedData.h" +#include "StylePropertySet.h" #include "TransformState.h" +#include <wtf/StackStats.h> #if ENABLE(MATHML) #include "MathMLElement.h" @@ -55,8 +56,8 @@ struct SameSizeAsRenderTableCell : public RenderBlock { COMPILE_ASSERT(sizeof(RenderTableCell) == sizeof(SameSizeAsRenderTableCell), RenderTableCell_should_stay_small); COMPILE_ASSERT(sizeof(CollapsedBorderValue) == 8, CollapsedBorderValue_should_stay_small); -RenderTableCell::RenderTableCell(Node* node) - : RenderBlock(node) +RenderTableCell::RenderTableCell(Element* element) + : RenderBlock(element) , m_column(unsetColumnIndex) , m_cellWidthChanged(false) , m_intrinsicPaddingBefore(0) @@ -79,10 +80,10 @@ unsigned RenderTableCell::parseColSpanFromDOM() const { ASSERT(node()); if (node()->hasTagName(tdTag) || node()->hasTagName(thTag)) - return toHTMLTableCellElement(node())->colSpan(); + return min<unsigned>(toHTMLTableCellElement(node())->colSpan(), maxColumnIndex); #if ENABLE(MATHML) if (node()->hasTagName(MathMLNames::mtdTag)) - return toMathMLElement(node())->colSpan(); + return min<unsigned>(toMathMLElement(node())->colSpan(), maxColumnIndex); #endif return 1; } @@ -91,10 +92,10 @@ unsigned RenderTableCell::parseRowSpanFromDOM() const { ASSERT(node()); if (node()->hasTagName(tdTag) || node()->hasTagName(thTag)) - return toHTMLTableCellElement(node())->rowSpan(); + return min<unsigned>(toHTMLTableCellElement(node())->rowSpan(), maxRowIndex); #if ENABLE(MATHML) if (node()->hasTagName(MathMLNames::mtdTag)) - return toMathMLElement(node())->rowSpan(); + return min<unsigned>(toMathMLElement(node())->rowSpan(), maxRowIndex); #endif return 1; } @@ -168,7 +169,7 @@ void RenderTableCell::computePreferredLogicalWidths() if (node() && style()->autoWrap()) { // See if nowrap was set. Length w = styleOrColLogicalWidth(); - String nowrap = static_cast<Element*>(node())->getAttribute(nowrapAttr); + String nowrap = toElement(node())->getAttribute(nowrapAttr); if (!nowrap.isNull() && w.isFixed()) // Nowrap is set, but we didn't actually use it because of the // fixed width set on the cell. Even so, it is a WinIE/Moz trait @@ -194,7 +195,7 @@ void RenderTableCell::computeIntrinsicPadding(int rowHeight) case LENGTH: case BASELINE: { LayoutUnit baseline = cellBaselinePosition(); - if (baseline > borderBefore() + paddingBefore()) + if (baseline > borderAndPaddingBefore()) intrinsicPaddingBefore = section()->rowBaseline(rowIndex()) - (baseline - oldIntrinsicPaddingBefore); break; } @@ -243,7 +244,21 @@ void RenderTableCell::layout() { StackStats::LayoutCheckPoint layoutCheckPoint; updateFirstLetter(); + + int oldCellBaseline = cellBaselinePosition(); layoutBlock(cellWidthChanged()); + + // If we have replaced content, the intrinsic height of our content may have changed since the last time we laid out. If that's the case the intrinsic padding we used + // for layout (the padding required to push the contents of the cell down to the row's baseline) is included in our new height and baseline and makes both + // of them wrong. So if our content's intrinsic height has changed push the new content up into the intrinsic padding and relayout so that the rest of + // table and row layout can use the correct baseline and height for this cell. + if (isBaselineAligned() && section()->rowBaseline(rowIndex()) && cellBaselinePosition() > section()->rowBaseline(rowIndex())) { + int newIntrinsicPaddingBefore = max<LayoutUnit>(0, intrinsicPaddingBefore() - max<LayoutUnit>(0, cellBaselinePosition() - oldCellBaseline)); + setIntrinsicPaddingBefore(newIntrinsicPaddingBefore); + setNeedsLayout(true, MarkOnlyThis); + layoutBlock(cellWidthChanged()); + } + setCellWidthChanged(false); } @@ -376,7 +391,7 @@ LayoutUnit RenderTableCell::cellBaselinePosition() const LayoutUnit firstLineBaseline = firstLineBoxBaseline(); if (firstLineBaseline != -1) return firstLineBaseline; - return paddingBefore() + borderBefore() + contentLogicalHeight(); + return borderAndPaddingBefore() + contentLogicalHeight(); } void RenderTableCell::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) @@ -390,6 +405,11 @@ void RenderTableCell::styleDidChange(StyleDifference diff, const RenderStyle* ol if (parent() && section() && oldStyle && style()->height() != oldStyle->height()) section()->rowLogicalHeightChanged(rowIndex()); + // Our intrinsic padding pushes us down to align with the baseline of other cells on the row. If our vertical-align + // has changed then so will the padding needed to align with other cells - clear it so we can recalculate it from scratch. + if (oldStyle && style()->verticalAlign() != oldStyle->verticalAlign()) + clearIntrinsicPadding(); + // If border was changed, notify table. if (parent()) { RenderTable* table = this->table(); @@ -1081,6 +1101,31 @@ void RenderTableCell::sortBorderValues(RenderTable::CollapsedBorderValues& borde compareBorderValuesForQSort); } +bool RenderTableCell::alignLeftRightBorderPaintRect(int& leftXOffset, int& rightXOffset) +{ + const RenderStyle* styleForTopCell = styleForCellFlow(); + int left = cachedCollapsedLeftBorder(styleForTopCell).width(); + int right = cachedCollapsedRightBorder(styleForTopCell).width(); + leftXOffset = max<int>(leftXOffset, left); + rightXOffset = max<int>(rightXOffset, right); + if (colSpan() > 1) + return false; + return true; +} + +bool RenderTableCell::alignTopBottomBorderPaintRect(int& topYOffset, int& bottomYOffset) +{ + const RenderStyle* styleForBottomCell = styleForCellFlow(); + int top = cachedCollapsedTopBorder(styleForBottomCell).width(); + int bottom = cachedCollapsedBottomBorder(styleForBottomCell).width(); + topYOffset = max<int>(topYOffset, top); + bottomYOffset = max<int>(bottomYOffset, bottom); + if (rowSpan() > 1) + return false; + return true; +} + + void RenderTableCell::paintCollapsedBorders(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { ASSERT(paintInfo.phase == PaintPhaseCollapsedTableBorders); @@ -1091,7 +1136,7 @@ void RenderTableCell::paintCollapsedBorders(PaintInfo& paintInfo, const LayoutPo LayoutRect localRepaintRect = paintInfo.rect; localRepaintRect.inflate(maximalOutlineSize(paintInfo.phase)); - LayoutRect paintRect = LayoutRect(paintOffset + location(), size()); + LayoutRect paintRect = LayoutRect(paintOffset + location(), pixelSnappedSize()); if (paintRect.y() - table()->outerBorderTop() >= localRepaintRect.maxY()) return; @@ -1114,35 +1159,80 @@ void RenderTableCell::paintCollapsedBorders(PaintInfo& paintInfo, const LayoutPo int leftWidth = leftVal.width(); int rightWidth = rightVal.width(); + int leftXOffsetTop = leftWidth; + int leftXOffsetBottom = leftWidth; + int rightXOffsetTop = rightWidth; + int rightXOffsetBottom = rightWidth; + int topYOffsetLeft = topWidth; + int topYOffsetRight = topWidth; + int bottomYOffsetLeft = bottomWidth; + int bottomYOffsetRight = bottomWidth; + + bool shouldDrawTopBorder = true; + bool shouldDrawLeftBorder = true; + bool shouldDrawRightBorder = true; + + if (RenderTableCell* top = table()->cellAbove(this)) { + shouldDrawTopBorder = top->alignLeftRightBorderPaintRect(leftXOffsetTop, rightXOffsetTop); + if (this->colSpan() > 1) + shouldDrawTopBorder = false; + } + + if (RenderTableCell* bottom = table()->cellBelow(this)) + bottom->alignLeftRightBorderPaintRect(leftXOffsetBottom, rightXOffsetBottom); + + if (RenderTableCell* left = table()->cellBefore(this)) + shouldDrawLeftBorder = left->alignTopBottomBorderPaintRect(topYOffsetLeft, bottomYOffsetLeft); + + if (RenderTableCell* right = table()->cellAfter(this)) + shouldDrawRightBorder = right->alignTopBottomBorderPaintRect(topYOffsetRight, bottomYOffsetRight); + + IntRect cellRect = pixelSnappedIntRect(paintRect.x(), paintRect.y(), paintRect.width(), paintRect.height()); + IntRect borderRect = pixelSnappedIntRect(paintRect.x() - leftWidth / 2, - paintRect.y() - topWidth / 2, - paintRect.width() + leftWidth / 2 + (rightWidth + 1) / 2, - paintRect.height() + topWidth / 2 + (bottomWidth + 1) / 2); + paintRect.y() - topWidth / 2, + paintRect.width() + leftWidth / 2 + (rightWidth + 1) / 2, + paintRect.height() + topWidth / 2 + (bottomWidth + 1) / 2); EBorderStyle topStyle = collapsedBorderStyle(topVal.style()); EBorderStyle bottomStyle = collapsedBorderStyle(bottomVal.style()); EBorderStyle leftStyle = collapsedBorderStyle(leftVal.style()); EBorderStyle rightStyle = collapsedBorderStyle(rightVal.style()); - bool renderTop = topStyle > BHIDDEN && !topVal.isTransparent(); + bool renderTop = topStyle > BHIDDEN && !topVal.isTransparent() && shouldDrawTopBorder; bool renderBottom = bottomStyle > BHIDDEN && !bottomVal.isTransparent(); - bool renderLeft = leftStyle > BHIDDEN && !leftVal.isTransparent(); - bool renderRight = rightStyle > BHIDDEN && !rightVal.isTransparent(); + bool renderLeft = leftStyle > BHIDDEN && !leftVal.isTransparent() && shouldDrawLeftBorder; + bool renderRight = rightStyle > BHIDDEN && !rightVal.isTransparent() && shouldDrawRightBorder; // We never paint diagonals at the joins. We simply let the border with the highest // precedence paint on top of borders with lower precedence. CollapsedBorders borders; - borders.addBorder(topVal, BSTop, renderTop, borderRect.x(), borderRect.y(), borderRect.maxX(), borderRect.y() + topWidth, topStyle); - borders.addBorder(bottomVal, BSBottom, renderBottom, borderRect.x(), borderRect.maxY() - bottomWidth, borderRect.maxX(), borderRect.maxY(), bottomStyle); - borders.addBorder(leftVal, BSLeft, renderLeft, borderRect.x(), borderRect.y(), borderRect.x() + leftWidth, borderRect.maxY(), leftStyle); - borders.addBorder(rightVal, BSRight, renderRight, borderRect.maxX() - rightWidth, borderRect.y(), borderRect.maxX(), borderRect.maxY(), rightStyle); + if (topVal.style() == DOTTED) + borders.addBorder(topVal, BSTop, renderTop, cellRect.x() - leftXOffsetTop / 2, cellRect.y() - topWidth / 2, cellRect.maxX() + rightXOffsetTop / 2, cellRect.y() + topWidth / 2 + topWidth % 2, topStyle); + else + borders.addBorder(topVal, BSTop, renderTop, borderRect.x(), borderRect.y(), borderRect.maxX(), borderRect.y() + topWidth, topStyle); + + if (bottomVal.style() == DOTTED) + borders.addBorder(bottomVal, BSBottom, renderBottom, cellRect.x() - leftXOffsetBottom / 2, cellRect.maxY() - bottomWidth / 2, cellRect.maxX() + rightXOffsetBottom / 2, cellRect.maxY() + bottomWidth / 2 + bottomWidth % 2, bottomStyle); + else + borders.addBorder(bottomVal, BSBottom, renderBottom, borderRect.x(), borderRect.maxY() - bottomWidth, borderRect.maxX(), borderRect.maxY(), bottomStyle); + + if (leftVal.style() == DOTTED) + borders.addBorder(leftVal, BSLeft, renderLeft, cellRect.x() - leftWidth / 2, cellRect.y() - topYOffsetLeft / 2, cellRect.x() + leftWidth / 2 + leftWidth % 2, cellRect.maxY() + bottomYOffsetLeft / 2 + bottomYOffsetLeft % 2, leftStyle); + else + borders.addBorder(leftVal, BSLeft, renderLeft, borderRect.x(), borderRect.y(), borderRect.x() + leftWidth, borderRect.maxY(), leftStyle); + + if (rightVal.style() == DOTTED) + borders.addBorder(rightVal, BSRight, renderRight, cellRect.maxX() - rightWidth / 2, cellRect.y() - topYOffsetRight / 2, cellRect.maxX() + rightWidth / 2 + rightWidth % 2, cellRect.maxY() + bottomYOffsetRight / 2 + bottomYOffsetRight % 2, rightStyle); + else + borders.addBorder(rightVal, BSRight, renderRight, borderRect.maxX() - rightWidth, borderRect.y(), borderRect.maxX(), borderRect.maxY(), rightStyle); bool antialias = shouldAntialiasLines(graphicsContext); for (CollapsedBorder* border = borders.nextBorder(); border; border = borders.nextBorder()) { if (border->borderValue.isSameIgnoringColor(*table()->currentBorderValue())) drawLineForBoxSide(graphicsContext, border->x1, border->y1, border->x2, border->y2, border->side, - border->borderValue.color(), border->style, 0, 0, antialias); + border->borderValue.color(), border->style, 0, 0, antialias); } } @@ -1178,7 +1268,7 @@ void RenderTableCell::paintBackgroundsBehindCell(PaintInfo& paintInfo, const Lay width() - borderLeft() - borderRight(), height() - borderTop() - borderBottom()); paintInfo.context->clip(clipRect); } - paintFillLayers(paintInfo, c, bgLayer, LayoutRect(adjustedPaintOffset, size()), BackgroundBleedNone, CompositeSourceOver, backgroundObject); + paintFillLayers(paintInfo, c, bgLayer, LayoutRect(adjustedPaintOffset, pixelSnappedSize()), BackgroundBleedNone, CompositeSourceOver, backgroundObject); } } @@ -1191,7 +1281,7 @@ void RenderTableCell::paintBoxDecorations(PaintInfo& paintInfo, const LayoutPoin if (!tableElt->collapseBorders() && style()->emptyCells() == HIDE && !firstChild()) return; - LayoutRect paintRect = LayoutRect(paintOffset, size()); + LayoutRect paintRect = LayoutRect(paintOffset, pixelSnappedSize()); paintBoxShadow(paintInfo, paintRect, style(), Normal); // Paint our cell background. @@ -1214,7 +1304,7 @@ void RenderTableCell::paintMask(PaintInfo& paintInfo, const LayoutPoint& paintOf if (!tableElt->collapseBorders() && style()->emptyCells() == HIDE && !firstChild()) return; - paintMaskImages(paintInfo, LayoutRect(paintOffset, size())); + paintMaskImages(paintInfo, LayoutRect(paintOffset, pixelSnappedSize())); } bool RenderTableCell::boxShadowShouldBeAppliedToBackground(BackgroundBleedAvoidance, InlineFlowBox*) const @@ -1246,10 +1336,17 @@ void RenderTableCell::scrollbarsChanged(bool horizontalScrollbarChanged, bool ve setIntrinsicPaddingAfter(intrinsicPaddingAfter() - scrollbarHeight); } +RenderTableCell* RenderTableCell::createAnonymous(Document* document) +{ + RenderTableCell* renderer = new (document->renderArena()) RenderTableCell(0); + renderer->setDocumentForAnonymous(document); + return renderer; +} + RenderTableCell* RenderTableCell::createAnonymousWithParentRenderer(const RenderObject* parent) { + RenderTableCell* newCell = RenderTableCell::createAnonymous(parent->document()); RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyleWithDisplay(parent->style(), TABLE_CELL); - RenderTableCell* newCell = new (parent->renderArena()) RenderTableCell(parent->document() /* is anonymous */); newCell->setStyle(newStyle.release()); return newCell; } diff --git a/Source/WebCore/rendering/RenderTableCell.h b/Source/WebCore/rendering/RenderTableCell.h index 2dd26a24d..720aa98c4 100644 --- a/Source/WebCore/rendering/RenderTableCell.h +++ b/Source/WebCore/rendering/RenderTableCell.h @@ -37,7 +37,7 @@ enum IncludeBorderColorOrNot { DoNotIncludeBorderColor, IncludeBorderColor }; class RenderTableCell : public RenderBlock { public: - explicit RenderTableCell(Node*); + explicit RenderTableCell(Element*); unsigned colSpan() const { @@ -90,19 +90,18 @@ public: return styleWidth; } - LayoutUnit logicalHeightForRowSizing() const + int logicalHeightForRowSizing() const { // FIXME: This function does too much work, and is very hot during table layout! - LayoutUnit adjustedLogicalHeight = logicalHeight() - (intrinsicPaddingBefore() + intrinsicPaddingAfter()); - LayoutUnit styleLogicalHeight = valueForLength(style()->logicalHeight(), 0, view()); + int adjustedLogicalHeight = pixelSnappedLogicalHeight() - (intrinsicPaddingBefore() + intrinsicPaddingAfter()); + int styleLogicalHeight = valueForLength(style()->logicalHeight(), 0, view()); // In strict mode, box-sizing: content-box do the right thing and actually add in the border and padding. // Call computedCSSPadding* directly to avoid including implicitPadding. if (!document()->inQuirksMode() && style()->boxSizing() != BORDER_BOX) - styleLogicalHeight += computedCSSPaddingBefore() + computedCSSPaddingAfter() + borderBefore() + borderAfter(); + styleLogicalHeight += (computedCSSPaddingBefore() + computedCSSPaddingAfter()).floor() + borderBefore() + borderAfter(); return max(styleLogicalHeight, adjustedLogicalHeight); } - virtual void computePreferredLogicalWidths(); void setCellLogicalWidth(int constrainedLogicalWidth); @@ -122,10 +121,17 @@ public: virtual void paint(PaintInfo&, const LayoutPoint&); + bool alignLeftRightBorderPaintRect(int& leftXOffset, int& rightXOffset); + bool alignTopBottomBorderPaintRect(int& topYOffset, int& bottomYOffset); void paintCollapsedBorders(PaintInfo&, const LayoutPoint&); void paintBackgroundsBehindCell(PaintInfo&, const LayoutPoint&, RenderObject* backgroundObject); LayoutUnit cellBaselinePosition() const; + bool isBaselineAligned() const + { + EVerticalAlign va = style()->verticalAlign(); + return va == BASELINE || va == TEXT_BOTTOM || va == TEXT_TOP || va == SUPER || va == SUB || va == LENGTH; + } void computeIntrinsicPadding(int rowHeight); void clearIntrinsicPadding() { setIntrinsicPadding(0, 0); } @@ -151,6 +157,7 @@ public: bool cellWidthChanged() const { return m_cellWidthChanged; } void setCellWidthChanged(bool b = true) { m_cellWidthChanged = b; } + static RenderTableCell* createAnonymous(Document*); static RenderTableCell* createAnonymousWithParentRenderer(const RenderObject*); virtual RenderBox* createAnonymousBoxWithSameTypeAs(const RenderObject* parent) const OVERRIDE { @@ -207,9 +214,10 @@ public: #endif protected: virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); + virtual void computePreferredLogicalWidths(); private: - virtual const char* renderName() const { return isAnonymous() ? "RenderTableCell (anonymous)" : "RenderTableCell"; } + virtual const char* renderName() const { return (isAnonymous() || isPseudoElement()) ? "RenderTableCell (anonymous)" : "RenderTableCell"; } virtual bool isTableCell() const { return true; } @@ -276,13 +284,13 @@ private: inline RenderTableCell* toRenderTableCell(RenderObject* object) { - ASSERT(!object || object->isTableCell()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isTableCell()); return static_cast<RenderTableCell*>(object); } inline const RenderTableCell* toRenderTableCell(const RenderObject* object) { - ASSERT(!object || object->isTableCell()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isTableCell()); return static_cast<const RenderTableCell*>(object); } diff --git a/Source/WebCore/rendering/RenderTableCol.cpp b/Source/WebCore/rendering/RenderTableCol.cpp index cc630cb11..23a06b966 100644 --- a/Source/WebCore/rendering/RenderTableCol.cpp +++ b/Source/WebCore/rendering/RenderTableCol.cpp @@ -26,7 +26,6 @@ #include "config.h" #include "RenderTableCol.h" -#include "CachedImage.h" #include "HTMLNames.h" #include "HTMLTableColElement.h" #include "RenderTable.h" @@ -36,8 +35,8 @@ namespace WebCore { using namespace HTMLNames; -RenderTableCol::RenderTableCol(Node* node) - : RenderBox(node) +RenderTableCol::RenderTableCol(Element* element) + : RenderBox(element) , m_span(1) { // init RenderObject attributes @@ -114,7 +113,7 @@ void RenderTableCol::imageChanged(WrappedImagePtr, const IntRect*) repaint(); } -void RenderTableCol::computePreferredLogicalWidths() +void RenderTableCol::clearPreferredLogicalWidthsDirtyBits() { setPreferredLogicalWidthsDirty(false); diff --git a/Source/WebCore/rendering/RenderTableCol.h b/Source/WebCore/rendering/RenderTableCol.h index a091a9399..c93b01922 100644 --- a/Source/WebCore/rendering/RenderTableCol.h +++ b/Source/WebCore/rendering/RenderTableCol.h @@ -35,7 +35,7 @@ class RenderTableCell; class RenderTableCol : public RenderBox { public: - explicit RenderTableCol(Node*); + explicit RenderTableCol(Element*); RenderObject* firstChild() const { ASSERT(children() == virtualChildren()); return children()->firstChild(); } RenderObject* lastChild() const { ASSERT(children() == virtualChildren()); return children()->lastChild(); } @@ -43,7 +43,7 @@ public: const RenderObjectChildList* children() const { return &m_children; } RenderObjectChildList* children() { return &m_children; } - virtual void computePreferredLogicalWidths(); + void clearPreferredLogicalWidthsDirtyBits(); unsigned span() const { return m_span; } void setSpan(unsigned span) { m_span = span; } @@ -83,6 +83,7 @@ private: virtual const char* renderName() const { return "RenderTableCol"; } virtual bool isRenderTableCol() const OVERRIDE { return true; } virtual void updateFromElement(); + virtual void computePreferredLogicalWidths() OVERRIDE { ASSERT_NOT_REACHED(); } virtual void insertedIntoTree() OVERRIDE; virtual void willBeRemovedFromTree() OVERRIDE; @@ -104,13 +105,13 @@ private: inline RenderTableCol* toRenderTableCol(RenderObject* object) { - ASSERT(!object || object->isRenderTableCol()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isRenderTableCol()); return static_cast<RenderTableCol*>(object); } inline const RenderTableCol* toRenderTableCol(const RenderObject* object) { - ASSERT(!object || object->isRenderTableCol()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isRenderTableCol()); return static_cast<const RenderTableCol*>(object); } diff --git a/Source/WebCore/rendering/RenderTableRow.cpp b/Source/WebCore/rendering/RenderTableRow.cpp index e00c9a64f..8dec058a2 100644 --- a/Source/WebCore/rendering/RenderTableRow.cpp +++ b/Source/WebCore/rendering/RenderTableRow.cpp @@ -25,7 +25,6 @@ #include "config.h" #include "RenderTableRow.h" -#include "CachedImage.h" #include "Document.h" #include "HTMLNames.h" #include "HitTestResult.h" @@ -33,13 +32,14 @@ #include "RenderTableCell.h" #include "RenderView.h" #include "StyleInheritedData.h" +#include <wtf/StackStats.h> namespace WebCore { using namespace HTMLNames; -RenderTableRow::RenderTableRow(Node* node) - : RenderBox(node) +RenderTableRow::RenderTableRow(Element* element) + : RenderBox(element) , m_rowIndex(unsetRowIndex) { // init RenderObject attributes @@ -53,12 +53,12 @@ void RenderTableRow::willBeRemovedFromTree() section()->setNeedsCellRecalc(); } -void RenderTableRow::updateBeforeAndAfterContent() +static bool borderWidthChanged(const RenderStyle* oldStyle, const RenderStyle* newStyle) { - if (!isAnonymous() && document()->styleSheetCollection()->usesBeforeAfterRules()) { - children()->updateBeforeAfterContent(this, BEFORE); - children()->updateBeforeAfterContent(this, AFTER); - } + return oldStyle->borderLeftWidth() != newStyle->borderLeftWidth() + || oldStyle->borderTopWidth() != newStyle->borderTopWidth() + || oldStyle->borderRightWidth() != newStyle->borderRightWidth() + || oldStyle->borderBottomWidth() != newStyle->borderBottomWidth(); } void RenderTableRow::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) @@ -68,9 +68,6 @@ void RenderTableRow::styleDidChange(StyleDifference diff, const RenderStyle* old RenderBox::styleDidChange(diff, oldStyle); propagateStyleToAnonymousChildren(); - if (parent()) - updateBeforeAndAfterContent(); - if (section() && oldStyle && style()->logicalHeight() != oldStyle->logicalHeight()) section()->rowLogicalHeightChanged(rowIndex()); @@ -79,6 +76,17 @@ void RenderTableRow::styleDidChange(StyleDifference diff, const RenderStyle* old RenderTable* table = this->table(); if (table && !table->selfNeedsLayout() && !table->normalChildNeedsLayout() && oldStyle && oldStyle->border() != style()->border()) table->invalidateCollapsedBorders(); + + if (table && oldStyle && diff == StyleDifferenceLayout && needsLayout() && table->collapseBorders() && borderWidthChanged(oldStyle, style())) { + // If the border width changes on a row, we need to make sure the cells in the row know to lay out again. + // This only happens when borders are collapsed, since they end up affecting the border sides of the cell + // itself. + for (RenderBox* childBox = firstChildBox(); childBox; childBox = childBox->nextSiblingBox()) { + if (!childBox->isTableCell()) + continue; + childBox->setChildNeedsLayout(true, MarkOnlyThis); + } + } } } @@ -98,10 +106,6 @@ const BorderValue& RenderTableRow::borderAdjoiningEndCell(const RenderTableCell* void RenderTableRow::addChild(RenderObject* child, RenderObject* beforeChild) { - // Make sure we don't append things after :after-generated content if we have it. - if (!beforeChild) - beforeChild = afterPseudoElementRenderer(); - if (!child->isTableCell()) { RenderObject* last = beforeChild; if (!last) @@ -155,7 +159,7 @@ void RenderTableRow::layout() ASSERT(needsLayout()); // Table rows do not add translation. - LayoutStateMaintainer statePusher(view(), this, LayoutSize(), style()->isFlippedBlocksWritingMode()); + LayoutStateMaintainer statePusher(view(), this, LayoutSize(), hasTransform() || hasReflection() || style()->isFlippedBlocksWritingMode()); bool paginated = view()->layoutState()->isPaginated(); @@ -233,7 +237,7 @@ void RenderTableRow::paintOutlineForRowIfNeeded(PaintInfo& paintInfo, const Layo LayoutPoint adjustedPaintOffset = paintOffset + location(); PaintPhase paintPhase = paintInfo.phase; if ((paintPhase == PaintPhaseOutline || paintPhase == PaintPhaseSelfOutline) && style()->visibility() == VISIBLE) - paintOutline(paintInfo.context, LayoutRect(adjustedPaintOffset, size())); + paintOutline(paintInfo, LayoutRect(adjustedPaintOffset, size())); } void RenderTableRow::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) @@ -260,10 +264,17 @@ void RenderTableRow::imageChanged(WrappedImagePtr, const IntRect*) repaint(); } +RenderTableRow* RenderTableRow::createAnonymous(Document* document) +{ + RenderTableRow* renderer = new (document->renderArena()) RenderTableRow(0); + renderer->setDocumentForAnonymous(document); + return renderer; +} + RenderTableRow* RenderTableRow::createAnonymousWithParentRenderer(const RenderObject* parent) { + RenderTableRow* newRow = RenderTableRow::createAnonymous(parent->document()); RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyleWithDisplay(parent->style(), TABLE_ROW); - RenderTableRow* newRow = new (parent->renderArena()) RenderTableRow(parent->document() /* is anonymous */); newRow->setStyle(newStyle.release()); return newRow; } diff --git a/Source/WebCore/rendering/RenderTableRow.h b/Source/WebCore/rendering/RenderTableRow.h index 6f195d304..d2bfbc6f6 100644 --- a/Source/WebCore/rendering/RenderTableRow.h +++ b/Source/WebCore/rendering/RenderTableRow.h @@ -34,7 +34,7 @@ static const unsigned maxRowIndex = 0x7FFFFFFE; // 2,147,483,646 class RenderTableRow : public RenderBox { public: - explicit RenderTableRow(Node*); + explicit RenderTableRow(Element*); RenderObject* firstChild() const { ASSERT(children() == virtualChildren()); return children()->firstChild(); } RenderObject* lastChild() const { ASSERT(children() == virtualChildren()); return children()->lastChild(); } @@ -45,9 +45,9 @@ public: RenderTableSection* section() const { return toRenderTableSection(parent()); } RenderTable* table() const { return toRenderTable(parent()->parent()); } - void updateBeforeAndAfterContent(); void paintOutlineForRowIfNeeded(PaintInfo&, const LayoutPoint&); + static RenderTableRow* createAnonymous(Document*); static RenderTableRow* createAnonymousWithParentRenderer(const RenderObject*); virtual RenderBox* createAnonymousBoxWithSameTypeAs(const RenderObject* parent) const OVERRIDE { @@ -92,7 +92,7 @@ private: virtual RenderObjectChildList* virtualChildren() { return children(); } virtual const RenderObjectChildList* virtualChildren() const { return children(); } - virtual const char* renderName() const { return isAnonymous() ? "RenderTableRow (anonymous)" : "RenderTableRow"; } + virtual const char* renderName() const { return (isAnonymous() || isPseudoElement()) ? "RenderTableRow (anonymous)" : "RenderTableRow"; } virtual bool isTableRow() const { return true; } @@ -117,13 +117,13 @@ private: inline RenderTableRow* toRenderTableRow(RenderObject* object) { - ASSERT(!object || object->isTableRow()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isTableRow()); return static_cast<RenderTableRow*>(object); } inline const RenderTableRow* toRenderTableRow(const RenderObject* object) { - ASSERT(!object || object->isTableRow()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isTableRow()); return static_cast<const RenderTableRow*>(object); } diff --git a/Source/WebCore/rendering/RenderTableSection.cpp b/Source/WebCore/rendering/RenderTableSection.cpp index 4bc186874..a415af583 100644 --- a/Source/WebCore/rendering/RenderTableSection.cpp +++ b/Source/WebCore/rendering/RenderTableSection.cpp @@ -25,7 +25,6 @@ #include "config.h" #include "RenderTableSection.h" -#include "CachedImage.h" #include "Document.h" #include "HitTestResult.h" #include "HTMLNames.h" @@ -37,7 +36,7 @@ #include "StyleInheritedData.h" #include <limits> #include <wtf/HashSet.h> -#include <wtf/Vector.h> +#include <wtf/StackStats.h> using namespace std; @@ -85,8 +84,8 @@ static inline void updateLogicalHeightForCell(RenderTableSection::RowStruct& row } -RenderTableSection::RenderTableSection(Node* node) - : RenderBox(node) +RenderTableSection::RenderTableSection(Element* element) + : RenderBox(element) , m_cCol(0) , m_cRow(0) , m_outerBorderStart(0) @@ -126,10 +125,6 @@ void RenderTableSection::willBeRemovedFromTree() void RenderTableSection::addChild(RenderObject* child, RenderObject* beforeChild) { - // Make sure we don't append things after :after-generated content if we have it. - if (!beforeChild) - beforeChild = afterPseudoElementRenderer(); - if (!child->isTableRow()) { RenderObject* last = beforeChild; if (!last) @@ -186,7 +181,6 @@ void RenderTableSection::addChild(RenderObject* child, RenderObject* beforeChild ASSERT(!beforeChild || beforeChild->isTableRow()); RenderBox::addChild(child, beforeChild); - toRenderTableRow(child)->updateBeforeAndAfterContent(); } void RenderTableSection::ensureRows(unsigned numRows) @@ -264,7 +258,7 @@ void RenderTableSection::addCell(RenderTableCell* cell, RenderTableRow* row) int RenderTableSection::calcRowLogicalHeight() { #ifndef NDEBUG - setNeedsLayoutIsForbidden(true); + SetLayoutNeededForbiddenScope layoutForbiddenScope(this); #endif ASSERT(!needsLayout()); @@ -279,7 +273,9 @@ int RenderTableSection::calcRowLogicalHeight() m_rowPos.resize(m_grid.size() + 1); m_rowPos[0] = spacing; - for (unsigned r = 0; r < m_grid.size(); r++) { + unsigned totalRows = m_grid.size(); + + for (unsigned r = 0; r < totalRows; r++) { m_grid[r].baseline = 0; LayoutUnit baselineDescent = 0; @@ -298,8 +294,26 @@ int RenderTableSection::calcRowLogicalHeight() // FIXME: We are always adding the height of a rowspan to the last rows which doesn't match // other browsers. See webkit.org/b/52185 for example. - if ((cell->rowIndex() + cell->rowSpan() - 1) != r) - continue; + if ((cell->rowIndex() + cell->rowSpan() - 1) != r) { + // We will apply the height of the rowspan to the current row if next row is not valid. + if ((r + 1) < totalRows) { + unsigned col = 0; + CellStruct nextRowCell = cellAt(r + 1, col); + + // We are trying to find that next row is valid or not. + while (nextRowCell.cells.size() && nextRowCell.cells[0]->rowSpan() > 1 && nextRowCell.cells[0]->rowIndex() < (r + 1)) { + col++; + if (col < totalCols) + nextRowCell = cellAt(r + 1, col); + else + break; + } + + // We are adding the height of the rowspan to the current row if next row is not valid. + if (col < totalCols && nextRowCell.cells.size()) + continue; + } + } // For row spanning cells, |r| is the last row in the span. unsigned cellStartRow = cell->rowIndex(); @@ -319,32 +333,30 @@ int RenderTableSection::calcRowLogicalHeight() int cellLogicalHeight = cell->logicalHeightForRowSizing(); m_rowPos[r + 1] = max(m_rowPos[r + 1], m_rowPos[cellStartRow] + cellLogicalHeight); - // find out the baseline - EVerticalAlign va = cell->style()->verticalAlign(); - if (va == BASELINE || va == TEXT_BOTTOM || va == TEXT_TOP || va == SUPER || va == SUB || va == LENGTH) { + // Find out the baseline. The baseline is set on the first row in a rowspan. + if (cell->isBaselineAligned()) { LayoutUnit baselinePosition = cell->cellBaselinePosition(); - if (baselinePosition > cell->borderBefore() + cell->paddingBefore()) { - m_grid[cellStartRow].baseline = max(m_grid[cellStartRow].baseline, baselinePosition - cell->intrinsicPaddingBefore()); - baselineDescent = max(baselineDescent, m_rowPos[cellStartRow] + cellLogicalHeight - (baselinePosition - cell->intrinsicPaddingBefore())); + if (baselinePosition > cell->borderAndPaddingBefore()) { + m_grid[cellStartRow].baseline = max(m_grid[cellStartRow].baseline, baselinePosition); + // The descent of a cell that spans multiple rows does not affect the height of the first row it spans, so don't let it + // become the baseline descent applied to the rest of the row. Also we don't account for the baseline descent of + // non-spanning cells when computing a spanning cell's extent. + int cellStartRowBaselineDescent = 0; + if (cell->rowSpan() == 1) { + baselineDescent = max(baselineDescent, cellLogicalHeight - (baselinePosition - cell->intrinsicPaddingBefore())); + cellStartRowBaselineDescent = baselineDescent; + } + m_rowPos[cellStartRow + 1] = max<int>(m_rowPos[cellStartRow + 1], m_rowPos[cellStartRow] + m_grid[cellStartRow].baseline + cellStartRowBaselineDescent); } } } } - // do we have baseline aligned elements? - if (m_grid[r].baseline) - // increase rowheight if baseline requires - m_rowPos[r + 1] = max<int>(m_rowPos[r + 1], m_grid[r].baseline + baselineDescent); - // Add the border-spacing to our final position. m_rowPos[r + 1] += m_grid[r].rowRenderer ? spacing : 0; m_rowPos[r + 1] = max(m_rowPos[r + 1], m_rowPos[r]); } -#ifndef NDEBUG - setNeedsLayoutIsForbidden(false); -#endif - ASSERT(!needsLayout()); statePusher.pop(); @@ -363,7 +375,7 @@ void RenderTableSection::layout() // can be called in a loop (e.g during parsing). Doing it now ensures we have a stable-enough structure. m_grid.shrinkToFit(); - LayoutStateMaintainer statePusher(view(), this, locationOffset(), style()->isFlippedBlocksWritingMode()); + LayoutStateMaintainer statePusher(view(), this, locationOffset(), hasTransform() || hasReflection() || style()->isFlippedBlocksWritingMode()); const Vector<int>& columnPos = table()->columnPositions(); @@ -494,7 +506,7 @@ int RenderTableSection::distributeExtraLogicalHeightToRows(int extraLogicalHeigh void RenderTableSection::layoutRows() { #ifndef NDEBUG - setNeedsLayoutIsForbidden(true); + SetLayoutNeededForbiddenScope layoutForbiddenScope(this); #endif ASSERT(!needsLayout()); @@ -503,14 +515,12 @@ void RenderTableSection::layoutRows() // Set the width of our section now. The rows will also be this width. setLogicalWidth(table()->contentLogicalWidth()); - m_overflow.clear(); - m_overflowingCells.clear(); m_forceSlowPaintPathWithOverflowingCell = false; int vspacing = table()->vBorderSpacing(); unsigned nEffCols = table()->numEffCols(); - LayoutStateMaintainer statePusher(view(), this, locationOffset(), style()->isFlippedBlocksWritingMode()); + LayoutStateMaintainer statePusher(view(), this, locationOffset(), hasTransform() || style()->isFlippedBlocksWritingMode()); for (unsigned r = 0; r < totalRows; r++) { // Set the row's x/y position and width/height. @@ -551,7 +561,7 @@ void RenderTableSection::layoutRows() || (!table()->style()->logicalHeight().isAuto() && rHeight != cell->logicalHeight()); for (RenderObject* o = cell->firstChild(); o; o = o->nextSibling()) { - if (!o->isText() && o->style()->logicalHeight().isPercent() && (flexAllChildren || o->isReplaced() || (o->isBox() && toRenderBox(o)->scrollsOverflow()))) { + if (!o->isText() && o->style()->logicalHeight().isPercent() && (flexAllChildren || ((o->isReplaced() || (o->isBox() && toRenderBox(o)->scrollsOverflow())) && !o->isTextControl()))) { // Tables with no sections do not flex. if (!o->isTable() || toRenderTable(o)->hasSections()) { o->setNeedsLayout(true, MarkOnlyThis); @@ -589,10 +599,9 @@ void RenderTableSection::layoutRows() cell->layoutIfNeeded(); // If the baseline moved, we may have to update the data for our row. Find out the new baseline. - EVerticalAlign va = cell->style()->verticalAlign(); - if (va == BASELINE || va == TEXT_BOTTOM || va == TEXT_TOP || va == SUPER || va == SUB || va == LENGTH) { + if (cell->isBaselineAligned()) { LayoutUnit baseline = cell->cellBaselinePosition(); - if (baseline > cell->borderBefore() + cell->paddingBefore()) + if (baseline > cell->borderAndPaddingBefore()) m_grid[r].baseline = max(m_grid[r].baseline, baseline); } } @@ -640,14 +649,26 @@ void RenderTableSection::layoutRows() } } -#ifndef NDEBUG - setNeedsLayoutIsForbidden(false); -#endif - ASSERT(!needsLayout()); setLogicalHeight(m_rowPos[totalRows]); + computeOverflowFromCells(totalRows, nEffCols); + + statePusher.pop(); +} + +void RenderTableSection::computeOverflowFromCells() +{ + unsigned totalRows = m_grid.size(); + unsigned nEffCols = table()->numEffCols(); + computeOverflowFromCells(totalRows, nEffCols); +} + +void RenderTableSection::computeOverflowFromCells(unsigned totalRows, unsigned nEffCols) +{ + m_overflow.clear(); + m_overflowingCells.clear(); unsigned totalCellsCount = nEffCols * totalRows; int maxAllowedOverflowingCellsCount = totalCellsCount < gMinTableSizeToUseFastPaintPathWithOverflowingCell ? 0 : gMaxAllowedOverflowingCellRatioForFastPaintPath * totalCellsCount; @@ -680,8 +701,6 @@ void RenderTableSection::layoutRows() } ASSERT(hasOverflowingCell == this->hasOverflowingCell()); - - statePusher.pop(); } int RenderTableSection::calcOuterBorderBefore() const @@ -898,7 +917,7 @@ int RenderTableSection::firstLineBoxBaseline() const const RenderTableCell* cell = cs.primaryCell(); // Only cells with content have a baseline if (cell && cell->contentLogicalHeight()) - firstLineBaseline = max<int>(firstLineBaseline, cell->logicalTop() + cell->paddingBefore() + cell->borderBefore() + cell->contentLogicalHeight()); + firstLineBaseline = max<int>(firstLineBaseline, cell->logicalTop() + cell->borderAndPaddingBefore() + cell->contentLogicalHeight()); } return firstLineBaseline; @@ -927,7 +946,7 @@ void RenderTableSection::paint(PaintInfo& paintInfo, const LayoutPoint& paintOff popContentsClip(paintInfo, phase, adjustedPaintOffset); if ((phase == PaintPhaseOutline || phase == PaintPhaseSelfOutline) && style()->visibility() == VISIBLE) - paintOutline(paintInfo.context, LayoutRect(adjustedPaintOffset, size())); + paintOutline(paintInfo, LayoutRect(adjustedPaintOffset, size())); } static inline bool compareCellPositions(RenderTableCell* elem1, RenderTableCell* elem2) @@ -1081,9 +1100,7 @@ CellSpan RenderTableSection::spannedColumns(const LayoutRect& flippedRect) const return CellSpan(startColumn, endColumn); } -#if defined(_MSC_VER) && _MSC_VER == 1700 -#pragma optimize("", off) -#endif + void RenderTableSection::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { PaintPhase paintPhase = paintInfo.phase; @@ -1101,8 +1118,10 @@ void RenderTableSection::paintObject(PaintInfo& paintInfo, const LayoutPoint& pa if (!m_hasMultipleCellLevels && !m_overflowingCells.size()) { if (paintInfo.phase == PaintPhaseCollapsedTableBorders) { // Collapsed borders are painted from the bottom right to the top left so that precedence - // due to cell position is respected. - for (unsigned r = dirtiedRows.end(); r > dirtiedRows.start(); r--) { + // due to cell position is respected. We need to paint one row beyond the topmost dirtied + // row to calculate its collapsed border value. + unsigned startRow = dirtiedRows.start() ? dirtiedRows.start() - 1 : 0; + for (unsigned r = dirtiedRows.end(); r > startRow; r--) { unsigned row = r - 1; for (unsigned c = dirtiedColumns.end(); c > dirtiedColumns.start(); c--) { unsigned col = c - 1; @@ -1156,9 +1175,8 @@ void RenderTableSection::paintObject(PaintInfo& paintInfo, const LayoutPoint& pa continue; if (current.cells[i]->rowSpan() > 1 || current.cells[i]->colSpan() > 1) { - if (spanningCells.contains(current.cells[i])) + if (!spanningCells.add(current.cells[i]).isNewEntry) continue; - spanningCells.add(current.cells[i]); } cells.append(current.cells[i]); @@ -1184,9 +1202,6 @@ void RenderTableSection::paintObject(PaintInfo& paintInfo, const LayoutPoint& pa } } } -#if defined(_MSC_VER) && _MSC_VER == 1700 -#pragma optimize("", on) -#endif void RenderTableSection::imageChanged(WrappedImagePtr, const IntRect*) { @@ -1312,7 +1327,7 @@ void RenderTableSection::splitColumn(unsigned pos, unsigned first) Row& r = m_grid[row].row; r.insert(pos + 1, CellStruct()); if (r[pos].hasCells()) { - r[pos + 1].cells.append(r[pos].cells); + r[pos + 1].cells.appendVector(r[pos].cells); RenderTableCell* cell = r[pos].primaryCell(); ASSERT(cell); ASSERT(cell->colSpan() >= (r[pos].inColSpan ? 1u : 0)); @@ -1421,7 +1436,8 @@ CollapsedBorderValue& RenderTableSection::cachedCollapsedBorder(const RenderTabl RenderTableSection* RenderTableSection::createAnonymousWithParentRenderer(const RenderObject* parent) { RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyleWithDisplay(parent->style(), TABLE_ROW_GROUP); - RenderTableSection* newSection = new (parent->renderArena()) RenderTableSection(parent->document() /* is anonymous */); + RenderTableSection* newSection = new (parent->renderArena()) RenderTableSection(0); + newSection->setDocumentForAnonymous(parent->document()); newSection->setStyle(newStyle.release()); return newSection; } diff --git a/Source/WebCore/rendering/RenderTableSection.h b/Source/WebCore/rendering/RenderTableSection.h index 79cdd4a1d..1e14b9a26 100644 --- a/Source/WebCore/rendering/RenderTableSection.h +++ b/Source/WebCore/rendering/RenderTableSection.h @@ -62,7 +62,7 @@ class RenderTableRow; class RenderTableSection : public RenderBox { public: - RenderTableSection(Node*); + RenderTableSection(Element*); virtual ~RenderTableSection(); RenderObject* firstChild() const { ASSERT(children() == virtualChildren()); return children()->firstChild(); } @@ -79,6 +79,7 @@ public: int calcRowLogicalHeight(); void layoutRows(); + void computeOverflowFromCells(); RenderTable* table() const { return toRenderTable(parent()); } @@ -149,6 +150,8 @@ public: return c.primaryCell(); } + RenderTableRow* rowRendererAt(unsigned row) const { return m_grid[row].rowRenderer; } + void appendColumn(unsigned pos); void splitColumn(unsigned pos, unsigned first); @@ -202,7 +205,7 @@ private: virtual RenderObjectChildList* virtualChildren() { return children(); } virtual const RenderObjectChildList* virtualChildren() const { return children(); } - virtual const char* renderName() const { return isAnonymous() ? "RenderTableSection (anonymous)" : "RenderTableSection"; } + virtual const char* renderName() const { return (isAnonymous() || isPseudoElement()) ? "RenderTableSection (anonymous)" : "RenderTableSection"; } virtual bool isTableSection() const { return true; } @@ -224,6 +227,7 @@ private: void distributeRemainingExtraLogicalHeight(int& extraLogicalHeight); bool hasOverflowingCell() const { return m_overflowingCells.size() || m_forceSlowPaintPathWithOverflowingCell; } + void computeOverflowFromCells(unsigned totalRows, unsigned nEffCols); CellSpan fullTableRowSpan() const { return CellSpan(0, m_grid.size()); } CellSpan fullTableColumnSpan() const { return CellSpan(0, table()->columns().size()); } @@ -272,13 +276,13 @@ private: inline RenderTableSection* toRenderTableSection(RenderObject* object) { - ASSERT(!object || object->isTableSection()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isTableSection()); return static_cast<RenderTableSection*>(object); } inline const RenderTableSection* toRenderTableSection(const RenderObject* object) { - ASSERT(!object || object->isTableSection()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isTableSection()); return static_cast<const RenderTableSection*>(object); } diff --git a/Source/WebCore/rendering/RenderText.cpp b/Source/WebCore/rendering/RenderText.cpp index a9a25df63..cc7607f74 100644 --- a/Source/WebCore/rendering/RenderText.cpp +++ b/Source/WebCore/rendering/RenderText.cpp @@ -44,7 +44,6 @@ #include "TextResourceDecoder.h" #include "VisiblePosition.h" #include "break_lines.h" -#include <wtf/AlwaysInline.h> #include <wtf/text/StringBuffer.h> #include <wtf/unicode/CharacterNames.h> @@ -58,7 +57,7 @@ struct SameSizeAsRenderText : public RenderObject { uint32_t bitfields : 16; float widths[4]; String text; - void* pointers[3]; + void* pointers[2]; }; COMPILE_ASSERT(sizeof(RenderText) == sizeof(SameSizeAsRenderText), RenderText_should_stay_small); @@ -101,7 +100,7 @@ static void makeCapitalized(String* string, UChar previous) return; unsigned length = string->length(); - const UChar* characters = string->characters(); + const StringImpl& stringImpl = *string->impl(); if (length >= numeric_limits<unsigned>::max()) CRASH(); @@ -110,52 +109,52 @@ static void makeCapitalized(String* string, UChar previous) stringWithPrevious[0] = previous == noBreakSpace ? ' ' : previous; for (unsigned i = 1; i < length + 1; i++) { // Replace   with a real space since ICU no longer treats   as a word separator. - if (characters[i - 1] == noBreakSpace) + if (stringImpl[i - 1] == noBreakSpace) stringWithPrevious[i] = ' '; else - stringWithPrevious[i] = characters[i - 1]; + stringWithPrevious[i] = stringImpl[i - 1]; } TextBreakIterator* boundary = wordBreakIterator(stringWithPrevious.characters(), length + 1); if (!boundary) return; - StringBuffer<UChar> data(length); + StringBuilder result; + result.reserveCapacity(length); int32_t endOfWord; int32_t startOfWord = textBreakFirst(boundary); for (endOfWord = textBreakNext(boundary); endOfWord != TextBreakDone; startOfWord = endOfWord, endOfWord = textBreakNext(boundary)) { if (startOfWord) // Ignore first char of previous string - data[startOfWord - 1] = characters[startOfWord - 1] == noBreakSpace ? noBreakSpace : toTitleCase(stringWithPrevious[startOfWord]); + result.append(stringImpl[startOfWord - 1] == noBreakSpace ? noBreakSpace : toTitleCase(stringWithPrevious[startOfWord])); for (int i = startOfWord + 1; i < endOfWord; i++) - data[i - 1] = characters[i - 1]; + result.append(stringImpl[i - 1]); } - *string = String::adopt(data); + *string = result.toString(); } RenderText::RenderText(Node* node, PassRefPtr<StringImpl> str) - : RenderObject(node) - , m_hasTab(false) - , m_linesDirty(false) - , m_containsReversedText(false) - , m_knownToHaveNoOverflowAndNoFallbackFonts(false) - , m_needsTranscoding(false) - , m_minWidth(-1) - , m_maxWidth(-1) - , m_beginMinWidth(0) - , m_endMinWidth(0) - , m_text(str) - , m_firstTextBox(0) - , m_lastTextBox(0) + : RenderObject(!node || node->isDocumentNode() ? 0 : node) + , m_hasTab(false) + , m_linesDirty(false) + , m_containsReversedText(false) + , m_knownToHaveNoOverflowAndNoFallbackFonts(false) + , m_needsTranscoding(false) + , m_minWidth(-1) + , m_maxWidth(-1) + , m_beginMinWidth(0) + , m_endMinWidth(0) + , m_text(str) + , m_firstTextBox(0) + , m_lastTextBox(0) { ASSERT(m_text); + // FIXME: Some clients of RenderText (and subclasses) pass Document as node to create anonymous renderer. + // They should be switched to passing null and using setDocumentForAnonymous. + if (node && node->isDocumentNode()) + setDocumentForAnonymous(toDocument(node)); - m_is8Bit = m_text.is8Bit(); - if (is8Bit()) - m_data.characters8 = m_text.characters8(); - else - m_data.characters16 = m_text.characters16(); m_isAllASCII = m_text.containsOnlyASCII(); m_canUseSimpleFontCodePath = computeCanUseSimpleFontCodePath(); setIsText(); @@ -210,7 +209,7 @@ void RenderText::styleDidChange(StyleDifference diff, const RenderStyle* oldStyl if (!oldStyle) { updateNeedsTranscoding(); needsResetText = m_needsTranscoding; - } else if (oldStyle->font().needsTranscoding() != newStyle->font().needsTranscoding() || (newStyle->font().needsTranscoding() && oldStyle->font().family().family() != newStyle->font().family().family())) { + } else if (oldStyle->font().needsTranscoding() != newStyle->font().needsTranscoding() || (newStyle->font().needsTranscoding() && oldStyle->font().firstFamily() != newStyle->font().firstFamily())) { updateNeedsTranscoding(); needsResetText = true; } @@ -332,10 +331,10 @@ static FloatRect localQuadForTextBox(InlineTextBox* box, unsigned start, unsigne // Change the height and y position (or width and x for vertical text) // because selectionRect uses selection-specific values. if (box->isHorizontal()) { - r.setHeight(box->logicalHeight()); + r.setHeight(box->height()); r.setY(box->y()); } else { - r.setWidth(box->logicalWidth()); + r.setWidth(box->width()); r.setX(box->x()); } } @@ -370,12 +369,12 @@ void RenderText::absoluteRectsForRange(Vector<IntRect>& rects, unsigned start, u r.setX(selectionRect.x()); } } - rects.append(localToAbsoluteQuad(r, SnapOffsetForTransforms, wasFixed).enclosingBoundingBox()); + rects.append(localToAbsoluteQuad(r, 0, wasFixed).enclosingBoundingBox()); } else { // FIXME: This code is wrong. It's converting local to absolute twice. http://webkit.org/b/65722 FloatRect rect = localQuadForTextBox(box, start, end, useSelectionHeight); if (!rect.isZero()) - rects.append(localToAbsoluteQuad(rect, SnapOffsetForTransforms, wasFixed).enclosingBoundingBox()); + rects.append(localToAbsoluteQuad(rect, 0, wasFixed).enclosingBoundingBox()); } } } @@ -418,7 +417,7 @@ void RenderText::absoluteQuads(Vector<FloatQuad>& quads, bool* wasFixed, Clippin else boundaries.setHeight(ellipsisRect.maxY() - boundaries.y()); } - quads.append(localToAbsoluteQuad(boundaries, SnapOffsetForTransforms, wasFixed)); + quads.append(localToAbsoluteQuad(boundaries, 0, wasFixed)); } } @@ -453,11 +452,11 @@ void RenderText::absoluteQuadsForRange(Vector<FloatQuad>& quads, unsigned start, r.setX(selectionRect.x()); } } - quads.append(localToAbsoluteQuad(r, SnapOffsetForTransforms, wasFixed)); + quads.append(localToAbsoluteQuad(r, 0, wasFixed)); } else { FloatRect rect = localQuadForTextBox(box, start, end, useSelectionHeight); if (!rect.isZero()) - quads.append(localToAbsoluteQuad(rect, SnapOffsetForTransforms, wasFixed)); + quads.append(localToAbsoluteQuad(rect, 0, wasFixed)); } } } @@ -895,7 +894,7 @@ static inline float hyphenWidth(RenderText* renderer, const Font& font) return font.width(RenderBlock::constructTextRun(renderer, font, style->hyphenString().string(), style)); } -static float maxWordFragmentWidth(RenderText* renderer, RenderStyle* style, const Font& font, const UChar* word, int wordLength, int minimumPrefixLength, int minimumSuffixLength, int& suffixStart) +static float maxWordFragmentWidth(RenderText* renderer, RenderStyle* style, const Font& font, const UChar* word, int wordLength, int minimumPrefixLength, int minimumSuffixLength, int& suffixStart, HashSet<const SimpleFontData*>& fallbackFonts, GlyphOverflow& glyphOverflow) { suffixStart = 0; if (wordLength <= minimumSuffixLength) @@ -922,7 +921,7 @@ static float maxWordFragmentWidth(RenderText* renderer, RenderStyle* style, cons TextRun run = RenderBlock::constructTextRun(renderer, font, fragmentWithHyphen.characters(), fragmentWithHyphen.length(), style); run.setCharactersLength(fragmentWithHyphen.length()); run.setCharacterScanForCodePath(!renderer->canUseSimpleFontCodePath()); - float fragmentWidth = font.width(run); + float fragmentWidth = font.width(run, &fallbackFonts, &glyphOverflow); // Narrow prefixes are ignored. See tryHyphenating in RenderBlockLineLayout.cpp. if (fragmentWidth <= minimumFragmentWidthToConsider) @@ -970,7 +969,7 @@ void RenderText::computePreferredLogicalWidths(float leadWidth, HashSet<const Si // Non-zero only when kerning is enabled, in which case we measure words with their trailing // space, then subtract its width. - float wordTrailingSpaceWidth = f.typesettingFeatures() & Kerning ? f.width(RenderBlock::constructTextRun(this, f, &space, 1, styleToUse)) + wordSpacing : 0; + float wordTrailingSpaceWidth = f.typesettingFeatures() & Kerning ? f.width(RenderBlock::constructTextRun(this, f, &space, 1, styleToUse), &fallbackFonts) + wordSpacing : 0; // If automatic hyphenation is allowed, we keep track of the width of the widest word (or word // fragment) encountered so far, and only try hyphenating words that are wider. @@ -1071,7 +1070,7 @@ void RenderText::computePreferredLogicalWidths(float leadWidth, HashSet<const Si if (w > maxWordWidth) { int suffixStart; - float maxFragmentWidth = maxWordFragmentWidth(this, styleToUse, f, characters() + i, wordLen, minimumPrefixLength, minimumSuffixLength, suffixStart); + float maxFragmentWidth = maxWordFragmentWidth(this, styleToUse, f, characters() + i, wordLen, minimumPrefixLength, minimumSuffixLength, suffixStart, fallbackFonts, glyphOverflow); if (suffixStart) { float suffixWidth; @@ -1152,7 +1151,7 @@ void RenderText::computePreferredLogicalWidths(float leadWidth, HashSet<const Si run.setTabSize(!style()->collapseWhiteSpace(), style()->tabSize()); run.setXPos(leadWidth + currMaxWidth); - currMaxWidth += f.width(run); + currMaxWidth += f.width(run, &fallbackFonts); glyphOverflow.right = 0; needsWordSpacing = isSpace && !previousCharacterIsSpace && i == len - 1; } @@ -1182,12 +1181,18 @@ void RenderText::computePreferredLogicalWidths(float leadWidth, HashSet<const Si setPreferredLogicalWidthsDirty(false); } -bool RenderText::isAllCollapsibleWhitespace() +bool RenderText::isAllCollapsibleWhitespace() const { - int length = textLength(); - const UChar* text = characters(); - for (int i = 0; i < length; i++) { - if (!style()->isCollapsibleWhiteSpace(text[i])) + unsigned length = textLength(); + if (is8Bit()) { + for (unsigned i = 0; i < length; ++i) { + if (!style()->isCollapsibleWhiteSpace(characters8()[i])) + return false; + } + return true; + } + for (unsigned i = 0; i < length; ++i) { + if (!style()->isCollapsibleWhiteSpace(characters16()[i])) return false; } return true; @@ -1422,11 +1427,6 @@ void RenderText::setTextInternal(PassRefPtr<StringImpl> text) ASSERT(m_text); ASSERT(!isBR() || (textLength() == 1 && m_text[0] == '\n')); - m_is8Bit = m_text.is8Bit(); - if (is8Bit()) - m_data.characters8 = m_text.characters8(); - else - m_data.characters16 = m_text.characters16(); m_isAllASCII = m_text.containsOnlyASCII(); m_canUseSimpleFontCodePath = computeCanUseSimpleFontCodePath(); } @@ -1464,9 +1464,8 @@ void RenderText::setText(PassRefPtr<StringImpl> text, bool force) setNeedsLayoutAndPrefWidthsRecalc(); m_knownToHaveNoOverflowAndNoFallbackFonts = false; - AXObjectCache* axObjectCache = document()->axObjectCache(); - if (axObjectCache->accessibilityEnabled()) - axObjectCache->textChanged(this); + if (AXObjectCache* cache = document()->existingAXObjectCache()) + cache->textChanged(this); } String RenderText::textWithoutTranscoding() const @@ -1727,8 +1726,11 @@ unsigned RenderText::renderedTextLength() const int RenderText::previousOffset(int current) const { - StringImpl* si = m_text.impl(); - TextBreakIterator* iterator = cursorMovementIterator(si->characters(), si->length()); + if (isAllASCII() || m_text.is8Bit()) + return current - 1; + + StringImpl* textImpl = m_text.impl(); + TextBreakIterator* iterator = cursorMovementIterator(textImpl->characters16(), textImpl->length()); if (!iterator) return current - 1; @@ -1740,7 +1742,7 @@ int RenderText::previousOffset(int current) const return result; } -#if PLATFORM(MAC) || PLATFORM(CHROMIUM) && OS(MAC_OS_X) +#if PLATFORM(MAC) #define HANGUL_CHOSEONG_START (0x1100) #define HANGUL_CHOSEONG_END (0x115F) @@ -1782,7 +1784,7 @@ inline bool isRegionalIndicator(UChar32 c) int RenderText::previousOffsetForBackwardDeletion(int current) const { -#if PLATFORM(MAC) || PLATFORM(CHROMIUM) && OS(MAC_OS_X) +#if PLATFORM(MAC) ASSERT(m_text); StringImpl& text = *m_text.impl(); UChar32 character; @@ -1880,8 +1882,11 @@ int RenderText::previousOffsetForBackwardDeletion(int current) const int RenderText::nextOffset(int current) const { - StringImpl* si = m_text.impl(); - TextBreakIterator* iterator = cursorMovementIterator(si->characters(), si->length()); + if (isAllASCII() || m_text.is8Bit()) + return current + 1; + + StringImpl* textImpl = m_text.impl(); + TextBreakIterator* iterator = cursorMovementIterator(textImpl->characters16(), textImpl->length()); if (!iterator) return current + 1; @@ -1889,7 +1894,6 @@ int RenderText::nextOffset(int current) const if (result == TextBreakDone) result = current + 1; - return result; } diff --git a/Source/WebCore/rendering/RenderText.h b/Source/WebCore/rendering/RenderText.h index 7b986917d..6e44e326e 100644 --- a/Source/WebCore/rendering/RenderText.h +++ b/Source/WebCore/rendering/RenderText.h @@ -45,12 +45,6 @@ public: virtual PassRefPtr<StringImpl> originalText() const; - void updateTextIfNeeded() - { - if (preferredLogicalWidthsDirty()) - updateText(); - } - void extractTextBox(InlineTextBox*); void attachTextBox(InlineTextBox*); void removeTextBox(InlineTextBox*); @@ -72,11 +66,11 @@ public: virtual VisiblePosition positionForPoint(const LayoutPoint&); - bool is8Bit() const { return m_is8Bit; } - const LChar* characters8() const { ASSERT(m_is8Bit); return m_data.characters8; } - const UChar* characters16() const { ASSERT(!m_is8Bit); return m_data.characters16; } + bool is8Bit() const { return m_text.is8Bit(); } + const LChar* characters8() const { return m_text.impl()->characters8(); } + const UChar* characters16() const { return m_text.impl()->characters16(); } const UChar* characters() const { return m_text.characters(); } - UChar characterAt(unsigned i) const { return m_is8Bit ? characters8()[i] : characters16()[i]; } + UChar characterAt(unsigned i) const { return is8Bit() ? characters8()[i] : characters16()[i]; } UChar operator[](unsigned i) const { return characterAt(i); } unsigned textLength() const { return m_text.length(); } // non virtual implementation of length() void positionLineBox(InlineBox*); @@ -136,8 +130,7 @@ public: void checkConsistency() const; - virtual void computePreferredLogicalWidths(float leadWidth); - bool isAllCollapsibleWhitespace(); + bool isAllCollapsibleWhitespace() const; bool canUseSimpleFontCodePath() const { return m_canUseSimpleFontCodePath; } bool knownToHaveNoOverflowAndNoFallbackFonts() const { return m_knownToHaveNoOverflowAndNoFallbackFonts; } @@ -145,12 +138,12 @@ public: void removeAndDestroyTextBoxes(); protected: + virtual void computePreferredLogicalWidths(float leadWidth); virtual void willBeDestroyed(); virtual void styleWillChange(StyleDifference, const RenderStyle*) { } virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); - virtual void updateText() { } virtual void setTextInternal(PassRefPtr<StringImpl>); virtual UChar previousCharacter() const; @@ -189,7 +182,6 @@ private: // just dirtying everything when character data is modified (e.g., appended/inserted // or removed). bool m_containsReversedText : 1; - bool m_is8Bit : 1; bool m_isAllASCII : 1; bool m_canUseSimpleFontCodePath : 1; mutable bool m_knownToHaveNoOverflowAndNoFallbackFonts : 1; @@ -201,10 +193,6 @@ private: float m_endMinWidth; String m_text; - union { - const LChar* characters8; - const UChar* characters16; - } m_data; InlineTextBox* m_firstTextBox; InlineTextBox* m_lastTextBox; @@ -212,13 +200,13 @@ private: inline RenderText* toRenderText(RenderObject* object) { - ASSERT(!object || object->isText()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isText()); return static_cast<RenderText*>(object); } inline const RenderText* toRenderText(const RenderObject* object) { - ASSERT(!object || object->isText()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isText()); return static_cast<const RenderText*>(object); } diff --git a/Source/WebCore/rendering/RenderTextControl.cpp b/Source/WebCore/rendering/RenderTextControl.cpp index 35da70455..3c328ef95 100644 --- a/Source/WebCore/rendering/RenderTextControl.cpp +++ b/Source/WebCore/rendering/RenderTextControl.cpp @@ -36,10 +36,10 @@ using namespace std; namespace WebCore { -RenderTextControl::RenderTextControl(Node* node) - : RenderBlock(node) +RenderTextControl::RenderTextControl(Element* element) + : RenderBlock(element) { - ASSERT(toTextFormControl(node)); + ASSERT(isHTMLTextFormControlElement(element)); } RenderTextControl::~RenderTextControl() @@ -48,7 +48,7 @@ RenderTextControl::~RenderTextControl() HTMLTextFormControlElement* RenderTextControl::textFormControlElement() const { - return static_cast<HTMLTextFormControlElement*>(node()); + return toHTMLTextFormControlElement(node()); } HTMLElement* RenderTextControl::innerTextElement() const @@ -76,17 +76,17 @@ void RenderTextControl::styleDidChange(StyleDifference diff, const RenderStyle* static inline bool updateUserModifyProperty(Node* node, RenderStyle* style) { - bool isEnabled = true; + bool isDisabled = false; bool isReadOnlyControl = false; if (node->isElementNode()) { - Element* element = static_cast<Element*>(node); - isEnabled = element->isEnabledFormControl(); - isReadOnlyControl = element->isTextFormControl() && static_cast<HTMLTextFormControlElement*>(element)->readOnly(); + Element* element = toElement(node); + isDisabled = element->isDisabledFormControl(); + isReadOnlyControl = element->isTextFormControl() && toHTMLTextFormControlElement(element)->isReadOnly(); } - style->setUserModify((isReadOnlyControl || !isEnabled) ? READ_ONLY : READ_WRITE_PLAINTEXT_ONLY); - return !isEnabled; + style->setUserModify((isReadOnlyControl || isDisabled) ? READ_ONLY : READ_WRITE_PLAINTEXT_ONLY); + return isDisabled; } void RenderTextControl::adjustInnerTextStyle(const RenderStyle* startStyle, RenderStyle* textBlockStyle) const @@ -101,19 +101,19 @@ void RenderTextControl::adjustInnerTextStyle(const RenderStyle* startStyle, Rend textBlockStyle->setColor(theme()->disabledTextColor(textBlockStyle->visitedDependentColor(CSSPropertyColor), startStyle->visitedDependentColor(CSSPropertyBackgroundColor))); } -int RenderTextControl::textBlockHeight() const +int RenderTextControl::textBlockLogicalHeight() const { - return height() - borderAndPaddingHeight(); + return logicalHeight() - borderAndPaddingLogicalHeight(); } -int RenderTextControl::textBlockWidth() const +int RenderTextControl::textBlockLogicalWidth() const { Element* innerText = innerTextElement(); ASSERT(innerText); - LayoutUnit unitWidth = width() - borderAndPaddingWidth(); + LayoutUnit unitWidth = logicalWidth() - borderAndPaddingLogicalWidth(); if (innerText->renderer()) - unitWidth -= innerText->renderBox()->paddingLeft() + innerText->renderBox()->paddingRight(); + unitWidth -= innerText->renderBox()->paddingStart() + innerText->renderBox()->paddingEnd(); return unitWidth; } @@ -129,10 +129,8 @@ VisiblePosition RenderTextControl::visiblePositionForIndex(int index) const { if (index <= 0) return VisiblePosition(firstPositionInNode(innerTextElement()), DOWNSTREAM); - ExceptionCode ec = 0; RefPtr<Range> range = Range::create(document()); - range->selectNodeContents(innerTextElement(), ec); - ASSERT(!ec); + range->selectNodeContents(innerTextElement(), ASSERT_NO_EXCEPTION); CharacterIterator it(range.get()); it.advance(index - 1); return VisiblePosition(it.range()->endPosition(), UPSTREAM); @@ -150,10 +148,11 @@ void RenderTextControl::computeLogicalHeight(LayoutUnit logicalHeight, LayoutUni ASSERT(innerText); if (RenderBox* innerTextBox = innerText->renderBox()) { LayoutUnit nonContentHeight = innerTextBox->borderAndPaddingHeight() + innerTextBox->marginHeight(); - logicalHeight = computeControlHeight(innerTextBox->lineHeight(true, HorizontalLine, PositionOfInteriorLineBoxes), nonContentHeight) + borderAndPaddingHeight(); + logicalHeight = computeControlLogicalHeight(innerTextBox->lineHeight(true, HorizontalLine, PositionOfInteriorLineBoxes), nonContentHeight) + borderAndPaddingHeight(); // We are able to have a horizontal scrollbar if the overflow style is scroll, or if its auto and there's no word wrap. - if (style()->overflowX() == OSCROLL || (style()->overflowX() == OAUTO && innerText->renderer()->style()->overflowWrap() == NormalOverflowWrap)) + if ((isHorizontalWritingMode() && (style()->overflowX() == OSCROLL || (style()->overflowX() == OAUTO && innerText->renderer()->style()->overflowWrap() == NormalOverflowWrap))) + || (!isHorizontalWritingMode() && (style()->overflowY() == OSCROLL || (style()->overflowY() == OAUTO && innerText->renderer()->style()->overflowWrap() == NormalOverflowWrap)))) logicalHeight += scrollbarThickness(); } @@ -219,11 +218,17 @@ static const char* fontFamiliesWithInvalidCharWidth[] = { // all platforms. bool RenderTextControl::hasValidAvgCharWidth(AtomicString family) { - static HashSet<AtomicString>* fontFamiliesWithInvalidCharWidthMap = 0; - if (family.isEmpty()) return false; + // Internal fonts on OS X also have an invalid entry in the table for avgCharWidth. + // They are hidden by having a name that begins with a period, so simply search + // for that here rather than try to keep the list up to date. + if (family.startsWith('.')) + return false; + + static HashSet<AtomicString>* fontFamiliesWithInvalidCharWidthMap = 0; + if (!fontFamiliesWithInvalidCharWidthMap) { fontFamiliesWithInvalidCharWidthMap = new HashSet<AtomicString>; @@ -254,6 +259,17 @@ float RenderTextControl::scaleEmToUnits(int x) const return roundf(style()->font().size() * x / unitsPerEm); } +void RenderTextControl::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const +{ + // Use average character width. Matches IE. + const AtomicString& family = style()->font().firstFamily(); + maxLogicalWidth = preferredContentLogicalWidth(const_cast<RenderTextControl*>(this)->getAvgCharWidth(family)); + if (RenderBox* innerTextRenderBox = innerTextElement()->renderBox()) + maxLogicalWidth += innerTextRenderBox->paddingStart() + innerTextRenderBox->paddingEnd(); + if (!style()->logicalWidth().isPercent()) + minLogicalWidth = maxLogicalWidth; +} + void RenderTextControl::computePreferredLogicalWidths() { ASSERT(preferredLogicalWidthsDirty()); @@ -261,30 +277,22 @@ void RenderTextControl::computePreferredLogicalWidths() m_minPreferredLogicalWidth = 0; m_maxPreferredLogicalWidth = 0; - if (style()->width().isFixed() && style()->width().value() >= 0) - m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = adjustContentBoxLogicalWidthForBoxSizing(style()->width().value()); - else { - // Use average character width. Matches IE. - AtomicString family = style()->font().family().family(); - m_maxPreferredLogicalWidth = preferredContentWidth(getAvgCharWidth(family)); - if (RenderBox* innerTextRenderBox = innerTextElement()->renderBox()) - m_maxPreferredLogicalWidth += innerTextRenderBox->paddingLeft() + innerTextRenderBox->paddingRight(); - } - - if (style()->minWidth().isFixed() && style()->minWidth().value() > 0) { - m_maxPreferredLogicalWidth = max(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style()->minWidth().value())); - m_minPreferredLogicalWidth = max(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style()->minWidth().value())); - } else if (style()->width().isPercent() || (style()->width().isAuto() && style()->height().isPercent())) - m_minPreferredLogicalWidth = 0; + if (style()->logicalWidth().isFixed() && style()->logicalWidth().value() >= 0) + m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = adjustContentBoxLogicalWidthForBoxSizing(style()->logicalWidth().value()); else - m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth; + computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth); + + if (style()->logicalMinWidth().isFixed() && style()->logicalMinWidth().value() > 0) { + m_maxPreferredLogicalWidth = max(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style()->logicalMinWidth().value())); + m_minPreferredLogicalWidth = max(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style()->logicalMinWidth().value())); + } - if (style()->maxWidth().isFixed()) { - m_maxPreferredLogicalWidth = min(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style()->maxWidth().value())); - m_minPreferredLogicalWidth = min(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style()->maxWidth().value())); + if (style()->logicalMaxWidth().isFixed()) { + m_maxPreferredLogicalWidth = min(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style()->logicalMaxWidth().value())); + m_minPreferredLogicalWidth = min(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style()->logicalMaxWidth().value())); } - LayoutUnit toAdd = borderAndPaddingWidth(); + LayoutUnit toAdd = borderAndPaddingLogicalWidth(); m_minPreferredLogicalWidth += toAdd; m_maxPreferredLogicalWidth += toAdd; @@ -292,7 +300,7 @@ void RenderTextControl::computePreferredLogicalWidths() setPreferredLogicalWidthsDirty(false); } -void RenderTextControl::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint& additionalOffset) +void RenderTextControl::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint& additionalOffset, const RenderLayerModelObject*) { if (!size().isEmpty()) rects.append(pixelSnappedIntRect(additionalOffset, size())); @@ -300,7 +308,7 @@ void RenderTextControl::addFocusRingRects(Vector<IntRect>& rects, const LayoutPo RenderObject* RenderTextControl::layoutSpecialExcludedChild(bool relayoutChildren) { - HTMLElement* placeholder = toTextFormControl(node())->placeholderElement(); + HTMLElement* placeholder = toHTMLTextFormControlElement(node())->placeholderElement(); RenderObject* placeholderRenderer = placeholder ? placeholder->renderer() : 0; if (!placeholderRenderer) return 0; diff --git a/Source/WebCore/rendering/RenderTextControl.h b/Source/WebCore/rendering/RenderTextControl.h index 4de76b4dc..1f6630cee 100644 --- a/Source/WebCore/rendering/RenderTextControl.h +++ b/Source/WebCore/rendering/RenderTextControl.h @@ -23,6 +23,7 @@ #define RenderTextControl_h #include "RenderBlock.h" +#include "RenderFlexibleBox.h" namespace WebCore { @@ -38,7 +39,7 @@ public: VisiblePosition visiblePositionForIndex(int index) const; protected: - RenderTextControl(Node*); + RenderTextControl(Element*); // This convenience function should not be made public because innerTextElement may outlive the render tree. HTMLElement* innerTextElement() const; @@ -50,15 +51,15 @@ protected: void hitInnerTextElement(HitTestResult&, const LayoutPoint& pointInContainer, const LayoutPoint& accumulatedOffset); - int textBlockWidth() const; - int textBlockHeight() const; + int textBlockLogicalWidth() const; + int textBlockLogicalHeight() const; float scaleEmToUnits(int x) const; static bool hasValidAvgCharWidth(AtomicString family); virtual float getAvgCharWidth(AtomicString family); - virtual LayoutUnit preferredContentWidth(float charWidth) const = 0; - virtual LayoutUnit computeControlHeight(LayoutUnit lineHeight, LayoutUnit nonContentHeight) const = 0; + virtual LayoutUnit preferredContentLogicalWidth(float charWidth) const = 0; + virtual LayoutUnit computeControlLogicalHeight(LayoutUnit lineHeight, LayoutUnit nonContentHeight) const = 0; virtual RenderStyle* textBaseStyle() const = 0; virtual void updateFromElement(); @@ -68,13 +69,14 @@ protected: private: virtual const char* renderName() const { return "RenderTextControl"; } virtual bool isTextControl() const { return true; } - virtual void computePreferredLogicalWidths(); + virtual void computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const OVERRIDE; + virtual void computePreferredLogicalWidths() OVERRIDE; virtual void removeLeftoverAnonymousBlock(RenderBlock*) { } virtual bool avoidsFloats() const { return true; } virtual bool canHaveGeneratedChildren() const OVERRIDE { return false; } virtual bool canBeReplacedWithInlineRunIn() const OVERRIDE; - virtual void addFocusRingRects(Vector<IntRect>&, const LayoutPoint&); + virtual void addFocusRingRects(Vector<IntRect>&, const LayoutPoint& additionalOffset, const RenderLayerModelObject* paintContainer = 0) OVERRIDE; virtual bool canBeProgramaticallyScrolled() const { return true; } @@ -83,19 +85,40 @@ private: inline RenderTextControl* toRenderTextControl(RenderObject* object) { - ASSERT(!object || object->isTextControl()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isTextControl()); return static_cast<RenderTextControl*>(object); } inline const RenderTextControl* toRenderTextControl(const RenderObject* object) { - ASSERT(!object || object->isTextControl()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isTextControl()); return static_cast<const RenderTextControl*>(object); } // This will catch anyone doing an unnecessary cast. void toRenderTextControl(const RenderTextControl*); +// Renderer for our inner container, for <search> and others. +// We can't use RenderFlexibleBox directly, because flexboxes have a different +// baseline definition, and then inputs of different types wouldn't line up +// anymore. +class RenderTextControlInnerContainer : public RenderFlexibleBox { +public: + explicit RenderTextControlInnerContainer(Element* element) + : RenderFlexibleBox(element) + { } + virtual ~RenderTextControlInnerContainer() { } + + virtual int baselinePosition(FontBaseline baseline, bool firstLine, LineDirectionMode direction, LinePositionMode position) const OVERRIDE + { + return RenderBlock::baselinePosition(baseline, firstLine, direction, position); + } + virtual int firstLineBoxBaseline() const OVERRIDE { return RenderBlock::firstLineBoxBaseline(); } + virtual int inlineBlockBaseline(LineDirectionMode direction) const OVERRIDE { return RenderBlock::inlineBlockBaseline(direction); } + +}; + + } // namespace WebCore #endif // RenderTextControl_h diff --git a/Source/WebCore/rendering/RenderTextControlMultiLine.cpp b/Source/WebCore/rendering/RenderTextControlMultiLine.cpp index f40994326..9a121c614 100644 --- a/Source/WebCore/rendering/RenderTextControlMultiLine.cpp +++ b/Source/WebCore/rendering/RenderTextControlMultiLine.cpp @@ -32,15 +32,15 @@ namespace WebCore { -RenderTextControlMultiLine::RenderTextControlMultiLine(Node* node) - : RenderTextControl(node) +RenderTextControlMultiLine::RenderTextControlMultiLine(Element* element) + : RenderTextControl(element) { } RenderTextControlMultiLine::~RenderTextControlMultiLine() { if (node() && node()->inDocument()) - static_cast<HTMLTextAreaElement*>(node())->rendererWillBeDestroyed(); + toHTMLTextAreaElement(node())->rendererWillBeDestroyed(); } bool RenderTextControlMultiLine::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction) @@ -65,15 +65,15 @@ float RenderTextControlMultiLine::getAvgCharWidth(AtomicString family) return RenderTextControl::getAvgCharWidth(family); } -LayoutUnit RenderTextControlMultiLine::preferredContentWidth(float charWidth) const +LayoutUnit RenderTextControlMultiLine::preferredContentLogicalWidth(float charWidth) const { - int factor = static_cast<HTMLTextAreaElement*>(node())->cols(); + int factor = toHTMLTextAreaElement(node())->cols(); return static_cast<LayoutUnit>(ceilf(charWidth * factor)) + scrollbarThickness(); } -LayoutUnit RenderTextControlMultiLine::computeControlHeight(LayoutUnit lineHeight, LayoutUnit nonContentHeight) const +LayoutUnit RenderTextControlMultiLine::computeControlLogicalHeight(LayoutUnit lineHeight, LayoutUnit nonContentHeight) const { - return lineHeight * static_cast<HTMLTextAreaElement*>(node())->rows() + nonContentHeight; + return lineHeight * toHTMLTextAreaElement(node())->rows() + nonContentHeight; } int RenderTextControlMultiLine::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const @@ -104,7 +104,7 @@ RenderObject* RenderTextControlMultiLine::layoutSpecialExcludedChild(bool relayo if (!placeholderRenderer->isBox()) return placeholderRenderer; RenderBox* placeholderBox = toRenderBox(placeholderRenderer); - placeholderBox->style()->setWidth(Length(contentWidth() - placeholderBox->borderAndPaddingWidth(), Fixed)); + placeholderBox->style()->setLogicalWidth(Length(contentLogicalWidth() - placeholderBox->borderAndPaddingLogicalWidth(), Fixed)); placeholderBox->layoutIfNeeded(); placeholderBox->setX(borderLeft() + paddingLeft()); placeholderBox->setY(borderTop() + paddingTop()); diff --git a/Source/WebCore/rendering/RenderTextControlMultiLine.h b/Source/WebCore/rendering/RenderTextControlMultiLine.h index 6dad346c5..592a137d9 100644 --- a/Source/WebCore/rendering/RenderTextControlMultiLine.h +++ b/Source/WebCore/rendering/RenderTextControlMultiLine.h @@ -28,7 +28,7 @@ namespace WebCore { class RenderTextControlMultiLine : public RenderTextControl { public: - RenderTextControlMultiLine(Node*); + RenderTextControlMultiLine(Element*); virtual ~RenderTextControlMultiLine(); private: @@ -37,8 +37,8 @@ private: virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction) OVERRIDE; virtual float getAvgCharWidth(AtomicString family); - virtual LayoutUnit preferredContentWidth(float charWidth) const; - virtual LayoutUnit computeControlHeight(LayoutUnit lineHeight, LayoutUnit nonContentHeight) const OVERRIDE; + virtual LayoutUnit preferredContentLogicalWidth(float charWidth) const; + virtual LayoutUnit computeControlLogicalHeight(LayoutUnit lineHeight, LayoutUnit nonContentHeight) const OVERRIDE; virtual int baselinePosition(FontBaseline, bool firstLine, LineDirectionMode, LinePositionMode = PositionOnContainingLine) const; virtual RenderStyle* textBaseStyle() const; @@ -48,7 +48,7 @@ private: inline RenderTextControlMultiLine* toRenderTextControlMultiLine(RenderObject* object) { - ASSERT(!object || object->isTextArea()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isTextArea()); return static_cast<RenderTextControlMultiLine*>(object); } diff --git a/Source/WebCore/rendering/RenderTextControlSingleLine.cpp b/Source/WebCore/rendering/RenderTextControlSingleLine.cpp index c7f075a4b..8ba8ea13b 100644 --- a/Source/WebCore/rendering/RenderTextControlSingleLine.cpp +++ b/Source/WebCore/rendering/RenderTextControlSingleLine.cpp @@ -42,6 +42,7 @@ #include "SimpleFontData.h" #include "StyleResolver.h" #include "TextControlInnerElements.h" +#include <wtf/StackStats.h> using namespace std; @@ -49,13 +50,13 @@ namespace WebCore { using namespace HTMLNames; -RenderTextControlSingleLine::RenderTextControlSingleLine(Node* node) - : RenderTextControl(node) +RenderTextControlSingleLine::RenderTextControlSingleLine(Element* element) + : RenderTextControl(element) , m_shouldDrawCapsLockIndicator(false) - , m_desiredInnerTextHeight(-1) + , m_desiredInnerTextLogicalHeight(-1) { - ASSERT(node->isHTMLElement()); - ASSERT(node->toInputElement()); + ASSERT(element->isHTMLElement()); + ASSERT(element->toInputElement()); } RenderTextControlSingleLine::~RenderTextControlSingleLine() @@ -80,8 +81,11 @@ void RenderTextControlSingleLine::paint(PaintInfo& paintInfo, const LayoutPoint& if (paintInfo.phase == PaintPhaseBlockBackground && m_shouldDrawCapsLockIndicator) { LayoutRect contentsRect = contentBoxRect(); - // Center vertically like the text. - contentsRect.setY((height() - contentsRect.height()) / 2); + // Center in the block progression direction. + if (isHorizontalWritingMode()) + contentsRect.setY((height() - contentsRect.height()) / 2); + else + contentsRect.setX((width() - contentsRect.width()) / 2); // Convert the rect into the coords used for painting the content contentsRect.moveBy(paintOffset + location()); @@ -89,9 +93,9 @@ void RenderTextControlSingleLine::paint(PaintInfo& paintInfo, const LayoutPoint& } } -LayoutUnit RenderTextControlSingleLine::computeHeightLimit() const +LayoutUnit RenderTextControlSingleLine::computeLogicalHeightLimit() const { - return containerElement() ? contentHeight() : height(); + return containerElement() ? contentLogicalHeight() : logicalHeight(); } void RenderTextControlSingleLine::layout() @@ -110,13 +114,17 @@ void RenderTextControlSingleLine::layout() // because of compability. RenderBox* innerTextRenderer = innerTextElement()->renderBox(); - ASSERT(innerTextRenderer); RenderBox* innerBlockRenderer = innerBlockElement() ? innerBlockElement()->renderBox() : 0; // To ensure consistency between layouts, we need to reset any conditionally overriden height. - innerTextRenderer->style()->setHeight(Length(Auto)); - if (innerBlockRenderer) - innerBlockRenderer->style()->setHeight(Length(Auto)); + if (innerTextRenderer && !innerTextRenderer->style()->logicalHeight().isAuto()) { + innerTextRenderer->style()->setLogicalHeight(Length(Auto)); + innerTextRenderer->setNeedsLayout(true, MarkOnlyThis); + } + if (innerBlockRenderer && !innerBlockRenderer->style()->logicalHeight().isAuto()) { + innerBlockRenderer->style()->setLogicalHeight(Length(Auto)); + innerBlockRenderer->setNeedsLayout(true, MarkOnlyThis); + } RenderBlock::layoutBlock(false); @@ -124,42 +132,43 @@ void RenderTextControlSingleLine::layout() RenderBox* containerRenderer = container ? container->renderBox() : 0; // Set the text block height - LayoutUnit desiredHeight = textBlockHeight(); - LayoutUnit currentHeight = innerTextRenderer->height(); - - LayoutUnit heightLimit = computeHeightLimit(); - if (currentHeight > heightLimit) { - if (desiredHeight != currentHeight) + LayoutUnit desiredLogicalHeight = textBlockLogicalHeight(); + LayoutUnit logicalHeightLimit = computeLogicalHeightLimit(); + if (innerTextRenderer && innerTextRenderer->logicalHeight() > logicalHeightLimit) { + if (desiredLogicalHeight != innerTextRenderer->logicalHeight()) setNeedsLayout(true, MarkOnlyThis); - innerTextRenderer->style()->setHeight(Length(desiredHeight, Fixed)); - m_desiredInnerTextHeight = desiredHeight; - if (innerBlockRenderer) - innerBlockRenderer->style()->setHeight(Length(desiredHeight, Fixed)); + m_desiredInnerTextLogicalHeight = desiredLogicalHeight; + + innerTextRenderer->style()->setLogicalHeight(Length(desiredLogicalHeight, Fixed)); + innerTextRenderer->setNeedsLayout(true, MarkOnlyThis); + if (innerBlockRenderer) { + innerBlockRenderer->style()->setLogicalHeight(Length(desiredLogicalHeight, Fixed)); + innerBlockRenderer->setNeedsLayout(true, MarkOnlyThis); + } } // The container might be taller because of decoration elements. if (containerRenderer) { containerRenderer->layoutIfNeeded(); - LayoutUnit containerHeight = containerRenderer->height(); - if (containerHeight > heightLimit) { - containerRenderer->style()->setHeight(Length(heightLimit, Fixed)); + LayoutUnit containerLogicalHeight = containerRenderer->logicalHeight(); + if (containerLogicalHeight > logicalHeightLimit) { + containerRenderer->style()->setLogicalHeight(Length(logicalHeightLimit, Fixed)); setNeedsLayout(true, MarkOnlyThis); - } else if (containerRenderer->height() < contentHeight()) { - containerRenderer->style()->setHeight(Length(contentHeight(), Fixed)); + } else if (containerRenderer->logicalHeight() < contentLogicalHeight()) { + containerRenderer->style()->setLogicalHeight(Length(contentLogicalHeight(), Fixed)); setNeedsLayout(true, MarkOnlyThis); } else - containerRenderer->style()->setHeight(Length(containerHeight, Fixed)); + containerRenderer->style()->setLogicalHeight(Length(containerLogicalHeight, Fixed)); } // If we need another layout pass, we have changed one of children's height so we need to relayout them. if (needsLayout()) RenderBlock::layoutBlock(true); - // Center the child block vertically - currentHeight = innerTextRenderer->height(); - if (!container && currentHeight != contentHeight()) { - LayoutUnit heightDiff = currentHeight - contentHeight(); - innerTextRenderer->setY(innerTextRenderer->y() - (heightDiff / 2 + layoutMod(heightDiff, 2))); + // Center the child block in the block progression direction (vertical centering for horizontal text fields). + if (!container && innerTextRenderer && innerTextRenderer->height() != contentLogicalHeight()) { + LayoutUnit logicalHeightDiff = innerTextRenderer->logicalHeight() - contentLogicalHeight(); + innerTextRenderer->setLogicalTop(innerTextRenderer->logicalTop() - (logicalHeightDiff / 2 + layoutMod(logicalHeightDiff, 2))); } else centerContainerIfNeeded(containerRenderer); @@ -167,19 +176,25 @@ void RenderTextControlSingleLine::layout() if (RenderBox* innerSpinBox = innerSpinButtonElement() ? innerSpinButtonElement()->renderBox() : 0) { RenderBox* parentBox = innerSpinBox->parentBox(); if (containerRenderer && !containerRenderer->style()->isLeftToRightDirection()) - innerSpinBox->setLocation(LayoutPoint(-paddingLeft(), -paddingTop())); + innerSpinBox->setLogicalLocation(LayoutPoint(-paddingLogicalLeft(), -paddingBefore())); else - innerSpinBox->setLocation(LayoutPoint(parentBox->width() - innerSpinBox->width() + paddingRight(), -paddingTop())); - innerSpinBox->setHeight(height() - borderTop() - borderBottom()); + innerSpinBox->setLogicalLocation(LayoutPoint(parentBox->logicalWidth() - innerSpinBox->logicalWidth() + paddingLogicalRight(), -paddingBefore())); + innerSpinBox->setLogicalHeight(logicalHeight() - borderBefore() - borderAfter()); } HTMLElement* placeholderElement = inputElement()->placeholderElement(); if (RenderBox* placeholderBox = placeholderElement ? placeholderElement->renderBox() : 0) { - placeholderBox->style()->setWidth(Length(innerTextRenderer->width() - placeholderBox->borderAndPaddingWidth(), Fixed)); - placeholderBox->style()->setHeight(Length(innerTextRenderer->height() - placeholderBox->borderAndPaddingHeight(), Fixed)); + LayoutSize innerTextSize; + if (innerTextRenderer) + innerTextSize = innerTextRenderer->size(); + placeholderBox->style()->setWidth(Length(innerTextSize.width() - placeholderBox->borderAndPaddingWidth(), Fixed)); + placeholderBox->style()->setHeight(Length(innerTextSize.height() - placeholderBox->borderAndPaddingHeight(), Fixed)); + bool neededLayout = placeholderBox->needsLayout(); bool placeholderBoxHadLayout = placeholderBox->everHadLayout(); placeholderBox->layoutIfNeeded(); - LayoutPoint textOffset = innerTextRenderer->location(); + LayoutPoint textOffset; + if (innerTextRenderer) + textOffset = innerTextRenderer->location(); if (innerBlockElement() && innerBlockElement()->renderBox()) textOffset += toLayoutSize(innerBlockElement()->renderBox()->location()); if (containerRenderer) @@ -191,6 +206,10 @@ void RenderTextControlSingleLine::layout() // logic should be shared with RenderBlock::layoutBlockChild. placeholderBox->repaint(); } + // The placeholder gets layout last, after the parent text control and its other children, + // so in order to get the correct overflow from the placeholder we need to recompute it now. + if (neededLayout) + computeOverflow(clientLogicalBottom()); } } @@ -219,7 +238,7 @@ bool RenderTextControlSingleLine::nodeAtPoint(const HitTestRequest& request, Hit void RenderTextControlSingleLine::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) { - m_desiredInnerTextHeight = -1; + m_desiredInnerTextLogicalHeight = -1; RenderTextControl::styleDidChange(diff, oldStyle); // We may have set the width and the height in the old style in layout(). @@ -234,6 +253,9 @@ void RenderTextControlSingleLine::styleDidChange(StyleDifference diff, const Ren containerRenderer->style()->setHeight(Length()); containerRenderer->style()->setWidth(Length()); } + RenderObject* innerTextRenderer = innerTextElement()->renderer(); + if (innerTextRenderer && diff == StyleDifferenceLayout) + innerTextRenderer->setNeedsLayout(true, MarkContainingBlockChain); if (HTMLElement* placeholder = inputElement()->placeholderElement()) placeholder->setInlineStyleProperty(CSSPropertyTextOverflow, textShouldBeTruncated() ? CSSValueEllipsis : CSSValueClip); setHasOverflowClip(false); @@ -254,7 +276,7 @@ void RenderTextControlSingleLine::capsLockStateMayHaveChanged() if (Frame* frame = document()->frame()) shouldDrawCapsLockIndicator = inputElement()->isPasswordField() && frame->selection()->isFocusedAndActive() - && document()->focusedNode() == node() + && document()->focusedElement() == node() && PlatformKeyboardEvent::currentCapsLockState(); if (shouldDrawCapsLockIndicator != m_shouldDrawCapsLockIndicator) { @@ -272,7 +294,9 @@ bool RenderTextControlSingleLine::hasControlClip() const LayoutRect RenderTextControlSingleLine::controlClipRect(const LayoutPoint& additionalOffset) const { ASSERT(hasControlClip()); - LayoutRect clipRect = unionRect(contentBoxRect(), containerElement()->renderBox()->frameRect()); + LayoutRect clipRect = contentBoxRect(); + if (containerElement()->renderBox()) + clipRect = unionRect(clipRect, containerElement()->renderBox()->frameRect()); clipRect.moveBy(additionalOffset); return clipRect; } @@ -289,7 +313,7 @@ float RenderTextControlSingleLine::getAvgCharWidth(AtomicString family) return RenderTextControl::getAvgCharWidth(family); } -LayoutUnit RenderTextControlSingleLine::preferredContentWidth(float charWidth) const +LayoutUnit RenderTextControlSingleLine::preferredContentLogicalWidth(float charWidth) const { int factor; bool includesDecoration = inputElement()->sizeShouldIncludeDecoration(factor); @@ -299,7 +323,7 @@ LayoutUnit RenderTextControlSingleLine::preferredContentWidth(float charWidth) c LayoutUnit result = static_cast<LayoutUnit>(ceiledLayoutUnit(charWidth * factor)); float maxCharWidth = 0.f; - AtomicString family = style()->font().family().family(); + const AtomicString& family = style()->font().firstFamily(); // Since Lucida Grande is the default font, we want this to match the width // of MS Shell Dlg, the default font for textareas in Firefox, Safari Win and // IE for some encodings (in IE, the default font is encoding specific). @@ -316,18 +340,17 @@ LayoutUnit RenderTextControlSingleLine::preferredContentWidth(float charWidth) c if (includesDecoration) { HTMLElement* spinButton = innerSpinButtonElement(); if (RenderBox* spinRenderer = spinButton ? spinButton->renderBox() : 0) { - result += spinRenderer->borderLeft() + spinRenderer->borderRight() + - spinRenderer->paddingLeft() + spinRenderer->paddingRight(); - // Since the width of spinRenderer is not calculated yet, spinRenderer->width() returns 0. - // So computedStyle()->width() is used instead. - result += spinButton->computedStyle()->width().value(); + result += spinRenderer->borderAndPaddingLogicalWidth(); + // Since the width of spinRenderer is not calculated yet, spinRenderer->logicalWidth() returns 0. + // So computedStyle()->logicalWidth() is used instead. + result += spinButton->computedStyle()->logicalWidth().value(); } } return result; } -LayoutUnit RenderTextControlSingleLine::computeControlHeight(LayoutUnit lineHeight, LayoutUnit nonContentHeight) const +LayoutUnit RenderTextControlSingleLine::computeControlLogicalHeight(LayoutUnit lineHeight, LayoutUnit nonContentHeight) const { return lineHeight + nonContentHeight; } @@ -349,8 +372,8 @@ PassRefPtr<RenderStyle> RenderTextControlSingleLine::createInnerTextStyle(const textBlockStyle->setOverflowY(OHIDDEN); textBlockStyle->setTextOverflow(textShouldBeTruncated() ? TextOverflowEllipsis : TextOverflowClip); - if (m_desiredInnerTextHeight >= 0) - textBlockStyle->setHeight(Length(m_desiredInnerTextHeight, Fixed)); + if (m_desiredInnerTextLogicalHeight >= 0) + textBlockStyle->setLogicalHeight(Length(m_desiredInnerTextLogicalHeight, Fixed)); // Do not allow line-height to be smaller than our default. if (textBlockStyle->fontMetrics().lineSpacing() > lineHeight(true, HorizontalLine, PositionOfInteriorLineBoxes)) textBlockStyle->setLineHeight(RenderStyle::initialLineHeight()); @@ -365,7 +388,10 @@ PassRefPtr<RenderStyle> RenderTextControlSingleLine::createInnerBlockStyle(const RefPtr<RenderStyle> innerBlockStyle = RenderStyle::create(); innerBlockStyle->inheritFrom(startStyle); - innerBlockStyle->setBoxFlex(1); + innerBlockStyle->setFlexGrow(1); + // min-width: 0; is needed for correct shrinking. + // FIXME: Remove this line when https://bugs.webkit.org/show_bug.cgi?id=111790 is fixed. + innerBlockStyle->setMinWidth(Length(0, Fixed)); innerBlockStyle->setDisplay(BLOCK); innerBlockStyle->setDirection(LTR); @@ -377,15 +403,18 @@ PassRefPtr<RenderStyle> RenderTextControlSingleLine::createInnerBlockStyle(const bool RenderTextControlSingleLine::textShouldBeTruncated() const { - return document()->focusedNode() != node() + return document()->focusedElement() != node() && style()->textOverflow() == TextOverflowEllipsis; } -void RenderTextControlSingleLine::autoscroll() +void RenderTextControlSingleLine::autoscroll(const IntPoint& position) { - RenderLayer* layer = innerTextElement()->renderBox()->layer(); + RenderBox* renderer = innerTextElement()->renderBox(); + if (!renderer) + return; + RenderLayer* layer = renderer->layer(); if (layer) - layer->autoscroll(); + layer->autoscroll(position); } int RenderTextControlSingleLine::scrollWidth() const @@ -430,7 +459,10 @@ void RenderTextControlSingleLine::setScrollTop(int newTop) bool RenderTextControlSingleLine::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier, Node** stopNode) { - RenderLayer* layer = innerTextElement()->renderBox()->layer(); + RenderBox* renderer = innerTextElement()->renderBox(); + if (!renderer) + return false; + RenderLayer* layer = renderer->layer(); if (layer && layer->scroll(direction, granularity, multiplier)) return true; return RenderBlock::scroll(direction, granularity, multiplier, stopNode); diff --git a/Source/WebCore/rendering/RenderTextControlSingleLine.h b/Source/WebCore/rendering/RenderTextControlSingleLine.h index af27bf758..148acfe4a 100644 --- a/Source/WebCore/rendering/RenderTextControlSingleLine.h +++ b/Source/WebCore/rendering/RenderTextControlSingleLine.h @@ -32,7 +32,7 @@ class HTMLInputElement; class RenderTextControlSingleLine : public RenderTextControl { public: - RenderTextControlSingleLine(Node*); + RenderTextControlSingleLine(Element*); virtual ~RenderTextControlSingleLine(); // FIXME: Move create*Style() to their classes. virtual PassRefPtr<RenderStyle> createInnerTextStyle(const RenderStyle* startStyle) const; @@ -42,7 +42,7 @@ public: protected: virtual void centerContainerIfNeeded(RenderBox*) const { } - virtual LayoutUnit computeHeightLimit() const; + virtual LayoutUnit computeLogicalHeightLimit() const; HTMLElement* containerElement() const; HTMLElement* innerBlockElement() const; HTMLInputElement* inputElement() const; @@ -58,7 +58,7 @@ private: virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction) OVERRIDE; - virtual void autoscroll(); + virtual void autoscroll(const IntPoint&); // Subclassed to forward to our inner div. virtual int scrollLeft() const; @@ -72,8 +72,8 @@ private: int textBlockWidth() const; virtual float getAvgCharWidth(AtomicString family); - virtual LayoutUnit preferredContentWidth(float charWidth) const; - virtual LayoutUnit computeControlHeight(LayoutUnit lineHeight, LayoutUnit nonContentHeight) const OVERRIDE; + virtual LayoutUnit preferredContentLogicalWidth(float charWidth) const; + virtual LayoutUnit computeControlLogicalHeight(LayoutUnit lineHeight, LayoutUnit nonContentHeight) const OVERRIDE; virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); @@ -84,7 +84,7 @@ private: HTMLElement* innerSpinButtonElement() const; bool m_shouldDrawCapsLockIndicator; - LayoutUnit m_desiredInnerTextHeight; + LayoutUnit m_desiredInnerTextLogicalHeight; }; inline HTMLElement* RenderTextControlSingleLine::containerElement() const @@ -99,7 +99,7 @@ inline HTMLElement* RenderTextControlSingleLine::innerBlockElement() const inline RenderTextControlSingleLine* toRenderTextControlSingleLine(RenderObject* object) { - ASSERT(!object || object->isTextField()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isTextField()); return static_cast<RenderTextControlSingleLine*>(object); } @@ -110,7 +110,7 @@ void toRenderTextControlSingleLine(const RenderTextControlSingleLine*); class RenderTextControlInnerBlock : public RenderBlock { public: - RenderTextControlInnerBlock(Node* node) : RenderBlock(node) { } + RenderTextControlInnerBlock(Element* element) : RenderBlock(element) { } private: virtual bool hasLineIfEmpty() const { return true; } diff --git a/Source/WebCore/rendering/RenderTextFragment.h b/Source/WebCore/rendering/RenderTextFragment.h index b7f3b0e9a..3f86357ff 100644 --- a/Source/WebCore/rendering/RenderTextFragment.h +++ b/Source/WebCore/rendering/RenderTextFragment.h @@ -71,13 +71,13 @@ private: inline RenderTextFragment* toRenderTextFragment(RenderObject* object) { - ASSERT(!object || toRenderText(object)->isTextFragment()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || toRenderText(object)->isTextFragment()); return static_cast<RenderTextFragment*>(object); } inline const RenderTextFragment* toRenderTextFragment(const RenderObject* object) { - ASSERT(!object || toRenderText(object)->isTextFragment()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || toRenderText(object)->isTextFragment()); return static_cast<const RenderTextFragment*>(object); } diff --git a/Source/WebCore/rendering/RenderTextTrackCue.cpp b/Source/WebCore/rendering/RenderTextTrackCue.cpp index 4e9dc1552..fd88b77ad 100644 --- a/Source/WebCore/rendering/RenderTextTrackCue.cpp +++ b/Source/WebCore/rendering/RenderTextTrackCue.cpp @@ -30,12 +30,14 @@ #include "RenderTextTrackCue.h" #include "TextTrackCue.h" +#include "TextTrackCueGeneric.h" +#include <wtf/StackStats.h> namespace WebCore { -RenderTextTrackCue::RenderTextTrackCue(TextTrackCueBox* node) - : RenderBlock(static_cast<Node*>(node)) - , m_cue(node->getCue()) +RenderTextTrackCue::RenderTextTrackCue(TextTrackCueBox* element) + : RenderBlock(element) + , m_cue(element->getCue()) { } @@ -45,10 +47,15 @@ void RenderTextTrackCue::layout() RenderBlock::layout(); LayoutStateMaintainer statePusher(view(), this, locationOffset(), hasTransform() || hasReflection() || style()->isFlippedBlocksWritingMode()); - if (m_cue->snapToLines()) - repositionCueSnapToLinesSet(); - else - repositionCueSnapToLinesNotSet(); + + if (m_cue->cueType()== TextTrackCue::WebVTT) { + if (m_cue->snapToLines()) + repositionCueSnapToLinesSet(); + else + repositionCueSnapToLinesNotSet(); + } else + repositionGenericCue(); + statePusher.pop(); } @@ -117,19 +124,35 @@ void RenderTextTrackCue::placeBoxInDefaultPosition(LayoutUnit position, bool& sw bool RenderTextTrackCue::isOutside() const { - return !containingBlock()->absoluteBoundingBoxRect().contains(absoluteBoundingBoxRect()); + return !rectIsWithinContainer(absoluteContentBox()); +} + +bool RenderTextTrackCue::rectIsWithinContainer(const IntRect& rect) const +{ + return containingBlock()->absoluteBoundingBoxRect().contains(rect); } + bool RenderTextTrackCue::isOverlapping() const { + return overlappingObject(); +} + +RenderObject* RenderTextTrackCue::overlappingObject() const +{ + return overlappingObjectForRect(absoluteBoundingBoxRect()); +} + +RenderObject* RenderTextTrackCue::overlappingObjectForRect(const IntRect& rect) const +{ for (RenderObject* box = previousSibling(); box; box = box->previousSibling()) { IntRect boxRect = box->absoluteBoundingBoxRect(); - if (absoluteBoundingBoxRect().intersects(boxRect)) - return true; + if (rect.intersects(boxRect)) + return box; } - return false; + return 0; } bool RenderTextTrackCue::shouldSwitchDirection(InlineFlowBox* firstLineBox, LayoutUnit step) const @@ -195,6 +218,76 @@ bool RenderTextTrackCue::switchDirection(bool& switched, LayoutUnit& step) return true; } +void RenderTextTrackCue::moveIfNecessaryToKeepWithinContainer() +{ + IntRect containerRect = containingBlock()->absoluteBoundingBoxRect(); + IntRect cueRect = absoluteBoundingBoxRect(); + + int topOverflow = cueRect.y() - containerRect.y(); + int bottomOverflow = containerRect.maxY() - cueRect.maxY(); + + int verticalAdjustment = 0; + if (topOverflow < 0) + verticalAdjustment = -topOverflow; + else if (bottomOverflow < 0) + verticalAdjustment = bottomOverflow; + + if (verticalAdjustment) + setY(y() + verticalAdjustment); + + int leftOverflow = cueRect.x() - containerRect.x(); + int rightOverflow = containerRect.maxX() - cueRect.maxX(); + + int horizontalAdjustment = 0; + if (leftOverflow < 0) + horizontalAdjustment = -leftOverflow; + else if (rightOverflow < 0) + horizontalAdjustment = rightOverflow; + + if (horizontalAdjustment) + setX(x() + horizontalAdjustment); +} + +bool RenderTextTrackCue::findNonOverlappingPosition(int& newX, int& newY) const +{ + newX = x(); + newY = y(); + IntRect srcRect = absoluteBoundingBoxRect(); + IntRect destRect = srcRect; + + // Move the box up, looking for a non-overlapping position: + while (RenderObject* box = overlappingObjectForRect(destRect)) { + if (m_cue->getWritingDirection() == TextTrackCue::Horizontal) + destRect.setY(box->absoluteBoundingBoxRect().y() - destRect.height()); + else + destRect.setX(box->absoluteBoundingBoxRect().x() - destRect.width()); + } + + if (rectIsWithinContainer(destRect)) { + newX += destRect.x() - srcRect.x(); + newY += destRect.y() - srcRect.y(); + return true; + } + + destRect = srcRect; + + // Move the box down, looking for a non-overlapping position: + while (RenderObject* box = overlappingObjectForRect(destRect)) { + if (m_cue->getWritingDirection() == TextTrackCue::Horizontal) + destRect.setY(box->absoluteBoundingBoxRect().maxY()); + else + destRect.setX(box->absoluteBoundingBoxRect().maxX()); + } + + if (rectIsWithinContainer(destRect)) { + newX += destRect.x() - srcRect.x(); + newY += destRect.y() - srcRect.y(); + return true; + } + + return false; +} + void RenderTextTrackCue::repositionCueSnapToLinesSet() { InlineFlowBox* firstLineBox; @@ -219,11 +312,48 @@ void RenderTextTrackCue::repositionCueSnapToLinesSet() // 19. Jump back to the step labeled step loop. } + + // Acommodate extra top and bottom padding, border or margin. + // Note: this is supported only for internal UA styling, not through the cue selector. + if (hasInlineDirectionBordersPaddingOrMargin()) + moveIfNecessaryToKeepWithinContainer(); +} + +void RenderTextTrackCue::repositionGenericCue() +{ + ASSERT(firstChild()); + InlineFlowBox* firstLineBox = toRenderInline(firstChild())->firstLineBox(); + if (static_cast<TextTrackCueGeneric*>(m_cue)->useDefaultPosition() && firstLineBox) { + LayoutUnit parentWidth = containingBlock()->logicalWidth(); + LayoutUnit width = firstLineBox->width(); + LayoutUnit right = (parentWidth / 2) - (width / 2); + setX(right); + } + repositionCueSnapToLinesNotSet(); } void RenderTextTrackCue::repositionCueSnapToLinesNotSet() { - // FIXME: Implement overlapping detection when snap-to-lines is not set. http://wkb.ug/84296 + // 3. If none of the boxes in boxes would overlap any of the boxes in output, and all the boxes in + // output are within the video's rendering area, then jump to the step labeled done positioning below. + if (!isOutside() && !isOverlapping()) + return; + + // 4. If there is a position to which the boxes in boxes can be moved while maintaining the relative + // positions of the boxes in boxes to each other such that none of the boxes in boxes would overlap + // any of the boxes in output, and all the boxes in output would be within the video's rendering area, + // then move the boxes in boxes to the closest such position to their current position, and then jump + // to the step labeled done positioning below. If there are multiple such positions that are equidistant + // from their current position, use the highest one amongst them; if there are several at that height, + // then use the leftmost one amongst them. + moveIfNecessaryToKeepWithinContainer(); + int x = 0; + int y = 0; + if (!findNonOverlappingPosition(x, y)) + return; + + setX(x); + setY(y); } } // namespace WebCore diff --git a/Source/WebCore/rendering/RenderTextTrackCue.h b/Source/WebCore/rendering/RenderTextTrackCue.h index 8973ad8a7..8ffcd91a4 100644 --- a/Source/WebCore/rendering/RenderTextTrackCue.h +++ b/Source/WebCore/rendering/RenderTextTrackCue.h @@ -35,6 +35,7 @@ namespace WebCore { +class RenderBox; class TextTrackCueBox; class RenderTextTrackCue : public RenderBlock { @@ -45,17 +46,22 @@ private: virtual void layout() OVERRIDE; bool isOutside() const; + bool rectIsWithinContainer(const IntRect&) const; bool isOverlapping() const; + RenderObject* overlappingObject() const; + RenderObject* overlappingObjectForRect(const IntRect&) const; bool shouldSwitchDirection(InlineFlowBox*, LayoutUnit) const; void moveBoxesByStep(LayoutUnit); bool switchDirection(bool&, LayoutUnit&); + void moveIfNecessaryToKeepWithinContainer(); + bool findNonOverlappingPosition(int& x, int& y) const; bool initializeLayoutParameters(InlineFlowBox*&, LayoutUnit&, LayoutUnit&); void placeBoxInDefaultPosition(LayoutUnit, bool&); void repositionCueSnapToLinesSet(); - void repositionCueSnapToLinesNotSet(); + void repositionGenericCue(); TextTrackCue* m_cue; FloatPoint m_fallbackPosition; diff --git a/Source/WebCore/rendering/RenderTheme.cpp b/Source/WebCore/rendering/RenderTheme.cpp index 0432dc32b..c0c584ddc 100644 --- a/Source/WebCore/rendering/RenderTheme.cpp +++ b/Source/WebCore/rendering/RenderTheme.cpp @@ -81,15 +81,14 @@ RenderTheme::RenderTheme() { } -void RenderTheme::adjustStyle(StyleResolver* styleResolver, RenderStyle* style, Element* e, - bool UAHasAppearance, const BorderData& border, const FillLayer& background, const Color& backgroundColor) +void RenderTheme::adjustStyle(StyleResolver* styleResolver, RenderStyle* style, Element* e, bool UAHasAppearance, const BorderData& border, const FillLayer& background, const Color& backgroundColor) { // Force inline and table display styles to be inline-block (except for table- which is block) ControlPart part = style->appearance(); - if (style->display() == INLINE || style->display() == INLINE_TABLE || style->display() == TABLE_ROW_GROUP || - style->display() == TABLE_HEADER_GROUP || style->display() == TABLE_FOOTER_GROUP || - style->display() == TABLE_ROW || style->display() == TABLE_COLUMN_GROUP || style->display() == TABLE_COLUMN || - style->display() == TABLE_CELL || style->display() == TABLE_CAPTION) + if (style->display() == INLINE || style->display() == INLINE_TABLE || style->display() == TABLE_ROW_GROUP + || style->display() == TABLE_HEADER_GROUP || style->display() == TABLE_FOOTER_GROUP + || style->display() == TABLE_ROW || style->display() == TABLE_COLUMN_GROUP || style->display() == TABLE_COLUMN + || style->display() == TABLE_CELL || style->display() == TABLE_CAPTION) style->setDisplay(INLINE_BLOCK); else if (style->display() == COMPACT || style->display() == RUN_IN || style->display() == LIST_ITEM || style->display() == TABLE) style->setDisplay(BLOCK); @@ -110,152 +109,152 @@ void RenderTheme::adjustStyle(StyleResolver* styleResolver, RenderStyle* style, #if USE(NEW_THEME) switch (part) { - case CheckboxPart: - case InnerSpinButtonPart: - case RadioPart: - case PushButtonPart: - case SquareButtonPart: - case DefaultButtonPart: - case ButtonPart: { - // Border - LengthBox borderBox(style->borderTopWidth(), style->borderRightWidth(), style->borderBottomWidth(), style->borderLeftWidth()); - borderBox = m_theme->controlBorder(part, style->font(), borderBox, style->effectiveZoom()); - if (borderBox.top().value() != static_cast<int>(style->borderTopWidth())) { - if (borderBox.top().value()) - style->setBorderTopWidth(borderBox.top().value()); - else - style->resetBorderTop(); - } - if (borderBox.right().value() != static_cast<int>(style->borderRightWidth())) { - if (borderBox.right().value()) - style->setBorderRightWidth(borderBox.right().value()); - else - style->resetBorderRight(); - } - if (borderBox.bottom().value() != static_cast<int>(style->borderBottomWidth())) { + case CheckboxPart: + case InnerSpinButtonPart: + case RadioPart: + case PushButtonPart: + case SquareButtonPart: + case DefaultButtonPart: + case ButtonPart: { + // Border + LengthBox borderBox(style->borderTopWidth(), style->borderRightWidth(), style->borderBottomWidth(), style->borderLeftWidth()); + borderBox = m_theme->controlBorder(part, style->font(), borderBox, style->effectiveZoom()); + if (borderBox.top().value() != static_cast<int>(style->borderTopWidth())) { + if (borderBox.top().value()) + style->setBorderTopWidth(borderBox.top().value()); + else + style->resetBorderTop(); + } + if (borderBox.right().value() != static_cast<int>(style->borderRightWidth())) { + if (borderBox.right().value()) + style->setBorderRightWidth(borderBox.right().value()); + else + style->resetBorderRight(); + } + if (borderBox.bottom().value() != static_cast<int>(style->borderBottomWidth())) { + style->setBorderBottomWidth(borderBox.bottom().value()); + if (borderBox.bottom().value()) style->setBorderBottomWidth(borderBox.bottom().value()); - if (borderBox.bottom().value()) - style->setBorderBottomWidth(borderBox.bottom().value()); - else - style->resetBorderBottom(); - } - if (borderBox.left().value() != static_cast<int>(style->borderLeftWidth())) { + else + style->resetBorderBottom(); + } + if (borderBox.left().value() != static_cast<int>(style->borderLeftWidth())) { + style->setBorderLeftWidth(borderBox.left().value()); + if (borderBox.left().value()) style->setBorderLeftWidth(borderBox.left().value()); - if (borderBox.left().value()) - style->setBorderLeftWidth(borderBox.left().value()); - else - style->resetBorderLeft(); - } - - // Padding - LengthBox paddingBox = m_theme->controlPadding(part, style->font(), style->paddingBox(), style->effectiveZoom()); - if (paddingBox != style->paddingBox()) - style->setPaddingBox(paddingBox); - - // Whitespace - if (m_theme->controlRequiresPreWhiteSpace(part)) - style->setWhiteSpace(PRE); + else + style->resetBorderLeft(); + } + + // Padding + LengthBox paddingBox = m_theme->controlPadding(part, style->font(), style->paddingBox(), style->effectiveZoom()); + if (paddingBox != style->paddingBox()) + style->setPaddingBox(paddingBox); + + // Whitespace + if (m_theme->controlRequiresPreWhiteSpace(part)) + style->setWhiteSpace(PRE); - // Width / Height - // The width and height here are affected by the zoom. - // FIXME: Check is flawed, since it doesn't take min-width/max-width into account. - LengthSize controlSize = m_theme->controlSize(part, style->font(), LengthSize(style->width(), style->height()), style->effectiveZoom()); - if (controlSize.width() != style->width()) - style->setWidth(controlSize.width()); - if (controlSize.height() != style->height()) - style->setHeight(controlSize.height()); - - // Min-Width / Min-Height - LengthSize minControlSize = m_theme->minimumControlSize(part, style->font(), style->effectiveZoom()); - if (minControlSize.width() != style->minWidth()) - style->setMinWidth(minControlSize.width()); - if (minControlSize.height() != style->minHeight()) - style->setMinHeight(minControlSize.height()); + // Width / Height + // The width and height here are affected by the zoom. + // FIXME: Check is flawed, since it doesn't take min-width/max-width into account. + LengthSize controlSize = m_theme->controlSize(part, style->font(), LengthSize(style->width(), style->height()), style->effectiveZoom()); + if (controlSize.width() != style->width()) + style->setWidth(controlSize.width()); + if (controlSize.height() != style->height()) + style->setHeight(controlSize.height()); - // Font - FontDescription controlFont = m_theme->controlFont(part, style->font(), style->effectiveZoom()); - if (controlFont != style->font().fontDescription()) { - // Reset our line-height - style->setLineHeight(RenderStyle::initialLineHeight()); + // Min-Width / Min-Height + LengthSize minControlSize = m_theme->minimumControlSize(part, style->font(), style->effectiveZoom()); + if (minControlSize.width() != style->minWidth()) + style->setMinWidth(minControlSize.width()); + if (minControlSize.height() != style->minHeight()) + style->setMinHeight(minControlSize.height()); - // Now update our font. - if (style->setFontDescription(controlFont)) - style->font().update(0); - } + // Font + FontDescription controlFont = m_theme->controlFont(part, style->font(), style->effectiveZoom()); + if (controlFont != style->font().fontDescription()) { + // Reset our line-height + style->setLineHeight(RenderStyle::initialLineHeight()); + + // Now update our font. + if (style->setFontDescription(controlFont)) + style->font().update(0); } - default: - break; + } + default: + break; } #endif // Call the appropriate style adjustment method based off the appearance value. switch (style->appearance()) { #if !USE(NEW_THEME) - case CheckboxPart: - return adjustCheckboxStyle(styleResolver, style, e); - case RadioPart: - return adjustRadioStyle(styleResolver, style, e); - case PushButtonPart: - case SquareButtonPart: - case DefaultButtonPart: - case ButtonPart: - return adjustButtonStyle(styleResolver, style, e); - case InnerSpinButtonPart: - return adjustInnerSpinButtonStyle(styleResolver, style, e); + case CheckboxPart: + return adjustCheckboxStyle(styleResolver, style, e); + case RadioPart: + return adjustRadioStyle(styleResolver, style, e); + case PushButtonPart: + case SquareButtonPart: + case DefaultButtonPart: + case ButtonPart: + return adjustButtonStyle(styleResolver, style, e); + case InnerSpinButtonPart: + return adjustInnerSpinButtonStyle(styleResolver, style, e); #endif - case TextFieldPart: - return adjustTextFieldStyle(styleResolver, style, e); - case TextAreaPart: - return adjustTextAreaStyle(styleResolver, style, e); - case MenulistPart: - return adjustMenuListStyle(styleResolver, style, e); - case MenulistButtonPart: - return adjustMenuListButtonStyle(styleResolver, style, e); - case MediaPlayButtonPart: - case MediaCurrentTimePart: - case MediaTimeRemainingPart: - case MediaEnterFullscreenButtonPart: - case MediaExitFullscreenButtonPart: - case MediaMuteButtonPart: - case MediaVolumeSliderContainerPart: - return adjustMediaControlStyle(styleResolver, style, e); - case MediaSliderPart: - case MediaVolumeSliderPart: - case MediaFullScreenVolumeSliderPart: - case SliderHorizontalPart: - case SliderVerticalPart: - return adjustSliderTrackStyle(styleResolver, style, e); - case SliderThumbHorizontalPart: - case SliderThumbVerticalPart: - return adjustSliderThumbStyle(styleResolver, style, e); - case SearchFieldPart: - return adjustSearchFieldStyle(styleResolver, style, e); - case SearchFieldCancelButtonPart: - return adjustSearchFieldCancelButtonStyle(styleResolver, style, e); - case SearchFieldDecorationPart: - return adjustSearchFieldDecorationStyle(styleResolver, style, e); - case SearchFieldResultsDecorationPart: - return adjustSearchFieldResultsDecorationStyle(styleResolver, style, e); - case SearchFieldResultsButtonPart: - return adjustSearchFieldResultsButtonStyle(styleResolver, style, e); + case TextFieldPart: + return adjustTextFieldStyle(styleResolver, style, e); + case TextAreaPart: + return adjustTextAreaStyle(styleResolver, style, e); + case MenulistPart: + return adjustMenuListStyle(styleResolver, style, e); + case MenulistButtonPart: + return adjustMenuListButtonStyle(styleResolver, style, e); + case MediaPlayButtonPart: + case MediaCurrentTimePart: + case MediaTimeRemainingPart: + case MediaEnterFullscreenButtonPart: + case MediaExitFullscreenButtonPart: + case MediaMuteButtonPart: + case MediaVolumeSliderContainerPart: + return adjustMediaControlStyle(styleResolver, style, e); + case MediaSliderPart: + case MediaVolumeSliderPart: + case MediaFullScreenVolumeSliderPart: + case SliderHorizontalPart: + case SliderVerticalPart: + return adjustSliderTrackStyle(styleResolver, style, e); + case SliderThumbHorizontalPart: + case SliderThumbVerticalPart: + return adjustSliderThumbStyle(styleResolver, style, e); + case SearchFieldPart: + return adjustSearchFieldStyle(styleResolver, style, e); + case SearchFieldCancelButtonPart: + return adjustSearchFieldCancelButtonStyle(styleResolver, style, e); + case SearchFieldDecorationPart: + return adjustSearchFieldDecorationStyle(styleResolver, style, e); + case SearchFieldResultsDecorationPart: + return adjustSearchFieldResultsDecorationStyle(styleResolver, style, e); + case SearchFieldResultsButtonPart: + return adjustSearchFieldResultsButtonStyle(styleResolver, style, e); #if ENABLE(PROGRESS_ELEMENT) - case ProgressBarPart: - return adjustProgressBarStyle(styleResolver, style, e); + case ProgressBarPart: + return adjustProgressBarStyle(styleResolver, style, e); #endif #if ENABLE(METER_ELEMENT) - case MeterPart: - case RelevancyLevelIndicatorPart: - case ContinuousCapacityLevelIndicatorPart: - case DiscreteCapacityLevelIndicatorPart: - case RatingLevelIndicatorPart: - return adjustMeterStyle(styleResolver, style, e); + case MeterPart: + case RelevancyLevelIndicatorPart: + case ContinuousCapacityLevelIndicatorPart: + case DiscreteCapacityLevelIndicatorPart: + case RatingLevelIndicatorPart: + return adjustMeterStyle(styleResolver, style, e); #endif #if ENABLE(INPUT_SPEECH) - case InputSpeechButtonPart: - return adjustInputFieldSpeechButtonStyle(styleResolver, style, e); + case InputSpeechButtonPart: + return adjustInputFieldSpeechButtonStyle(styleResolver, style, e); #endif - default: - break; + default: + break; } } @@ -276,117 +275,119 @@ bool RenderTheme::paint(RenderObject* o, const PaintInfo& paintInfo, const IntRe #if USE(NEW_THEME) switch (part) { - case CheckboxPart: - case RadioPart: - case PushButtonPart: - case SquareButtonPart: - case DefaultButtonPart: - case ButtonPart: - case InnerSpinButtonPart: - m_theme->paint(part, controlStatesForRenderer(o), const_cast<GraphicsContext*>(paintInfo.context), r, o->style()->effectiveZoom(), o->view()->frameView()); - return false; - default: - break; + case CheckboxPart: + case RadioPart: + case PushButtonPart: + case SquareButtonPart: + case DefaultButtonPart: + case ButtonPart: + case InnerSpinButtonPart: + m_theme->paint(part, controlStatesForRenderer(o), const_cast<GraphicsContext*>(paintInfo.context), r, o->style()->effectiveZoom(), o->view()->frameView()); + return false; + default: + break; } #endif // Call the appropriate paint method based off the appearance value. switch (part) { #if !USE(NEW_THEME) - case CheckboxPart: - return paintCheckbox(o, paintInfo, r); - case RadioPart: - return paintRadio(o, paintInfo, r); - case PushButtonPart: - case SquareButtonPart: - case DefaultButtonPart: - case ButtonPart: - return paintButton(o, paintInfo, r); - case InnerSpinButtonPart: - return paintInnerSpinButton(o, paintInfo, r); + case CheckboxPart: + return paintCheckbox(o, paintInfo, r); + case RadioPart: + return paintRadio(o, paintInfo, r); + case PushButtonPart: + case SquareButtonPart: + case DefaultButtonPart: + case ButtonPart: + return paintButton(o, paintInfo, r); + case InnerSpinButtonPart: + return paintInnerSpinButton(o, paintInfo, r); #endif - case MenulistPart: - return paintMenuList(o, paintInfo, r); + case MenulistPart: + return paintMenuList(o, paintInfo, r); #if ENABLE(METER_ELEMENT) - case MeterPart: - case RelevancyLevelIndicatorPart: - case ContinuousCapacityLevelIndicatorPart: - case DiscreteCapacityLevelIndicatorPart: - case RatingLevelIndicatorPart: - return paintMeter(o, paintInfo, r); + case MeterPart: + case RelevancyLevelIndicatorPart: + case ContinuousCapacityLevelIndicatorPart: + case DiscreteCapacityLevelIndicatorPart: + case RatingLevelIndicatorPart: + return paintMeter(o, paintInfo, r); #endif #if ENABLE(PROGRESS_ELEMENT) - case ProgressBarPart: - return paintProgressBar(o, paintInfo, r); + case ProgressBarPart: + return paintProgressBar(o, paintInfo, r); #endif - case SliderHorizontalPart: - case SliderVerticalPart: - return paintSliderTrack(o, paintInfo, r); - case SliderThumbHorizontalPart: - case SliderThumbVerticalPart: - return paintSliderThumb(o, paintInfo, r); - case MediaEnterFullscreenButtonPart: - case MediaExitFullscreenButtonPart: - return paintMediaFullscreenButton(o, paintInfo, r); - case MediaPlayButtonPart: - return paintMediaPlayButton(o, paintInfo, r); - case MediaOverlayPlayButtonPart: - return paintMediaOverlayPlayButton(o, paintInfo, r); - case MediaMuteButtonPart: - return paintMediaMuteButton(o, paintInfo, r); - case MediaSeekBackButtonPart: - return paintMediaSeekBackButton(o, paintInfo, r); - case MediaSeekForwardButtonPart: - return paintMediaSeekForwardButton(o, paintInfo, r); - case MediaRewindButtonPart: - return paintMediaRewindButton(o, paintInfo, r); - case MediaReturnToRealtimeButtonPart: - return paintMediaReturnToRealtimeButton(o, paintInfo, r); - case MediaToggleClosedCaptionsButtonPart: - return paintMediaToggleClosedCaptionsButton(o, paintInfo, r); - case MediaSliderPart: - return paintMediaSliderTrack(o, paintInfo, r); - case MediaSliderThumbPart: - return paintMediaSliderThumb(o, paintInfo, r); - case MediaVolumeSliderMuteButtonPart: - return paintMediaMuteButton(o, paintInfo, r); - case MediaVolumeSliderContainerPart: - return paintMediaVolumeSliderContainer(o, paintInfo, r); - case MediaVolumeSliderPart: - return paintMediaVolumeSliderTrack(o, paintInfo, r); - case MediaVolumeSliderThumbPart: - return paintMediaVolumeSliderThumb(o, paintInfo, r); - case MediaFullScreenVolumeSliderPart: - return paintMediaFullScreenVolumeSliderTrack(o, paintInfo, r); - case MediaFullScreenVolumeSliderThumbPart: - return paintMediaFullScreenVolumeSliderThumb(o, paintInfo, r); - case MediaTimeRemainingPart: - return paintMediaTimeRemaining(o, paintInfo, r); - case MediaCurrentTimePart: - return paintMediaCurrentTime(o, paintInfo, r); - case MediaControlsBackgroundPart: - return paintMediaControlsBackground(o, paintInfo, r); - case MenulistButtonPart: - case TextFieldPart: - case TextAreaPart: - case ListboxPart: - return true; - case SearchFieldPart: - return paintSearchField(o, paintInfo, r); - case SearchFieldCancelButtonPart: - return paintSearchFieldCancelButton(o, paintInfo, r); - case SearchFieldDecorationPart: - return paintSearchFieldDecoration(o, paintInfo, r); - case SearchFieldResultsDecorationPart: - return paintSearchFieldResultsDecoration(o, paintInfo, r); - case SearchFieldResultsButtonPart: - return paintSearchFieldResultsButton(o, paintInfo, r); + case SliderHorizontalPart: + case SliderVerticalPart: + return paintSliderTrack(o, paintInfo, r); + case SliderThumbHorizontalPart: + case SliderThumbVerticalPart: + return paintSliderThumb(o, paintInfo, r); + case MediaEnterFullscreenButtonPart: + case MediaExitFullscreenButtonPart: + return paintMediaFullscreenButton(o, paintInfo, r); + case MediaPlayButtonPart: + return paintMediaPlayButton(o, paintInfo, r); + case MediaOverlayPlayButtonPart: + return paintMediaOverlayPlayButton(o, paintInfo, r); + case MediaMuteButtonPart: + return paintMediaMuteButton(o, paintInfo, r); + case MediaSeekBackButtonPart: + return paintMediaSeekBackButton(o, paintInfo, r); + case MediaSeekForwardButtonPart: + return paintMediaSeekForwardButton(o, paintInfo, r); + case MediaRewindButtonPart: + return paintMediaRewindButton(o, paintInfo, r); + case MediaReturnToRealtimeButtonPart: + return paintMediaReturnToRealtimeButton(o, paintInfo, r); + case MediaToggleClosedCaptionsButtonPart: + return paintMediaToggleClosedCaptionsButton(o, paintInfo, r); + case MediaSliderPart: + return paintMediaSliderTrack(o, paintInfo, r); + case MediaSliderThumbPart: + return paintMediaSliderThumb(o, paintInfo, r); + case MediaVolumeSliderMuteButtonPart: + return paintMediaMuteButton(o, paintInfo, r); + case MediaVolumeSliderContainerPart: + return paintMediaVolumeSliderContainer(o, paintInfo, r); + case MediaVolumeSliderPart: + return paintMediaVolumeSliderTrack(o, paintInfo, r); + case MediaVolumeSliderThumbPart: + return paintMediaVolumeSliderThumb(o, paintInfo, r); + case MediaFullScreenVolumeSliderPart: + return paintMediaFullScreenVolumeSliderTrack(o, paintInfo, r); + case MediaFullScreenVolumeSliderThumbPart: + return paintMediaFullScreenVolumeSliderThumb(o, paintInfo, r); + case MediaTimeRemainingPart: + return paintMediaTimeRemaining(o, paintInfo, r); + case MediaCurrentTimePart: + return paintMediaCurrentTime(o, paintInfo, r); + case MediaControlsBackgroundPart: + return paintMediaControlsBackground(o, paintInfo, r); + case MenulistButtonPart: + case TextFieldPart: + case TextAreaPart: + case ListboxPart: + return true; + case SearchFieldPart: + return paintSearchField(o, paintInfo, r); + case SearchFieldCancelButtonPart: + return paintSearchFieldCancelButton(o, paintInfo, r); + case SearchFieldDecorationPart: + return paintSearchFieldDecoration(o, paintInfo, r); + case SearchFieldResultsDecorationPart: + return paintSearchFieldResultsDecoration(o, paintInfo, r); + case SearchFieldResultsButtonPart: + return paintSearchFieldResultsButton(o, paintInfo, r); + case SnapshottedPluginOverlayPart: + return paintSnapshottedPluginOverlay(o, paintInfo, r); #if ENABLE(INPUT_SPEECH) - case InputSpeechButtonPart: - return paintInputFieldSpeechButton(o, paintInfo, r); + case InputSpeechButtonPart: + return paintInputFieldSpeechButton(o, paintInfo, r); #endif - default: - break; + default: + break; } return true; // We don't support the appearance, so let the normal background/border paint. @@ -399,44 +400,44 @@ bool RenderTheme::paintBorderOnly(RenderObject* o, const PaintInfo& paintInfo, c // Call the appropriate paint method based off the appearance value. switch (o->style()->appearance()) { - case TextFieldPart: - return paintTextField(o, paintInfo, r); - case ListboxPart: - case TextAreaPart: - return paintTextArea(o, paintInfo, r); - case MenulistButtonPart: - case SearchFieldPart: - return true; - case CheckboxPart: - case RadioPart: - case PushButtonPart: - case SquareButtonPart: - case DefaultButtonPart: - case ButtonPart: - case MenulistPart: + case TextFieldPart: + return paintTextField(o, paintInfo, r); + case ListboxPart: + case TextAreaPart: + return paintTextArea(o, paintInfo, r); + case MenulistButtonPart: + case SearchFieldPart: + return true; + case CheckboxPart: + case RadioPart: + case PushButtonPart: + case SquareButtonPart: + case DefaultButtonPart: + case ButtonPart: + case MenulistPart: #if ENABLE(METER_ELEMENT) - case MeterPart: - case RelevancyLevelIndicatorPart: - case ContinuousCapacityLevelIndicatorPart: - case DiscreteCapacityLevelIndicatorPart: - case RatingLevelIndicatorPart: + case MeterPart: + case RelevancyLevelIndicatorPart: + case ContinuousCapacityLevelIndicatorPart: + case DiscreteCapacityLevelIndicatorPart: + case RatingLevelIndicatorPart: #endif #if ENABLE(PROGRESS_ELEMENT) - case ProgressBarPart: + case ProgressBarPart: #endif - case SliderHorizontalPart: - case SliderVerticalPart: - case SliderThumbHorizontalPart: - case SliderThumbVerticalPart: - case SearchFieldCancelButtonPart: - case SearchFieldDecorationPart: - case SearchFieldResultsDecorationPart: - case SearchFieldResultsButtonPart: + case SliderHorizontalPart: + case SliderVerticalPart: + case SliderThumbHorizontalPart: + case SliderThumbVerticalPart: + case SearchFieldCancelButtonPart: + case SearchFieldDecorationPart: + case SearchFieldResultsDecorationPart: + case SearchFieldResultsButtonPart: #if ENABLE(INPUT_SPEECH) - case InputSpeechButtonPart: + case InputSpeechButtonPart: #endif - default: - break; + default: + break; } return false; @@ -449,42 +450,42 @@ bool RenderTheme::paintDecorations(RenderObject* o, const PaintInfo& paintInfo, // Call the appropriate paint method based off the appearance value. switch (o->style()->appearance()) { - case MenulistButtonPart: - return paintMenuListButton(o, paintInfo, r); - case TextFieldPart: - case TextAreaPart: - case ListboxPart: - case CheckboxPart: - case RadioPart: - case PushButtonPart: - case SquareButtonPart: - case DefaultButtonPart: - case ButtonPart: - case MenulistPart: + case MenulistButtonPart: + return paintMenuListButton(o, paintInfo, r); + case TextFieldPart: + case TextAreaPart: + case ListboxPart: + case CheckboxPart: + case RadioPart: + case PushButtonPart: + case SquareButtonPart: + case DefaultButtonPart: + case ButtonPart: + case MenulistPart: #if ENABLE(METER_ELEMENT) - case MeterPart: - case RelevancyLevelIndicatorPart: - case ContinuousCapacityLevelIndicatorPart: - case DiscreteCapacityLevelIndicatorPart: - case RatingLevelIndicatorPart: + case MeterPart: + case RelevancyLevelIndicatorPart: + case ContinuousCapacityLevelIndicatorPart: + case DiscreteCapacityLevelIndicatorPart: + case RatingLevelIndicatorPart: #endif #if ENABLE(PROGRESS_ELEMENT) - case ProgressBarPart: + case ProgressBarPart: #endif - case SliderHorizontalPart: - case SliderVerticalPart: - case SliderThumbHorizontalPart: - case SliderThumbVerticalPart: - case SearchFieldPart: - case SearchFieldCancelButtonPart: - case SearchFieldDecorationPart: - case SearchFieldResultsDecorationPart: - case SearchFieldResultsButtonPart: + case SliderHorizontalPart: + case SliderVerticalPart: + case SliderThumbHorizontalPart: + case SliderThumbVerticalPart: + case SearchFieldPart: + case SearchFieldCancelButtonPart: + case SearchFieldDecorationPart: + case SearchFieldResultsDecorationPart: + case SearchFieldResultsButtonPart: #if ENABLE(INPUT_SPEECH) - case InputSpeechButtonPart: + case InputSpeechButtonPart: #endif - default: - break; + default: + break; } return false; @@ -494,7 +495,7 @@ bool RenderTheme::paintDecorations(RenderObject* o, const PaintInfo& paintInfo, String RenderTheme::formatMediaControlsTime(float time) const { - if (!isfinite(time)) + if (!std::isfinite(time)) time = 0; int seconds = (int)fabsf(time); int hours = seconds / (60 * 60); @@ -523,7 +524,7 @@ String RenderTheme::formatMediaControlsRemainingTime(float currentTime, float du IntPoint RenderTheme::volumeSliderOffsetFromMuteButton(RenderBox* muteButtonBox, const IntSize& size) const { int y = -size.height(); - FloatPoint absPoint = muteButtonBox->localToAbsolute(FloatPoint(muteButtonBox->pixelSnappedOffsetLeft(), y), IsFixed | UseTransforms | SnapOffsetForTransforms); + FloatPoint absPoint = muteButtonBox->localToAbsolute(FloatPoint(muteButtonBox->pixelSnappedOffsetLeft(), y), IsFixed | UseTransforms); if (absPoint.y() < 0) y = muteButtonBox->height(); return IntPoint(0, y); @@ -632,13 +633,6 @@ Color RenderTheme::platformInactiveListBoxSelectionForegroundColor() const return platformInactiveSelectionForegroundColor(); } -#if ENABLE(CALENDAR_PICKER) -CString RenderTheme::extraCalendarPickerStyleSheet() -{ - return CString(); -} -#endif - int RenderTheme::baselinePosition(const RenderObject* o) const { if (!o->isBox()) @@ -660,32 +654,31 @@ bool RenderTheme::isControlContainer(ControlPart appearance) const return appearance != CheckboxPart && appearance != RadioPart; } -bool RenderTheme::isControlStyled(const RenderStyle* style, const BorderData& border, const FillLayer& background, - const Color& backgroundColor) const +bool RenderTheme::isControlStyled(const RenderStyle* style, const BorderData& border, const FillLayer& background, const Color& backgroundColor) const { switch (style->appearance()) { - case PushButtonPart: - case SquareButtonPart: - case DefaultButtonPart: - case ButtonPart: - case ListboxPart: - case MenulistPart: - case ProgressBarPart: - case MeterPart: - case RelevancyLevelIndicatorPart: - case ContinuousCapacityLevelIndicatorPart: - case DiscreteCapacityLevelIndicatorPart: - case RatingLevelIndicatorPart: - // FIXME: Uncomment this when making search fields style-able. - // case SearchFieldPart: - case TextFieldPart: - case TextAreaPart: - // Test the style to see if the UA border and background match. - return (style->border() != border || - *style->backgroundLayers() != background || - style->visitedDependentColor(CSSPropertyBackgroundColor) != backgroundColor); - default: - return false; + case PushButtonPart: + case SquareButtonPart: + case DefaultButtonPart: + case ButtonPart: + case ListboxPart: + case MenulistPart: + case ProgressBarPart: + case MeterPart: + case RelevancyLevelIndicatorPart: + case ContinuousCapacityLevelIndicatorPart: + case DiscreteCapacityLevelIndicatorPart: + case RatingLevelIndicatorPart: + // FIXME: Uncomment this when making search fields style-able. + // case SearchFieldPart: + case TextFieldPart: + case TextAreaPart: + // Test the style to see if the UA border and background match. + return (style->border() != border + || *style->backgroundLayers() != background + || style->visitedDependentColor(CSSPropertyBackgroundColor) != backgroundColor); + default: + return false; } } @@ -787,7 +780,7 @@ bool RenderTheme::isIndeterminate(const RenderObject* o) const if (!inputElement) return false; - return inputElement->isIndeterminate(); + return inputElement->shouldAppearIndeterminate(); } bool RenderTheme::isEnabled(const RenderObject* o) const @@ -795,36 +788,37 @@ bool RenderTheme::isEnabled(const RenderObject* o) const Node* node = o->node(); if (!node || !node->isElementNode()) return true; - return static_cast<Element*>(node)->isEnabledFormControl(); + return !toElement(node)->isDisabledFormControl(); } bool RenderTheme::isFocused(const RenderObject* o) const { Node* node = o->node(); - if (!node) + if (!node || !node->isElementNode()) return false; - node = node->focusDelegate(); - Document* document = node->document(); + Element* focusDelegate = toElement(node)->focusDelegate(); + Document* document = focusDelegate->document(); Frame* frame = document->frame(); - return node == document->focusedNode() && frame && frame->selection()->isFocusedAndActive(); + return focusDelegate == document->focusedElement() && frame && frame->selection()->isFocusedAndActive(); } bool RenderTheme::isPressed(const RenderObject* o) const { - if (!o->node()) + if (!o->node() || !o->node()->isElementNode()) return false; - return o->node()->active(); + return toElement(o->node())->active(); } bool RenderTheme::isSpinUpButtonPartPressed(const RenderObject* o) const { Node* node = o->node(); - if (!node || !node->active() || !node->isElementNode() - || !static_cast<Element*>(node)->isSpinButtonElement()) + if (!node || !node->isElementNode()) return false; - SpinButtonElement* element = static_cast<SpinButtonElement*>(node); - return element->upDownState() == SpinButtonElement::Up; + Element* element = toElement(node); + if (!element->active() || !element->isSpinButtonElement()) + return false; + return static_cast<SpinButtonElement*>(element)->upDownState() == SpinButtonElement::Up; } bool RenderTheme::isReadOnlyControl(const RenderObject* o) const @@ -832,16 +826,16 @@ bool RenderTheme::isReadOnlyControl(const RenderObject* o) const Node* node = o->node(); if (!node || !node->isElementNode()) return false; - return static_cast<Element*>(node)->shouldMatchReadOnlySelector(); + return toElement(node)->matchesReadOnlyPseudoClass(); } bool RenderTheme::isHovered(const RenderObject* o) const { Node* node = o->node(); - if (!node) + if (!node || !node->isElementNode()) return false; - if (!node->isElementNode() || !static_cast<Element*>(node)->isSpinButtonElement()) - return node->hovered(); + if (!toElement(node)->isSpinButtonElement()) + return toElement(node)->hovered(); SpinButtonElement* element = static_cast<SpinButtonElement*>(node); return element->hovered() && element->upDownState() != SpinButtonElement::Indeterminate; } @@ -849,7 +843,7 @@ bool RenderTheme::isHovered(const RenderObject* o) const bool RenderTheme::isSpinUpButtonPartHovered(const RenderObject* o) const { Node* node = o->node(); - if (!node || !node->isElementNode() || !static_cast<Element*>(node)->isSpinButtonElement()) + if (!node || !node->isElementNode() || !toElement(node)->isSpinButtonElement()) return false; SpinButtonElement* element = static_cast<SpinButtonElement*>(node); return element->upDownState() == SpinButtonElement::Up; @@ -865,7 +859,7 @@ bool RenderTheme::isDefault(const RenderObject* o) const return false; Settings* settings = o->document()->settings(); - if (!settings || !settings->inApplicationChromeMode()) + if (!settings || !settings->applicationChromeMode()) return false; return o->style()->appearance() == DefaultButtonPart; @@ -909,8 +903,9 @@ void RenderTheme::adjustRadioStyle(StyleResolver*, RenderStyle* style, Element*) void RenderTheme::adjustButtonStyle(StyleResolver*, RenderStyle* style, Element*) const { - // Most platforms will completely honor all CSS, and so we have no need to adjust the style - // at all by default. We will still allow the theme a crack at setting up a desired vertical size. + // Most platforms will completely honor all CSS, and so we have no need to + // adjust the style at all by default. We will still allow the theme a crack + // at setting up a desired vertical size. setButtonSize(style); } @@ -1037,8 +1032,8 @@ void RenderTheme::paintSliderTicks(RenderObject* o, const PaintInfo& paintInfo, GraphicsContextStateSaver stateSaver(*paintInfo.context); paintInfo.context->setFillColor(o->style()->visitedDependentColor(CSSPropertyColor), ColorSpaceDeviceRGB); for (unsigned i = 0; Node* node = options->item(i); i++) { - ASSERT(node->hasTagName(optionTag)); - HTMLOptionElement* optionElement = static_cast<HTMLOptionElement*>(node); + ASSERT(isHTMLOptionElement(node)); + HTMLOptionElement* optionElement = toHTMLOptionElement(node); String value = optionElement->value(); if (!input->isValidValue(value)) continue; @@ -1129,70 +1124,72 @@ void RenderTheme::platformColorsDidChange() m_activeListBoxSelectionBackgroundColor = Color(); m_inactiveListBoxSelectionForegroundColor = Color(); - Page::scheduleForcedStyleRecalcForAllPages(); + Page::updateStyleForAllPagesAfterGlobalChangeInEnvironment(); } -Color RenderTheme::systemColor(int cssValueId) const +Color RenderTheme::systemColor(CSSValueID cssValueId) const { switch (cssValueId) { - case CSSValueActiveborder: - return 0xFFFFFFFF; - case CSSValueActivecaption: - return 0xFFCCCCCC; - case CSSValueAppworkspace: - return 0xFFFFFFFF; - case CSSValueBackground: - return 0xFF6363CE; - case CSSValueButtonface: - return 0xFFC0C0C0; - case CSSValueButtonhighlight: - return 0xFFDDDDDD; - case CSSValueButtonshadow: - return 0xFF888888; - case CSSValueButtontext: - return 0xFF000000; - case CSSValueCaptiontext: - return 0xFF000000; - case CSSValueGraytext: - return 0xFF808080; - case CSSValueHighlight: - return 0xFFB5D5FF; - case CSSValueHighlighttext: - return 0xFF000000; - case CSSValueInactiveborder: - return 0xFFFFFFFF; - case CSSValueInactivecaption: - return 0xFFFFFFFF; - case CSSValueInactivecaptiontext: - return 0xFF7F7F7F; - case CSSValueInfobackground: - return 0xFFFBFCC5; - case CSSValueInfotext: - return 0xFF000000; - case CSSValueMenu: - return 0xFFC0C0C0; - case CSSValueMenutext: - return 0xFF000000; - case CSSValueScrollbar: - return 0xFFFFFFFF; - case CSSValueText: - return 0xFF000000; - case CSSValueThreeddarkshadow: - return 0xFF666666; - case CSSValueThreedface: - return 0xFFC0C0C0; - case CSSValueThreedhighlight: - return 0xFFDDDDDD; - case CSSValueThreedlightshadow: - return 0xFFC0C0C0; - case CSSValueThreedshadow: - return 0xFF888888; - case CSSValueWindow: - return 0xFFFFFFFF; - case CSSValueWindowframe: - return 0xFFCCCCCC; - case CSSValueWindowtext: - return 0xFF000000; + case CSSValueActiveborder: + return 0xFFFFFFFF; + case CSSValueActivecaption: + return 0xFFCCCCCC; + case CSSValueAppworkspace: + return 0xFFFFFFFF; + case CSSValueBackground: + return 0xFF6363CE; + case CSSValueButtonface: + return 0xFFC0C0C0; + case CSSValueButtonhighlight: + return 0xFFDDDDDD; + case CSSValueButtonshadow: + return 0xFF888888; + case CSSValueButtontext: + return 0xFF000000; + case CSSValueCaptiontext: + return 0xFF000000; + case CSSValueGraytext: + return 0xFF808080; + case CSSValueHighlight: + return 0xFFB5D5FF; + case CSSValueHighlighttext: + return 0xFF000000; + case CSSValueInactiveborder: + return 0xFFFFFFFF; + case CSSValueInactivecaption: + return 0xFFFFFFFF; + case CSSValueInactivecaptiontext: + return 0xFF7F7F7F; + case CSSValueInfobackground: + return 0xFFFBFCC5; + case CSSValueInfotext: + return 0xFF000000; + case CSSValueMenu: + return 0xFFC0C0C0; + case CSSValueMenutext: + return 0xFF000000; + case CSSValueScrollbar: + return 0xFFFFFFFF; + case CSSValueText: + return 0xFF000000; + case CSSValueThreeddarkshadow: + return 0xFF666666; + case CSSValueThreedface: + return 0xFFC0C0C0; + case CSSValueThreedhighlight: + return 0xFFDDDDDD; + case CSSValueThreedlightshadow: + return 0xFFC0C0C0; + case CSSValueThreedshadow: + return 0xFF888888; + case CSSValueWindow: + return 0xFFFFFFFF; + case CSSValueWindowframe: + return 0xFFCCCCCC; + case CSSValueWindowtext: + return 0xFF000000; + default: + break; } return Color(); } diff --git a/Source/WebCore/rendering/RenderTheme.h b/Source/WebCore/rendering/RenderTheme.h index bbb86e192..d3b92b2b1 100644 --- a/Source/WebCore/rendering/RenderTheme.h +++ b/Source/WebCore/rendering/RenderTheme.h @@ -86,17 +86,15 @@ public: // RenderThemeMac.cpp for Mac OS X. // These methods return the theme's extra style sheets rules, to let each platform - // adjust the default CSS rules in html.css, quirks.css, or mediaControls.css + // adjust the default CSS rules in html.css, quirks.css, mediaControls.css, or plugIns.css virtual String extraDefaultStyleSheet() { return String(); } virtual String extraQuirksStyleSheet() { return String(); } + virtual String extraPlugInsStyleSheet() { return String(); } #if ENABLE(VIDEO) - virtual String extraMediaControlsStyleSheet() { return String(); }; + virtual String extraMediaControlsStyleSheet() { return String(); } #endif #if ENABLE(FULLSCREEN_API) - virtual String extraFullScreenStyleSheet() { return String(); }; -#endif -#if ENABLE(CALENDAR_PICKER) - virtual CString extraCalendarPickerStyleSheet(); + virtual String extraFullScreenStyleSheet() { return String(); } #endif // A method to obtain the baseline position for a "leaf" control. This will only be used if a baseline @@ -138,11 +136,6 @@ public: // A method asking if the platform is able to show datalist suggestions for a given input type. virtual bool supportsDataListUI(const AtomicString&) const { return false; } -#if ENABLE(INPUT_MULTIPLE_FIELDS_UI) - // A method asking if the platform is able to show a calendar picker for a given input type. - virtual bool supportsCalendarPicker(const AtomicString&) const { return false; } -#endif - // Text selection colors. Color activeSelectionBackgroundColor() const; Color inactiveSelectionBackgroundColor() const; @@ -173,8 +166,8 @@ public: virtual double caretBlinkInterval() const { return 0.5; } // System fonts and colors for CSS. - virtual void systemFont(int cssValueId, FontDescription&) const = 0; - virtual Color systemColor(int cssValueId) const; + virtual void systemFont(CSSValueID, FontDescription&) const = 0; + virtual Color systemColor(CSSValueID) const; virtual int minimumMenuListSize(RenderStyle*) const { return 0; } @@ -207,7 +200,6 @@ public: virtual bool usesVerticalVolumeSlider() const { return true; } virtual double mediaControlsFadeInDuration() { return 0.1; } virtual double mediaControlsFadeOutDuration() { return 0.3; } - virtual double timeWithoutMouseMovementBeforeHidingControls() { return 3.0; } virtual String formatMediaControlsTime(float time) const; virtual String formatMediaControlsCurrentTime(float currentTime, float duration) const; virtual String formatMediaControlsRemainingTime(float currentTime, float duration) const; @@ -347,6 +339,8 @@ protected: virtual bool paintMediaFullScreenVolumeSliderTrack(RenderObject*, const PaintInfo&, const IntRect&) { return true; } virtual bool paintMediaFullScreenVolumeSliderThumb(RenderObject*, const PaintInfo&, const IntRect&) { return true; } + virtual bool paintSnapshottedPluginOverlay(RenderObject*, const PaintInfo&, const IntRect&) { return true; } + public: // Methods for state querying ControlStates controlStatesForRenderer(const RenderObject* o) const; diff --git a/Source/WebCore/rendering/RenderThemeChromiumAndroid.cpp b/Source/WebCore/rendering/RenderThemeChromiumAndroid.cpp deleted file mode 100644 index d25a80444..000000000 --- a/Source/WebCore/rendering/RenderThemeChromiumAndroid.cpp +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "RenderThemeChromiumAndroid.h" - -#include "CSSValueKeywords.h" -#include "Color.h" -#include "LayoutTestSupport.h" -#include "PaintInfo.h" -#include "RenderMediaControlsChromium.h" -#include "RenderObject.h" -#include "RenderProgress.h" -#include "RenderSlider.h" -#include "ScrollbarTheme.h" -#include "UserAgentStyleSheets.h" - -#include <public/Platform.h> -#include <public/android/WebThemeEngine.h> - -namespace WebCore { - -PassRefPtr<RenderTheme> RenderThemeChromiumAndroid::create() -{ - return adoptRef(new RenderThemeChromiumAndroid()); -} - -PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page) -{ - static RenderTheme* renderTheme = RenderThemeChromiumAndroid::create().leakRef(); - return renderTheme; -} - -RenderThemeChromiumAndroid::~RenderThemeChromiumAndroid() -{ -} - -Color RenderThemeChromiumAndroid::systemColor(int cssValueId) const -{ - if (isRunningLayoutTest() && cssValueId == CSSValueButtonface) { - // Match Chromium Linux' button color in layout tests. - static const Color linuxButtonGrayColor(0xffdddddd); - return linuxButtonGrayColor; - } - return RenderTheme::systemColor(cssValueId); -} - -String RenderThemeChromiumAndroid::extraMediaControlsStyleSheet() -{ - return String(mediaControlsChromiumAndroidUserAgentStyleSheet, sizeof(mediaControlsChromiumAndroidUserAgentStyleSheet)); -} - -String RenderThemeChromiumAndroid::extraDefaultStyleSheet() -{ - return RenderThemeChromiumLinux::extraDefaultStyleSheet() + - String(themeChromiumAndroidUserAgentStyleSheet, sizeof(themeChromiumAndroidUserAgentStyleSheet)); -} - -void RenderThemeChromiumAndroid::adjustInnerSpinButtonStyle(StyleResolver*, RenderStyle* style, Element*) const -{ - if (isRunningLayoutTest()) { - // Match Chromium Linux spin button style in layout tests. - // FIXME: Consider removing the conditional if a future Android theme matches this. - IntSize size = WebKit::Platform::current()->themeEngine()->getSize(WebKit::WebThemeEngine::PartInnerSpinButton); - - style->setWidth(Length(size.width(), Fixed)); - style->setMinWidth(Length(size.width(), Fixed)); - } -} - -bool RenderThemeChromiumAndroid::paintMediaOverlayPlayButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) -{ -#if ENABLE(VIDEO) - return RenderMediaControlsChromium::paintMediaControlsPart(MediaOverlayPlayButton, object, paintInfo, rect); -#else - UNUSED_PARAM(object); - UNUSED_PARAM(paintInfo); - UNUSED_PARAM(rect); - return false; -#endif -} - -int RenderThemeChromiumAndroid::menuListArrowPadding() const -{ - // We cannot use the scrollbar thickness here, as it's width is 0 on Android. - // Instead, use the width of the scrollbar down arrow. - IntSize scrollbarSize = WebKit::Platform::current()->themeEngine()->getSize(WebKit::WebThemeEngine::PartScrollbarDownArrow); - return scrollbarSize.width(); -} - -} // namespace WebCore diff --git a/Source/WebCore/rendering/RenderThemeChromiumAndroid.h b/Source/WebCore/rendering/RenderThemeChromiumAndroid.h deleted file mode 100644 index 027628e81..000000000 --- a/Source/WebCore/rendering/RenderThemeChromiumAndroid.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef RenderThemeChromiumAndroid_h -#define RenderThemeChromiumAndroid_h - -#include "RenderThemeChromiumLinux.h" - -namespace WebCore { - -class RenderThemeChromiumAndroid : public RenderThemeChromiumLinux { -public: - static PassRefPtr<RenderTheme> create(); - virtual String extraDefaultStyleSheet() OVERRIDE; - - virtual Color systemColor(int cssValidId) const OVERRIDE; - - virtual void adjustInnerSpinButtonStyle(StyleResolver*, RenderStyle*, Element*) const OVERRIDE; - - virtual bool delegatesMenuListRendering() const OVERRIDE { return true; } - - virtual bool paintMediaOverlayPlayButton(RenderObject*, const PaintInfo&, const IntRect&) OVERRIDE; - -#if ENABLE(VIDEO) - virtual String extraMediaControlsStyleSheet() OVERRIDE; -#endif - -#if ENABLE(TOUCH_EVENTS) - virtual Color platformTapHighlightColor() const OVERRIDE - { - return RenderThemeChromiumAndroid::defaultTapHighlightColor; - } -#endif - - virtual Color platformActiveSelectionBackgroundColor() const OVERRIDE - { - return RenderThemeChromiumAndroid::defaultActiveSelectionBackgroundColor; - } - -protected: - virtual int menuListArrowPadding() const OVERRIDE; - -private: - virtual ~RenderThemeChromiumAndroid(); - - static const RGBA32 defaultTapHighlightColor = 0x6633b5e5; - static const RGBA32 defaultActiveSelectionBackgroundColor = 0x6633b5e5; -}; - -} // namespace WebCore - -#endif // RenderThemeChromiumAndroid_h diff --git a/Source/WebCore/rendering/RenderThemeChromiumCommon.cpp b/Source/WebCore/rendering/RenderThemeChromiumCommon.cpp deleted file mode 100644 index 51f87c5e3..000000000 --- a/Source/WebCore/rendering/RenderThemeChromiumCommon.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2012 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "RenderThemeChromiumCommon.h" - -#include "InputTypeNames.h" -#include "LayoutUnit.h" - -namespace WebCore { - -bool RenderThemeChromiumCommon::supportsDataListUI(const AtomicString& type) -{ - return type == InputTypeNames::text() || type == InputTypeNames::search() || type == InputTypeNames::url() - || type == InputTypeNames::telephone() || type == InputTypeNames::email() || type == InputTypeNames::number() -#if ENABLE(INPUT_TYPE_COLOR) - || type == InputTypeNames::color() -#endif -#if ENABLE(CALENDAR_PICKER) - || type == InputTypeNames::date() -#endif - || type == InputTypeNames::datetime() - || type == InputTypeNames::datetimelocal() - || type == InputTypeNames::month() - || type == InputTypeNames::week() - || type == InputTypeNames::time() - || type == InputTypeNames::range(); -} - -#if ENABLE(INPUT_MULTIPLE_FIELDS_UI) -bool RenderThemeChromiumCommon::supportsCalendarPicker(const AtomicString& type) -{ - return type == InputTypeNames::date() - || type == InputTypeNames::datetime() - || type == InputTypeNames::datetimelocal() - || type == InputTypeNames::month() - || type == InputTypeNames::week(); -} -#endif - -LayoutUnit RenderThemeChromiumCommon::sliderTickSnappingThreshold() -{ - return 5; -} - -} diff --git a/Source/WebCore/rendering/RenderThemeChromiumCommon.h b/Source/WebCore/rendering/RenderThemeChromiumCommon.h deleted file mode 100644 index 5f6d71a6e..000000000 --- a/Source/WebCore/rendering/RenderThemeChromiumCommon.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2012 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - -#ifndef RenderThemeChromiumCommon_h -#define RenderThemeChromiumCommon_h - -#include "LayoutUnit.h" -#include <wtf/text/AtomicString.h> - -namespace WebCore { - -class RenderThemeChromiumCommon { -public: - static bool supportsDataListUI(const AtomicString& type); -#if ENABLE(INPUT_MULTIPLE_FIELDS_UI) - static bool supportsCalendarPicker(const AtomicString& type); -#endif - static LayoutUnit sliderTickSnappingThreshold(); -}; - -} - -#endif // RenderThemeChromiumCommon_h diff --git a/Source/WebCore/rendering/RenderThemeChromiumDefault.cpp b/Source/WebCore/rendering/RenderThemeChromiumDefault.cpp deleted file mode 100644 index d643729ad..000000000 --- a/Source/WebCore/rendering/RenderThemeChromiumDefault.cpp +++ /dev/null @@ -1,389 +0,0 @@ -/* - * Copyright (C) 2007 Apple Inc. - * Copyright (C) 2007 Alp Toker <alp@atoker.com> - * Copyright (C) 2008 Collabora Ltd. - * Copyright (C) 2008, 2009 Google Inc. - * Copyright (C) 2009 Kenneth Rohde Christiansen - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#include "config.h" -#include "RenderThemeChromiumDefault.h" - -#include "CSSValueKeywords.h" -#include "Color.h" -#include "PaintInfo.h" -#include "PlatformContextSkia.h" -#include "RenderObject.h" -#include "RenderProgress.h" -#include "RenderSlider.h" -#include "ScrollbarTheme.h" -#include "UserAgentStyleSheets.h" -#include <public/Platform.h> -#include <public/WebRect.h> -#include <public/default/WebThemeEngine.h> - -namespace WebCore { - -unsigned RenderThemeChromiumDefault::m_activeSelectionBackgroundColor = - 0xff1e90ff; -unsigned RenderThemeChromiumDefault::m_activeSelectionForegroundColor = - Color::black; -unsigned RenderThemeChromiumDefault::m_inactiveSelectionBackgroundColor = - 0xffc8c8c8; -unsigned RenderThemeChromiumDefault::m_inactiveSelectionForegroundColor = - 0xff323232; - -double RenderThemeChromiumDefault::m_caretBlinkInterval; - -static const unsigned defaultButtonBackgroundColor = 0xffdddddd; - -static WebKit::WebThemeEngine::State getWebThemeState(const RenderTheme* theme, const RenderObject* o) -{ - if (!theme->isEnabled(o)) - return WebKit::WebThemeEngine::StateDisabled; - if (theme->isPressed(o)) - return WebKit::WebThemeEngine::StatePressed; - if (theme->isHovered(o)) - return WebKit::WebThemeEngine::StateHover; - - return WebKit::WebThemeEngine::StateNormal; -} - -PassRefPtr<RenderTheme> RenderThemeChromiumDefault::create() -{ - return adoptRef(new RenderThemeChromiumDefault()); -} - -// RenderTheme::themeForPage for Android is defined in RenderThemeChromiumAndroid.cpp. -#if !OS(ANDROID) -PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page) -{ - static RenderTheme* rt = RenderThemeChromiumDefault::create().leakRef(); - return rt; -} -#endif - -RenderThemeChromiumDefault::RenderThemeChromiumDefault() -{ - m_caretBlinkInterval = RenderTheme::caretBlinkInterval(); -} - -RenderThemeChromiumDefault::~RenderThemeChromiumDefault() -{ -} - -Color RenderThemeChromiumDefault::systemColor(int cssValueId) const -{ - static const Color defaultButtonGrayColor(0xffdddddd); - - if (cssValueId == CSSValueButtonface) - return defaultButtonGrayColor; - return RenderTheme::systemColor(cssValueId); -} - -String RenderThemeChromiumDefault::extraDefaultStyleSheet() -{ -#if !OS(WINDOWS) - return RenderThemeChromiumSkia::extraDefaultStyleSheet() + - String(themeChromiumLinuxUserAgentStyleSheet, sizeof(themeChromiumLinuxUserAgentStyleSheet)); -#else - return RenderThemeChromiumSkia::extraDefaultStyleSheet(); -#endif -} - -bool RenderThemeChromiumDefault::controlSupportsTints(const RenderObject* o) const -{ - return isEnabled(o); -} - -Color RenderThemeChromiumDefault::activeListBoxSelectionBackgroundColor() const -{ - return Color(0x28, 0x28, 0x28); -} - -Color RenderThemeChromiumDefault::activeListBoxSelectionForegroundColor() const -{ - return Color::black; -} - -Color RenderThemeChromiumDefault::inactiveListBoxSelectionBackgroundColor() const -{ - return Color(0xc8, 0xc8, 0xc8); -} - -Color RenderThemeChromiumDefault::inactiveListBoxSelectionForegroundColor() const -{ - return Color(0x32, 0x32, 0x32); -} - -Color RenderThemeChromiumDefault::platformActiveSelectionBackgroundColor() const -{ - return m_activeSelectionBackgroundColor; -} - -Color RenderThemeChromiumDefault::platformInactiveSelectionBackgroundColor() const -{ - return m_inactiveSelectionBackgroundColor; -} - -Color RenderThemeChromiumDefault::platformActiveSelectionForegroundColor() const -{ - return m_activeSelectionForegroundColor; -} - -Color RenderThemeChromiumDefault::platformInactiveSelectionForegroundColor() const -{ - return m_inactiveSelectionForegroundColor; -} - -#if ENABLE(DATALIST_ELEMENT) -IntSize RenderThemeChromiumDefault::sliderTickSize() const -{ - return IntSize(1, 6); -} - -int RenderThemeChromiumDefault::sliderTickOffsetFromTrackCenter() const -{ - return -16; -} -#endif - -void RenderThemeChromiumDefault::adjustSliderThumbSize(RenderStyle* style, Element* element) const -{ - IntSize size = WebKit::Platform::current()->themeEngine()->getSize(WebKit::WebThemeEngine::PartSliderThumb); - - if (style->appearance() == SliderThumbHorizontalPart) { - style->setWidth(Length(size.width(), Fixed)); - style->setHeight(Length(size.height(), Fixed)); - } else if (style->appearance() == SliderThumbVerticalPart) { - style->setWidth(Length(size.height(), Fixed)); - style->setHeight(Length(size.width(), Fixed)); - } else - RenderThemeChromiumSkia::adjustSliderThumbSize(style, element); -} - -bool RenderThemeChromiumDefault::supportsControlTints() const -{ - return true; -} - -void RenderThemeChromiumDefault::setCaretBlinkInterval(double interval) -{ - m_caretBlinkInterval = interval; -} - -double RenderThemeChromiumDefault::caretBlinkIntervalInternal() const -{ - return m_caretBlinkInterval; -} - -void RenderThemeChromiumDefault::setSelectionColors( - unsigned activeBackgroundColor, - unsigned activeForegroundColor, - unsigned inactiveBackgroundColor, - unsigned inactiveForegroundColor) -{ - m_activeSelectionBackgroundColor = activeBackgroundColor; - m_activeSelectionForegroundColor = activeForegroundColor; - m_inactiveSelectionBackgroundColor = inactiveBackgroundColor; - m_inactiveSelectionForegroundColor = inactiveForegroundColor; -} - -bool RenderThemeChromiumDefault::paintCheckbox(RenderObject* o, const PaintInfo& i, const IntRect& rect) -{ - WebKit::WebThemeEngine::ExtraParams extraParams; - WebKit::WebCanvas* canvas = i.context->platformContext()->canvas(); - extraParams.button.checked = isChecked(o); - extraParams.button.indeterminate = isIndeterminate(o); - - WebKit::Platform::current()->themeEngine()->paint(canvas, WebKit::WebThemeEngine::PartCheckbox, getWebThemeState(this, o), WebKit::WebRect(rect), &extraParams); - return false; -} - -void RenderThemeChromiumDefault::setCheckboxSize(RenderStyle* style) const -{ - // If the width and height are both specified, then we have nothing to do. - if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto()) - return; - - IntSize size = WebKit::Platform::current()->themeEngine()->getSize(WebKit::WebThemeEngine::PartCheckbox); - setSizeIfAuto(style, size); -} - -bool RenderThemeChromiumDefault::paintRadio(RenderObject* o, const PaintInfo& i, const IntRect& rect) -{ - WebKit::WebThemeEngine::ExtraParams extraParams; - WebKit::WebCanvas* canvas = i.context->platformContext()->canvas(); - extraParams.button.checked = isChecked(o); - - WebKit::Platform::current()->themeEngine()->paint(canvas, WebKit::WebThemeEngine::PartRadio, getWebThemeState(this, o), WebKit::WebRect(rect), &extraParams); - return false; -} - -void RenderThemeChromiumDefault::setRadioSize(RenderStyle* style) const -{ - // If the width and height are both specified, then we have nothing to do. - if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto()) - return; - - IntSize size = WebKit::Platform::current()->themeEngine()->getSize(WebKit::WebThemeEngine::PartRadio); - setSizeIfAuto(style, size); -} - -bool RenderThemeChromiumDefault::paintButton(RenderObject* o, const PaintInfo& i, const IntRect& rect) -{ - WebKit::WebThemeEngine::ExtraParams extraParams; - WebKit::WebCanvas* canvas = i.context->platformContext()->canvas(); - extraParams.button.isDefault = isDefault(o); - extraParams.button.hasBorder = true; - extraParams.button.backgroundColor = defaultButtonBackgroundColor; - if (o->hasBackground()) - extraParams.button.backgroundColor = o->style()->visitedDependentColor(CSSPropertyBackgroundColor).rgb(); - - WebKit::Platform::current()->themeEngine()->paint(canvas, WebKit::WebThemeEngine::PartButton, getWebThemeState(this, o), WebKit::WebRect(rect), &extraParams); - return false; -} - -bool RenderThemeChromiumDefault::paintTextField(RenderObject* o, const PaintInfo& i, const IntRect& rect) -{ - // WebThemeEngine does not handle border rounded corner and background image - // so return true to draw CSS border and background. - if (o->style()->hasBorderRadius() || o->style()->hasBackgroundImage()) - return true; - - ControlPart part = o->style()->appearance(); - - WebKit::WebThemeEngine::ExtraParams extraParams; - extraParams.textField.isTextArea = part == TextAreaPart; - extraParams.textField.isListbox = part == ListboxPart; - - WebKit::WebCanvas* canvas = i.context->platformContext()->canvas(); - - // Fallback to white if the specified color object is invalid. - Color backgroundColor(Color::white); - if (o->style()->visitedDependentColor(CSSPropertyBackgroundColor).isValid()) - backgroundColor = o->style()->visitedDependentColor(CSSPropertyBackgroundColor); - extraParams.textField.backgroundColor = backgroundColor.rgb(); - - WebKit::Platform::current()->themeEngine()->paint(canvas, WebKit::WebThemeEngine::PartTextField, getWebThemeState(this, o), WebKit::WebRect(rect), &extraParams); - return false; -} - -bool RenderThemeChromiumDefault::paintMenuList(RenderObject* o, const PaintInfo& i, const IntRect& rect) -{ - if (!o->isBox()) - return false; - - const int right = rect.x() + rect.width(); - const int middle = rect.y() + rect.height() / 2; - - WebKit::WebThemeEngine::ExtraParams extraParams; - extraParams.menuList.arrowX = (o->style()->direction() == RTL) ? rect.x() + 7 : right - 13; - extraParams.menuList.arrowY = middle; - const RenderBox* box = toRenderBox(o); - // Match Chromium Win behaviour of showing all borders if any are shown. - extraParams.menuList.hasBorder = box->borderRight() || box->borderLeft() || box->borderTop() || box->borderBottom(); - extraParams.menuList.hasBorderRadius = o->style()->hasBorderRadius(); - // Fallback to transparent if the specified color object is invalid. - extraParams.menuList.backgroundColor = Color::transparent; - if (o->hasBackground()) - extraParams.menuList.backgroundColor = o->style()->visitedDependentColor(CSSPropertyBackgroundColor).rgb(); - - WebKit::WebCanvas* canvas = i.context->platformContext()->canvas(); - - WebKit::Platform::current()->themeEngine()->paint(canvas, WebKit::WebThemeEngine::PartMenuList, getWebThemeState(this, o), WebKit::WebRect(rect), &extraParams); - return false; -} - -bool RenderThemeChromiumDefault::paintSliderTrack(RenderObject* o, const PaintInfo& i, const IntRect& rect) -{ - WebKit::WebThemeEngine::ExtraParams extraParams; - WebKit::WebCanvas* canvas = i.context->platformContext()->canvas(); - extraParams.slider.vertical = o->style()->appearance() == SliderVerticalPart; - - WebKit::Platform::current()->themeEngine()->paint(canvas, WebKit::WebThemeEngine::PartSliderTrack, getWebThemeState(this, o), WebKit::WebRect(rect), &extraParams); - -#if ENABLE(DATALIST_ELEMENT) - paintSliderTicks(o, i, rect); -#endif - - return false; -} - -bool RenderThemeChromiumDefault::paintSliderThumb(RenderObject* o, const PaintInfo& i, const IntRect& rect) -{ - WebKit::WebThemeEngine::ExtraParams extraParams; - WebKit::WebCanvas* canvas = i.context->platformContext()->canvas(); - extraParams.slider.vertical = o->style()->appearance() == SliderThumbVerticalPart; - extraParams.slider.inDrag = isPressed(o); - - WebKit::Platform::current()->themeEngine()->paint(canvas, WebKit::WebThemeEngine::PartSliderThumb, getWebThemeState(this, o), WebKit::WebRect(rect), &extraParams); - return false; -} - -void RenderThemeChromiumDefault::adjustInnerSpinButtonStyle(StyleResolver*, RenderStyle* style, Element*) const -{ - IntSize size = WebKit::Platform::current()->themeEngine()->getSize(WebKit::WebThemeEngine::PartInnerSpinButton); - - style->setWidth(Length(size.width(), Fixed)); - style->setMinWidth(Length(size.width(), Fixed)); -} - -bool RenderThemeChromiumDefault::paintInnerSpinButton(RenderObject* o, const PaintInfo& i, const IntRect& rect) -{ - WebKit::WebThemeEngine::ExtraParams extraParams; - WebKit::WebCanvas* canvas = i.context->platformContext()->canvas(); - extraParams.innerSpin.spinUp = (controlStatesForRenderer(o) & SpinUpState); - extraParams.innerSpin.readOnly = isReadOnlyControl(o); - - WebKit::Platform::current()->themeEngine()->paint(canvas, WebKit::WebThemeEngine::PartInnerSpinButton, getWebThemeState(this, o), WebKit::WebRect(rect), &extraParams); - return false; -} - -#if ENABLE(PROGRESS_ELEMENT) - -bool RenderThemeChromiumDefault::paintProgressBar(RenderObject* o, const PaintInfo& i, const IntRect& rect) -{ - if (!o->isProgress()) - return true; - - RenderProgress* renderProgress = toRenderProgress(o); - IntRect valueRect = progressValueRectFor(renderProgress, rect); - - WebKit::WebThemeEngine::ExtraParams extraParams; - extraParams.progressBar.determinate = renderProgress->isDeterminate(); - extraParams.progressBar.valueRectX = valueRect.x(); - extraParams.progressBar.valueRectY = valueRect.y(); - extraParams.progressBar.valueRectWidth = valueRect.width(); - extraParams.progressBar.valueRectHeight = valueRect.height(); - - DirectionFlippingScope scope(o, i, rect); - WebKit::WebCanvas* canvas = i.context->platformContext()->canvas(); - WebKit::Platform::current()->themeEngine()->paint(canvas, WebKit::WebThemeEngine::PartProgressBar, getWebThemeState(this, o), WebKit::WebRect(rect), &extraParams); - return false; -} - -#endif - -bool RenderThemeChromiumDefault::shouldOpenPickerWithF4Key() const -{ - return true; -} - -} // namespace WebCore diff --git a/Source/WebCore/rendering/RenderThemeChromiumDefault.h b/Source/WebCore/rendering/RenderThemeChromiumDefault.h deleted file mode 100644 index 129b482cb..000000000 --- a/Source/WebCore/rendering/RenderThemeChromiumDefault.h +++ /dev/null @@ -1,109 +0,0 @@ -/* - * This file is part of the WebKit project. - * - * Copyright (C) 2006 Apple Computer, Inc. - * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com - * Copyright (C) 2007 Holger Hans Peter Freyther - * Copyright (C) 2007 Alp Toker <alp@atoker.com> - * Copyright (C) 2008, 2009 Google, Inc. - * All rights reserved. - * Copyright (C) 2009 Kenneth Rohde Christiansen - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#ifndef RenderThemeChromiumDefault_h -#define RenderThemeChromiumDefault_h - -#include "RenderThemeChromiumSkia.h" - -namespace WebCore { - -class RenderThemeChromiumDefault : public RenderThemeChromiumSkia { -public: - static PassRefPtr<RenderTheme> create(); - virtual String extraDefaultStyleSheet(); - - virtual Color systemColor(int cssValidId) const; - - // A method asking if the control changes its tint when the window has focus or not. - virtual bool controlSupportsTints(const RenderObject*) const; - - // List Box selection color - virtual Color activeListBoxSelectionBackgroundColor() const; - virtual Color activeListBoxSelectionForegroundColor() const; - virtual Color inactiveListBoxSelectionBackgroundColor() const; - virtual Color inactiveListBoxSelectionForegroundColor() const; - - virtual Color platformActiveSelectionBackgroundColor() const; - virtual Color platformInactiveSelectionBackgroundColor() const; - virtual Color platformActiveSelectionForegroundColor() const; - virtual Color platformInactiveSelectionForegroundColor() const; - -#if ENABLE(DATALIST_ELEMENT) - virtual IntSize sliderTickSize() const OVERRIDE; - virtual int sliderTickOffsetFromTrackCenter() const OVERRIDE; -#endif - virtual void adjustSliderThumbSize(RenderStyle*, Element*) const; - - static void setCaretBlinkInterval(double); - virtual double caretBlinkIntervalInternal() const; - - virtual bool paintCheckbox(RenderObject*, const PaintInfo&, const IntRect&); - virtual void setCheckboxSize(RenderStyle*) const; - - virtual bool paintRadio(RenderObject*, const PaintInfo&, const IntRect&); - virtual void setRadioSize(RenderStyle*) const; - - virtual bool paintButton(RenderObject*, const PaintInfo&, const IntRect&); - virtual bool paintTextField(RenderObject*, const PaintInfo&, const IntRect&); - virtual bool paintMenuList(RenderObject*, const PaintInfo&, const IntRect&); - virtual bool paintSliderTrack(RenderObject*, const PaintInfo&, const IntRect&); - virtual bool paintSliderThumb(RenderObject*, const PaintInfo&, const IntRect&); - - virtual void adjustInnerSpinButtonStyle(StyleResolver*, RenderStyle*, Element*) const; - virtual bool paintInnerSpinButton(RenderObject*, const PaintInfo&, const IntRect&); - - virtual bool popsMenuBySpaceOrReturn() const OVERRIDE { return true; } - -#if ENABLE(PROGRESS_ELEMENT) - virtual bool paintProgressBar(RenderObject*, const PaintInfo&, const IntRect&); -#endif - - virtual bool shouldOpenPickerWithF4Key() const OVERRIDE; - - static void setSelectionColors(unsigned activeBackgroundColor, unsigned activeForegroundColor, unsigned inactiveBackgroundColor, unsigned inactiveForegroundColor); - -protected: - RenderThemeChromiumDefault(); - virtual ~RenderThemeChromiumDefault(); - -private: - // A general method asking if any control tinting is supported at all. - virtual bool supportsControlTints() const; - - static double m_caretBlinkInterval; - - static unsigned m_activeSelectionBackgroundColor; - static unsigned m_activeSelectionForegroundColor; - static unsigned m_inactiveSelectionBackgroundColor; - static unsigned m_inactiveSelectionForegroundColor; -}; - -} // namespace WebCore - -#endif // RenderThemeChromiumDefault_h diff --git a/Source/WebCore/rendering/RenderThemeChromiumFontProvider.cpp b/Source/WebCore/rendering/RenderThemeChromiumFontProvider.cpp deleted file mode 100644 index 78068e537..000000000 --- a/Source/WebCore/rendering/RenderThemeChromiumFontProvider.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2012 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "RenderThemeChromiumFontProvider.h" - -#include <wtf/StdLibExtras.h> -#include <wtf/text/WTFString.h> - -namespace WebCore { - -// The default variable-width font size. We use this as the default font -// size for the "system font", and as a base size (which we then shrink) for -// form control fonts. -// static -float RenderThemeChromiumFontProvider::s_defaultFontSize = 16.0; - -// We aim to match IE here. -// -IE uses a font based on the encoding as the default font for form controls. -// -Gecko uses MS Shell Dlg (actually calls GetStockObject(DEFAULT_GUI_FONT), -// which returns MS Shell Dlg) -// -Safari uses Lucida Grande. -// -// FIXME: The only case where we know we don't match IE is for ANSI encodings. -// IE uses MS Shell Dlg there, which we render incorrectly at certain pixel -// sizes (e.g. 15px). So, for now we just use Arial. -const String& RenderThemeChromiumFontProvider::defaultGUIFont() -{ - DEFINE_STATIC_LOCAL(String, fontFace, (ASCIILiteral("Arial"))); - return fontFace; -} - -} // namespace WebCore diff --git a/Source/WebCore/rendering/RenderThemeChromiumFontProvider.h b/Source/WebCore/rendering/RenderThemeChromiumFontProvider.h deleted file mode 100644 index 0f7eeac27..000000000 --- a/Source/WebCore/rendering/RenderThemeChromiumFontProvider.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2012 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef RenderThemeChromiumFontProvider_h -#define RenderThemeChromiumFontProvider_h - -namespace WTF { -class String; -} - -namespace WebCore { - -class FontDescription; - -class RenderThemeChromiumFontProvider { -public: - static void systemFont(int propId, FontDescription&); - static void setDefaultFontSize(int); - -protected: - static const WTF::String& defaultGUIFont(); - - static float s_defaultFontSize; -}; - -} // namespace WebCore - -#endif // RenderThemeChromiumFontProvider_h diff --git a/Source/WebCore/rendering/RenderThemeChromiumFontProviderLinux.cpp b/Source/WebCore/rendering/RenderThemeChromiumFontProviderLinux.cpp deleted file mode 100644 index 009c72417..000000000 --- a/Source/WebCore/rendering/RenderThemeChromiumFontProviderLinux.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) 2012 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "RenderThemeChromiumFontProvider.h" - -#include "CSSValueKeywords.h" -#include "FontDescription.h" - -#include <wtf/StdLibExtras.h> -#include <wtf/text/WTFString.h> - -namespace WebCore { - -// static -void RenderThemeChromiumFontProvider::setDefaultFontSize(int fontSize) -{ - s_defaultFontSize = static_cast<float>(fontSize); -} - -// static -void RenderThemeChromiumFontProvider::systemFont(int propId, FontDescription& fontDescription) -{ - float fontSize = s_defaultFontSize; - - switch (propId) { - case CSSValueWebkitMiniControl: - case CSSValueWebkitSmallControl: - case CSSValueWebkitControl: - // Why 2 points smaller? Because that's what Gecko does. Note that we - // are assuming a 96dpi screen, which is the default that we use on - // Windows. - static const float pointsPerInch = 72.0f; - static const float pixelsPerInch = 96.0f; - fontSize -= (2.0f / pointsPerInch) * pixelsPerInch; - break; - } - - fontDescription.firstFamily().setFamily(defaultGUIFont()); - fontDescription.setSpecifiedSize(fontSize); - fontDescription.setIsAbsoluteSize(true); - fontDescription.setGenericFamily(FontDescription::NoFamily); - fontDescription.setWeight(FontWeightNormal); - fontDescription.setItalic(false); -} - -} // namespace WebCore diff --git a/Source/WebCore/rendering/RenderThemeChromiumFontProviderWin.cpp b/Source/WebCore/rendering/RenderThemeChromiumFontProviderWin.cpp deleted file mode 100644 index faf3c942b..000000000 --- a/Source/WebCore/rendering/RenderThemeChromiumFontProviderWin.cpp +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright (C) 2012 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "RenderThemeChromiumFontProvider.h" - -#include "CSSValueKeywords.h" -#include "FontDescription.h" -#include "HWndDC.h" -#include "SystemInfo.h" - -#include <windows.h> -#include <wtf/text/WTFString.h> - -#define SIZEOF_STRUCT_WITH_SPECIFIED_LAST_MEMBER(structName, member) \ - offsetof(structName, member) + \ - (sizeof static_cast<structName*>(0)->member) -#define NONCLIENTMETRICS_SIZE_PRE_VISTA \ - SIZEOF_STRUCT_WITH_SPECIFIED_LAST_MEMBER(NONCLIENTMETRICS, lfMessageFont) - -namespace WebCore { - -static FontDescription& smallSystemFont() -{ - DEFINE_STATIC_LOCAL(FontDescription, font, ()); - return font; -} - -static FontDescription& menuFont() -{ - DEFINE_STATIC_LOCAL(FontDescription, font, ()); - return font; -} - -static FontDescription& labelFont() -{ - DEFINE_STATIC_LOCAL(FontDescription, font, ()); - return font; -} - -// Converts |points| to pixels. One point is 1/72 of an inch. -static float pointsToPixels(float points) -{ - static float pixelsPerInch = 0.0f; - if (!pixelsPerInch) { - HWndDC hdc(0); // What about printing? Is this the right DC? - if (hdc) // Can this ever actually be 0? - pixelsPerInch = GetDeviceCaps(hdc, LOGPIXELSY); - else - pixelsPerInch = 96.0f; - } - - static const float pointsPerInch = 72.0f; - return points / pointsPerInch * pixelsPerInch; -} - -static void getNonClientMetrics(NONCLIENTMETRICS* metrics) -{ - static UINT size = (windowsVersion() >= WindowsVista) ? - sizeof(NONCLIENTMETRICS) : NONCLIENTMETRICS_SIZE_PRE_VISTA; - metrics->cbSize = size; - bool success = !!SystemParametersInfo(SPI_GETNONCLIENTMETRICS, size, metrics, 0); - ASSERT(success); -} - -// Return the height of system font |font| in pixels. We use this size by -// default for some non-form-control elements. -static float systemFontSize(const LOGFONT& font) -{ - float size = -font.lfHeight; - if (size < 0) { - HFONT hFont = CreateFontIndirect(&font); - if (hFont) { - HWndDC hdc(0); // What about printing? Is this the right DC? - if (hdc) { - HGDIOBJ hObject = SelectObject(hdc, hFont); - TEXTMETRIC tm; - GetTextMetrics(hdc, &tm); - SelectObject(hdc, hObject); - size = tm.tmAscent; - } - DeleteObject(hFont); - } - } - - // The "codepage 936" bit here is from Gecko; apparently this helps make - // fonts more legible in Simplified Chinese where the default font size is - // too small. - // - // FIXME: http://b/1119883 Since this is only used for "small caption", - // "menu", and "status bar" objects, I'm not sure how much this even - // matters. Plus the Gecko patch went in back in 2002, and maybe this - // isn't even relevant anymore. We should investigate whether this should - // be removed, or perhaps broadened to be "any CJK locale". - // - return ((size < 12.0f) && (GetACP() == 936)) ? 12.0f : size; -} - -// static -void RenderThemeChromiumFontProvider::systemFont(int propId, FontDescription& fontDescription) -{ - // This logic owes much to RenderThemeSafari.cpp. - FontDescription* cachedDesc = 0; - AtomicString faceName; - float fontSize = 0; - switch (propId) { - case CSSValueSmallCaption: - cachedDesc = &smallSystemFont(); - if (!smallSystemFont().isAbsoluteSize()) { - NONCLIENTMETRICS metrics; - getNonClientMetrics(&metrics); - faceName = AtomicString(metrics.lfSmCaptionFont.lfFaceName, wcslen(metrics.lfSmCaptionFont.lfFaceName)); - fontSize = systemFontSize(metrics.lfSmCaptionFont); - } - break; - case CSSValueMenu: - cachedDesc = &menuFont(); - if (!menuFont().isAbsoluteSize()) { - NONCLIENTMETRICS metrics; - getNonClientMetrics(&metrics); - faceName = AtomicString(metrics.lfMenuFont.lfFaceName, wcslen(metrics.lfMenuFont.lfFaceName)); - fontSize = systemFontSize(metrics.lfMenuFont); - } - break; - case CSSValueStatusBar: - cachedDesc = &labelFont(); - if (!labelFont().isAbsoluteSize()) { - NONCLIENTMETRICS metrics; - getNonClientMetrics(&metrics); - faceName = metrics.lfStatusFont.lfFaceName; - fontSize = systemFontSize(metrics.lfStatusFont); - } - break; - case CSSValueWebkitMiniControl: - case CSSValueWebkitSmallControl: - case CSSValueWebkitControl: - faceName = defaultGUIFont(); - // Why 2 points smaller? Because that's what Gecko does. - fontSize = s_defaultFontSize - pointsToPixels(2); - break; - default: - faceName = defaultGUIFont(); - fontSize = s_defaultFontSize; - break; - } - - if (!cachedDesc) - cachedDesc = &fontDescription; - - if (fontSize) { - cachedDesc->firstFamily().setFamily(faceName); - cachedDesc->setIsAbsoluteSize(true); - cachedDesc->setGenericFamily(FontDescription::NoFamily); - cachedDesc->setSpecifiedSize(fontSize); - cachedDesc->setWeight(FontWeightNormal); - cachedDesc->setItalic(false); - } - fontDescription = *cachedDesc; -} - -// static -void RenderThemeChromiumFontProvider::setDefaultFontSize(int fontSize) -{ - s_defaultFontSize = static_cast<float>(fontSize); - - // Reset cached fonts. - smallSystemFont() = menuFont() = labelFont() = FontDescription(); -} - -} // namespace WebCore diff --git a/Source/WebCore/rendering/RenderThemeChromiumLinux.cpp b/Source/WebCore/rendering/RenderThemeChromiumLinux.cpp deleted file mode 100644 index 8af4fc782..000000000 --- a/Source/WebCore/rendering/RenderThemeChromiumLinux.cpp +++ /dev/null @@ -1,385 +0,0 @@ -/* - * Copyright (C) 2007 Apple Inc. - * Copyright (C) 2007 Alp Toker <alp@atoker.com> - * Copyright (C) 2008 Collabora Ltd. - * Copyright (C) 2008, 2009 Google Inc. - * Copyright (C) 2009 Kenneth Rohde Christiansen - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#include "config.h" -#include "RenderThemeChromiumLinux.h" - -#include "CSSValueKeywords.h" -#include "Color.h" -#include "PaintInfo.h" -#include "PlatformContextSkia.h" -#include "RenderObject.h" -#include "RenderProgress.h" -#include "RenderSlider.h" -#include "ScrollbarTheme.h" -#include "UserAgentStyleSheets.h" -#include <public/Platform.h> -#include <public/WebRect.h> -#include <public/linux/WebThemeEngine.h> - -namespace WebCore { - -unsigned RenderThemeChromiumLinux::m_activeSelectionBackgroundColor = - 0xff1e90ff; -unsigned RenderThemeChromiumLinux::m_activeSelectionForegroundColor = - Color::black; -unsigned RenderThemeChromiumLinux::m_inactiveSelectionBackgroundColor = - 0xffc8c8c8; -unsigned RenderThemeChromiumLinux::m_inactiveSelectionForegroundColor = - 0xff323232; - -double RenderThemeChromiumLinux::m_caretBlinkInterval; - -static const unsigned defaultButtonBackgroundColor = 0xffdddddd; - -static WebKit::WebThemeEngine::State getWebThemeState(const RenderTheme* theme, const RenderObject* o) -{ - if (!theme->isEnabled(o)) - return WebKit::WebThemeEngine::StateDisabled; - if (theme->isPressed(o)) - return WebKit::WebThemeEngine::StatePressed; - if (theme->isHovered(o)) - return WebKit::WebThemeEngine::StateHover; - - return WebKit::WebThemeEngine::StateNormal; -} - -PassRefPtr<RenderTheme> RenderThemeChromiumLinux::create() -{ - return adoptRef(new RenderThemeChromiumLinux()); -} - -// RenderTheme::themeForPage for Android is defined in RenderThemeChromiumAndroid.cpp. -#if !OS(ANDROID) -PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page) -{ - static RenderTheme* rt = RenderThemeChromiumLinux::create().leakRef(); - return rt; -} -#endif - -RenderThemeChromiumLinux::RenderThemeChromiumLinux() -{ - m_caretBlinkInterval = RenderTheme::caretBlinkInterval(); -} - -RenderThemeChromiumLinux::~RenderThemeChromiumLinux() -{ -} - -Color RenderThemeChromiumLinux::systemColor(int cssValueId) const -{ - static const Color linuxButtonGrayColor(0xffdddddd); - - if (cssValueId == CSSValueButtonface) - return linuxButtonGrayColor; - return RenderTheme::systemColor(cssValueId); -} - -String RenderThemeChromiumLinux::extraDefaultStyleSheet() -{ - return RenderThemeChromiumSkia::extraDefaultStyleSheet() + - String(themeChromiumLinuxUserAgentStyleSheet, sizeof(themeChromiumLinuxUserAgentStyleSheet)); -} - -bool RenderThemeChromiumLinux::controlSupportsTints(const RenderObject* o) const -{ - return isEnabled(o); -} - -Color RenderThemeChromiumLinux::activeListBoxSelectionBackgroundColor() const -{ - return Color(0x28, 0x28, 0x28); -} - -Color RenderThemeChromiumLinux::activeListBoxSelectionForegroundColor() const -{ - return Color::black; -} - -Color RenderThemeChromiumLinux::inactiveListBoxSelectionBackgroundColor() const -{ - return Color(0xc8, 0xc8, 0xc8); -} - -Color RenderThemeChromiumLinux::inactiveListBoxSelectionForegroundColor() const -{ - return Color(0x32, 0x32, 0x32); -} - -Color RenderThemeChromiumLinux::platformActiveSelectionBackgroundColor() const -{ - return m_activeSelectionBackgroundColor; -} - -Color RenderThemeChromiumLinux::platformInactiveSelectionBackgroundColor() const -{ - return m_inactiveSelectionBackgroundColor; -} - -Color RenderThemeChromiumLinux::platformActiveSelectionForegroundColor() const -{ - return m_activeSelectionForegroundColor; -} - -Color RenderThemeChromiumLinux::platformInactiveSelectionForegroundColor() const -{ - return m_inactiveSelectionForegroundColor; -} - -#if ENABLE(DATALIST_ELEMENT) -IntSize RenderThemeChromiumLinux::sliderTickSize() const -{ - return IntSize(1, 6); -} - -int RenderThemeChromiumLinux::sliderTickOffsetFromTrackCenter() const -{ - return -16; -} -#endif - -void RenderThemeChromiumLinux::adjustSliderThumbSize(RenderStyle* style, Element* element) const -{ - IntSize size = WebKit::Platform::current()->themeEngine()->getSize(WebKit::WebThemeEngine::PartSliderThumb); - - if (style->appearance() == SliderThumbHorizontalPart) { - style->setWidth(Length(size.width(), Fixed)); - style->setHeight(Length(size.height(), Fixed)); - } else if (style->appearance() == SliderThumbVerticalPart) { - style->setWidth(Length(size.height(), Fixed)); - style->setHeight(Length(size.width(), Fixed)); - } else - RenderThemeChromiumSkia::adjustSliderThumbSize(style, element); -} - -bool RenderThemeChromiumLinux::supportsControlTints() const -{ - return true; -} - -void RenderThemeChromiumLinux::setCaretBlinkInterval(double interval) -{ - m_caretBlinkInterval = interval; -} - -double RenderThemeChromiumLinux::caretBlinkIntervalInternal() const -{ - return m_caretBlinkInterval; -} - -void RenderThemeChromiumLinux::setSelectionColors( - unsigned activeBackgroundColor, - unsigned activeForegroundColor, - unsigned inactiveBackgroundColor, - unsigned inactiveForegroundColor) -{ - m_activeSelectionBackgroundColor = activeBackgroundColor; - m_activeSelectionForegroundColor = activeForegroundColor; - m_inactiveSelectionBackgroundColor = inactiveBackgroundColor; - m_inactiveSelectionForegroundColor = inactiveForegroundColor; -} - -bool RenderThemeChromiumLinux::paintCheckbox(RenderObject* o, const PaintInfo& i, const IntRect& rect) -{ - WebKit::WebThemeEngine::ExtraParams extraParams; - WebKit::WebCanvas* canvas = i.context->platformContext()->canvas(); - extraParams.button.checked = isChecked(o); - extraParams.button.indeterminate = isIndeterminate(o); - - WebKit::Platform::current()->themeEngine()->paint(canvas, WebKit::WebThemeEngine::PartCheckbox, getWebThemeState(this, o), WebKit::WebRect(rect), &extraParams); - return false; -} - -void RenderThemeChromiumLinux::setCheckboxSize(RenderStyle* style) const -{ - // If the width and height are both specified, then we have nothing to do. - if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto()) - return; - - IntSize size = WebKit::Platform::current()->themeEngine()->getSize(WebKit::WebThemeEngine::PartCheckbox); - setSizeIfAuto(style, size); -} - -bool RenderThemeChromiumLinux::paintRadio(RenderObject* o, const PaintInfo& i, const IntRect& rect) -{ - WebKit::WebThemeEngine::ExtraParams extraParams; - WebKit::WebCanvas* canvas = i.context->platformContext()->canvas(); - extraParams.button.checked = isChecked(o); - - WebKit::Platform::current()->themeEngine()->paint(canvas, WebKit::WebThemeEngine::PartRadio, getWebThemeState(this, o), WebKit::WebRect(rect), &extraParams); - return false; -} - -void RenderThemeChromiumLinux::setRadioSize(RenderStyle* style) const -{ - // If the width and height are both specified, then we have nothing to do. - if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto()) - return; - - IntSize size = WebKit::Platform::current()->themeEngine()->getSize(WebKit::WebThemeEngine::PartRadio); - setSizeIfAuto(style, size); -} - -bool RenderThemeChromiumLinux::paintButton(RenderObject* o, const PaintInfo& i, const IntRect& rect) -{ - WebKit::WebThemeEngine::ExtraParams extraParams; - WebKit::WebCanvas* canvas = i.context->platformContext()->canvas(); - extraParams.button.isDefault = isDefault(o); - extraParams.button.hasBorder = true; - extraParams.button.backgroundColor = defaultButtonBackgroundColor; - if (o->hasBackground()) - extraParams.button.backgroundColor = o->style()->visitedDependentColor(CSSPropertyBackgroundColor).rgb(); - - WebKit::Platform::current()->themeEngine()->paint(canvas, WebKit::WebThemeEngine::PartButton, getWebThemeState(this, o), WebKit::WebRect(rect), &extraParams); - return false; -} - -bool RenderThemeChromiumLinux::paintTextField(RenderObject* o, const PaintInfo& i, const IntRect& rect) -{ - // WebThemeEngine does not handle border rounded corner and background image - // so return true to draw CSS border and background. - if (o->style()->hasBorderRadius() || o->style()->hasBackgroundImage()) - return true; - - ControlPart part = o->style()->appearance(); - - WebKit::WebThemeEngine::ExtraParams extraParams; - extraParams.textField.isTextArea = part == TextAreaPart; - extraParams.textField.isListbox = part == ListboxPart; - - WebKit::WebCanvas* canvas = i.context->platformContext()->canvas(); - - // Fallback to white if the specified color object is invalid. - Color backgroundColor(Color::white); - if (o->style()->visitedDependentColor(CSSPropertyBackgroundColor).isValid()) - backgroundColor = o->style()->visitedDependentColor(CSSPropertyBackgroundColor); - extraParams.textField.backgroundColor = backgroundColor.rgb(); - - WebKit::Platform::current()->themeEngine()->paint(canvas, WebKit::WebThemeEngine::PartTextField, getWebThemeState(this, o), WebKit::WebRect(rect), &extraParams); - return false; -} - -bool RenderThemeChromiumLinux::paintMenuList(RenderObject* o, const PaintInfo& i, const IntRect& rect) -{ - if (!o->isBox()) - return false; - - const int right = rect.x() + rect.width(); - const int middle = rect.y() + rect.height() / 2; - - WebKit::WebThemeEngine::ExtraParams extraParams; - extraParams.menuList.arrowX = (o->style()->direction() == RTL) ? rect.x() + 7 : right - 13; - extraParams.menuList.arrowY = middle; - const RenderBox* box = toRenderBox(o); - // Match Chromium Win behaviour of showing all borders if any are shown. - extraParams.menuList.hasBorder = box->borderRight() || box->borderLeft() || box->borderTop() || box->borderBottom(); - extraParams.menuList.hasBorderRadius = o->style()->hasBorderRadius(); - // Fallback to transparent if the specified color object is invalid. - extraParams.menuList.backgroundColor = Color::transparent; - if (o->hasBackground()) - extraParams.menuList.backgroundColor = o->style()->visitedDependentColor(CSSPropertyBackgroundColor).rgb(); - - WebKit::WebCanvas* canvas = i.context->platformContext()->canvas(); - - WebKit::Platform::current()->themeEngine()->paint(canvas, WebKit::WebThemeEngine::PartMenuList, getWebThemeState(this, o), WebKit::WebRect(rect), &extraParams); - return false; -} - -bool RenderThemeChromiumLinux::paintSliderTrack(RenderObject* o, const PaintInfo& i, const IntRect& rect) -{ - WebKit::WebThemeEngine::ExtraParams extraParams; - WebKit::WebCanvas* canvas = i.context->platformContext()->canvas(); - extraParams.slider.vertical = o->style()->appearance() == SliderVerticalPart; - - WebKit::Platform::current()->themeEngine()->paint(canvas, WebKit::WebThemeEngine::PartSliderTrack, getWebThemeState(this, o), WebKit::WebRect(rect), &extraParams); - -#if ENABLE(DATALIST_ELEMENT) - paintSliderTicks(o, i, rect); -#endif - - return false; -} - -bool RenderThemeChromiumLinux::paintSliderThumb(RenderObject* o, const PaintInfo& i, const IntRect& rect) -{ - WebKit::WebThemeEngine::ExtraParams extraParams; - WebKit::WebCanvas* canvas = i.context->platformContext()->canvas(); - extraParams.slider.vertical = o->style()->appearance() == SliderThumbVerticalPart; - extraParams.slider.inDrag = isPressed(o); - - WebKit::Platform::current()->themeEngine()->paint(canvas, WebKit::WebThemeEngine::PartSliderThumb, getWebThemeState(this, o), WebKit::WebRect(rect), &extraParams); - return false; -} - -void RenderThemeChromiumLinux::adjustInnerSpinButtonStyle(StyleResolver*, RenderStyle* style, Element*) const -{ - IntSize size = WebKit::Platform::current()->themeEngine()->getSize(WebKit::WebThemeEngine::PartInnerSpinButton); - - style->setWidth(Length(size.width(), Fixed)); - style->setMinWidth(Length(size.width(), Fixed)); -} - -bool RenderThemeChromiumLinux::paintInnerSpinButton(RenderObject* o, const PaintInfo& i, const IntRect& rect) -{ - WebKit::WebThemeEngine::ExtraParams extraParams; - WebKit::WebCanvas* canvas = i.context->platformContext()->canvas(); - extraParams.innerSpin.spinUp = (controlStatesForRenderer(o) & SpinUpState); - extraParams.innerSpin.readOnly = isReadOnlyControl(o); - - WebKit::Platform::current()->themeEngine()->paint(canvas, WebKit::WebThemeEngine::PartInnerSpinButton, getWebThemeState(this, o), WebKit::WebRect(rect), &extraParams); - return false; -} - -#if ENABLE(PROGRESS_ELEMENT) - -bool RenderThemeChromiumLinux::paintProgressBar(RenderObject* o, const PaintInfo& i, const IntRect& rect) -{ - if (!o->isProgress()) - return true; - - RenderProgress* renderProgress = toRenderProgress(o); - IntRect valueRect = progressValueRectFor(renderProgress, rect); - - WebKit::WebThemeEngine::ExtraParams extraParams; - extraParams.progressBar.determinate = renderProgress->isDeterminate(); - extraParams.progressBar.valueRectX = valueRect.x(); - extraParams.progressBar.valueRectY = valueRect.y(); - extraParams.progressBar.valueRectWidth = valueRect.width(); - extraParams.progressBar.valueRectHeight = valueRect.height(); - - DirectionFlippingScope scope(o, i, rect); - WebKit::WebCanvas* canvas = i.context->platformContext()->canvas(); - WebKit::Platform::current()->themeEngine()->paint(canvas, WebKit::WebThemeEngine::PartProgressBar, getWebThemeState(this, o), WebKit::WebRect(rect), &extraParams); - return false; -} - -#endif - -bool RenderThemeChromiumLinux::shouldOpenPickerWithF4Key() const -{ - return true; -} - -} // namespace WebCore diff --git a/Source/WebCore/rendering/RenderThemeChromiumLinux.h b/Source/WebCore/rendering/RenderThemeChromiumLinux.h deleted file mode 100644 index 5fc4b35db..000000000 --- a/Source/WebCore/rendering/RenderThemeChromiumLinux.h +++ /dev/null @@ -1,112 +0,0 @@ -/* - * This file is part of the WebKit project. - * - * Copyright (C) 2006 Apple Computer, Inc. - * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com - * Copyright (C) 2007 Holger Hans Peter Freyther - * Copyright (C) 2007 Alp Toker <alp@atoker.com> - * Copyright (C) 2008, 2009 Google, Inc. - * All rights reserved. - * Copyright (C) 2009 Kenneth Rohde Christiansen - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#ifndef RenderThemeChromiumLinux_h -#define RenderThemeChromiumLinux_h - -#include "RenderThemeChromiumSkia.h" - -namespace WebCore { - - class RenderThemeChromiumLinux : public RenderThemeChromiumSkia { - public: - static PassRefPtr<RenderTheme> create(); - virtual String extraDefaultStyleSheet(); - - virtual Color systemColor(int cssValidId) const; - - // A method asking if the control changes its tint when the window has focus or not. - virtual bool controlSupportsTints(const RenderObject*) const; - - // List Box selection color - virtual Color activeListBoxSelectionBackgroundColor() const; - virtual Color activeListBoxSelectionForegroundColor() const; - virtual Color inactiveListBoxSelectionBackgroundColor() const; - virtual Color inactiveListBoxSelectionForegroundColor() const; - - virtual Color platformActiveSelectionBackgroundColor() const; - virtual Color platformInactiveSelectionBackgroundColor() const; - virtual Color platformActiveSelectionForegroundColor() const; - virtual Color platformInactiveSelectionForegroundColor() const; - -#if ENABLE(DATALIST_ELEMENT) - virtual IntSize sliderTickSize() const OVERRIDE; - virtual int sliderTickOffsetFromTrackCenter() const OVERRIDE; -#endif - virtual void adjustSliderThumbSize(RenderStyle*, Element*) const; - - static void setCaretBlinkInterval(double interval); - virtual double caretBlinkIntervalInternal() const; - - virtual bool paintCheckbox(RenderObject*, const PaintInfo&, const IntRect&); - virtual void setCheckboxSize(RenderStyle*) const; - - virtual bool paintRadio(RenderObject*, const PaintInfo&, const IntRect&); - virtual void setRadioSize(RenderStyle*) const; - - virtual bool paintButton(RenderObject*, const PaintInfo&, const IntRect&); - virtual bool paintTextField(RenderObject*, const PaintInfo&, const IntRect&); - virtual bool paintMenuList(RenderObject*, const PaintInfo&, const IntRect&); - virtual bool paintSliderTrack(RenderObject*, const PaintInfo&, const IntRect&); - virtual bool paintSliderThumb(RenderObject*, const PaintInfo&, const IntRect&); - - virtual void adjustInnerSpinButtonStyle(StyleResolver*, RenderStyle*, Element*) const; - virtual bool paintInnerSpinButton(RenderObject*, const PaintInfo&, const IntRect&); - - virtual bool popsMenuBySpaceOrReturn() const OVERRIDE { return true; } - -#if ENABLE(PROGRESS_ELEMENT) - virtual bool paintProgressBar(RenderObject*, const PaintInfo&, const IntRect&); -#endif - - virtual bool shouldOpenPickerWithF4Key() const OVERRIDE; - - static void setSelectionColors(unsigned activeBackgroundColor, - unsigned activeForegroundColor, - unsigned inactiveBackgroundColor, - unsigned inactiveForegroundColor); - - protected: - RenderThemeChromiumLinux(); - virtual ~RenderThemeChromiumLinux(); - - private: - // A general method asking if any control tinting is supported at all. - virtual bool supportsControlTints() const; - - static double m_caretBlinkInterval; - - static unsigned m_activeSelectionBackgroundColor; - static unsigned m_activeSelectionForegroundColor; - static unsigned m_inactiveSelectionBackgroundColor; - static unsigned m_inactiveSelectionForegroundColor; - }; - -} // namespace WebCore - -#endif // RenderThemeChromiumLinux_h diff --git a/Source/WebCore/rendering/RenderThemeChromiumMac.h b/Source/WebCore/rendering/RenderThemeChromiumMac.h deleted file mode 100644 index 5acd13af5..000000000 --- a/Source/WebCore/rendering/RenderThemeChromiumMac.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * This file is part of the theme implementation for form controls in WebCore. - * - * Copyright (C) 2005 Apple Computer, Inc. - * Copyright (C) 2008, 2009 Google, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#ifndef RenderThemeChromiumMac_h -#define RenderThemeChromiumMac_h - -#import "RenderThemeChromiumCommon.h" -#import "RenderThemeMacShared.h" - -namespace WebCore { - -class RenderThemeChromiumMac : public RenderThemeMacShared { -public: - static PassRefPtr<RenderTheme> create(); - - virtual bool supportsDataListUI(const AtomicString& type) const OVERRIDE; - -protected: -#if ENABLE(VIDEO) - virtual void adjustMediaSliderThumbSize(RenderStyle*) const; - virtual bool paintMediaPlayButton(RenderObject*, const PaintInfo&, const IntRect&); - virtual bool paintMediaMuteButton(RenderObject*, const PaintInfo&, const IntRect&); - virtual bool paintMediaSliderTrack(RenderObject*, const PaintInfo&, const IntRect&); - virtual String extraMediaControlsStyleSheet(); -#if ENABLE(FULLSCREEN_API) - virtual String extraFullScreenStyleSheet(); -#endif - - virtual bool paintMediaSliderThumb(RenderObject*, const PaintInfo&, const IntRect&); - virtual bool paintMediaVolumeSliderContainer(RenderObject*, const PaintInfo&, const IntRect&); - virtual bool paintMediaVolumeSliderTrack(RenderObject*, const PaintInfo&, const IntRect&); - virtual bool paintMediaVolumeSliderThumb(RenderObject*, const PaintInfo&, const IntRect&); - virtual IntPoint volumeSliderOffsetFromMuteButton(RenderBox*, const IntSize&) const OVERRIDE; - virtual bool usesMediaControlStatusDisplay() { return false; } - virtual bool hasOwnDisabledStateHandlingFor(ControlPart) const { return true; } - virtual bool usesVerticalVolumeSlider() const { return false; } - virtual String formatMediaControlsTime(float time) const; - virtual String formatMediaControlsCurrentTime(float currentTime, float duration) const; - virtual String formatMediaControlsRemainingTime(float currentTime, float duration) const; - virtual bool paintMediaFullscreenButton(RenderObject*, const PaintInfo&, const IntRect&); - virtual bool paintMediaToggleClosedCaptionsButton(RenderObject*, const PaintInfo&, const IntRect&); -#endif - - virtual bool usesTestModeFocusRingColor() const; - virtual NSView* documentViewFor(RenderObject*) const; - - virtual int popupInternalPaddingLeft(RenderStyle*) const; - virtual int popupInternalPaddingRight(RenderStyle*) const; - -private: - virtual Color disabledTextColor(const Color& textColor, const Color&) const OVERRIDE { return textColor; } - virtual void updateActiveState(NSCell*, const RenderObject*); - virtual String extraDefaultStyleSheet(); -#if ENABLE(DATALIST_ELEMENT) - virtual LayoutUnit sliderTickSnappingThreshold() const OVERRIDE; -#endif -#if ENABLE(CALENDAR_PICKER) - virtual CString extraCalendarPickerStyleSheet() OVERRIDE; -#endif -#if ENABLE(INPUT_MULTIPLE_FIELDS_UI) - virtual bool supportsCalendarPicker(const AtomicString& type) const OVERRIDE; -#endif - virtual bool shouldShowPlaceholderWhenFocused() const OVERRIDE; -}; - -} // namespace WebCore - -#endif // RenderThemeChromiumMac_h diff --git a/Source/WebCore/rendering/RenderThemeChromiumMac.mm b/Source/WebCore/rendering/RenderThemeChromiumMac.mm deleted file mode 100644 index 91fe6ba66..000000000 --- a/Source/WebCore/rendering/RenderThemeChromiumMac.mm +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. - * Copyright (C) 2008, 2009 Google, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#import "config.h" - -#import "CalendarPickerMac.h" -#import "LayoutTestSupport.h" -#import "LocalCurrentGraphicsContext.h" -#import "RenderThemeChromiumMac.h" -#import "PaintInfo.h" -#import "RenderMediaControlsChromium.h" -#import "WebCoreSystemInterface.h" -#import "UserAgentStyleSheets.h" -#import <Carbon/Carbon.h> -#import <Cocoa/Cocoa.h> -#import <wtf/RetainPtr.h> -#import <wtf/StdLibExtras.h> -#import <math.h> - -@interface RTCMFlippedView : NSView -{} - -- (BOOL)isFlipped; -- (NSText *)currentEditor; - -@end - -@implementation RTCMFlippedView - -- (BOOL)isFlipped { - return [[NSGraphicsContext currentContext] isFlipped]; -} - -- (NSText *)currentEditor { - return nil; -} - -@end - -namespace WebCore { - -NSView* FlippedView() -{ - static NSView* view = [[RTCMFlippedView alloc] init]; - return view; -} - -PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page*) -{ - static RenderTheme* rt = RenderThemeChromiumMac::create().leakRef(); - return rt; -} - -PassRefPtr<RenderTheme> RenderThemeChromiumMac::create() -{ - return adoptRef(new RenderThemeChromiumMac); -} - -bool RenderThemeChromiumMac::supportsDataListUI(const AtomicString& type) const -{ - return RenderThemeChromiumCommon::supportsDataListUI(type); -} - -bool RenderThemeChromiumMac::usesTestModeFocusRingColor() const -{ - return isRunningLayoutTest(); -} - -NSView* RenderThemeChromiumMac::documentViewFor(RenderObject*) const -{ - return FlippedView(); -} - -const int autofillPopupHorizontalPadding = 4; - -// These functions are called with MenuListPart or MenulistButtonPart appearance by RenderMenuList, or with TextFieldPart appearance by AutofillPopupMenuClient. -// We assume only AutofillPopupMenuClient gives TexfieldPart appearance here. -// We want to change only Autofill padding. -// In the future, we have to separate Autofill popup window logic from WebKit to Chromium. -int RenderThemeChromiumMac::popupInternalPaddingLeft(RenderStyle* style) const -{ - if (style->appearance() == TextFieldPart) - return autofillPopupHorizontalPadding; - - return RenderThemeMacShared::popupInternalPaddingLeft(style); -} - -int RenderThemeChromiumMac::popupInternalPaddingRight(RenderStyle* style) const -{ - if (style->appearance() == TextFieldPart) - return autofillPopupHorizontalPadding; - - return RenderThemeMacShared::popupInternalPaddingRight(style); -} - -// Updates the control tint (a.k.a. active state) of |cell| (from |o|). -// In the Chromium port, the renderer runs as a background process and controls' -// NSCell(s) lack a parent NSView. Therefore controls don't have their tint -// color updated correctly when the application is activated/deactivated. -// FocusController's setActive() is called when the application is -// activated/deactivated, which causes a repaint at which time this code is -// called. -// This function should be called before drawing any NSCell-derived controls, -// unless you're sure it isn't needed. -void RenderThemeChromiumMac::updateActiveState(NSCell* cell, const RenderObject* o) -{ - NSControlTint oldTint = [cell controlTint]; - NSControlTint tint = isActive(o) ? [NSColor currentControlTint] : - static_cast<NSControlTint>(NSClearControlTint); - - if (tint != oldTint) - [cell setControlTint:tint]; -} - -bool RenderThemeChromiumMac::shouldShowPlaceholderWhenFocused() const -{ - return true; -} - -#if ENABLE(VIDEO) - -void RenderThemeChromiumMac::adjustMediaSliderThumbSize(RenderStyle* style) const -{ - RenderMediaControlsChromium::adjustMediaSliderThumbSize(style); -} - -bool RenderThemeChromiumMac::paintMediaPlayButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) -{ - return RenderMediaControlsChromium::paintMediaControlsPart(MediaPlayButton, object, paintInfo, rect); -} - -bool RenderThemeChromiumMac::paintMediaMuteButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) -{ - return RenderMediaControlsChromium::paintMediaControlsPart(MediaMuteButton, object, paintInfo, rect); -} - -bool RenderThemeChromiumMac::paintMediaSliderTrack(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) -{ - return RenderMediaControlsChromium::paintMediaControlsPart(MediaSlider, object, paintInfo, rect); -} - -String RenderThemeChromiumMac::extraMediaControlsStyleSheet() -{ - return String(mediaControlsChromiumUserAgentStyleSheet, sizeof(mediaControlsChromiumUserAgentStyleSheet)); -} - -#if ENABLE(FULLSCREEN_API) -String RenderThemeChromiumMac::extraFullScreenStyleSheet() -{ - // FIXME: Chromium may wish to style its default media controls differently in fullscreen. - return String(); -} -#endif - -String RenderThemeChromiumMac::extraDefaultStyleSheet() -{ - return RenderThemeMacShared::extraDefaultStyleSheet() + - String(themeChromiumUserAgentStyleSheet, sizeof(themeChromiumUserAgentStyleSheet)); -} - -#if ENABLE(DATALIST_ELEMENT) -LayoutUnit RenderThemeChromiumMac::sliderTickSnappingThreshold() const -{ - return RenderThemeChromiumCommon::sliderTickSnappingThreshold(); -} -#endif - -#if ENABLE(CALENDAR_PICKER) -CString RenderThemeChromiumMac::extraCalendarPickerStyleSheet() -{ - return CString(calendarPickerMacCss, WTF_ARRAY_LENGTH(calendarPickerMacCss)); -} -#endif - -#if ENABLE(INPUT_MULTIPLE_FIELDS_UI) && ENABLE(CALENDAR_PICKER) -bool RenderThemeChromiumMac::supportsCalendarPicker(const AtomicString& type) const -{ - return RenderThemeChromiumCommon::supportsCalendarPicker(type); -} -#endif - -bool RenderThemeChromiumMac::paintMediaVolumeSliderContainer(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) -{ - return true; -} - -bool RenderThemeChromiumMac::paintMediaVolumeSliderTrack(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) -{ - return RenderMediaControlsChromium::paintMediaControlsPart(MediaVolumeSlider, object, paintInfo, rect); -} - -bool RenderThemeChromiumMac::paintMediaVolumeSliderThumb(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) -{ - return RenderMediaControlsChromium::paintMediaControlsPart(MediaVolumeSliderThumb, object, paintInfo, rect); -} - -bool RenderThemeChromiumMac::paintMediaSliderThumb(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) -{ - return RenderMediaControlsChromium::paintMediaControlsPart(MediaSliderThumb, object, paintInfo, rect); -} - -IntPoint RenderThemeChromiumMac::volumeSliderOffsetFromMuteButton(RenderBox* muteButtonBox, const IntSize& size) const -{ - return RenderTheme::volumeSliderOffsetFromMuteButton(muteButtonBox, size); -} - -String RenderThemeChromiumMac::formatMediaControlsTime(float time) const -{ - return RenderMediaControlsChromium::formatMediaControlsTime(time); -} - -String RenderThemeChromiumMac::formatMediaControlsCurrentTime(float currentTime, float duration) const -{ - return RenderMediaControlsChromium::formatMediaControlsCurrentTime(currentTime, duration); -} - -String RenderThemeChromiumMac::formatMediaControlsRemainingTime(float currentTime, float duration) const -{ - return RenderThemeChromiumMac::formatMediaControlsRemainingTime(currentTime, duration); -} - -bool RenderThemeChromiumMac::paintMediaFullscreenButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) -{ - return RenderMediaControlsChromium::paintMediaControlsPart(MediaEnterFullscreenButton, object, paintInfo, rect); -} - -bool RenderThemeChromiumMac::paintMediaToggleClosedCaptionsButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) -{ - return RenderMediaControlsChromium::paintMediaControlsPart(MediaShowClosedCaptionsButton, object, paintInfo, rect); -} -#endif - -} // namespace WebCore diff --git a/Source/WebCore/rendering/RenderThemeChromiumSkia.cpp b/Source/WebCore/rendering/RenderThemeChromiumSkia.cpp deleted file mode 100644 index 968423d81..000000000 --- a/Source/WebCore/rendering/RenderThemeChromiumSkia.cpp +++ /dev/null @@ -1,658 +0,0 @@ -/* - * Copyright (C) 2007 Apple Inc. - * Copyright (C) 2007 Alp Toker <alp@atoker.com> - * Copyright (C) 2008 Collabora Ltd. - * Copyright (C) 2008, 2009 Google Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#include "config.h" -#include "RenderThemeChromiumSkia.h" - -#include "CSSValueKeywords.h" -#include "Font.h" -#include "GraphicsContext.h" -#include "HTMLMediaElement.h" -#include "HTMLNames.h" -#include "Image.h" -#include "LayoutTestSupport.h" -#include "MediaControlElements.h" -#include "PaintInfo.h" -#include "PlatformContextSkia.h" -#include "RenderBox.h" -#include "RenderMediaControlsChromium.h" -#include "RenderObject.h" -#include "RenderProgress.h" -#include "RenderSlider.h" -#include "RenderThemeChromiumFontProvider.h" -#include "ScrollbarTheme.h" -#include "TimeRanges.h" -#include "TransformationMatrix.h" -#include "UserAgentStyleSheets.h" - -#include <wtf/CurrentTime.h> - -#include "SkShader.h" -#include "SkGradientShader.h" - -namespace WebCore { - -enum PaddingType { - TopPadding, - RightPadding, - BottomPadding, - LeftPadding -}; - -static const int styledMenuListInternalPadding[4] = { 1, 4, 1, 4 }; - -// These values all match Safari/Win. -static const float defaultControlFontPixelSize = 13; -static const float defaultCancelButtonSize = 9; -static const float minCancelButtonSize = 5; -static const float maxCancelButtonSize = 21; -static const float defaultSearchFieldResultsDecorationSize = 13; -static const float minSearchFieldResultsDecorationSize = 9; -static const float maxSearchFieldResultsDecorationSize = 30; -static const float defaultSearchFieldResultsButtonWidth = 18; - -RenderThemeChromiumSkia::RenderThemeChromiumSkia() -{ -} - -RenderThemeChromiumSkia::~RenderThemeChromiumSkia() -{ -} - -// Use the Windows style sheets to match their metrics. -String RenderThemeChromiumSkia::extraDefaultStyleSheet() -{ - return String(themeWinUserAgentStyleSheet, sizeof(themeWinUserAgentStyleSheet)) + - String(themeChromiumSkiaUserAgentStyleSheet, sizeof(themeChromiumSkiaUserAgentStyleSheet)) + - String(themeChromiumUserAgentStyleSheet, sizeof(themeChromiumUserAgentStyleSheet)); -} - -String RenderThemeChromiumSkia::extraQuirksStyleSheet() -{ - return String(themeWinQuirksUserAgentStyleSheet, sizeof(themeWinQuirksUserAgentStyleSheet)); -} - -#if ENABLE(VIDEO) -String RenderThemeChromiumSkia::extraMediaControlsStyleSheet() -{ - return String(mediaControlsChromiumUserAgentStyleSheet, sizeof(mediaControlsChromiumUserAgentStyleSheet)); -} -#endif - -bool RenderThemeChromiumSkia::supportsHover(const RenderStyle* style) const -{ - return true; -} - -bool RenderThemeChromiumSkia::supportsFocusRing(const RenderStyle* style) const -{ - // This causes WebKit to draw the focus rings for us. - return false; -} - -bool RenderThemeChromiumSkia::supportsDataListUI(const AtomicString& type) const -{ - return RenderThemeChromiumCommon::supportsDataListUI(type); -} - -#if ENABLE(INPUT_MULTIPLE_FIELDS_UI) && ENABLE(CALENDAR_PICKER) -bool RenderThemeChromiumSkia::supportsCalendarPicker(const AtomicString& type) const -{ - return RenderThemeChromiumCommon::supportsCalendarPicker(type); -} -#endif - -#if ENABLE(VIDEO_TRACK) -bool RenderThemeChromiumSkia::supportsClosedCaptioning() const -{ - return true; -} -#endif - -Color RenderThemeChromiumSkia::platformActiveSelectionBackgroundColor() const -{ - return Color(0x1e, 0x90, 0xff); -} - -Color RenderThemeChromiumSkia::platformInactiveSelectionBackgroundColor() const -{ - return Color(0xc8, 0xc8, 0xc8); -} - -Color RenderThemeChromiumSkia::platformActiveSelectionForegroundColor() const -{ - return Color::black; -} - -Color RenderThemeChromiumSkia::platformInactiveSelectionForegroundColor() const -{ - return Color(0x32, 0x32, 0x32); -} - -Color RenderThemeChromiumSkia::platformFocusRingColor() const -{ - static Color focusRingColor(229, 151, 0, 255); - return focusRingColor; -} - -double RenderThemeChromiumSkia::caretBlinkInterval() const -{ - // Disable the blinking caret in layout test mode, as it introduces - // a race condition for the pixel tests. http://b/1198440 - if (isRunningLayoutTest()) - return 0; - - return caretBlinkIntervalInternal(); -} - -void RenderThemeChromiumSkia::systemFont(int propId, FontDescription& fontDescription) const -{ - RenderThemeChromiumFontProvider::systemFont(propId, fontDescription); -} - -int RenderThemeChromiumSkia::minimumMenuListSize(RenderStyle* style) const -{ - return 0; -} - -// These are the default dimensions of radio buttons and checkboxes. -static const int widgetStandardWidth = 13; -static const int widgetStandardHeight = 13; - -// Return a rectangle that has the same center point as |original|, but with a -// size capped at |width| by |height|. -IntRect center(const IntRect& original, int width, int height) -{ - width = std::min(original.width(), width); - height = std::min(original.height(), height); - int x = original.x() + (original.width() - width) / 2; - int y = original.y() + (original.height() - height) / 2; - - return IntRect(x, y, width, height); -} - -void RenderThemeChromiumSkia::setCheckboxSize(RenderStyle* style) const -{ - // If the width and height are both specified, then we have nothing to do. - if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto()) - return; - - // FIXME: A hard-coded size of 13 is used. This is wrong but necessary - // for now. It matches Firefox. At different DPI settings on Windows, - // querying the theme gives you a larger size that accounts for the higher - // DPI. Until our entire engine honors a DPI setting other than 96, we - // can't rely on the theme's metrics. - const IntSize size(widgetStandardWidth, widgetStandardHeight); - setSizeIfAuto(style, size); -} - -void RenderThemeChromiumSkia::setRadioSize(RenderStyle* style) const -{ - // Use same sizing for radio box as checkbox. - setCheckboxSize(style); -} - -void RenderThemeChromiumSkia::adjustButtonStyle(StyleResolver*, RenderStyle* style, Element*) const -{ - if (style->appearance() == PushButtonPart) { - // Ignore line-height. - style->setLineHeight(RenderStyle::initialLineHeight()); - } -} - -bool RenderThemeChromiumSkia::paintTextArea(RenderObject* o, const PaintInfo& i, const IntRect& r) -{ - return paintTextField(o, i, r); -} - -void RenderThemeChromiumSkia::adjustSearchFieldStyle(StyleResolver*, RenderStyle* style, Element*) const -{ - // Ignore line-height. - style->setLineHeight(RenderStyle::initialLineHeight()); -} - -bool RenderThemeChromiumSkia::paintSearchField(RenderObject* o, const PaintInfo& i, const IntRect& r) -{ - return paintTextField(o, i, r); -} - -void RenderThemeChromiumSkia::adjustSearchFieldCancelButtonStyle(StyleResolver*, RenderStyle* style, Element*) const -{ - // Scale the button size based on the font size - float fontScale = style->fontSize() / defaultControlFontPixelSize; - int cancelButtonSize = lroundf(std::min(std::max(minCancelButtonSize, defaultCancelButtonSize * fontScale), maxCancelButtonSize)); - style->setWidth(Length(cancelButtonSize, Fixed)); - style->setHeight(Length(cancelButtonSize, Fixed)); -} - -IntRect RenderThemeChromiumSkia::convertToPaintingRect(RenderObject* inputRenderer, const RenderObject* partRenderer, LayoutRect partRect, const IntRect& localOffset) const -{ - // Compute an offset between the part renderer and the input renderer. - LayoutSize offsetFromInputRenderer = -partRenderer->offsetFromAncestorContainer(inputRenderer); - // Move the rect into partRenderer's coords. - partRect.move(offsetFromInputRenderer); - // Account for the local drawing offset. - partRect.move(localOffset.x(), localOffset.y()); - - return pixelSnappedIntRect(partRect); -} - -bool RenderThemeChromiumSkia::paintSearchFieldCancelButton(RenderObject* cancelButtonObject, const PaintInfo& paintInfo, const IntRect& r) -{ - // Get the renderer of <input> element. - Node* input = cancelButtonObject->node()->shadowHost(); - RenderObject* baseRenderer = input ? input->renderer() : cancelButtonObject; - if (!baseRenderer->isBox()) - return false; - RenderBox* inputRenderBox = toRenderBox(baseRenderer); - LayoutRect inputContentBox = inputRenderBox->contentBoxRect(); - - // Make sure the scaled button stays square and will fit in its parent's box. - LayoutUnit cancelButtonSize = std::min(inputContentBox.width(), std::min<LayoutUnit>(inputContentBox.height(), r.height())); - // Calculate cancel button's coordinates relative to the input element. - // Center the button vertically. Round up though, so if it has to be one pixel off-center, it will - // be one pixel closer to the bottom of the field. This tends to look better with the text. - LayoutRect cancelButtonRect(cancelButtonObject->offsetFromAncestorContainer(inputRenderBox).width(), - inputContentBox.y() + (inputContentBox.height() - cancelButtonSize + 1) / 2, - cancelButtonSize, cancelButtonSize); - IntRect paintingRect = convertToPaintingRect(inputRenderBox, cancelButtonObject, cancelButtonRect, r); - - static Image* cancelImage = Image::loadPlatformResource("searchCancel").leakRef(); - static Image* cancelPressedImage = Image::loadPlatformResource("searchCancelPressed").leakRef(); - paintInfo.context->drawImage(isPressed(cancelButtonObject) ? cancelPressedImage : cancelImage, - cancelButtonObject->style()->colorSpace(), paintingRect); - return false; -} - -void RenderThemeChromiumSkia::adjustSearchFieldDecorationStyle(StyleResolver*, RenderStyle* style, Element*) const -{ - IntSize emptySize(1, 11); - style->setWidth(Length(emptySize.width(), Fixed)); - style->setHeight(Length(emptySize.height(), Fixed)); -} - -void RenderThemeChromiumSkia::adjustSearchFieldResultsDecorationStyle(StyleResolver*, RenderStyle* style, Element*) const -{ - // Scale the decoration size based on the font size - float fontScale = style->fontSize() / defaultControlFontPixelSize; - int magnifierSize = lroundf(std::min(std::max(minSearchFieldResultsDecorationSize, defaultSearchFieldResultsDecorationSize * fontScale), - maxSearchFieldResultsDecorationSize)); - style->setWidth(Length(magnifierSize, Fixed)); - style->setHeight(Length(magnifierSize, Fixed)); -} - -bool RenderThemeChromiumSkia::paintSearchFieldResultsDecoration(RenderObject* magnifierObject, const PaintInfo& paintInfo, const IntRect& r) -{ - // Get the renderer of <input> element. - Node* input = magnifierObject->node()->shadowHost(); - RenderObject* baseRenderer = input ? input->renderer() : magnifierObject; - if (!baseRenderer->isBox()) - return false; - RenderBox* inputRenderBox = toRenderBox(baseRenderer); - LayoutRect inputContentBox = inputRenderBox->contentBoxRect(); - - // Make sure the scaled decoration stays square and will fit in its parent's box. - LayoutUnit magnifierSize = std::min(inputContentBox.width(), std::min<LayoutUnit>(inputContentBox.height(), r.height())); - // Calculate decoration's coordinates relative to the input element. - // Center the decoration vertically. Round up though, so if it has to be one pixel off-center, it will - // be one pixel closer to the bottom of the field. This tends to look better with the text. - LayoutRect magnifierRect(magnifierObject->offsetFromAncestorContainer(inputRenderBox).width(), - inputContentBox.y() + (inputContentBox.height() - magnifierSize + 1) / 2, - magnifierSize, magnifierSize); - IntRect paintingRect = convertToPaintingRect(inputRenderBox, magnifierObject, magnifierRect, r); - - static Image* magnifierImage = Image::loadPlatformResource("searchMagnifier").leakRef(); - paintInfo.context->drawImage(magnifierImage, magnifierObject->style()->colorSpace(), paintingRect); - return false; -} - -void RenderThemeChromiumSkia::adjustSearchFieldResultsButtonStyle(StyleResolver*, RenderStyle* style, Element*) const -{ - // Scale the button size based on the font size - float fontScale = style->fontSize() / defaultControlFontPixelSize; - int magnifierHeight = lroundf(std::min(std::max(minSearchFieldResultsDecorationSize, defaultSearchFieldResultsDecorationSize * fontScale), - maxSearchFieldResultsDecorationSize)); - int magnifierWidth = lroundf(magnifierHeight * defaultSearchFieldResultsButtonWidth / defaultSearchFieldResultsDecorationSize); - style->setWidth(Length(magnifierWidth, Fixed)); - style->setHeight(Length(magnifierHeight, Fixed)); -} - -bool RenderThemeChromiumSkia::paintSearchFieldResultsButton(RenderObject* magnifierObject, const PaintInfo& paintInfo, const IntRect& r) -{ - // Get the renderer of <input> element. - Node* input = magnifierObject->node()->shadowHost(); - RenderObject* baseRenderer = input ? input->renderer() : magnifierObject; - if (!baseRenderer->isBox()) - return false; - RenderBox* inputRenderBox = toRenderBox(baseRenderer); - LayoutRect inputContentBox = inputRenderBox->contentBoxRect(); - - // Make sure the scaled decoration will fit in its parent's box. - LayoutUnit magnifierHeight = std::min<LayoutUnit>(inputContentBox.height(), r.height()); - LayoutUnit magnifierWidth = std::min<LayoutUnit>(inputContentBox.width(), magnifierHeight * defaultSearchFieldResultsButtonWidth / defaultSearchFieldResultsDecorationSize); - LayoutRect magnifierRect(magnifierObject->offsetFromAncestorContainer(inputRenderBox).width(), - inputContentBox.y() + (inputContentBox.height() - magnifierHeight + 1) / 2, - magnifierWidth, magnifierHeight); - IntRect paintingRect = convertToPaintingRect(inputRenderBox, magnifierObject, magnifierRect, r); - - static Image* magnifierImage = Image::loadPlatformResource("searchMagnifierResults").leakRef(); - paintInfo.context->drawImage(magnifierImage, magnifierObject->style()->colorSpace(), paintingRect); - return false; -} - -bool RenderThemeChromiumSkia::paintMediaSliderTrack(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) -{ -#if ENABLE(VIDEO) - return RenderMediaControlsChromium::paintMediaControlsPart(MediaSlider, object, paintInfo, rect); -#else - UNUSED_PARAM(object); - UNUSED_PARAM(paintInfo); - UNUSED_PARAM(rect); - return false; -#endif -} - -bool RenderThemeChromiumSkia::paintMediaVolumeSliderTrack(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) -{ -#if ENABLE(VIDEO) - return RenderMediaControlsChromium::paintMediaControlsPart(MediaVolumeSlider, object, paintInfo, rect); -#else - UNUSED_PARAM(object); - UNUSED_PARAM(paintInfo); - UNUSED_PARAM(rect); - return false; -#endif -} - -void RenderThemeChromiumSkia::adjustSliderThumbSize(RenderStyle* style, Element*) const -{ -#if ENABLE(VIDEO) - RenderMediaControlsChromium::adjustMediaSliderThumbSize(style); -#else - UNUSED_PARAM(style); -#endif -} - -bool RenderThemeChromiumSkia::paintMediaSliderThumb(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) -{ -#if ENABLE(VIDEO) - return RenderMediaControlsChromium::paintMediaControlsPart(MediaSliderThumb, object, paintInfo, rect); -#else - UNUSED_PARAM(object); - UNUSED_PARAM(paintInfo); - UNUSED_PARAM(rect); - return false; -#endif -} - -bool RenderThemeChromiumSkia::paintMediaToggleClosedCaptionsButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) -{ -#if ENABLE(VIDEO_TRACK) - return RenderMediaControlsChromium::paintMediaControlsPart(MediaShowClosedCaptionsButton, o, paintInfo, r); -#else - UNUSED_PARAM(object); - UNUSED_PARAM(paintInfo); - UNUSED_PARAM(rect); - return false; -#endif -} - -bool RenderThemeChromiumSkia::paintMediaVolumeSliderThumb(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) -{ -#if ENABLE(VIDEO) - return RenderMediaControlsChromium::paintMediaControlsPart(MediaVolumeSliderThumb, object, paintInfo, rect); -#else - UNUSED_PARAM(object); - UNUSED_PARAM(paintInfo); - UNUSED_PARAM(rect); - return false; -#endif -} - -bool RenderThemeChromiumSkia::paintMediaPlayButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) -{ -#if ENABLE(VIDEO) - return RenderMediaControlsChromium::paintMediaControlsPart(MediaPlayButton, object, paintInfo, rect); -#else - UNUSED_PARAM(object); - UNUSED_PARAM(paintInfo); - UNUSED_PARAM(rect); - return false; -#endif -} - -bool RenderThemeChromiumSkia::paintMediaMuteButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) -{ -#if ENABLE(VIDEO) - return RenderMediaControlsChromium::paintMediaControlsPart(MediaMuteButton, object, paintInfo, rect); -#else - UNUSED_PARAM(object); - UNUSED_PARAM(paintInfo); - UNUSED_PARAM(rect); - return false; -#endif -} - -String RenderThemeChromiumSkia::formatMediaControlsTime(float time) const -{ -#if ENABLE(VIDEO) - return RenderMediaControlsChromium::formatMediaControlsTime(time); -#else - UNUSED_PARAM(time); - return 0; -#endif -} - -String RenderThemeChromiumSkia::formatMediaControlsCurrentTime(float currentTime, float duration) const -{ -#if ENABLE(VIDEO) - return RenderMediaControlsChromium::formatMediaControlsCurrentTime(currentTime, duration); -#else - UNUSED_PARAM(currentTime); - UNUSED_PARAM(duration); - return 0; -#endif -} - -String RenderThemeChromiumSkia::formatMediaControlsRemainingTime(float currentTime, float duration) const -{ -#if ENABLE(VIDEO) - return RenderMediaControlsChromium::formatMediaControlsRemainingTime(currentTime, duration); -#else - UNUSED_PARAM(currentTime); - UNUSED_PARAM(duration); - return 0; -#endif -} - -bool RenderThemeChromiumSkia::paintMediaFullscreenButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) -{ -#if ENABLE(VIDEO) - return RenderMediaControlsChromium::paintMediaControlsPart(MediaEnterFullscreenButton, object, paintInfo, rect); -#else - UNUSED_PARAM(object); - UNUSED_PARAM(paintInfo); - UNUSED_PARAM(rect); - return false; -#endif -} - -void RenderThemeChromiumSkia::adjustMenuListStyle(StyleResolver*, RenderStyle* style, WebCore::Element*) const -{ - // Height is locked to auto on all browsers. - style->setLineHeight(RenderStyle::initialLineHeight()); -} - -void RenderThemeChromiumSkia::adjustMenuListButtonStyle(StyleResolver* styleResolver, RenderStyle* style, Element* e) const -{ - adjustMenuListStyle(styleResolver, style, e); -} - -// Used to paint styled menulists (i.e. with a non-default border) -bool RenderThemeChromiumSkia::paintMenuListButton(RenderObject* o, const PaintInfo& i, const IntRect& rect) -{ - return paintMenuList(o, i, rect); -} - -int RenderThemeChromiumSkia::popupInternalPaddingLeft(RenderStyle* style) const -{ - return menuListInternalPadding(style, LeftPadding); -} - -int RenderThemeChromiumSkia::popupInternalPaddingRight(RenderStyle* style) const -{ - return menuListInternalPadding(style, RightPadding); -} - -int RenderThemeChromiumSkia::popupInternalPaddingTop(RenderStyle* style) const -{ - return menuListInternalPadding(style, TopPadding); -} - -int RenderThemeChromiumSkia::popupInternalPaddingBottom(RenderStyle* style) const -{ - return menuListInternalPadding(style, BottomPadding); -} - -// static -void RenderThemeChromiumSkia::setDefaultFontSize(int fontSize) -{ - RenderThemeChromiumFontProvider::setDefaultFontSize(fontSize); -} - -double RenderThemeChromiumSkia::caretBlinkIntervalInternal() const -{ - return RenderTheme::caretBlinkInterval(); -} - -int RenderThemeChromiumSkia::menuListArrowPadding() const -{ - return ScrollbarTheme::theme()->scrollbarThickness(); -} - -// static -void RenderThemeChromiumSkia::setSizeIfAuto(RenderStyle* style, const IntSize& size) -{ - if (style->width().isIntrinsicOrAuto()) - style->setWidth(Length(size.width(), Fixed)); - if (style->height().isAuto()) - style->setHeight(Length(size.height(), Fixed)); -} - -int RenderThemeChromiumSkia::menuListInternalPadding(RenderStyle* style, int paddingType) const -{ - // This internal padding is in addition to the user-supplied padding. - // Matches the FF behavior. - int padding = styledMenuListInternalPadding[paddingType]; - - // Reserve the space for right arrow here. The rest of the padding is - // set by adjustMenuListStyle, since PopMenuWin.cpp uses the padding from - // RenderMenuList to lay out the individual items in the popup. - // If the MenuList actually has appearance "NoAppearance", then that means - // we don't draw a button, so don't reserve space for it. - const int barType = style->direction() == LTR ? RightPadding : LeftPadding; - if (paddingType == barType && style->appearance() != NoControlPart) - padding += menuListArrowPadding(); - - return padding; -} - -bool RenderThemeChromiumSkia::shouldShowPlaceholderWhenFocused() const -{ - return true; -} - -#if ENABLE(DATALIST_ELEMENT) -LayoutUnit RenderThemeChromiumSkia::sliderTickSnappingThreshold() const -{ - return RenderThemeChromiumCommon::sliderTickSnappingThreshold(); -} -#endif - -#if ENABLE(PROGRESS_ELEMENT) - -// -// Following values are come from default of GTK+ -// -static const int progressDeltaPixelsPerSecond = 100; -static const int progressActivityBlocks = 5; -static const int progressAnimationFrmaes = 10; -static const double progressAnimationInterval = 0.125; - -IntRect RenderThemeChromiumSkia::determinateProgressValueRectFor(RenderProgress* renderProgress, const IntRect& rect) const -{ - int dx = rect.width() * renderProgress->position(); - return IntRect(rect.x(), rect.y(), dx, rect.height()); -} - -IntRect RenderThemeChromiumSkia::indeterminateProgressValueRectFor(RenderProgress* renderProgress, const IntRect& rect) const -{ - - int valueWidth = rect.width() / progressActivityBlocks; - int movableWidth = rect.width() - valueWidth; - if (movableWidth <= 0) - return IntRect(); - - double progress = renderProgress->animationProgress(); - if (progress < 0.5) - return IntRect(rect.x() + progress * 2 * movableWidth, rect.y(), valueWidth, rect.height()); - return IntRect(rect.x() + (1.0 - progress) * 2 * movableWidth, rect.y(), valueWidth, rect.height()); -} - -double RenderThemeChromiumSkia::animationRepeatIntervalForProgressBar(RenderProgress*) const -{ - return progressAnimationInterval; -} - -double RenderThemeChromiumSkia::animationDurationForProgressBar(RenderProgress* renderProgress) const -{ - return progressAnimationInterval * progressAnimationFrmaes * 2; // "2" for back and forth -} - -IntRect RenderThemeChromiumSkia::progressValueRectFor(RenderProgress* renderProgress, const IntRect& rect) const -{ - return renderProgress->isDeterminate() ? determinateProgressValueRectFor(renderProgress, rect) : indeterminateProgressValueRectFor(renderProgress, rect); -} - -RenderThemeChromiumSkia::DirectionFlippingScope::DirectionFlippingScope(RenderObject* renderer, const PaintInfo& paintInfo, const IntRect& rect) - : m_needsFlipping(!renderer->style()->isLeftToRightDirection()) - , m_paintInfo(paintInfo) -{ - if (!m_needsFlipping) - return; - m_paintInfo.context->save(); - m_paintInfo.context->translate(2 * rect.x() + rect.width(), 0); - m_paintInfo.context->scale(FloatSize(-1, 1)); -} - -RenderThemeChromiumSkia::DirectionFlippingScope::~DirectionFlippingScope() -{ - if (!m_needsFlipping) - return; - m_paintInfo.context->restore(); -} - - -#endif - -} // namespace WebCore diff --git a/Source/WebCore/rendering/RenderThemeChromiumSkia.h b/Source/WebCore/rendering/RenderThemeChromiumSkia.h deleted file mode 100644 index 8dc3a3a36..000000000 --- a/Source/WebCore/rendering/RenderThemeChromiumSkia.h +++ /dev/null @@ -1,197 +0,0 @@ -/* - * This file is part of the WebKit project. - * - * Copyright (C) 2006 Apple Computer, Inc. - * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com - * Copyright (C) 2007 Holger Hans Peter Freyther - * Copyright (C) 2007 Alp Toker <alp@atoker.com> - * Copyright (C) 2008, 2009 Google, Inc. - * All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#ifndef RenderThemeChromiumSkia_h -#define RenderThemeChromiumSkia_h - -#include "RenderTheme.h" -#include "RenderThemeChromiumCommon.h" - -namespace WebCore { - -class RenderProgress; - -class RenderThemeChromiumSkia : public RenderTheme { -public: - RenderThemeChromiumSkia(); - virtual ~RenderThemeChromiumSkia(); - - virtual String extraDefaultStyleSheet(); - virtual String extraQuirksStyleSheet(); -#if ENABLE(VIDEO) - virtual String extraMediaControlsStyleSheet(); -#endif - -#if ENABLE(TOUCH_EVENTS) - virtual Color platformTapHighlightColor() const OVERRIDE - { - return Color(defaultTapHighlightColor); - } -#endif - - // A method asking if the theme's controls actually care about redrawing when hovered. - virtual bool supportsHover(const RenderStyle*) const; - - // A method asking if the theme is able to draw the focus ring. - virtual bool supportsFocusRing(const RenderStyle*) const; - - virtual bool supportsDataListUI(const AtomicString& type) const OVERRIDE; - -#if ENABLE(VIDEO_TRACK) - virtual bool supportsClosedCaptioning() const OVERRIDE; -#endif - // The platform selection color. - virtual Color platformActiveSelectionBackgroundColor() const; - virtual Color platformInactiveSelectionBackgroundColor() const; - virtual Color platformActiveSelectionForegroundColor() const; - virtual Color platformInactiveSelectionForegroundColor() const; - virtual Color platformFocusRingColor() const; - - // To change the blink interval, override caretBlinkIntervalInternal instead of this one so that we may share layout test code an intercepts. - virtual double caretBlinkInterval() const; - - // System fonts. - virtual void systemFont(int propId, FontDescription&) const; - - virtual int minimumMenuListSize(RenderStyle*) const; - - virtual void setCheckboxSize(RenderStyle*) const; - - virtual void setRadioSize(RenderStyle*) const; - - virtual void adjustButtonStyle(StyleResolver*, RenderStyle*, Element*) const; - - virtual bool paintTextArea(RenderObject*, const PaintInfo&, const IntRect&); - - virtual void adjustSearchFieldStyle(StyleResolver*, RenderStyle*, Element*) const; - virtual bool paintSearchField(RenderObject*, const PaintInfo&, const IntRect&); - - virtual void adjustSearchFieldCancelButtonStyle(StyleResolver*, RenderStyle*, Element*) const; - virtual bool paintSearchFieldCancelButton(RenderObject*, const PaintInfo&, const IntRect&); - - virtual void adjustSearchFieldDecorationStyle(StyleResolver*, RenderStyle*, Element*) const; - - virtual void adjustSearchFieldResultsDecorationStyle(StyleResolver*, RenderStyle*, Element*) const; - virtual bool paintSearchFieldResultsDecoration(RenderObject*, const PaintInfo&, const IntRect&); - - virtual void adjustSearchFieldResultsButtonStyle(StyleResolver*, RenderStyle*, Element*) const; - virtual bool paintSearchFieldResultsButton(RenderObject*, const PaintInfo&, const IntRect&); - - virtual bool paintMediaSliderTrack(RenderObject*, const PaintInfo&, const IntRect&); - virtual bool paintMediaVolumeSliderTrack(RenderObject*, const PaintInfo&, const IntRect&); - virtual void adjustSliderThumbSize(RenderStyle*, Element*) const; - virtual bool paintMediaSliderThumb(RenderObject*, const PaintInfo&, const IntRect&); - virtual bool paintMediaToggleClosedCaptionsButton(RenderObject*, const PaintInfo&, const IntRect&); - virtual bool paintMediaVolumeSliderThumb(RenderObject*, const PaintInfo&, const IntRect&); - virtual bool paintMediaPlayButton(RenderObject*, const PaintInfo&, const IntRect&); - virtual bool paintMediaMuteButton(RenderObject*, const PaintInfo&, const IntRect&); - virtual String formatMediaControlsTime(float time) const; - virtual String formatMediaControlsCurrentTime(float currentTime, float duration) const; - virtual String formatMediaControlsRemainingTime(float currentTime, float duration) const; - virtual bool paintMediaFullscreenButton(RenderObject*, const PaintInfo&, const IntRect&); - - // MenuList refers to an unstyled menulist (meaning a menulist without - // background-color or border set) and MenuListButton refers to a styled - // menulist (a menulist with background-color or border set). They have - // this distinction to support showing aqua style themes whenever they - // possibly can, which is something we don't want to replicate. - // - // In short, we either go down the MenuList code path or the MenuListButton - // codepath. We never go down both. And in both cases, they render the - // entire menulist. - virtual void adjustMenuListStyle(StyleResolver*, RenderStyle*, Element*) const; - virtual void adjustMenuListButtonStyle(StyleResolver*, RenderStyle*, Element*) const; - virtual bool paintMenuListButton(RenderObject*, const PaintInfo&, const IntRect&); - -#if ENABLE(PROGRESS_ELEMENT) - virtual double animationRepeatIntervalForProgressBar(RenderProgress*) const; - virtual double animationDurationForProgressBar(RenderProgress*) const; -#endif - - // These methods define the padding for the MenuList's inner block. - virtual int popupInternalPaddingLeft(RenderStyle*) const; - virtual int popupInternalPaddingRight(RenderStyle*) const; - virtual int popupInternalPaddingTop(RenderStyle*) const; - virtual int popupInternalPaddingBottom(RenderStyle*) const; - -#if ENABLE(VIDEO) - // Media controls - virtual bool hasOwnDisabledStateHandlingFor(ControlPart) const { return true; } - virtual bool usesVerticalVolumeSlider() const { return false; } -#endif - - // Provide a way to pass the default font size from the Settings object - // to the render theme. FIXME: http://b/1129186 A cleaner way would be - // to remove the default font size from this object and have callers - // that need the value to get it directly from the appropriate Settings - // object. - static void setDefaultFontSize(int); - -protected: - virtual double caretBlinkIntervalInternal() const; - - virtual int menuListArrowPadding() const; - - static void setSizeIfAuto(RenderStyle*, const IntSize&); - -#if ENABLE(PROGRESS_ELEMENT) - IntRect determinateProgressValueRectFor(RenderProgress*, const IntRect&) const; - IntRect indeterminateProgressValueRectFor(RenderProgress*, const IntRect&) const; - IntRect progressValueRectFor(RenderProgress*, const IntRect&) const; - - class DirectionFlippingScope { - public: - DirectionFlippingScope(RenderObject*, const PaintInfo&, const IntRect&); - ~DirectionFlippingScope(); - - private: - bool m_needsFlipping; - const PaintInfo& m_paintInfo; - }; -#endif - -private: - virtual Color disabledTextColor(const Color& textColor, const Color&) const OVERRIDE { return textColor; } - virtual bool shouldShowPlaceholderWhenFocused() const OVERRIDE; - -#if ENABLE(DATALIST_ELEMENT) - virtual LayoutUnit sliderTickSnappingThreshold() const OVERRIDE; -#endif -#if ENABLE(INPUT_MULTIPLE_FIELDS_UI) - virtual bool supportsCalendarPicker(const AtomicString& type) const OVERRIDE; -#endif - - int menuListInternalPadding(RenderStyle*, int paddingType) const; - bool paintMediaButtonInternal(GraphicsContext*, const IntRect&, Image*); - IntRect convertToPaintingRect(RenderObject* inputRenderer, const RenderObject* partRenderer, LayoutRect partRect, const IntRect& localOffset) const; - - static const RGBA32 defaultTapHighlightColor = 0x2e000000; // 18% black. -}; - -} // namespace WebCore - -#endif // RenderThemeChromiumSkia_h diff --git a/Source/WebCore/rendering/RenderThemeChromiumWin.cpp b/Source/WebCore/rendering/RenderThemeChromiumWin.cpp deleted file mode 100644 index 709904c59..000000000 --- a/Source/WebCore/rendering/RenderThemeChromiumWin.cpp +++ /dev/null @@ -1,679 +0,0 @@ -/* - * This file is part of the WebKit project. - * - * Copyright (C) 2006 Apple Computer, Inc. - * Copyright (C) 2008, 2009 Google, Inc. - * Copyright (C) 2009 Kenneth Rohde Christiansen - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - * - */ - -#include "config.h" -#include "RenderThemeChromiumWin.h" - -#include <windows.h> -#include <uxtheme.h> -#include <vssym32.h> - -#include "CSSValueKeywords.h" -#include "FontSelector.h" -#include "FontUtilsChromiumWin.h" -#include "GraphicsContext.h" -#include "HTMLMediaElement.h" -#include "HTMLNames.h" -#include "LayoutTestSupport.h" -#include "MediaControlElements.h" -#include "PaintInfo.h" -#include "PlatformSupport.h" -#include "RenderBox.h" -#include "RenderProgress.h" -#include "RenderSlider.h" -#include "RenderThemeChromiumCommon.h" -#include "ScrollbarTheme.h" -#include "SystemInfo.h" -#include "TransparencyWin.h" -#include <wtf/CurrentTime.h> - - -// FIXME: This dependency should eventually be removed. -#include <skia/ext/skia_utils_win.h> - -namespace WebCore { - -// The standard width for the menu list drop-down button when run under -// layout test mode. Use the value that's currently captured in most baselines. -static const int kStandardMenuListButtonWidth = 17; - -namespace { -// We must not create multiple ThemePainter instances. -class ThemePainter { -public: - ThemePainter(GraphicsContext* context, const IntRect& r) - { -#ifndef NDEBUG - ASSERT(!s_hasInstance); - s_hasInstance = true; -#endif - TransparencyWin::TransformMode transformMode = getTransformMode(context->getCTM()); - m_helper.init(context, getLayerMode(context, transformMode), transformMode, r); - - if (!m_helper.context()) { - // TransparencyWin doesn't have well-defined copy-ctor nor op=() - // so we re-initialize it instead of assigning a fresh istance. - // On the reinitialization, we fallback to use NoLayer mode. - // Note that the original initialization failure can be caused by - // a failure of an internal buffer allocation and NoLayer mode - // does not have such buffer allocations. - m_helper.~TransparencyWin(); - new (&m_helper) TransparencyWin(); - m_helper.init(context, TransparencyWin::NoLayer, transformMode, r); - } - } - - ~ThemePainter() - { - m_helper.composite(); -#ifndef NDEBUG - s_hasInstance = false; -#endif - } - - GraphicsContext* context() { return m_helper.context(); } - const IntRect& drawRect() { return m_helper.drawRect(); } - -private: - - static TransparencyWin::LayerMode getLayerMode(GraphicsContext* context, TransparencyWin::TransformMode transformMode) - { - if (context->platformContext()->isDrawingToImageBuffer()) // Might have transparent background. - return TransparencyWin::WhiteLayer; - if (context->platformContext()->canvas()->isDrawingToLayer()) // Needs antialiasing help. - return TransparencyWin::OpaqueCompositeLayer; - // Nothing interesting. - return transformMode == TransparencyWin::KeepTransform ? TransparencyWin::NoLayer : TransparencyWin::OpaqueCompositeLayer; - } - - static TransparencyWin::TransformMode getTransformMode(const AffineTransform& matrix) - { - if (matrix.b() || matrix.c()) // Skew. - return TransparencyWin::Untransform; - if (matrix.a() != 1.0 || matrix.d() != 1.0) // Scale. - return TransparencyWin::ScaleTransform; - // Nothing interesting. - return TransparencyWin::KeepTransform; - } - - TransparencyWin m_helper; -#ifndef NDEBUG - static bool s_hasInstance; -#endif -}; - -#ifndef NDEBUG -bool ThemePainter::s_hasInstance = false; -#endif - -} // namespace - -// Internal static helper functions. We don't put them in an anonymous -// namespace so they have easier access to the WebCore namespace. - -static bool supportsFocus(ControlPart appearance) -{ - switch (appearance) { - case SquareButtonPart: - case PushButtonPart: - case ButtonPart: - case DefaultButtonPart: - case SearchFieldPart: - case TextFieldPart: - case TextAreaPart: - return true; - } - return false; -} - -static double querySystemBlinkInterval(double defaultInterval) -{ - UINT blinkTime = GetCaretBlinkTime(); - if (!blinkTime) - return defaultInterval; - if (blinkTime == INFINITE) - return 0; - return blinkTime / 1000.0; -} - -PassRefPtr<RenderTheme> RenderThemeChromiumWin::create() -{ - return adoptRef(new RenderThemeChromiumWin); -} - -PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page) -{ - static RenderTheme* rt = RenderThemeChromiumWin::create().leakRef(); - return rt; -} - -bool RenderThemeChromiumWin::supportsFocusRing(const RenderStyle* style) const -{ - // Let webkit draw one of its halo rings around any focused element, - // except push buttons. For buttons we use the windows PBS_DEFAULTED - // styling to give it a blue border. - return style->appearance() == ButtonPart - || style->appearance() == PushButtonPart - || style->appearance() == SquareButtonPart; -} - -Color RenderThemeChromiumWin::platformActiveSelectionBackgroundColor() const -{ - if (isRunningLayoutTest()) - return Color(0x00, 0x00, 0xff); // Royal blue. - COLORREF color = GetSysColor(COLOR_HIGHLIGHT); - return Color(GetRValue(color), GetGValue(color), GetBValue(color), 0xff); -} - -Color RenderThemeChromiumWin::platformInactiveSelectionBackgroundColor() const -{ - if (isRunningLayoutTest()) - return Color(0x99, 0x99, 0x99); // Medium gray. - COLORREF color = GetSysColor(COLOR_GRAYTEXT); - return Color(GetRValue(color), GetGValue(color), GetBValue(color), 0xff); -} - -Color RenderThemeChromiumWin::platformActiveSelectionForegroundColor() const -{ - if (isRunningLayoutTest()) - return Color(0xff, 0xff, 0xcc); // Pale yellow. - COLORREF color = GetSysColor(COLOR_HIGHLIGHTTEXT); - return Color(GetRValue(color), GetGValue(color), GetBValue(color), 0xff); -} - -Color RenderThemeChromiumWin::platformInactiveSelectionForegroundColor() const -{ - return Color::white; -} - -Color RenderThemeChromiumWin::platformActiveTextSearchHighlightColor() const -{ - return Color(0xff, 0x96, 0x32); // Orange. -} - -Color RenderThemeChromiumWin::platformInactiveTextSearchHighlightColor() const -{ - return Color(0xff, 0xff, 0x96); // Yellow. -} - -// Map a CSSValue* system color to an index understood by GetSysColor(). -static int cssValueIdToSysColorIndex(int cssValueId) -{ - switch (cssValueId) { - case CSSValueActiveborder: return COLOR_ACTIVEBORDER; - case CSSValueActivecaption: return COLOR_ACTIVECAPTION; - case CSSValueAppworkspace: return COLOR_APPWORKSPACE; - case CSSValueBackground: return COLOR_BACKGROUND; - case CSSValueButtonface: return COLOR_BTNFACE; - case CSSValueButtonhighlight: return COLOR_BTNHIGHLIGHT; - case CSSValueButtonshadow: return COLOR_BTNSHADOW; - case CSSValueButtontext: return COLOR_BTNTEXT; - case CSSValueCaptiontext: return COLOR_CAPTIONTEXT; - case CSSValueGraytext: return COLOR_GRAYTEXT; - case CSSValueHighlight: return COLOR_HIGHLIGHT; - case CSSValueHighlighttext: return COLOR_HIGHLIGHTTEXT; - case CSSValueInactiveborder: return COLOR_INACTIVEBORDER; - case CSSValueInactivecaption: return COLOR_INACTIVECAPTION; - case CSSValueInactivecaptiontext: return COLOR_INACTIVECAPTIONTEXT; - case CSSValueInfobackground: return COLOR_INFOBK; - case CSSValueInfotext: return COLOR_INFOTEXT; - case CSSValueMenu: return COLOR_MENU; - case CSSValueMenutext: return COLOR_MENUTEXT; - case CSSValueScrollbar: return COLOR_SCROLLBAR; - case CSSValueThreeddarkshadow: return COLOR_3DDKSHADOW; - case CSSValueThreedface: return COLOR_3DFACE; - case CSSValueThreedhighlight: return COLOR_3DHIGHLIGHT; - case CSSValueThreedlightshadow: return COLOR_3DLIGHT; - case CSSValueThreedshadow: return COLOR_3DSHADOW; - case CSSValueWindow: return COLOR_WINDOW; - case CSSValueWindowframe: return COLOR_WINDOWFRAME; - case CSSValueWindowtext: return COLOR_WINDOWTEXT; - default: return -1; // Unsupported CSSValue - } -} - -Color RenderThemeChromiumWin::systemColor(int cssValueId) const -{ - int sysColorIndex = cssValueIdToSysColorIndex(cssValueId); - if (isRunningLayoutTest() || (sysColorIndex == -1)) - return RenderTheme::systemColor(cssValueId); - - COLORREF color = GetSysColor(sysColorIndex); - return Color(GetRValue(color), GetGValue(color), GetBValue(color)); -} - -#if ENABLE(DATALIST_ELEMENT) -IntSize RenderThemeChromiumWin::sliderTickSize() const -{ - return IntSize(1, 3); -} - -int RenderThemeChromiumWin::sliderTickOffsetFromTrackCenter() const -{ - return 11; -} -#endif - -void RenderThemeChromiumWin::adjustSliderThumbSize(RenderStyle* style, Element* element) const -{ - // These sizes match what WinXP draws for various menus. - const int sliderThumbAlongAxis = 11; - const int sliderThumbAcrossAxis = 21; - if (style->appearance() == SliderThumbHorizontalPart) { - style->setWidth(Length(sliderThumbAlongAxis, Fixed)); - style->setHeight(Length(sliderThumbAcrossAxis, Fixed)); - } else if (style->appearance() == SliderThumbVerticalPart) { - style->setWidth(Length(sliderThumbAcrossAxis, Fixed)); - style->setHeight(Length(sliderThumbAlongAxis, Fixed)); - } else - RenderThemeChromiumSkia::adjustSliderThumbSize(style, element); -} - -bool RenderThemeChromiumWin::paintCheckbox(RenderObject* o, const PaintInfo& i, const IntRect& r) -{ - return paintButton(o, i, r); -} -bool RenderThemeChromiumWin::paintRadio(RenderObject* o, const PaintInfo& i, const IntRect& r) -{ - return paintButton(o, i, r); -} - -bool RenderThemeChromiumWin::paintButton(RenderObject* o, const PaintInfo& i, const IntRect& r) -{ - const ThemeData& themeData = getThemeData(o); - - ThemePainter painter(i.context, r); - PlatformSupport::paintButton(painter.context(), - themeData.m_part, - themeData.m_state, - themeData.m_classicState, - painter.drawRect()); - return false; -} - -bool RenderThemeChromiumWin::paintTextField(RenderObject* o, const PaintInfo& i, const IntRect& r) -{ - return paintTextFieldInternal(o, i, r, true); -} - -bool RenderThemeChromiumWin::paintSliderTrack(RenderObject* o, const PaintInfo& i, const IntRect& r) -{ - const ThemeData& themeData = getThemeData(o); - - ThemePainter painter(i.context, r); - PlatformSupport::paintTrackbar(painter.context(), - themeData.m_part, - themeData.m_state, - themeData.m_classicState, - painter.drawRect()); - -#if ENABLE(DATALIST_ELEMENT) - paintSliderTicks(o, i, r); -#endif - - return false; -} - -bool RenderThemeChromiumWin::paintSliderThumb(RenderObject* o, const PaintInfo& i, const IntRect& r) -{ - const ThemeData& themeData = getThemeData(o); - - ThemePainter painter(i.context, r); - PlatformSupport::paintTrackbar(painter.context(), - themeData.m_part, - themeData.m_state, - themeData.m_classicState, - painter.drawRect()); - - return false; -} - -static int menuListButtonWidth() -{ - static int width = isRunningLayoutTest() ? kStandardMenuListButtonWidth : GetSystemMetrics(SM_CXVSCROLL); - return width; -} - -// Used to paint unstyled menulists (i.e. with the default border) -bool RenderThemeChromiumWin::paintMenuList(RenderObject* o, const PaintInfo& i, const IntRect& r) -{ - if (!o->isBox()) - return false; - - const RenderBox* box = toRenderBox(o); - int borderRight = box->borderRight(); - int borderLeft = box->borderLeft(); - int borderTop = box->borderTop(); - int borderBottom = box->borderBottom(); - - // If all the borders are 0, then tell skia not to paint the border on the - // textfield. FIXME: http://b/1210017 Figure out how to get Windows to not - // draw individual borders and then pass that to skia so we can avoid - // drawing any borders that are set to 0. For non-zero borders, we draw the - // border, but webkit just draws over it. - bool drawEdges = !(!borderRight && !borderLeft && !borderTop && !borderBottom); - - paintTextFieldInternal(o, i, r, drawEdges); - - // Take padding and border into account. If the MenuList is smaller than - // the size of a button, make sure to shrink it appropriately and not put - // its x position to the left of the menulist. - const int buttonWidth = menuListButtonWidth(); - int spacingLeft = borderLeft + box->paddingLeft(); - int spacingRight = borderRight + box->paddingRight(); - int spacingTop = borderTop + box->paddingTop(); - int spacingBottom = borderBottom + box->paddingBottom(); - - int buttonX; - if (r.maxX() - r.x() < buttonWidth) - buttonX = r.x(); - else - buttonX = o->style()->direction() == LTR ? r.maxX() - spacingRight - buttonWidth : r.x() + spacingLeft; - - // Compute the rectangle of the button in the destination image. - IntRect rect(buttonX, - r.y() + spacingTop, - std::min(buttonWidth, r.maxX() - r.x()), - r.height() - (spacingTop + spacingBottom)); - - // Get the correct theme data for a textfield and paint the menu. - ThemePainter painter(i.context, rect); - PlatformSupport::paintMenuList(painter.context(), - CP_DROPDOWNBUTTON, - determineState(o), - determineClassicState(o), - painter.drawRect()); - return false; -} - -double RenderThemeChromiumWin::caretBlinkIntervalInternal() const -{ - // This involves a system call, so we cache the result. - static double blinkInterval = querySystemBlinkInterval(RenderTheme::caretBlinkInterval()); - return blinkInterval; -} - -unsigned RenderThemeChromiumWin::determineState(RenderObject* o, ControlSubPart subPart) -{ - unsigned result = TS_NORMAL; - ControlPart appearance = o->style()->appearance(); - if (!isEnabled(o)) - result = TS_DISABLED; - else if (isReadOnlyControl(o)) - result = (appearance == TextFieldPart || appearance == TextAreaPart || appearance == SearchFieldPart) ? ETS_READONLY : TS_DISABLED; - // Active overrides hover and focused. - else if (isPressed(o) && (subPart == SpinButtonUp) == isSpinUpButtonPartPressed(o)) - result = TS_PRESSED; - else if (supportsFocus(appearance) && isFocused(o)) - result = ETS_FOCUSED; - else if (isHovered(o) && (subPart == SpinButtonUp) == isSpinUpButtonPartHovered(o)) - result = TS_HOT; - - // CBS_UNCHECKED*: 1-4 - // CBS_CHECKED*: 5-8 - // CBS_MIXED*: 9-12 - if (isIndeterminate(o)) - result += 8; - else if (isChecked(o)) - result += 4; - return result; -} - -unsigned RenderThemeChromiumWin::determineSliderThumbState(RenderObject* o) -{ - unsigned result = TUS_NORMAL; - if (!isEnabled(o)) - result = TUS_DISABLED; - else if (supportsFocus(o->style()->appearance()) && isFocused(o)) - result = TUS_FOCUSED; - else if (isPressed(o)) - result = TUS_PRESSED; - else if (isHovered(o)) - result = TUS_HOT; - return result; -} - -unsigned RenderThemeChromiumWin::determineClassicState(RenderObject* o, ControlSubPart subPart) -{ - unsigned result = 0; - - ControlPart part = o->style()->appearance(); - - // Sliders are always in the normal state. - if (part == SliderHorizontalPart || part == SliderVerticalPart) - return result; - - // So are readonly text fields. - if (isReadOnlyControl(o) && (part == TextFieldPart || part == TextAreaPart || part == SearchFieldPart)) - return result; - - if (part == SliderThumbHorizontalPart || part == SliderThumbVerticalPart) { - if (!isEnabled(o)) - result = DFCS_INACTIVE; - else if (isPressed(o)) // Active supersedes hover - result = DFCS_PUSHED; - else if (isHovered(o)) - result = DFCS_HOT; - } else { - if (!isEnabled(o) || isReadOnlyControl(o)) - result = DFCS_INACTIVE; - // Active supersedes hover - else if (isPressed(o) && (subPart == SpinButtonUp) == isSpinUpButtonPartPressed(o)) - result = DFCS_PUSHED; - else if (supportsFocus(part) && isFocused(o)) // So does focused - result = 0; - else if (isHovered(o) && (subPart == SpinButtonUp) == isSpinUpButtonPartHovered(o)) - result = DFCS_HOT; - // Classic theme can't represent indeterminate states. Use unchecked appearance. - if (isChecked(o) && !isIndeterminate(o)) - result |= DFCS_CHECKED; - } - return result; -} - -ThemeData RenderThemeChromiumWin::getThemeData(RenderObject* o, ControlSubPart subPart) -{ - ThemeData result; - switch (o->style()->appearance()) { - case CheckboxPart: - result.m_part = BP_CHECKBOX; - result.m_state = determineState(o); - result.m_classicState = DFCS_BUTTONCHECK; - break; - case RadioPart: - result.m_part = BP_RADIOBUTTON; - result.m_state = determineState(o); - result.m_classicState = DFCS_BUTTONRADIO; - break; - case SquareButtonPart: - case PushButtonPart: - case ButtonPart: - result.m_part = BP_PUSHBUTTON; - result.m_state = determineState(o); - result.m_classicState = DFCS_BUTTONPUSH; - break; - case SliderHorizontalPart: - result.m_part = TKP_TRACK; - result.m_state = TRS_NORMAL; - break; - case SliderVerticalPart: - result.m_part = TKP_TRACKVERT; - result.m_state = TRVS_NORMAL; - break; - case SliderThumbHorizontalPart: - result.m_part = TKP_THUMBBOTTOM; - result.m_state = determineSliderThumbState(o); - break; - case SliderThumbVerticalPart: - result.m_part = TKP_THUMBVERT; - result.m_state = determineSliderThumbState(o); - break; - case ListboxPart: - case MenulistPart: - case MenulistButtonPart: - case SearchFieldPart: - case TextFieldPart: - case TextAreaPart: - result.m_part = EP_EDITTEXT; - result.m_state = determineState(o); - break; - case InnerSpinButtonPart: - result.m_part = subPart == SpinButtonUp ? SPNP_UP : SPNP_DOWN; - result.m_state = determineState(o, subPart); - result.m_classicState = subPart == SpinButtonUp ? DFCS_SCROLLUP : DFCS_SCROLLDOWN; - break; - } - - result.m_classicState |= determineClassicState(o, subPart); - - return result; -} - -bool RenderThemeChromiumWin::paintTextFieldInternal(RenderObject* o, - const PaintInfo& i, - const IntRect& r, - bool drawEdges) -{ - // Fallback to white if the specified color object is invalid. - // (Note PlatformSupport::paintTextField duplicates this check). - Color backgroundColor(Color::white); - if (o->style()->visitedDependentColor(CSSPropertyBackgroundColor).isValid()) - backgroundColor = o->style()->visitedDependentColor(CSSPropertyBackgroundColor); - - // If we have background-image, don't fill the content area to expose the - // parent's background. Also, we shouldn't fill the content area if the - // alpha of the color is 0. The API of Windows GDI ignores the alpha. - // - // Note that we should paint the content area white if we have neither the - // background color nor background image explicitly specified to keep the - // appearance of select element consistent with other browsers. - bool fillContentArea = !o->style()->hasBackgroundImage() && backgroundColor.alpha(); - - if (o->style()->hasBorderRadius()) { - // If the style has rounded borders, setup the context to clip the - // background (themed or filled) appropriately. - // FIXME: make sure we do the right thing if css background-clip is set. - i.context->save(); - i.context->addRoundedRectClip(o->style()->getRoundedBorderFor(r)); - } - { - const ThemeData& themeData = getThemeData(o); - ThemePainter painter(i.context, r); - PlatformSupport::paintTextField(painter.context(), - themeData.m_part, - themeData.m_state, - themeData.m_classicState, - painter.drawRect(), - backgroundColor, - fillContentArea, - drawEdges); - // End of block commits the painter before restoring context. - } - if (o->style()->hasBorderRadius()) - i.context->restore(); - return false; -} - -void RenderThemeChromiumWin::adjustInnerSpinButtonStyle(StyleResolver*, RenderStyle* style, Element*) const -{ - int width = ScrollbarTheme::theme()->scrollbarThickness(); - style->setWidth(Length(width, Fixed)); - style->setMinWidth(Length(width, Fixed)); -} - -bool RenderThemeChromiumWin::paintInnerSpinButton(RenderObject* object, const PaintInfo& info, const IntRect& rect) -{ - IntRect half = rect; - - // Need explicit blocks to avoid to create multiple ThemePainter instances. - { - half.setHeight(rect.height() / 2); - const ThemeData& upThemeData = getThemeData(object, SpinButtonUp); - ThemePainter upPainter(info.context, half); - PlatformSupport::paintSpinButton(upPainter.context(), - upThemeData.m_part, - upThemeData.m_state, - upThemeData.m_classicState, - upPainter.drawRect()); - } - - { - half.setY(rect.y() + rect.height() / 2); - const ThemeData& downThemeData = getThemeData(object, SpinButtonDown); - ThemePainter downPainter(info.context, half); - PlatformSupport::paintSpinButton(downPainter.context(), - downThemeData.m_part, - downThemeData.m_state, - downThemeData.m_classicState, - downPainter.drawRect()); - } - return false; -} - -#if ENABLE(PROGRESS_ELEMENT) - -// MSDN says that update intervals for the bar is 30ms. -// http://msdn.microsoft.com/en-us/library/bb760842(v=VS.85).aspx -static const double progressAnimationFrameRate = 0.033; - -double RenderThemeChromiumWin::animationRepeatIntervalForProgressBar(RenderProgress*) const -{ - return progressAnimationFrameRate; -} - -double RenderThemeChromiumWin::animationDurationForProgressBar(RenderProgress* renderProgress) const -{ - // On Chromium Windows port, animationProgress() and associated values aren't used. - // So here we can return arbitrary positive value. - return progressAnimationFrameRate; -} - -void RenderThemeChromiumWin::adjustProgressBarStyle(StyleResolver*, RenderStyle*, Element*) const -{ -} - -bool RenderThemeChromiumWin::paintProgressBar(RenderObject* o, const PaintInfo& i, const IntRect& r) -{ - if (!o->isProgress()) - return true; - - RenderProgress* renderProgress = toRenderProgress(o); - // For indeterminate bar, valueRect is ignored and it is computed by the theme engine - // because the animation is a platform detail and WebKit doesn't need to know how. - IntRect valueRect = renderProgress->isDeterminate() ? determinateProgressValueRectFor(renderProgress, r) : IntRect(0, 0, 0, 0); - double animatedSeconds = renderProgress->animationStartTime() ? WTF::currentTime() - renderProgress->animationStartTime() : 0; - ThemePainter painter(i.context, r); - DirectionFlippingScope scope(o, i, r); - PlatformSupport::paintProgressBar(painter.context(), r, valueRect, renderProgress->isDeterminate(), animatedSeconds); - return false; -} - -#endif - -bool RenderThemeChromiumWin::shouldOpenPickerWithF4Key() const -{ - return true; -} - -} // namespace WebCore diff --git a/Source/WebCore/rendering/RenderThemeChromiumWin.h b/Source/WebCore/rendering/RenderThemeChromiumWin.h deleted file mode 100644 index bf335c7b8..000000000 --- a/Source/WebCore/rendering/RenderThemeChromiumWin.h +++ /dev/null @@ -1,123 +0,0 @@ -/* - * This file is part of the WebKit project. - * - * Copyright (C) 2006 Apple Computer, Inc. - * Copyright (C) 2008, 2009 Google, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#ifndef RenderThemeChromiumWin_h -#define RenderThemeChromiumWin_h - -#include "RenderThemeChromiumSkia.h" - -#if WIN32 -typedef void* HANDLE; -typedef struct HINSTANCE__* HINSTANCE; -typedef HINSTANCE HMODULE; -#endif - -namespace WebCore { - - struct ThemeData { - ThemeData() : m_part(0), m_state(0), m_classicState(0) {} - - unsigned m_part; - unsigned m_state; - unsigned m_classicState; - }; - - class RenderThemeChromiumWin : public RenderThemeChromiumSkia { - public: - static PassRefPtr<RenderTheme> create(); - - // A method asking if the theme is able to draw the focus ring. - virtual bool supportsFocusRing(const RenderStyle*) const; - - // The platform selection color. - virtual Color platformActiveSelectionBackgroundColor() const; - virtual Color platformInactiveSelectionBackgroundColor() const; - virtual Color platformActiveSelectionForegroundColor() const; - virtual Color platformInactiveSelectionForegroundColor() const; - virtual Color platformActiveTextSearchHighlightColor() const; - virtual Color platformInactiveTextSearchHighlightColor() const; - - virtual Color systemColor(int cssValueId) const; - -#if ENABLE(DATALIST_ELEMENT) - virtual IntSize sliderTickSize() const OVERRIDE; - virtual int sliderTickOffsetFromTrackCenter() const OVERRIDE; -#endif - virtual void adjustSliderThumbSize(RenderStyle*, Element*) const; - - // Various paint functions. - virtual bool paintCheckbox(RenderObject*, const PaintInfo&, const IntRect&); - virtual bool paintRadio(RenderObject*, const PaintInfo&, const IntRect&); - virtual bool paintButton(RenderObject*, const PaintInfo&, const IntRect&); - virtual bool paintTextField(RenderObject*, const PaintInfo&, const IntRect&); - virtual bool paintSliderTrack(RenderObject*, const PaintInfo&, const IntRect&); - virtual bool paintSliderThumb(RenderObject*, const PaintInfo&, const IntRect&); - - // MenuList refers to an unstyled menulist (meaning a menulist without - // background-color or border set) and MenuListButton refers to a styled - // menulist (a menulist with background-color or border set). They have - // this distinction to support showing aqua style themes whenever they - // possibly can, which is something we don't want to replicate. - // - // In short, we either go down the MenuList code path or the MenuListButton - // codepath. We never go down both. And in both cases, they render the - // entire menulist. - virtual bool paintMenuList(RenderObject*, const PaintInfo&, const IntRect&); - - virtual void adjustInnerSpinButtonStyle(StyleResolver*, RenderStyle*, Element*) const; - virtual bool paintInnerSpinButton(RenderObject*, const PaintInfo&, const IntRect&); - -#if ENABLE(PROGRESS_ELEMENT) - virtual double animationRepeatIntervalForProgressBar(RenderProgress*) const; - virtual double animationDurationForProgressBar(RenderProgress*) const; - virtual void adjustProgressBarStyle(StyleResolver*, RenderStyle*, Element*) const; - virtual bool paintProgressBar(RenderObject*, const PaintInfo&, const IntRect&); -#endif - - virtual bool shouldOpenPickerWithF4Key() const OVERRIDE; - - protected: - virtual double caretBlinkIntervalInternal() const; - - private: - enum ControlSubPart { - None, - SpinButtonDown, - SpinButtonUp, - }; - - RenderThemeChromiumWin() { } - virtual ~RenderThemeChromiumWin() { } - - unsigned determineState(RenderObject*, ControlSubPart = None); - unsigned determineSliderThumbState(RenderObject*); - unsigned determineClassicState(RenderObject*, ControlSubPart = None); - - ThemeData getThemeData(RenderObject*, ControlSubPart = None); - - bool paintTextFieldInternal(RenderObject*, const PaintInfo&, const IntRect&, bool); - }; - -} // namespace WebCore - -#endif diff --git a/Source/WebCore/rendering/RenderThemeMac.h b/Source/WebCore/rendering/RenderThemeMac.h index 03d809ed6..a3cefee7e 100644 --- a/Source/WebCore/rendering/RenderThemeMac.h +++ b/Source/WebCore/rendering/RenderThemeMac.h @@ -23,19 +23,83 @@ #ifndef RenderThemeMac_h #define RenderThemeMac_h -#import "RenderThemeMacShared.h" +#import "RenderTheme.h" #import <wtf/RetainPtr.h> +#import <wtf/HashMap.h> + +OBJC_CLASS WebCoreRenderThemeNotificationObserver; namespace WebCore { class RenderProgress; class RenderStyle; -class RenderThemeMac : public RenderThemeMacShared { +class RenderThemeMac : public RenderTheme { public: static PassRefPtr<RenderTheme> create(); - virtual NSView* documentViewFor(RenderObject*) const; + // A method asking if the control changes its tint when the window has focus or not. + virtual bool controlSupportsTints(const RenderObject*) const; + + // A general method asking if any control tinting is supported at all. + virtual bool supportsControlTints() const { return true; } + + virtual void adjustRepaintRect(const RenderObject*, IntRect&) OVERRIDE; + + virtual bool isControlStyled(const RenderStyle*, const BorderData&, const FillLayer&, const Color& backgroundColor) const; + + virtual Color platformActiveSelectionBackgroundColor() const; + virtual Color platformInactiveSelectionBackgroundColor() const; + virtual Color platformActiveListBoxSelectionBackgroundColor() const; + virtual Color platformActiveListBoxSelectionForegroundColor() const; + virtual Color platformInactiveListBoxSelectionBackgroundColor() const; + virtual Color platformInactiveListBoxSelectionForegroundColor() const; + virtual Color platformFocusRingColor() const; + + virtual ScrollbarControlSize scrollbarControlSizeForPart(ControlPart) { return SmallScrollbar; } + + virtual void platformColorsDidChange(); + + // System fonts. + virtual void systemFont(CSSValueID, FontDescription&) const; + + virtual int minimumMenuListSize(RenderStyle*) const; + + virtual void adjustSliderThumbSize(RenderStyle*, Element*) const; + +#if ENABLE(DATALIST_ELEMENT) + virtual IntSize sliderTickSize() const OVERRIDE; + virtual int sliderTickOffsetFromTrackCenter() const OVERRIDE; +#endif + + virtual int popupInternalPaddingLeft(RenderStyle*) const; + virtual int popupInternalPaddingRight(RenderStyle*) const; + virtual int popupInternalPaddingTop(RenderStyle*) const; + virtual int popupInternalPaddingBottom(RenderStyle*) const; + + virtual bool paintCapsLockIndicator(RenderObject*, const PaintInfo&, const IntRect&) OVERRIDE; + + virtual bool popsMenuByArrowKeys() const OVERRIDE { return true; } + +#if ENABLE(METER_ELEMENT) + virtual IntSize meterSizeForBounds(const RenderMeter*, const IntRect&) const OVERRIDE; + virtual bool paintMeter(RenderObject*, const PaintInfo&, const IntRect&); + virtual bool supportsMeter(ControlPart) const; +#endif + +#if ENABLE(PROGRESS_ELEMENT) + // Returns the repeat interval of the animation for the progress bar. + virtual double animationRepeatIntervalForProgressBar(RenderProgress*) const; + // Returns the duration of the animation for the progress bar. + virtual double animationDurationForProgressBar(RenderProgress*) const; +#endif + + virtual Color systemColor(CSSValueID) const; + // Controls color values returned from platformFocusRingColor(). systemColor() will be used when false. + virtual bool usesTestModeFocusRingColor() const; + // A view associated to the contained document. Subclasses may not have such a view and return a fake. + NSView* documentViewFor(RenderObject*) const; + protected: RenderThemeMac(); @@ -51,7 +115,6 @@ protected: virtual bool paintMediaSliderThumb(RenderObject*, const PaintInfo&, const IntRect&); virtual bool paintMediaRewindButton(RenderObject*, const PaintInfo&, const IntRect&); virtual bool paintMediaReturnToRealtimeButton(RenderObject*, const PaintInfo&, const IntRect&); - virtual bool paintMediaToggleClosedCaptionsButton(RenderObject*, const PaintInfo&, const IntRect&); virtual bool paintMediaControlsBackground(RenderObject*, const PaintInfo&, const IntRect&); virtual bool paintMediaCurrentTime(RenderObject*, const PaintInfo&, const IntRect&); virtual bool paintMediaTimeRemaining(RenderObject*, const PaintInfo&, const IntRect&); @@ -73,6 +136,125 @@ protected: virtual void adjustMediaSliderThumbSize(RenderStyle*) const; virtual IntPoint volumeSliderOffsetFromMuteButton(RenderBox*, const IntSize&) const OVERRIDE; #endif + virtual bool supportsSelectionForegroundColors() const { return false; } + + virtual bool paintTextField(RenderObject*, const PaintInfo&, const IntRect&); + virtual void adjustTextFieldStyle(StyleResolver*, RenderStyle*, Element*) const; + + virtual bool paintTextArea(RenderObject*, const PaintInfo&, const IntRect&); + virtual void adjustTextAreaStyle(StyleResolver*, RenderStyle*, Element*) const; + + virtual bool paintMenuList(RenderObject*, const PaintInfo&, const IntRect&); + virtual void adjustMenuListStyle(StyleResolver*, RenderStyle*, Element*) const; + + virtual bool paintMenuListButton(RenderObject*, const PaintInfo&, const IntRect&); + virtual void adjustMenuListButtonStyle(StyleResolver*, RenderStyle*, Element*) const; + +#if ENABLE(PROGRESS_ELEMENT) + virtual void adjustProgressBarStyle(StyleResolver*, RenderStyle*, Element*) const; + virtual bool paintProgressBar(RenderObject*, const PaintInfo&, const IntRect&); +#endif + + virtual bool paintSliderTrack(RenderObject*, const PaintInfo&, const IntRect&); + virtual void adjustSliderTrackStyle(StyleResolver*, RenderStyle*, Element*) const; + + virtual bool paintSliderThumb(RenderObject*, const PaintInfo&, const IntRect&); + virtual void adjustSliderThumbStyle(StyleResolver*, RenderStyle*, Element*) const; + + virtual bool paintSearchField(RenderObject*, const PaintInfo&, const IntRect&); + virtual void adjustSearchFieldStyle(StyleResolver*, RenderStyle*, Element*) const; + + virtual void adjustSearchFieldCancelButtonStyle(StyleResolver*, RenderStyle*, Element*) const; + virtual bool paintSearchFieldCancelButton(RenderObject*, const PaintInfo&, const IntRect&); + + virtual void adjustSearchFieldDecorationStyle(StyleResolver*, RenderStyle*, Element*) const; + virtual bool paintSearchFieldDecoration(RenderObject*, const PaintInfo&, const IntRect&); + + virtual void adjustSearchFieldResultsDecorationStyle(StyleResolver*, RenderStyle*, Element*) const; + virtual bool paintSearchFieldResultsDecoration(RenderObject*, const PaintInfo&, const IntRect&); + + virtual void adjustSearchFieldResultsButtonStyle(StyleResolver*, RenderStyle*, Element*) const; + virtual bool paintSearchFieldResultsButton(RenderObject*, const PaintInfo&, const IntRect&); + +#if ENABLE(VIDEO) + virtual bool supportsClosedCaptioning() const { return true; } +#endif + + virtual bool shouldShowPlaceholderWhenFocused() const; + + virtual bool paintSnapshottedPluginOverlay(RenderObject*, const PaintInfo&, const IntRect&); + +private: + virtual String fileListNameForWidth(const FileList*, const Font&, int width, bool multipleFilesAllowed) const OVERRIDE; + + IntRect inflateRect(const IntRect&, const IntSize&, const int* margins, float zoomLevel = 1.0f) const; + + FloatRect convertToPaintingRect(const RenderObject* inputRenderer, const RenderObject* partRenderer, const FloatRect& inputRect, const IntRect&) const; + + // Get the control size based off the font. Used by some of the controls (like buttons). + NSControlSize controlSizeForFont(RenderStyle*) const; + NSControlSize controlSizeForSystemFont(RenderStyle*) const; + void setControlSize(NSCell*, const IntSize* sizes, const IntSize& minSize, float zoomLevel = 1.0f); + void setSizeFromFont(RenderStyle*, const IntSize* sizes) const; + IntSize sizeForFont(RenderStyle*, const IntSize* sizes) const; + IntSize sizeForSystemFont(RenderStyle*, const IntSize* sizes) const; + void setFontFromControlSize(StyleResolver*, RenderStyle*, NSControlSize) const; + + void updateCheckedState(NSCell*, const RenderObject*); + void updateEnabledState(NSCell*, const RenderObject*); + void updateFocusedState(NSCell*, const RenderObject*); + void updatePressedState(NSCell*, const RenderObject*); + // An optional hook for subclasses to update the control tint of NSCell. + virtual void updateActiveState(NSCell*, const RenderObject*) { } + + // Helpers for adjusting appearance and for painting + + void setPopupButtonCellState(const RenderObject*, const IntRect&); + const IntSize* popupButtonSizes() const; + const int* popupButtonMargins() const; + const int* popupButtonPadding(NSControlSize) const; + void paintMenuListButtonGradients(RenderObject*, const PaintInfo&, const IntRect&); + const IntSize* menuListSizes() const; + + const IntSize* searchFieldSizes() const; + const IntSize* cancelButtonSizes() const; + const IntSize* resultsButtonSizes() const; + void setSearchCellState(RenderObject*, const IntRect&); + void setSearchFieldSize(RenderStyle*) const; + + NSPopUpButtonCell* popupButton() const; + NSSearchFieldCell* search() const; + NSMenu* searchMenuTemplate() const; + NSSliderCell* sliderThumbHorizontal() const; + NSSliderCell* sliderThumbVertical() const; + NSTextFieldCell* textField() const; + +#if ENABLE(METER_ELEMENT) + NSLevelIndicatorStyle levelIndicatorStyleFor(ControlPart) const; + NSLevelIndicatorCell* levelIndicatorFor(const RenderMeter*) const; +#endif + +#if ENABLE(PROGRESS_ELEMENT) + int minimumProgressBarHeight(RenderStyle*) const; + const IntSize* progressBarSizes() const; + const int* progressBarMargins(NSControlSize) const; +#endif + +private: + mutable RetainPtr<NSPopUpButtonCell> m_popupButton; + mutable RetainPtr<NSSearchFieldCell> m_search; + mutable RetainPtr<NSMenu> m_searchMenuTemplate; + mutable RetainPtr<NSSliderCell> m_sliderThumbHorizontal; + mutable RetainPtr<NSSliderCell> m_sliderThumbVertical; + mutable RetainPtr<NSLevelIndicatorCell> m_levelIndicator; + mutable RetainPtr<NSTextFieldCell> m_textField; + + bool m_isSliderThumbHorizontalPressed; + bool m_isSliderThumbVerticalPressed; + + mutable HashMap<int, RGBA32> m_systemColorCache; + + RetainPtr<WebCoreRenderThemeNotificationObserver> m_notificationObserver; }; } // namespace WebCore diff --git a/Source/WebCore/rendering/RenderThemeMac.mm b/Source/WebCore/rendering/RenderThemeMac.mm index fadd09011..3a8c2868f 100644 --- a/Source/WebCore/rendering/RenderThemeMac.mm +++ b/Source/WebCore/rendering/RenderThemeMac.mm @@ -20,27 +20,134 @@ #import "config.h" #import "RenderThemeMac.h" +#import "BitmapImage.h" +#import "CSSValueKeywords.h" +#import "CSSValueList.h" +#import "ColorMac.h" +#import "Document.h" #import "Element.h" +#import "ExceptionCodePlaceholder.h" +#import "FileList.h" +#import "FrameView.h" #import "GraphicsContextCG.h" +#import "HTMLAudioElement.h" +#import "HTMLInputElement.h" #import "HTMLMediaElement.h" +#import "HTMLNames.h" +#import "HTMLPlugInImageElement.h" +#import "Image.h" +#import "ImageBuffer.h" #import "LocalCurrentGraphicsContext.h" +#import "LocalizedStrings.h" #import "MediaControlElements.h" #import "PaintInfo.h" +#import "RenderLayer.h" #import "RenderMedia.h" +#import "RenderMediaControlElements.h" #import "RenderMediaControls.h" +#import "RenderProgress.h" +#import "RenderSlider.h" +#import "RenderSnapshottedPlugIn.h" #import "RenderView.h" -#import "TimeRanges.h" +#import "SharedBuffer.h" +#import "StringTruncator.h" +#import "StyleResolver.h" #import "ThemeMac.h" -#import "WebCoreSystemInterface.h" +#import "TimeRanges.h" #import "UserAgentStyleSheets.h" +#import "WebCoreNSCellExtras.h" +#import "WebCoreSystemInterface.h" +#import <wtf/RetainPtr.h> +#import <wtf/RetainPtr.h> +#import <wtf/StdLibExtras.h> #import <Carbon/Carbon.h> #import <Cocoa/Cocoa.h> -#import <wtf/RetainPtr.h> +#import <math.h> + +#if ENABLE(METER_ELEMENT) +#include "RenderMeter.h" +#include "HTMLMeterElement.h" +#endif + +using namespace std; + +// The methods in this file are specific to the Mac OS X platform. + +// FIXME: The platform-independent code in this class should be factored out and merged with RenderThemeSafari. + +// We estimate the animation rate of a Mac OS X progress bar is 33 fps. +// Hard code the value here because we haven't found API for it. +const double progressAnimationFrameRate = 0.033; + +// Mac OS X progress bar animation seems to have 256 frames. +const double progressAnimationNumFrames = 256; + +@interface WebCoreRenderThemeNotificationObserver : NSObject +{ + WebCore::RenderTheme *_theme; +} + +- (id)initWithTheme:(WebCore::RenderTheme *)theme; +- (void)systemColorsDidChange:(NSNotification *)notification; + +@end + +@implementation WebCoreRenderThemeNotificationObserver + +- (id)initWithTheme:(WebCore::RenderTheme *)theme +{ + if (!(self = [super init])) + return nil; + + _theme = theme; + return self; +} + +- (void)systemColorsDidChange:(NSNotification *)unusedNotification +{ + ASSERT_UNUSED(unusedNotification, [[unusedNotification name] isEqualToString:NSSystemColorsDidChangeNotification]); + _theme->platformColorsDidChange(); +} + +@end + +@interface NSTextFieldCell (WKDetails) +- (CFDictionaryRef)_coreUIDrawOptionsWithFrame:(NSRect)cellFrame inView:(NSView *)controlView includeFocus:(BOOL)includeFocus; +@end + + +@interface WebCoreTextFieldCell : NSTextFieldCell +- (CFDictionaryRef)_coreUIDrawOptionsWithFrame:(NSRect)cellFrame inView:(NSView *)controlView includeFocus:(BOOL)includeFocus; +@end + +@implementation WebCoreTextFieldCell +- (CFDictionaryRef)_coreUIDrawOptionsWithFrame:(NSRect)cellFrame inView:(NSView *)controlView includeFocus:(BOOL)includeFocus +{ + // FIXME: This is a post-Lion-only workaround for <rdar://problem/11385461>. When that bug is resolved, we should remove this code. + CFMutableDictionaryRef coreUIDrawOptions = CFDictionaryCreateMutableCopy(NULL, 0, [super _coreUIDrawOptionsWithFrame:cellFrame inView:controlView includeFocus:includeFocus]); + CFDictionarySetValue(coreUIDrawOptions, @"borders only", kCFBooleanTrue); + return (CFDictionaryRef)[NSMakeCollectable(coreUIDrawOptions) autorelease]; +} +@end namespace WebCore { using namespace HTMLNames; +enum { + topMargin, + rightMargin, + bottomMargin, + leftMargin +}; + +enum { + topPadding, + rightPadding, + bottomPadding, + leftPadding +}; + PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page*) { static RenderTheme* rt = RenderThemeMac::create().leakRef(); @@ -53,11 +160,19 @@ PassRefPtr<RenderTheme> RenderThemeMac::create() } RenderThemeMac::RenderThemeMac() + : m_isSliderThumbHorizontalPressed(false) + , m_isSliderThumbVerticalPressed(false) + , m_notificationObserver(adoptNS([[WebCoreRenderThemeNotificationObserver alloc] initWithTheme:this])) { + [[NSNotificationCenter defaultCenter] addObserver:m_notificationObserver.get() + selector:@selector(systemColorsDidChange:) + name:NSSystemColorsDidChangeNotification + object:nil]; } RenderThemeMac::~RenderThemeMac() { + [[NSNotificationCenter defaultCenter] removeObserver:m_notificationObserver.get()]; } NSView* RenderThemeMac::documentViewFor(RenderObject* o) const @@ -136,9 +251,9 @@ static unsigned getMediaUIPartStateFlags(Node* node) { unsigned flags = 0; - if (node->disabled()) + if (isDisabledFormControl(node)) flags |= MediaUIPartDisabledFlag; - else if (node->active()) + else if (node->isElementNode() && toElement(node)->active()) flags |= MediaUIPartPressedFlag; return flags; } @@ -175,7 +290,7 @@ bool RenderThemeMac::paintMediaMuteButton(RenderObject* o, const PaintInfo& pain { Node* node = o->node(); Node* mediaNode = node ? node->shadowHost() : 0; - if (!mediaNode || (!mediaNode->hasTagName(videoTag) && !mediaNode->hasTagName(audioTag))) + if (!mediaNode || (!mediaNode->hasTagName(videoTag) && !isHTMLAudioElement(mediaNode))) return false; if (node->isMediaControlElement()) { @@ -189,7 +304,7 @@ bool RenderThemeMac::paintMediaPlayButton(RenderObject* o, const PaintInfo& pain { Node* node = o->node(); Node* mediaNode = node ? node->shadowHost() : 0; - if (!mediaNode || (!mediaNode->hasTagName(videoTag) && !mediaNode->hasTagName(audioTag))) + if (!mediaNode || (!mediaNode->hasTagName(videoTag) && !isHTMLAudioElement(mediaNode))) return false; if (node->isMediaControlElement()) { @@ -228,16 +343,15 @@ bool RenderThemeMac::paintMediaSliderTrack(RenderObject* o, const PaintInfo& pai if (!mediaNode || !mediaNode->isMediaElement()) return false; - HTMLMediaElement* mediaElement = static_cast<HTMLMediaElement*>(mediaNode); + HTMLMediaElement* mediaElement = toHTMLMediaElement(mediaNode); if (!mediaElement) return false; RefPtr<TimeRanges> timeRanges = mediaElement->buffered(); - ExceptionCode ignoredException; - float timeLoaded = timeRanges->length() ? timeRanges->end(0, ignoredException) : 0; + float timeLoaded = timeRanges->length() ? timeRanges->end(0, IGNORE_EXCEPTION) : 0; float currentTime = mediaElement->currentTime(); float duration = mediaElement->duration(); - if (isnan(duration)) + if (std::isnan(duration)) duration = 0; ContextContainer cgContextContainer(paintInfo.context); @@ -282,19 +396,6 @@ bool RenderThemeMac::paintMediaReturnToRealtimeButton(RenderObject* o, const Pai return false; } -bool RenderThemeMac::paintMediaToggleClosedCaptionsButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) -{ - Node* node = o->node(); - if (!node) - return false; - if (!node->isMediaControlElement()) - return false; - - LocalCurrentGraphicsContext localContext(paintInfo.context); - wkDrawMediaUIPart(mediaControlElementType(node), mediaControllerTheme(), localContext.cgContext(), r, getMediaUIPartStateFlags(node)); - return false; -} - bool RenderThemeMac::paintMediaControlsBackground(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) { Node* node = o->node(); @@ -430,4 +531,1752 @@ IntPoint RenderThemeMac::volumeSliderOffsetFromMuteButton(RenderBox* muteButtonB #endif // ENABLE(VIDEO) +Color RenderThemeMac::platformActiveSelectionBackgroundColor() const +{ + NSColor* color = [[NSColor selectedTextBackgroundColor] colorUsingColorSpaceName:NSDeviceRGBColorSpace]; + return Color(static_cast<int>(255.0 * [color redComponent]), static_cast<int>(255.0 * [color greenComponent]), static_cast<int>(255.0 * [color blueComponent])); +} + +Color RenderThemeMac::platformInactiveSelectionBackgroundColor() const +{ + NSColor* color = [[NSColor secondarySelectedControlColor] colorUsingColorSpaceName:NSDeviceRGBColorSpace]; + return Color(static_cast<int>(255.0 * [color redComponent]), static_cast<int>(255.0 * [color greenComponent]), static_cast<int>(255.0 * [color blueComponent])); +} + +Color RenderThemeMac::platformActiveListBoxSelectionBackgroundColor() const +{ + NSColor* color = [[NSColor alternateSelectedControlColor] colorUsingColorSpaceName:NSDeviceRGBColorSpace]; + return Color(static_cast<int>(255.0 * [color redComponent]), static_cast<int>(255.0 * [color greenComponent]), static_cast<int>(255.0 * [color blueComponent])); +} + +Color RenderThemeMac::platformActiveListBoxSelectionForegroundColor() const +{ + return Color::white; +} + +Color RenderThemeMac::platformInactiveListBoxSelectionForegroundColor() const +{ + return Color::black; +} + +Color RenderThemeMac::platformFocusRingColor() const +{ + if (usesTestModeFocusRingColor()) + return oldAquaFocusRingColor(); + + return systemColor(CSSValueWebkitFocusRingColor); +} + +Color RenderThemeMac::platformInactiveListBoxSelectionBackgroundColor() const +{ + return platformInactiveSelectionBackgroundColor(); +} + +static FontWeight toFontWeight(NSInteger appKitFontWeight) +{ + ASSERT(appKitFontWeight > 0 && appKitFontWeight < 15); + if (appKitFontWeight > 14) + appKitFontWeight = 14; + else if (appKitFontWeight < 1) + appKitFontWeight = 1; + + static FontWeight fontWeights[] = { + FontWeight100, + FontWeight100, + FontWeight200, + FontWeight300, + FontWeight400, + FontWeight500, + FontWeight600, + FontWeight600, + FontWeight700, + FontWeight800, + FontWeight800, + FontWeight900, + FontWeight900, + FontWeight900 + }; + return fontWeights[appKitFontWeight - 1]; +} + +void RenderThemeMac::systemFont(CSSValueID cssValueId, FontDescription& fontDescription) const +{ + DEFINE_STATIC_LOCAL(FontDescription, systemFont, ()); + DEFINE_STATIC_LOCAL(FontDescription, smallSystemFont, ()); + DEFINE_STATIC_LOCAL(FontDescription, menuFont, ()); + DEFINE_STATIC_LOCAL(FontDescription, labelFont, ()); + DEFINE_STATIC_LOCAL(FontDescription, miniControlFont, ()); + DEFINE_STATIC_LOCAL(FontDescription, smallControlFont, ()); + DEFINE_STATIC_LOCAL(FontDescription, controlFont, ()); + + FontDescription* cachedDesc; + NSFont* font = nil; + switch (cssValueId) { + case CSSValueSmallCaption: + cachedDesc = &smallSystemFont; + if (!smallSystemFont.isAbsoluteSize()) + font = [NSFont systemFontOfSize:[NSFont smallSystemFontSize]]; + break; + case CSSValueMenu: + cachedDesc = &menuFont; + if (!menuFont.isAbsoluteSize()) + font = [NSFont menuFontOfSize:[NSFont systemFontSize]]; + break; + case CSSValueStatusBar: + cachedDesc = &labelFont; + if (!labelFont.isAbsoluteSize()) + font = [NSFont labelFontOfSize:[NSFont labelFontSize]]; + break; + case CSSValueWebkitMiniControl: + cachedDesc = &miniControlFont; + if (!miniControlFont.isAbsoluteSize()) + font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSMiniControlSize]]; + break; + case CSSValueWebkitSmallControl: + cachedDesc = &smallControlFont; + if (!smallControlFont.isAbsoluteSize()) + font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]]; + break; + case CSSValueWebkitControl: + cachedDesc = &controlFont; + if (!controlFont.isAbsoluteSize()) + font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSRegularControlSize]]; + break; + default: + cachedDesc = &systemFont; + if (!systemFont.isAbsoluteSize()) + font = [NSFont systemFontOfSize:[NSFont systemFontSize]]; + } + + if (font) { + NSFontManager *fontManager = [NSFontManager sharedFontManager]; + cachedDesc->setIsAbsoluteSize(true); + cachedDesc->setGenericFamily(FontDescription::NoFamily); + cachedDesc->setOneFamily([font webCoreFamilyName]); + cachedDesc->setSpecifiedSize([font pointSize]); + cachedDesc->setWeight(toFontWeight([fontManager weightOfFont:font])); + cachedDesc->setItalic([fontManager traitsOfFont:font] & NSItalicFontMask); + } + fontDescription = *cachedDesc; +} + +static RGBA32 convertNSColorToColor(NSColor *color) +{ + NSColor *colorInColorSpace = [color colorUsingColorSpaceName:NSDeviceRGBColorSpace]; + if (colorInColorSpace) { + static const double scaleFactor = nextafter(256.0, 0.0); + return makeRGB(static_cast<int>(scaleFactor * [colorInColorSpace redComponent]), + static_cast<int>(scaleFactor * [colorInColorSpace greenComponent]), + static_cast<int>(scaleFactor * [colorInColorSpace blueComponent])); + } + + // This conversion above can fail if the NSColor in question is an NSPatternColor + // (as many system colors are). These colors are actually a repeating pattern + // not just a solid color. To work around this we simply draw a 1x1 image of + // the color and use that pixel's color. It might be better to use an average of + // the colors in the pattern instead. + NSBitmapImageRep *offscreenRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil + pixelsWide:1 + pixelsHigh:1 + bitsPerSample:8 + samplesPerPixel:4 + hasAlpha:YES + isPlanar:NO + colorSpaceName:NSDeviceRGBColorSpace + bytesPerRow:4 + bitsPerPixel:32]; + + [NSGraphicsContext saveGraphicsState]; + [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:offscreenRep]]; + NSEraseRect(NSMakeRect(0, 0, 1, 1)); + [color drawSwatchInRect:NSMakeRect(0, 0, 1, 1)]; + [NSGraphicsContext restoreGraphicsState]; + + NSUInteger pixel[4]; + [offscreenRep getPixel:pixel atX:0 y:0]; + + [offscreenRep release]; + + return makeRGB(pixel[0], pixel[1], pixel[2]); +} + +static RGBA32 menuBackgroundColor() +{ + NSBitmapImageRep *offscreenRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil + pixelsWide:1 + pixelsHigh:1 + bitsPerSample:8 + samplesPerPixel:4 + hasAlpha:YES + isPlanar:NO + colorSpaceName:NSDeviceRGBColorSpace + bytesPerRow:4 + bitsPerPixel:32]; + + CGContextRef context = static_cast<CGContextRef>([[NSGraphicsContext graphicsContextWithBitmapImageRep:offscreenRep] graphicsPort]); + CGRect rect = CGRectMake(0, 0, 1, 1); + HIThemeMenuDrawInfo drawInfo; + drawInfo.version = 0; + drawInfo.menuType = kThemeMenuTypePopUp; + HIThemeDrawMenuBackground(&rect, &drawInfo, context, kHIThemeOrientationInverted); + + NSUInteger pixel[4]; + [offscreenRep getPixel:pixel atX:0 y:0]; + + [offscreenRep release]; + + return makeRGB(pixel[0], pixel[1], pixel[2]); +} + +void RenderThemeMac::platformColorsDidChange() +{ + m_systemColorCache.clear(); + RenderTheme::platformColorsDidChange(); +} + +Color RenderThemeMac::systemColor(CSSValueID cssValueId) const +{ + { + HashMap<int, RGBA32>::iterator it = m_systemColorCache.find(cssValueId); + if (it != m_systemColorCache.end()) + return it->value; + } + + Color color; + switch (cssValueId) { + case CSSValueActiveborder: + color = convertNSColorToColor([NSColor keyboardFocusIndicatorColor]); + break; + case CSSValueActivecaption: + color = convertNSColorToColor([NSColor windowFrameTextColor]); + break; + case CSSValueAppworkspace: + color = convertNSColorToColor([NSColor headerColor]); + break; + case CSSValueBackground: + // Use theme independent default + break; + case CSSValueButtonface: + // We use this value instead of NSColor's controlColor to avoid website incompatibilities. + // We may want to change this to use the NSColor in future. + color = 0xFFC0C0C0; + break; + case CSSValueButtonhighlight: + color = convertNSColorToColor([NSColor controlHighlightColor]); + break; + case CSSValueButtonshadow: + color = convertNSColorToColor([NSColor controlShadowColor]); + break; + case CSSValueButtontext: + color = convertNSColorToColor([NSColor controlTextColor]); + break; + case CSSValueCaptiontext: + color = convertNSColorToColor([NSColor textColor]); + break; + case CSSValueGraytext: + color = convertNSColorToColor([NSColor disabledControlTextColor]); + break; + case CSSValueHighlight: + color = convertNSColorToColor([NSColor selectedTextBackgroundColor]); + break; + case CSSValueHighlighttext: + color = convertNSColorToColor([NSColor selectedTextColor]); + break; + case CSSValueInactiveborder: + color = convertNSColorToColor([NSColor controlBackgroundColor]); + break; + case CSSValueInactivecaption: + color = convertNSColorToColor([NSColor controlBackgroundColor]); + break; + case CSSValueInactivecaptiontext: + color = convertNSColorToColor([NSColor textColor]); + break; + case CSSValueInfobackground: + // There is no corresponding NSColor for this so we use a hard coded value. + color = 0xFFFBFCC5; + break; + case CSSValueInfotext: + color = convertNSColorToColor([NSColor textColor]); + break; + case CSSValueMenu: + color = menuBackgroundColor(); + break; + case CSSValueMenutext: + color = convertNSColorToColor([NSColor selectedMenuItemTextColor]); + break; + case CSSValueScrollbar: + color = convertNSColorToColor([NSColor scrollBarColor]); + break; + case CSSValueText: + color = convertNSColorToColor([NSColor textColor]); + break; + case CSSValueThreeddarkshadow: + color = convertNSColorToColor([NSColor controlDarkShadowColor]); + break; + case CSSValueThreedshadow: + color = convertNSColorToColor([NSColor shadowColor]); + break; + case CSSValueThreedface: + // We use this value instead of NSColor's controlColor to avoid website incompatibilities. + // We may want to change this to use the NSColor in future. + color = 0xFFC0C0C0; + break; + case CSSValueThreedhighlight: + color = convertNSColorToColor([NSColor highlightColor]); + break; + case CSSValueThreedlightshadow: + color = convertNSColorToColor([NSColor controlLightHighlightColor]); + break; + case CSSValueWebkitFocusRingColor: + color = convertNSColorToColor([NSColor keyboardFocusIndicatorColor]); + break; + case CSSValueWindow: + color = convertNSColorToColor([NSColor windowBackgroundColor]); + break; + case CSSValueWindowframe: + color = convertNSColorToColor([NSColor windowFrameColor]); + break; + case CSSValueWindowtext: + color = convertNSColorToColor([NSColor windowFrameTextColor]); + break; + default: + break; + } + + if (!color.isValid()) + color = RenderTheme::systemColor(cssValueId); + + if (color.isValid()) + m_systemColorCache.set(cssValueId, color.rgb()); + + return color; +} + +bool RenderThemeMac::usesTestModeFocusRingColor() const +{ + return WebCore::usesTestModeFocusRingColor(); +} + +bool RenderThemeMac::isControlStyled(const RenderStyle* style, const BorderData& border, + const FillLayer& background, const Color& backgroundColor) const +{ + if (style->appearance() == TextFieldPart || style->appearance() == TextAreaPart || style->appearance() == ListboxPart) + return style->border() != border; + + // FIXME: This is horrible, but there is not much else that can be done. Menu lists cannot draw properly when + // scaled. They can't really draw properly when transformed either. We can't detect the transform case at style + // adjustment time so that will just have to stay broken. We can however detect that we're zooming. If zooming + // is in effect we treat it like the control is styled. + if (style->appearance() == MenulistPart && style->effectiveZoom() != 1.0f) + return true; + + return RenderTheme::isControlStyled(style, border, background, backgroundColor); +} + +void RenderThemeMac::adjustRepaintRect(const RenderObject* o, IntRect& r) +{ + ControlPart part = o->style()->appearance(); + +#if USE(NEW_THEME) + switch (part) { + case CheckboxPart: + case RadioPart: + case PushButtonPart: + case SquareButtonPart: + case DefaultButtonPart: + case ButtonPart: + case InnerSpinButtonPart: + return RenderTheme::adjustRepaintRect(o, r); + default: + break; + } +#endif + + float zoomLevel = o->style()->effectiveZoom(); + + if (part == MenulistPart) { + setPopupButtonCellState(o, r); + IntSize size = popupButtonSizes()[[popupButton() controlSize]]; + size.setHeight(size.height() * zoomLevel); + size.setWidth(r.width()); + r = inflateRect(r, size, popupButtonMargins(), zoomLevel); + } +} + +IntRect RenderThemeMac::inflateRect(const IntRect& r, const IntSize& size, const int* margins, float zoomLevel) const +{ + // Only do the inflation if the available width/height are too small. Otherwise try to + // fit the glow/check space into the available box's width/height. + int widthDelta = r.width() - (size.width() + margins[leftMargin] * zoomLevel + margins[rightMargin] * zoomLevel); + int heightDelta = r.height() - (size.height() + margins[topMargin] * zoomLevel + margins[bottomMargin] * zoomLevel); + IntRect result(r); + if (widthDelta < 0) { + result.setX(result.x() - margins[leftMargin] * zoomLevel); + result.setWidth(result.width() - widthDelta); + } + if (heightDelta < 0) { + result.setY(result.y() - margins[topMargin] * zoomLevel); + result.setHeight(result.height() - heightDelta); + } + return result; +} + +FloatRect RenderThemeMac::convertToPaintingRect(const RenderObject* inputRenderer, const RenderObject* partRenderer, const FloatRect& inputRect, const IntRect& r) const +{ + FloatRect partRect(inputRect); + + // Compute an offset between the part renderer and the input renderer + FloatSize offsetFromInputRenderer; + const RenderObject* renderer = partRenderer; + while (renderer && renderer != inputRenderer) { + RenderObject* containingRenderer = renderer->container(); + offsetFromInputRenderer -= roundedIntSize(renderer->offsetFromContainer(containingRenderer, LayoutPoint())); + renderer = containingRenderer; + } + // If the input renderer was not a container, something went wrong + ASSERT(renderer == inputRenderer); + // Move the rect into partRenderer's coords + partRect.move(offsetFromInputRenderer); + // Account for the local drawing offset (tx, ty) + partRect.move(r.x(), r.y()); + + return partRect; +} + +void RenderThemeMac::updateCheckedState(NSCell* cell, const RenderObject* o) +{ + bool oldIndeterminate = [cell state] == NSMixedState; + bool indeterminate = isIndeterminate(o); + bool checked = isChecked(o); + + if (oldIndeterminate != indeterminate) { + [cell setState:indeterminate ? NSMixedState : (checked ? NSOnState : NSOffState)]; + return; + } + + bool oldChecked = [cell state] == NSOnState; + if (checked != oldChecked) + [cell setState:checked ? NSOnState : NSOffState]; +} + +void RenderThemeMac::updateEnabledState(NSCell* cell, const RenderObject* o) +{ + bool oldEnabled = [cell isEnabled]; + bool enabled = isEnabled(o); + if (enabled != oldEnabled) + [cell setEnabled:enabled]; +} + +void RenderThemeMac::updateFocusedState(NSCell* cell, const RenderObject* o) +{ + bool oldFocused = [cell showsFirstResponder]; + bool focused = isFocused(o) && o->style()->outlineStyleIsAuto(); + if (focused != oldFocused) + [cell setShowsFirstResponder:focused]; +} + +void RenderThemeMac::updatePressedState(NSCell* cell, const RenderObject* o) +{ + bool oldPressed = [cell isHighlighted]; + bool pressed = o->node() && o->node()->isElementNode() && toElement(o->node())->active(); + if (pressed != oldPressed) + [cell setHighlighted:pressed]; +} + +bool RenderThemeMac::controlSupportsTints(const RenderObject* o) const +{ + // An alternate way to implement this would be to get the appropriate cell object + // and call the private _needRedrawOnWindowChangedKeyState method. An advantage of + // that would be that we would match AppKit behavior more closely, but a disadvantage + // would be that we would rely on an AppKit SPI method. + + if (!isEnabled(o)) + return false; + + // Checkboxes only have tint when checked. + if (o->style()->appearance() == CheckboxPart) + return isChecked(o); + + // For now assume other controls have tint if enabled. + return true; +} + +NSControlSize RenderThemeMac::controlSizeForFont(RenderStyle* style) const +{ + int fontSize = style->fontSize(); + if (fontSize >= 16) + return NSRegularControlSize; + if (fontSize >= 11) + return NSSmallControlSize; + return NSMiniControlSize; +} + +void RenderThemeMac::setControlSize(NSCell* cell, const IntSize* sizes, const IntSize& minSize, float zoomLevel) +{ + NSControlSize size; + if (minSize.width() >= static_cast<int>(sizes[NSRegularControlSize].width() * zoomLevel) && + minSize.height() >= static_cast<int>(sizes[NSRegularControlSize].height() * zoomLevel)) + size = NSRegularControlSize; + else if (minSize.width() >= static_cast<int>(sizes[NSSmallControlSize].width() * zoomLevel) && + minSize.height() >= static_cast<int>(sizes[NSSmallControlSize].height() * zoomLevel)) + size = NSSmallControlSize; + else + size = NSMiniControlSize; + if (size != [cell controlSize]) // Only update if we have to, since AppKit does work even if the size is the same. + [cell setControlSize:size]; +} + +IntSize RenderThemeMac::sizeForFont(RenderStyle* style, const IntSize* sizes) const +{ + if (style->effectiveZoom() != 1.0f) { + IntSize result = sizes[controlSizeForFont(style)]; + return IntSize(result.width() * style->effectiveZoom(), result.height() * style->effectiveZoom()); + } + return sizes[controlSizeForFont(style)]; +} + +IntSize RenderThemeMac::sizeForSystemFont(RenderStyle* style, const IntSize* sizes) const +{ + if (style->effectiveZoom() != 1.0f) { + IntSize result = sizes[controlSizeForSystemFont(style)]; + return IntSize(result.width() * style->effectiveZoom(), result.height() * style->effectiveZoom()); + } + return sizes[controlSizeForSystemFont(style)]; +} + +void RenderThemeMac::setSizeFromFont(RenderStyle* style, const IntSize* sizes) const +{ + // FIXME: Check is flawed, since it doesn't take min-width/max-width into account. + IntSize size = sizeForFont(style, sizes); + if (style->width().isIntrinsicOrAuto() && size.width() > 0) + style->setWidth(Length(size.width(), Fixed)); + if (style->height().isAuto() && size.height() > 0) + style->setHeight(Length(size.height(), Fixed)); +} + +void RenderThemeMac::setFontFromControlSize(StyleResolver*, RenderStyle* style, NSControlSize controlSize) const +{ + FontDescription fontDescription; + fontDescription.setIsAbsoluteSize(true); + fontDescription.setGenericFamily(FontDescription::SerifFamily); + + NSFont* font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:controlSize]]; + fontDescription.setOneFamily([font webCoreFamilyName]); + fontDescription.setComputedSize([font pointSize] * style->effectiveZoom()); + fontDescription.setSpecifiedSize([font pointSize] * style->effectiveZoom()); + + // Reset line height + style->setLineHeight(RenderStyle::initialLineHeight()); + + if (style->setFontDescription(fontDescription)) + style->font().update(0); +} + +NSControlSize RenderThemeMac::controlSizeForSystemFont(RenderStyle* style) const +{ + int fontSize = style->fontSize(); + if (fontSize >= [NSFont systemFontSizeForControlSize:NSRegularControlSize]) + return NSRegularControlSize; + if (fontSize >= [NSFont systemFontSizeForControlSize:NSSmallControlSize]) + return NSSmallControlSize; + return NSMiniControlSize; +} + +bool RenderThemeMac::paintTextField(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) +{ + LocalCurrentGraphicsContext localContext(paintInfo.context); + +#if __MAC_OS_X_VERSION_MIN_REQUIRED <= 1070 + bool useNSTextFieldCell = o->style()->hasAppearance() + && o->style()->visitedDependentColor(CSSPropertyBackgroundColor) == Color::white + && !o->style()->hasBackgroundImage(); + + // We do not use NSTextFieldCell to draw styled text fields on Lion and SnowLeopard because + // there are a number of bugs on those platforms that require NSTextFieldCell to be in charge + // of painting its own background. We need WebCore to paint styled backgrounds, so we'll use + // this WebCoreSystemInterface function instead. + if (!useNSTextFieldCell) { + wkDrawBezeledTextFieldCell(r, isEnabled(o) && !isReadOnlyControl(o)); + return false; + } +#endif + + NSTextFieldCell *textField = this->textField(); + + GraphicsContextStateSaver stateSaver(*paintInfo.context); + + [textField setEnabled:(isEnabled(o) && !isReadOnlyControl(o))]; + [textField drawWithFrame:NSRect(r) inView:documentViewFor(o)]; + + [textField setControlView:nil]; + + return false; +} + +void RenderThemeMac::adjustTextFieldStyle(StyleResolver*, RenderStyle*, Element*) const +{ +} + +bool RenderThemeMac::paintCapsLockIndicator(RenderObject*, const PaintInfo& paintInfo, const IntRect& r) +{ + if (paintInfo.context->paintingDisabled()) + return true; + + LocalCurrentGraphicsContext localContext(paintInfo.context); + wkDrawCapsLockIndicator(localContext.cgContext(), r); + + return false; +} + +bool RenderThemeMac::paintTextArea(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) +{ + LocalCurrentGraphicsContext localContext(paintInfo.context); + wkDrawBezeledTextArea(r, isEnabled(o) && !isReadOnlyControl(o)); + return false; +} + +void RenderThemeMac::adjustTextAreaStyle(StyleResolver*, RenderStyle*, Element*) const +{ +} + +const int* RenderThemeMac::popupButtonMargins() const +{ + static const int margins[3][4] = + { + { 0, 3, 1, 3 }, + { 0, 3, 2, 3 }, + { 0, 1, 0, 1 } + }; + return margins[[popupButton() controlSize]]; +} + +const IntSize* RenderThemeMac::popupButtonSizes() const +{ + static const IntSize sizes[3] = { IntSize(0, 21), IntSize(0, 18), IntSize(0, 15) }; + return sizes; +} + +const int* RenderThemeMac::popupButtonPadding(NSControlSize size) const +{ + static const int padding[3][4] = + { + { 2, 26, 3, 8 }, + { 2, 23, 3, 8 }, + { 2, 22, 3, 10 } + }; + return padding[size]; +} + +bool RenderThemeMac::paintMenuList(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) +{ + LocalCurrentGraphicsContext localContext(paintInfo.context); + setPopupButtonCellState(o, r); + + NSPopUpButtonCell* popupButton = this->popupButton(); + + float zoomLevel = o->style()->effectiveZoom(); + IntSize size = popupButtonSizes()[[popupButton controlSize]]; + size.setHeight(size.height() * zoomLevel); + size.setWidth(r.width()); + + // Now inflate it to account for the shadow. + IntRect inflatedRect = r; + if (r.width() >= minimumMenuListSize(o->style())) + inflatedRect = inflateRect(inflatedRect, size, popupButtonMargins(), zoomLevel); + + GraphicsContextStateSaver stateSaver(*paintInfo.context); + + // On Leopard, the cell will draw outside of the given rect, so we have to clip to the rect + paintInfo.context->clip(inflatedRect); + + if (zoomLevel != 1.0f) { + inflatedRect.setWidth(inflatedRect.width() / zoomLevel); + inflatedRect.setHeight(inflatedRect.height() / zoomLevel); + paintInfo.context->translate(inflatedRect.x(), inflatedRect.y()); + paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel)); + paintInfo.context->translate(-inflatedRect.x(), -inflatedRect.y()); + } + + NSView *view = documentViewFor(o); + [popupButton drawWithFrame:inflatedRect inView:view]; +#if !BUTTON_CELL_DRAW_WITH_FRAME_DRAWS_FOCUS_RING + if (isFocused(o) && o->style()->outlineStyleIsAuto()) + [popupButton _web_drawFocusRingWithFrame:inflatedRect inView:view]; +#endif + [popupButton setControlView:nil]; + + return false; +} + +#if ENABLE(METER_ELEMENT) + +IntSize RenderThemeMac::meterSizeForBounds(const RenderMeter* renderMeter, const IntRect& bounds) const +{ + if (NoControlPart == renderMeter->style()->appearance()) + return bounds.size(); + + NSLevelIndicatorCell* cell = levelIndicatorFor(renderMeter); + // Makes enough room for cell's intrinsic size. + NSSize cellSize = [cell cellSizeForBounds:IntRect(IntPoint(), bounds.size())]; + return IntSize(bounds.width() < cellSize.width ? cellSize.width : bounds.width(), + bounds.height() < cellSize.height ? cellSize.height : bounds.height()); +} + +bool RenderThemeMac::paintMeter(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) +{ + if (!renderObject->isMeter()) + return true; + + LocalCurrentGraphicsContext localContext(paintInfo.context); + + NSLevelIndicatorCell* cell = levelIndicatorFor(toRenderMeter(renderObject)); + GraphicsContextStateSaver stateSaver(*paintInfo.context); + + [cell drawWithFrame:rect inView:documentViewFor(renderObject)]; + [cell setControlView:nil]; + return false; +} + +bool RenderThemeMac::supportsMeter(ControlPart part) const +{ + switch (part) { + case RelevancyLevelIndicatorPart: + case DiscreteCapacityLevelIndicatorPart: + case RatingLevelIndicatorPart: + case MeterPart: + case ContinuousCapacityLevelIndicatorPart: + return true; + default: + return false; + } +} + +NSLevelIndicatorStyle RenderThemeMac::levelIndicatorStyleFor(ControlPart part) const +{ + switch (part) { + case RelevancyLevelIndicatorPart: + return NSRelevancyLevelIndicatorStyle; + case DiscreteCapacityLevelIndicatorPart: + return NSDiscreteCapacityLevelIndicatorStyle; + case RatingLevelIndicatorPart: + return NSRatingLevelIndicatorStyle; + case MeterPart: + case ContinuousCapacityLevelIndicatorPart: + default: + return NSContinuousCapacityLevelIndicatorStyle; + } + +} + +NSLevelIndicatorCell* RenderThemeMac::levelIndicatorFor(const RenderMeter* renderMeter) const +{ + RenderStyle* style = renderMeter->style(); + ASSERT(style->appearance() != NoControlPart); + + if (!m_levelIndicator) + m_levelIndicator = adoptNS([[NSLevelIndicatorCell alloc] initWithLevelIndicatorStyle:NSContinuousCapacityLevelIndicatorStyle]); + NSLevelIndicatorCell* cell = m_levelIndicator.get(); + + HTMLMeterElement* element = renderMeter->meterElement(); + double value = element->value(); + + // Because NSLevelIndicatorCell does not support optimum-in-the-middle type coloring, + // we explicitly control the color instead giving low and high value to NSLevelIndicatorCell as is. + switch (element->gaugeRegion()) { + case HTMLMeterElement::GaugeRegionOptimum: + // Make meter the green + [cell setWarningValue:value + 1]; + [cell setCriticalValue:value + 2]; + break; + case HTMLMeterElement::GaugeRegionSuboptimal: + // Make the meter yellow + [cell setWarningValue:value - 1]; + [cell setCriticalValue:value + 1]; + break; + case HTMLMeterElement::GaugeRegionEvenLessGood: + // Make the meter red + [cell setWarningValue:value - 2]; + [cell setCriticalValue:value - 1]; + break; + } + + [cell setLevelIndicatorStyle:levelIndicatorStyleFor(style->appearance())]; + [cell setBaseWritingDirection:style->isLeftToRightDirection() ? NSWritingDirectionLeftToRight : NSWritingDirectionRightToLeft]; + [cell setMinValue:element->min()]; + [cell setMaxValue:element->max()]; + RetainPtr<NSNumber> valueObject = [NSNumber numberWithDouble:value]; + [cell setObjectValue:valueObject.get()]; + + return cell; +} + +#endif + +#if ENABLE(PROGRESS_ELEMENT) +const IntSize* RenderThemeMac::progressBarSizes() const +{ + static const IntSize sizes[3] = { IntSize(0, 20), IntSize(0, 12), IntSize(0, 12) }; + return sizes; +} + +const int* RenderThemeMac::progressBarMargins(NSControlSize controlSize) const +{ + static const int margins[3][4] = + { + { 0, 0, 1, 0 }, + { 0, 0, 1, 0 }, + { 0, 0, 1, 0 }, + }; + return margins[controlSize]; +} + +int RenderThemeMac::minimumProgressBarHeight(RenderStyle* style) const +{ + return sizeForSystemFont(style, progressBarSizes()).height(); +} + +double RenderThemeMac::animationRepeatIntervalForProgressBar(RenderProgress*) const +{ + return progressAnimationFrameRate; +} + +double RenderThemeMac::animationDurationForProgressBar(RenderProgress*) const +{ + return progressAnimationNumFrames * progressAnimationFrameRate; +} + +void RenderThemeMac::adjustProgressBarStyle(StyleResolver*, RenderStyle*, Element*) const +{ +} + +bool RenderThemeMac::paintProgressBar(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) +{ + if (!renderObject->isProgress()) + return true; + + float zoomLevel = renderObject->style()->effectiveZoom(); + int controlSize = controlSizeForFont(renderObject->style()); + IntSize size = progressBarSizes()[controlSize]; + size.setHeight(size.height() * zoomLevel); + size.setWidth(rect.width()); + + // Now inflate it to account for the shadow. + IntRect inflatedRect = rect; + if (rect.height() <= minimumProgressBarHeight(renderObject->style())) + inflatedRect = inflateRect(inflatedRect, size, progressBarMargins(controlSize), zoomLevel); + + RenderProgress* renderProgress = toRenderProgress(renderObject); + HIThemeTrackDrawInfo trackInfo; + trackInfo.version = 0; + if (controlSize == NSRegularControlSize) + trackInfo.kind = renderProgress->position() < 0 ? kThemeLargeIndeterminateBar : kThemeLargeProgressBar; + else + trackInfo.kind = renderProgress->position() < 0 ? kThemeMediumIndeterminateBar : kThemeMediumProgressBar; + + trackInfo.bounds = IntRect(IntPoint(), inflatedRect.size()); + trackInfo.min = 0; + trackInfo.max = numeric_limits<SInt32>::max(); + trackInfo.value = lround(renderProgress->position() * nextafter(trackInfo.max, 0)); + trackInfo.trackInfo.progress.phase = lround(renderProgress->animationProgress() * nextafter(progressAnimationNumFrames, 0)); + trackInfo.attributes = kThemeTrackHorizontal; + trackInfo.enableState = isActive(renderObject) ? kThemeTrackActive : kThemeTrackInactive; + trackInfo.reserved = 0; + trackInfo.filler1 = 0; + + OwnPtr<ImageBuffer> imageBuffer = ImageBuffer::create(inflatedRect.size(), 1); + if (!imageBuffer) + return true; + + ContextContainer cgContextContainer(imageBuffer->context()); + CGContextRef cgContext = cgContextContainer.context(); + HIThemeDrawTrack(&trackInfo, 0, cgContext, kHIThemeOrientationNormal); + + GraphicsContextStateSaver stateSaver(*paintInfo.context); + + if (!renderProgress->style()->isLeftToRightDirection()) { + paintInfo.context->translate(2 * inflatedRect.x() + inflatedRect.width(), 0); + paintInfo.context->scale(FloatSize(-1, 1)); + } + + paintInfo.context->drawImageBuffer(imageBuffer.get(), ColorSpaceDeviceRGB, inflatedRect.location()); + return false; +} +#endif + +const float baseFontSize = 11.0f; +const float baseArrowHeight = 4.0f; +const float baseArrowWidth = 5.0f; +const float baseSpaceBetweenArrows = 2.0f; +const int arrowPaddingLeft = 6; +const int arrowPaddingRight = 6; +const int paddingBeforeSeparator = 4; +const int baseBorderRadius = 5; +const int styledPopupPaddingLeft = 8; +const int styledPopupPaddingTop = 1; +const int styledPopupPaddingBottom = 2; + +static void TopGradientInterpolate(void*, const CGFloat* inData, CGFloat* outData) +{ + static float dark[4] = { 1.0f, 1.0f, 1.0f, 0.4f }; + static float light[4] = { 1.0f, 1.0f, 1.0f, 0.15f }; + float a = inData[0]; + int i = 0; + for (i = 0; i < 4; i++) + outData[i] = (1.0f - a) * dark[i] + a * light[i]; +} + +static void BottomGradientInterpolate(void*, const CGFloat* inData, CGFloat* outData) +{ + static float dark[4] = { 1.0f, 1.0f, 1.0f, 0.0f }; + static float light[4] = { 1.0f, 1.0f, 1.0f, 0.3f }; + float a = inData[0]; + int i = 0; + for (i = 0; i < 4; i++) + outData[i] = (1.0f - a) * dark[i] + a * light[i]; +} + +static void MainGradientInterpolate(void*, const CGFloat* inData, CGFloat* outData) +{ + static float dark[4] = { 0.0f, 0.0f, 0.0f, 0.15f }; + static float light[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + float a = inData[0]; + int i = 0; + for (i = 0; i < 4; i++) + outData[i] = (1.0f - a) * dark[i] + a * light[i]; +} + +static void TrackGradientInterpolate(void*, const CGFloat* inData, CGFloat* outData) +{ + static float dark[4] = { 0.0f, 0.0f, 0.0f, 0.678f }; + static float light[4] = { 0.0f, 0.0f, 0.0f, 0.13f }; + float a = inData[0]; + int i = 0; + for (i = 0; i < 4; i++) + outData[i] = (1.0f - a) * dark[i] + a * light[i]; +} + +void RenderThemeMac::paintMenuListButtonGradients(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) +{ + if (r.isEmpty()) + return; + + ContextContainer cgContextContainer(paintInfo.context); + CGContextRef context = cgContextContainer.context(); + + GraphicsContextStateSaver stateSaver(*paintInfo.context); + + RoundedRect border = o->style()->getRoundedBorderFor(r, o->view()); + int radius = border.radii().topLeft().width(); + + CGColorSpaceRef cspace = deviceRGBColorSpaceRef(); + + FloatRect topGradient(r.x(), r.y(), r.width(), r.height() / 2.0f); + struct CGFunctionCallbacks topCallbacks = { 0, TopGradientInterpolate, NULL }; + RetainPtr<CGFunctionRef> topFunction = adoptCF(CGFunctionCreate(NULL, 1, NULL, 4, NULL, &topCallbacks)); + RetainPtr<CGShadingRef> topShading = adoptCF(CGShadingCreateAxial(cspace, CGPointMake(topGradient.x(), topGradient.y()), CGPointMake(topGradient.x(), topGradient.maxY()), topFunction.get(), false, false)); + + FloatRect bottomGradient(r.x() + radius, r.y() + r.height() / 2.0f, r.width() - 2.0f * radius, r.height() / 2.0f); + struct CGFunctionCallbacks bottomCallbacks = { 0, BottomGradientInterpolate, NULL }; + RetainPtr<CGFunctionRef> bottomFunction = adoptCF(CGFunctionCreate(NULL, 1, NULL, 4, NULL, &bottomCallbacks)); + RetainPtr<CGShadingRef> bottomShading = adoptCF(CGShadingCreateAxial(cspace, CGPointMake(bottomGradient.x(), bottomGradient.y()), CGPointMake(bottomGradient.x(), bottomGradient.maxY()), bottomFunction.get(), false, false)); + + struct CGFunctionCallbacks mainCallbacks = { 0, MainGradientInterpolate, NULL }; + RetainPtr<CGFunctionRef> mainFunction = adoptCF(CGFunctionCreate(NULL, 1, NULL, 4, NULL, &mainCallbacks)); + RetainPtr<CGShadingRef> mainShading = adoptCF(CGShadingCreateAxial(cspace, CGPointMake(r.x(), r.y()), CGPointMake(r.x(), r.maxY()), mainFunction.get(), false, false)); + + RetainPtr<CGShadingRef> leftShading = adoptCF(CGShadingCreateAxial(cspace, CGPointMake(r.x(), r.y()), CGPointMake(r.x() + radius, r.y()), mainFunction.get(), false, false)); + + RetainPtr<CGShadingRef> rightShading = adoptCF(CGShadingCreateAxial(cspace, CGPointMake(r.maxX(), r.y()), CGPointMake(r.maxX() - radius, r.y()), mainFunction.get(), false, false)); + + { + GraphicsContextStateSaver stateSaver(*paintInfo.context); + CGContextClipToRect(context, r); + paintInfo.context->clipRoundedRect(border); + context = cgContextContainer.context(); + CGContextDrawShading(context, mainShading.get()); + } + + { + GraphicsContextStateSaver stateSaver(*paintInfo.context); + CGContextClipToRect(context, topGradient); + paintInfo.context->clipRoundedRect(RoundedRect(enclosingIntRect(topGradient), border.radii().topLeft(), border.radii().topRight(), IntSize(), IntSize())); + context = cgContextContainer.context(); + CGContextDrawShading(context, topShading.get()); + } + + if (!bottomGradient.isEmpty()) { + GraphicsContextStateSaver stateSaver(*paintInfo.context); + CGContextClipToRect(context, bottomGradient); + paintInfo.context->clipRoundedRect(RoundedRect(enclosingIntRect(bottomGradient), IntSize(), IntSize(), border.radii().bottomLeft(), border.radii().bottomRight())); + context = cgContextContainer.context(); + CGContextDrawShading(context, bottomShading.get()); + } + + { + GraphicsContextStateSaver stateSaver(*paintInfo.context); + CGContextClipToRect(context, r); + paintInfo.context->clipRoundedRect(border); + context = cgContextContainer.context(); + CGContextDrawShading(context, leftShading.get()); + CGContextDrawShading(context, rightShading.get()); + } +} + +bool RenderThemeMac::paintMenuListButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) +{ + IntRect bounds = IntRect(r.x() + o->style()->borderLeftWidth(), + r.y() + o->style()->borderTopWidth(), + r.width() - o->style()->borderLeftWidth() - o->style()->borderRightWidth(), + r.height() - o->style()->borderTopWidth() - o->style()->borderBottomWidth()); + // Draw the gradients to give the styled popup menu a button appearance + paintMenuListButtonGradients(o, paintInfo, bounds); + + // Since we actually know the size of the control here, we restrict the font scale to make sure the arrows will fit vertically in the bounds + float fontScale = min(o->style()->fontSize() / baseFontSize, bounds.height() / (baseArrowHeight * 2 + baseSpaceBetweenArrows)); + float centerY = bounds.y() + bounds.height() / 2.0f; + float arrowHeight = baseArrowHeight * fontScale; + float arrowWidth = baseArrowWidth * fontScale; + float leftEdge = bounds.maxX() - arrowPaddingRight * o->style()->effectiveZoom() - arrowWidth; + float spaceBetweenArrows = baseSpaceBetweenArrows * fontScale; + + if (bounds.width() < arrowWidth + arrowPaddingLeft * o->style()->effectiveZoom()) + return false; + + GraphicsContextStateSaver stateSaver(*paintInfo.context); + + paintInfo.context->setFillColor(o->style()->visitedDependentColor(CSSPropertyColor), o->style()->colorSpace()); + paintInfo.context->setStrokeStyle(NoStroke); + + FloatPoint arrow1[3]; + arrow1[0] = FloatPoint(leftEdge, centerY - spaceBetweenArrows / 2.0f); + arrow1[1] = FloatPoint(leftEdge + arrowWidth, centerY - spaceBetweenArrows / 2.0f); + arrow1[2] = FloatPoint(leftEdge + arrowWidth / 2.0f, centerY - spaceBetweenArrows / 2.0f - arrowHeight); + + // Draw the top arrow + paintInfo.context->drawConvexPolygon(3, arrow1, true); + + FloatPoint arrow2[3]; + arrow2[0] = FloatPoint(leftEdge, centerY + spaceBetweenArrows / 2.0f); + arrow2[1] = FloatPoint(leftEdge + arrowWidth, centerY + spaceBetweenArrows / 2.0f); + arrow2[2] = FloatPoint(leftEdge + arrowWidth / 2.0f, centerY + spaceBetweenArrows / 2.0f + arrowHeight); + + // Draw the bottom arrow + paintInfo.context->drawConvexPolygon(3, arrow2, true); + + Color leftSeparatorColor(0, 0, 0, 40); + Color rightSeparatorColor(255, 255, 255, 40); + + // FIXME: Should the separator thickness and space be scaled up by fontScale? + int separatorSpace = 2; // Deliberately ignores zoom since it looks nicer if it stays thin. + int leftEdgeOfSeparator = static_cast<int>(leftEdge - arrowPaddingLeft * o->style()->effectiveZoom()); // FIXME: Round? + + // Draw the separator to the left of the arrows + paintInfo.context->setStrokeThickness(1.0f); // Deliberately ignores zoom since it looks nicer if it stays thin. + paintInfo.context->setStrokeStyle(SolidStroke); + paintInfo.context->setStrokeColor(leftSeparatorColor, ColorSpaceDeviceRGB); + paintInfo.context->drawLine(IntPoint(leftEdgeOfSeparator, bounds.y()), + IntPoint(leftEdgeOfSeparator, bounds.maxY())); + + paintInfo.context->setStrokeColor(rightSeparatorColor, ColorSpaceDeviceRGB); + paintInfo.context->drawLine(IntPoint(leftEdgeOfSeparator + separatorSpace, bounds.y()), + IntPoint(leftEdgeOfSeparator + separatorSpace, bounds.maxY())); + return false; +} + +static const IntSize* menuListButtonSizes() +{ + static const IntSize sizes[3] = { IntSize(0, 21), IntSize(0, 18), IntSize(0, 15) }; + return sizes; +} + +void RenderThemeMac::adjustMenuListStyle(StyleResolver* styleResolver, RenderStyle* style, Element* e) const +{ + NSControlSize controlSize = controlSizeForFont(style); + + style->resetBorder(); + style->resetPadding(); + + // Height is locked to auto. + style->setHeight(Length(Auto)); + + // White-space is locked to pre + style->setWhiteSpace(PRE); + + // Set the foreground color to black or gray when we have the aqua look. + // Cast to RGB32 is to work around a compiler bug. + style->setColor(e && !e->isDisabledFormControl() ? static_cast<RGBA32>(Color::black) : Color::darkGray); + + // Set the button's vertical size. + setSizeFromFont(style, menuListButtonSizes()); + + // Our font is locked to the appropriate system font size for the control. To clarify, we first use the CSS-specified font to figure out + // a reasonable control size, but once that control size is determined, we throw that font away and use the appropriate + // system font for the control size instead. + setFontFromControlSize(styleResolver, style, controlSize); + + style->setBoxShadow(nullptr); +} + +int RenderThemeMac::popupInternalPaddingLeft(RenderStyle* style) const +{ + if (style->appearance() == MenulistPart) + return popupButtonPadding(controlSizeForFont(style))[leftPadding] * style->effectiveZoom(); + if (style->appearance() == MenulistButtonPart) + return styledPopupPaddingLeft * style->effectiveZoom(); + return 0; +} + +int RenderThemeMac::popupInternalPaddingRight(RenderStyle* style) const +{ + if (style->appearance() == MenulistPart) + return popupButtonPadding(controlSizeForFont(style))[rightPadding] * style->effectiveZoom(); + if (style->appearance() == MenulistButtonPart) { + float fontScale = style->fontSize() / baseFontSize; + float arrowWidth = baseArrowWidth * fontScale; + return static_cast<int>(ceilf(arrowWidth + (arrowPaddingLeft + arrowPaddingRight + paddingBeforeSeparator) * style->effectiveZoom())); + } + return 0; +} + +int RenderThemeMac::popupInternalPaddingTop(RenderStyle* style) const +{ + if (style->appearance() == MenulistPart) + return popupButtonPadding(controlSizeForFont(style))[topPadding] * style->effectiveZoom(); + if (style->appearance() == MenulistButtonPart) + return styledPopupPaddingTop * style->effectiveZoom(); + return 0; +} + +int RenderThemeMac::popupInternalPaddingBottom(RenderStyle* style) const +{ + if (style->appearance() == MenulistPart) + return popupButtonPadding(controlSizeForFont(style))[bottomPadding] * style->effectiveZoom(); + if (style->appearance() == MenulistButtonPart) + return styledPopupPaddingBottom * style->effectiveZoom(); + return 0; +} + +void RenderThemeMac::adjustMenuListButtonStyle(StyleResolver*, RenderStyle* style, Element*) const +{ + float fontScale = style->fontSize() / baseFontSize; + + style->resetPadding(); + style->setBorderRadius(IntSize(int(baseBorderRadius + fontScale - 1), int(baseBorderRadius + fontScale - 1))); // FIXME: Round up? + + const int minHeight = 15; + style->setMinHeight(Length(minHeight, Fixed)); + + style->setLineHeight(RenderStyle::initialLineHeight()); +} + +void RenderThemeMac::setPopupButtonCellState(const RenderObject* o, const IntRect& r) +{ + NSPopUpButtonCell* popupButton = this->popupButton(); + + // Set the control size based off the rectangle we're painting into. + setControlSize(popupButton, popupButtonSizes(), r.size(), o->style()->effectiveZoom()); + + // Update the various states we respond to. + updateActiveState(popupButton, o); + updateCheckedState(popupButton, o); + updateEnabledState(popupButton, o); + updatePressedState(popupButton, o); +#if BUTTON_CELL_DRAW_WITH_FRAME_DRAWS_FOCUS_RING + updateFocusedState(popupButton, o); +#endif +} + +const IntSize* RenderThemeMac::menuListSizes() const +{ + static const IntSize sizes[3] = { IntSize(9, 0), IntSize(5, 0), IntSize(0, 0) }; + return sizes; +} + +int RenderThemeMac::minimumMenuListSize(RenderStyle* style) const +{ + return sizeForSystemFont(style, menuListSizes()).width(); +} + +const int trackWidth = 5; +const int trackRadius = 2; + +void RenderThemeMac::adjustSliderTrackStyle(StyleResolver*, RenderStyle* style, Element*) const +{ + style->setBoxShadow(nullptr); +} + +bool RenderThemeMac::paintSliderTrack(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) +{ + IntRect bounds = r; + float zoomLevel = o->style()->effectiveZoom(); + float zoomedTrackWidth = trackWidth * zoomLevel; + + if (o->style()->appearance() == SliderHorizontalPart || o->style()->appearance() == MediaSliderPart) { + bounds.setHeight(zoomedTrackWidth); + bounds.setY(r.y() + r.height() / 2 - zoomedTrackWidth / 2); + } else if (o->style()->appearance() == SliderVerticalPart) { + bounds.setWidth(zoomedTrackWidth); + bounds.setX(r.x() + r.width() / 2 - zoomedTrackWidth / 2); + } + + LocalCurrentGraphicsContext localContext(paintInfo.context); + CGContextRef context = localContext.cgContext(); + CGColorSpaceRef cspace = deviceRGBColorSpaceRef(); + +#if ENABLE(DATALIST_ELEMENT) + paintSliderTicks(o, paintInfo, r); +#endif + + GraphicsContextStateSaver stateSaver(*paintInfo.context); + CGContextClipToRect(context, bounds); + + struct CGFunctionCallbacks mainCallbacks = { 0, TrackGradientInterpolate, NULL }; + RetainPtr<CGFunctionRef> mainFunction = adoptCF(CGFunctionCreate(NULL, 1, NULL, 4, NULL, &mainCallbacks)); + RetainPtr<CGShadingRef> mainShading; + if (o->style()->appearance() == SliderVerticalPart) + mainShading = adoptCF(CGShadingCreateAxial(cspace, CGPointMake(bounds.x(), bounds.maxY()), CGPointMake(bounds.maxX(), bounds.maxY()), mainFunction.get(), false, false)); + else + mainShading = adoptCF(CGShadingCreateAxial(cspace, CGPointMake(bounds.x(), bounds.y()), CGPointMake(bounds.x(), bounds.maxY()), mainFunction.get(), false, false)); + + IntSize radius(trackRadius, trackRadius); + paintInfo.context->clipRoundedRect(RoundedRect(bounds, radius, radius, radius, radius)); + context = localContext.cgContext(); + CGContextDrawShading(context, mainShading.get()); + + return false; +} + +void RenderThemeMac::adjustSliderThumbStyle(StyleResolver* styleResolver, RenderStyle* style, Element* element) const +{ + RenderTheme::adjustSliderThumbStyle(styleResolver, style, element); + style->setBoxShadow(nullptr); +} + +const float verticalSliderHeightPadding = 0.1f; + +bool RenderThemeMac::paintSliderThumb(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) +{ + NSSliderCell* sliderThumbCell = o->style()->appearance() == SliderThumbVerticalPart + ? sliderThumbVertical() + : sliderThumbHorizontal(); + + LocalCurrentGraphicsContext localContext(paintInfo.context); + + // Update the various states we respond to. + updateActiveState(sliderThumbCell, o); + updateEnabledState(sliderThumbCell, o); + Element* focusDelegate = (o->node() && o->node()->isElementNode()) ? toElement(o->node())->focusDelegate() : 0; + updateFocusedState(sliderThumbCell, focusDelegate ? focusDelegate->renderer() : 0); + + // Update the pressed state using the NSCell tracking methods, since that's how NSSliderCell keeps track of it. + bool oldPressed; + if (o->style()->appearance() == SliderThumbVerticalPart) + oldPressed = m_isSliderThumbVerticalPressed; + else + oldPressed = m_isSliderThumbHorizontalPressed; + + bool pressed = isPressed(o); + + if (o->style()->appearance() == SliderThumbVerticalPart) + m_isSliderThumbVerticalPressed = pressed; + else + m_isSliderThumbHorizontalPressed = pressed; + + if (pressed != oldPressed) { + if (pressed) + [sliderThumbCell startTrackingAt:NSPoint() inView:nil]; + else + [sliderThumbCell stopTracking:NSPoint() at:NSPoint() inView:nil mouseIsUp:YES]; + } + + FloatRect bounds = r; + // Make the height of the vertical slider slightly larger so NSSliderCell will draw a vertical slider. + if (o->style()->appearance() == SliderThumbVerticalPart) + bounds.setHeight(bounds.height() + verticalSliderHeightPadding * o->style()->effectiveZoom()); + + GraphicsContextStateSaver stateSaver(*paintInfo.context); + float zoomLevel = o->style()->effectiveZoom(); + + FloatRect unzoomedRect = bounds; + if (zoomLevel != 1.0f) { + unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel); + unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel); + paintInfo.context->translate(unzoomedRect.x(), unzoomedRect.y()); + paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel)); + paintInfo.context->translate(-unzoomedRect.x(), -unzoomedRect.y()); + } + + [sliderThumbCell drawInteriorWithFrame:unzoomedRect inView:documentViewFor(o)]; + [sliderThumbCell setControlView:nil]; + + return false; +} + +bool RenderThemeMac::paintSearchField(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) +{ + LocalCurrentGraphicsContext localContext(paintInfo.context); + NSSearchFieldCell* search = this->search(); + + setSearchCellState(o, r); + + GraphicsContextStateSaver stateSaver(*paintInfo.context); + + float zoomLevel = o->style()->effectiveZoom(); + + IntRect unzoomedRect = r; + + if (zoomLevel != 1.0f) { + unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel); + unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel); + paintInfo.context->translate(unzoomedRect.x(), unzoomedRect.y()); + paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel)); + paintInfo.context->translate(-unzoomedRect.x(), -unzoomedRect.y()); + } + + // Set the search button to nil before drawing. Then reset it so we can draw it later. + [search setSearchButtonCell:nil]; + + [search drawWithFrame:NSRect(unzoomedRect) inView:documentViewFor(o)]; + + [search setControlView:nil]; + [search resetSearchButtonCell]; + + return false; +} + +void RenderThemeMac::setSearchCellState(RenderObject* o, const IntRect&) +{ + NSSearchFieldCell* search = this->search(); + + [search setControlSize:controlSizeForFont(o->style())]; + + // Update the various states we respond to. + updateActiveState(search, o); + updateEnabledState(search, o); + updateFocusedState(search, o); +} + +const IntSize* RenderThemeMac::searchFieldSizes() const +{ + static const IntSize sizes[3] = { IntSize(0, 22), IntSize(0, 19), IntSize(0, 17) }; + return sizes; +} + +void RenderThemeMac::setSearchFieldSize(RenderStyle* style) const +{ + // If the width and height are both specified, then we have nothing to do. + if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto()) + return; + + // Use the font size to determine the intrinsic width of the control. + setSizeFromFont(style, searchFieldSizes()); +} + +void RenderThemeMac::adjustSearchFieldStyle(StyleResolver* styleResolver, RenderStyle* style, Element*) const +{ + // Override border. + style->resetBorder(); + const short borderWidth = 2 * style->effectiveZoom(); + style->setBorderLeftWidth(borderWidth); + style->setBorderLeftStyle(INSET); + style->setBorderRightWidth(borderWidth); + style->setBorderRightStyle(INSET); + style->setBorderBottomWidth(borderWidth); + style->setBorderBottomStyle(INSET); + style->setBorderTopWidth(borderWidth); + style->setBorderTopStyle(INSET); + + // Override height. + style->setHeight(Length(Auto)); + setSearchFieldSize(style); + + // Override padding size to match AppKit text positioning. + const int padding = 1 * style->effectiveZoom(); + style->setPaddingLeft(Length(padding, Fixed)); + style->setPaddingRight(Length(padding, Fixed)); + style->setPaddingTop(Length(padding, Fixed)); + style->setPaddingBottom(Length(padding, Fixed)); + + NSControlSize controlSize = controlSizeForFont(style); + setFontFromControlSize(styleResolver, style, controlSize); + + style->setBoxShadow(nullptr); +} + +bool RenderThemeMac::paintSearchFieldCancelButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) +{ + Element* input = o->node()->shadowHost(); + if (!input) + input = toElement(o->node()); + + if (!input->renderer()->isBox()) + return false; + + LocalCurrentGraphicsContext localContext(paintInfo.context); + setSearchCellState(input->renderer(), r); + + NSSearchFieldCell* search = this->search(); + + if (!input->isDisabledFormControl() && (input->isTextFormControl() && !toHTMLTextFormControlElement(input)->isReadOnly())) { + updateActiveState([search cancelButtonCell], o); + updatePressedState([search cancelButtonCell], o); + } + else if ([[search cancelButtonCell] isHighlighted]) + [[search cancelButtonCell] setHighlighted:NO]; + + GraphicsContextStateSaver stateSaver(*paintInfo.context); + + float zoomLevel = o->style()->effectiveZoom(); + + FloatRect localBounds = [search cancelButtonRectForBounds:NSRect(input->renderBox()->pixelSnappedBorderBoxRect())]; + +#if ENABLE(INPUT_SPEECH) + // Take care of cases where the cancel button was not aligned with the right border of the input element (for e.g. + // when speech input is enabled for the input element. + IntRect absBoundingBox = input->renderer()->absoluteBoundingBoxRect(); + int absRight = absBoundingBox.x() + absBoundingBox.width() - input->renderBox()->paddingRight() - input->renderBox()->borderRight(); + int spaceToRightOfCancelButton = absRight - (r.x() + r.width()); + localBounds.setX(localBounds.x() - spaceToRightOfCancelButton); +#endif + + localBounds = convertToPaintingRect(input->renderer(), o, localBounds, r); + + FloatRect unzoomedRect(localBounds); + if (zoomLevel != 1.0f) { + unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel); + unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel); + paintInfo.context->translate(unzoomedRect.x(), unzoomedRect.y()); + paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel)); + paintInfo.context->translate(-unzoomedRect.x(), -unzoomedRect.y()); + } + + [[search cancelButtonCell] drawWithFrame:unzoomedRect inView:documentViewFor(o)]; + [[search cancelButtonCell] setControlView:nil]; + return false; +} + +const IntSize* RenderThemeMac::cancelButtonSizes() const +{ + static const IntSize sizes[3] = { IntSize(16, 13), IntSize(13, 11), IntSize(13, 9) }; + return sizes; +} + +void RenderThemeMac::adjustSearchFieldCancelButtonStyle(StyleResolver*, RenderStyle* style, Element*) const +{ + IntSize size = sizeForSystemFont(style, cancelButtonSizes()); + style->setWidth(Length(size.width(), Fixed)); + style->setHeight(Length(size.height(), Fixed)); + style->setBoxShadow(nullptr); +} + +const IntSize* RenderThemeMac::resultsButtonSizes() const +{ + static const IntSize sizes[3] = { IntSize(19, 13), IntSize(17, 11), IntSize(17, 9) }; + return sizes; +} + +const int emptyResultsOffset = 9; +void RenderThemeMac::adjustSearchFieldDecorationStyle(StyleResolver*, RenderStyle* style, Element*) const +{ + IntSize size = sizeForSystemFont(style, resultsButtonSizes()); + style->setWidth(Length(size.width() - emptyResultsOffset, Fixed)); + style->setHeight(Length(size.height(), Fixed)); + style->setBoxShadow(nullptr); +} + +bool RenderThemeMac::paintSearchFieldDecoration(RenderObject*, const PaintInfo&, const IntRect&) +{ + return false; +} + +void RenderThemeMac::adjustSearchFieldResultsDecorationStyle(StyleResolver*, RenderStyle* style, Element*) const +{ + IntSize size = sizeForSystemFont(style, resultsButtonSizes()); + style->setWidth(Length(size.width(), Fixed)); + style->setHeight(Length(size.height(), Fixed)); + style->setBoxShadow(nullptr); +} + +bool RenderThemeMac::paintSearchFieldResultsDecoration(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) +{ + Node* input = o->node()->shadowHost(); + if (!input) + input = o->node(); + if (!input->renderer()->isBox()) + return false; + + LocalCurrentGraphicsContext localContext(paintInfo.context); + setSearchCellState(input->renderer(), r); + + NSSearchFieldCell* search = this->search(); + + if ([search searchMenuTemplate] != nil) + [search setSearchMenuTemplate:nil]; + + updateActiveState([search searchButtonCell], o); + + FloatRect localBounds = [search searchButtonRectForBounds:NSRect(input->renderBox()->pixelSnappedBorderBoxRect())]; + localBounds = convertToPaintingRect(input->renderer(), o, localBounds, r); + + [[search searchButtonCell] drawWithFrame:localBounds inView:documentViewFor(o)]; + [[search searchButtonCell] setControlView:nil]; + return false; +} + +const int resultsArrowWidth = 5; +void RenderThemeMac::adjustSearchFieldResultsButtonStyle(StyleResolver*, RenderStyle* style, Element*) const +{ + IntSize size = sizeForSystemFont(style, resultsButtonSizes()); + style->setWidth(Length(size.width() + resultsArrowWidth, Fixed)); + style->setHeight(Length(size.height(), Fixed)); + style->setBoxShadow(nullptr); +} + +bool RenderThemeMac::paintSearchFieldResultsButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) +{ + Node* input = o->node()->shadowHost(); + if (!input) + input = o->node(); + if (!input->renderer()->isBox()) + return false; + + LocalCurrentGraphicsContext localContext(paintInfo.context); + setSearchCellState(input->renderer(), r); + + NSSearchFieldCell* search = this->search(); + + updateActiveState([search searchButtonCell], o); + + if (![search searchMenuTemplate]) + [search setSearchMenuTemplate:searchMenuTemplate()]; + + GraphicsContextStateSaver stateSaver(*paintInfo.context); + float zoomLevel = o->style()->effectiveZoom(); + + FloatRect localBounds = [search searchButtonRectForBounds:NSRect(input->renderBox()->pixelSnappedBorderBoxRect())]; + localBounds = convertToPaintingRect(input->renderer(), o, localBounds, r); + + IntRect unzoomedRect(localBounds); + if (zoomLevel != 1.0f) { + unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel); + unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel); + paintInfo.context->translate(unzoomedRect.x(), unzoomedRect.y()); + paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel)); + paintInfo.context->translate(-unzoomedRect.x(), -unzoomedRect.y()); + } + + [[search searchButtonCell] drawWithFrame:unzoomedRect inView:documentViewFor(o)]; + [[search searchButtonCell] setControlView:nil]; + + return false; +} + +bool RenderThemeMac::paintSnapshottedPluginOverlay(RenderObject* o, const PaintInfo& paintInfo, const IntRect&) +{ + if (paintInfo.phase != PaintPhaseBlockBackground) + return true; + + if (!o->isRenderBlock()) + return true; + + RenderBlock* renderBlock = toRenderBlock(o); + + LayoutUnit contentWidth = renderBlock->contentWidth(); + LayoutUnit contentHeight = renderBlock->contentHeight(); + if (!contentWidth || !contentHeight) + return true; + + GraphicsContext* context = paintInfo.context; + + LayoutSize contentSize(contentWidth, contentHeight); + LayoutPoint contentLocation = renderBlock->location(); + contentLocation.move(renderBlock->borderLeft() + renderBlock->paddingLeft(), renderBlock->borderTop() + renderBlock->paddingTop()); + + LayoutRect rect(contentLocation, contentSize); + IntRect alignedRect = pixelSnappedIntRect(rect); + if (alignedRect.width() <= 0 || alignedRect.height() <= 0) + return true; + + // We need to get the snapshot image from the plugin element, which should be available + // from our node. Assuming this node is the plugin overlay element, we should get to the + // plugin itself by asking for the shadow root parent, and then its parent. + + if (!renderBlock->node()->isHTMLElement()) + return true; + + HTMLElement* plugInOverlay = toHTMLElement(renderBlock->node()); + Element* parent = plugInOverlay->parentOrShadowHostElement(); + while (parent && !parent->isPluginElement()) + parent = parent->parentOrShadowHostElement(); + + if (!parent) + return true; + + HTMLPlugInElement* plugInElement = toHTMLPlugInElement(parent); + if (!plugInElement->isPlugInImageElement()) + return true; + + HTMLPlugInImageElement* plugInImageElement = toHTMLPlugInImageElement(plugInElement); + + Image* snapshot = plugInImageElement->snapshotImage(); + if (!snapshot) + return true; + + RenderSnapshottedPlugIn* plugInRenderer = toRenderSnapshottedPlugIn(plugInImageElement->renderer()); + FloatPoint snapshotAbsPos = plugInRenderer->localToAbsolute(); + snapshotAbsPos.move(plugInRenderer->borderLeft() + plugInRenderer->paddingLeft(), plugInRenderer->borderTop() + plugInRenderer->paddingTop()); + + // We could draw the snapshot with that coordinates, but we need to make sure there + // isn't a composited layer between us and the plugInRenderer. + RenderBox* renderBox = toRenderBox(o); + while (renderBox != plugInRenderer) { + if (renderBox->hasLayer() && renderBox->layer() && renderBox->layer()->isComposited()) { + snapshotAbsPos = -renderBox->location(); + break; + } + renderBox = renderBox->parentBox(); + } + + LayoutSize pluginSize(plugInRenderer->contentWidth(), plugInRenderer->contentHeight()); + LayoutRect pluginRect(snapshotAbsPos, pluginSize); + IntRect alignedPluginRect = pixelSnappedIntRect(pluginRect); + + if (alignedPluginRect.width() <= 0 || alignedPluginRect.height() <= 0) + return true; + + context->drawImage(snapshot, plugInRenderer->style()->colorSpace(), alignedPluginRect, CompositeSourceOver); + return false; +} + +#if ENABLE(DATALIST_ELEMENT) +IntSize RenderThemeMac::sliderTickSize() const +{ + return IntSize(1, 3); +} + +int RenderThemeMac::sliderTickOffsetFromTrackCenter() const +{ + return -9; +} +#endif + +const int sliderThumbWidth = 15; +const int sliderThumbHeight = 15; + +void RenderThemeMac::adjustSliderThumbSize(RenderStyle* style, Element*) const +{ + float zoomLevel = style->effectiveZoom(); + if (style->appearance() == SliderThumbHorizontalPart || style->appearance() == SliderThumbVerticalPart) { + style->setWidth(Length(static_cast<int>(sliderThumbWidth * zoomLevel), Fixed)); + style->setHeight(Length(static_cast<int>(sliderThumbHeight * zoomLevel), Fixed)); + } + +#if ENABLE(VIDEO) + adjustMediaSliderThumbSize(style); +#endif +} + +bool RenderThemeMac::shouldShowPlaceholderWhenFocused() const +{ +#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 + return true; +#else + return false; +#endif +} + +NSPopUpButtonCell* RenderThemeMac::popupButton() const +{ + if (!m_popupButton) { + m_popupButton = adoptNS([[NSPopUpButtonCell alloc] initTextCell:@"" pullsDown:NO]); + [m_popupButton.get() setUsesItemFromMenu:NO]; + [m_popupButton.get() setFocusRingType:NSFocusRingTypeExterior]; + } + + return m_popupButton.get(); +} + +NSSearchFieldCell* RenderThemeMac::search() const +{ + if (!m_search) { + m_search = adoptNS([[NSSearchFieldCell alloc] initTextCell:@""]); + [m_search.get() setBezelStyle:NSTextFieldRoundedBezel]; + [m_search.get() setBezeled:YES]; + [m_search.get() setEditable:YES]; + [m_search.get() setFocusRingType:NSFocusRingTypeExterior]; + } + + return m_search.get(); +} + +NSMenu* RenderThemeMac::searchMenuTemplate() const +{ + if (!m_searchMenuTemplate) + m_searchMenuTemplate = adoptNS([[NSMenu alloc] initWithTitle:@""]); + + return m_searchMenuTemplate.get(); +} + +NSSliderCell* RenderThemeMac::sliderThumbHorizontal() const +{ + if (!m_sliderThumbHorizontal) { + m_sliderThumbHorizontal = adoptNS([[NSSliderCell alloc] init]); + [m_sliderThumbHorizontal.get() setSliderType:NSLinearSlider]; + [m_sliderThumbHorizontal.get() setControlSize:NSSmallControlSize]; + [m_sliderThumbHorizontal.get() setFocusRingType:NSFocusRingTypeExterior]; + } + + return m_sliderThumbHorizontal.get(); +} + +NSSliderCell* RenderThemeMac::sliderThumbVertical() const +{ + if (!m_sliderThumbVertical) { + m_sliderThumbVertical = adoptNS([[NSSliderCell alloc] init]); + [m_sliderThumbVertical.get() setSliderType:NSLinearSlider]; + [m_sliderThumbVertical.get() setControlSize:NSSmallControlSize]; + [m_sliderThumbVertical.get() setFocusRingType:NSFocusRingTypeExterior]; + } + + return m_sliderThumbVertical.get(); +} + +NSTextFieldCell* RenderThemeMac::textField() const +{ + if (!m_textField) { + m_textField = adoptNS([[WebCoreTextFieldCell alloc] initTextCell:@""]); + [m_textField.get() setBezeled:YES]; + [m_textField.get() setEditable:YES]; + [m_textField.get() setFocusRingType:NSFocusRingTypeExterior]; +#if __MAC_OS_X_VERSION_MIN_REQUIRED <= 1070 + [m_textField.get() setDrawsBackground:YES]; + [m_textField.get() setBackgroundColor:[NSColor whiteColor]]; +#else + // Post-Lion, WebCore can be in charge of paintinng the background thanks to + // the workaround in place for <rdar://problem/11385461>, which is implemented + // above as _coreUIDrawOptionsWithFrame. + [m_textField.get() setDrawsBackground:NO]; +#endif + } + + return m_textField.get(); +} + +String RenderThemeMac::fileListNameForWidth(const FileList* fileList, const Font& font, int width, bool multipleFilesAllowed) const +{ + if (width <= 0) + return String(); + + String strToTruncate; + if (fileList->isEmpty()) + strToTruncate = fileListDefaultLabel(multipleFilesAllowed); + else if (fileList->length() == 1) + strToTruncate = [[NSFileManager defaultManager] displayNameAtPath:(fileList->item(0)->path())]; + else + return StringTruncator::rightTruncate(multipleFileUploadText(fileList->length()), width, font, StringTruncator::EnableRoundingHacks); + + return StringTruncator::centerTruncate(strToTruncate, width, font, StringTruncator::EnableRoundingHacks); +} + + } // namespace WebCore diff --git a/Source/WebCore/rendering/RenderThemeMacShared.h b/Source/WebCore/rendering/RenderThemeMacShared.h deleted file mode 100644 index 5283cb8db..000000000 --- a/Source/WebCore/rendering/RenderThemeMacShared.h +++ /dev/null @@ -1,227 +0,0 @@ -/* - * This file is part of the theme implementation for form controls in WebCore. - * - * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Computer, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#ifndef RenderThemeMacShared_h -#define RenderThemeMacShared_h - -#import "RenderTheme.h" -#import <wtf/HashMap.h> -#import <wtf/RetainPtr.h> - -OBJC_CLASS WebCoreRenderThemeNotificationObserver; - -namespace WebCore { - -class RenderProgress; -class RenderStyle; - -class RenderThemeMacShared : public RenderTheme { -public: - // A method asking if the control changes its tint when the window has focus or not. - virtual bool controlSupportsTints(const RenderObject*) const; - - // A general method asking if any control tinting is supported at all. - virtual bool supportsControlTints() const { return true; } - - virtual void adjustRepaintRect(const RenderObject*, IntRect&) OVERRIDE; - - virtual bool isControlStyled(const RenderStyle*, const BorderData&, const FillLayer&, const Color& backgroundColor) const; - - virtual Color platformActiveSelectionBackgroundColor() const; - virtual Color platformInactiveSelectionBackgroundColor() const; - virtual Color platformActiveListBoxSelectionBackgroundColor() const; - virtual Color platformActiveListBoxSelectionForegroundColor() const; - virtual Color platformInactiveListBoxSelectionBackgroundColor() const; - virtual Color platformInactiveListBoxSelectionForegroundColor() const; - virtual Color platformFocusRingColor() const; - - virtual ScrollbarControlSize scrollbarControlSizeForPart(ControlPart) { return SmallScrollbar; } - - virtual void platformColorsDidChange(); - - // System fonts. - virtual void systemFont(int cssValueId, FontDescription&) const; - - virtual int minimumMenuListSize(RenderStyle*) const; - - virtual void adjustSliderThumbSize(RenderStyle*, Element*) const; - -#if ENABLE(DATALIST_ELEMENT) - virtual IntSize sliderTickSize() const OVERRIDE; - virtual int sliderTickOffsetFromTrackCenter() const OVERRIDE; -#endif - - virtual int popupInternalPaddingLeft(RenderStyle*) const; - virtual int popupInternalPaddingRight(RenderStyle*) const; - virtual int popupInternalPaddingTop(RenderStyle*) const; - virtual int popupInternalPaddingBottom(RenderStyle*) const; - - virtual bool paintCapsLockIndicator(RenderObject*, const PaintInfo&, const IntRect&) OVERRIDE; - - virtual bool popsMenuByArrowKeys() const OVERRIDE { return true; } - -#if ENABLE(METER_ELEMENT) - virtual IntSize meterSizeForBounds(const RenderMeter*, const IntRect&) const OVERRIDE; - virtual bool paintMeter(RenderObject*, const PaintInfo&, const IntRect&); - virtual bool supportsMeter(ControlPart) const; -#endif - -#if ENABLE(PROGRESS_ELEMENT) - // Returns the repeat interval of the animation for the progress bar. - virtual double animationRepeatIntervalForProgressBar(RenderProgress*) const; - // Returns the duration of the animation for the progress bar. - virtual double animationDurationForProgressBar(RenderProgress*) const; -#endif - - virtual Color systemColor(int cssValueId) const; - // Controls color values returned from platformFocusRingColor(). systemColor() will be used when false. - virtual bool usesTestModeFocusRingColor() const; - // A view associated to the contained document. Subclasses may not have such a view and return a fake. - virtual NSView* documentViewFor(RenderObject*) const = 0; - -protected: - RenderThemeMacShared(); - virtual ~RenderThemeMacShared(); - - virtual bool supportsSelectionForegroundColors() const { return false; } - - virtual bool paintTextField(RenderObject*, const PaintInfo&, const IntRect&); - virtual void adjustTextFieldStyle(StyleResolver*, RenderStyle*, Element*) const; - - virtual bool paintTextArea(RenderObject*, const PaintInfo&, const IntRect&); - virtual void adjustTextAreaStyle(StyleResolver*, RenderStyle*, Element*) const; - - virtual bool paintMenuList(RenderObject*, const PaintInfo&, const IntRect&); - virtual void adjustMenuListStyle(StyleResolver*, RenderStyle*, Element*) const; - - virtual bool paintMenuListButton(RenderObject*, const PaintInfo&, const IntRect&); - virtual void adjustMenuListButtonStyle(StyleResolver*, RenderStyle*, Element*) const; - -#if ENABLE(PROGRESS_ELEMENT) - virtual void adjustProgressBarStyle(StyleResolver*, RenderStyle*, Element*) const; - virtual bool paintProgressBar(RenderObject*, const PaintInfo&, const IntRect&); -#endif - - virtual bool paintSliderTrack(RenderObject*, const PaintInfo&, const IntRect&); - virtual void adjustSliderTrackStyle(StyleResolver*, RenderStyle*, Element*) const; - - virtual bool paintSliderThumb(RenderObject*, const PaintInfo&, const IntRect&); - virtual void adjustSliderThumbStyle(StyleResolver*, RenderStyle*, Element*) const; - - virtual bool paintSearchField(RenderObject*, const PaintInfo&, const IntRect&); - virtual void adjustSearchFieldStyle(StyleResolver*, RenderStyle*, Element*) const; - - virtual void adjustSearchFieldCancelButtonStyle(StyleResolver*, RenderStyle*, Element*) const; - virtual bool paintSearchFieldCancelButton(RenderObject*, const PaintInfo&, const IntRect&); - - virtual void adjustSearchFieldDecorationStyle(StyleResolver*, RenderStyle*, Element*) const; - virtual bool paintSearchFieldDecoration(RenderObject*, const PaintInfo&, const IntRect&); - - virtual void adjustSearchFieldResultsDecorationStyle(StyleResolver*, RenderStyle*, Element*) const; - virtual bool paintSearchFieldResultsDecoration(RenderObject*, const PaintInfo&, const IntRect&); - - virtual void adjustSearchFieldResultsButtonStyle(StyleResolver*, RenderStyle*, Element*) const; - virtual bool paintSearchFieldResultsButton(RenderObject*, const PaintInfo&, const IntRect&); - -#if ENABLE(VIDEO) - virtual void adjustMediaSliderThumbSize(RenderStyle*) const = 0; - virtual bool supportsClosedCaptioning() const { return true; } -#endif - - virtual bool shouldShowPlaceholderWhenFocused() const; - -private: - virtual String fileListNameForWidth(const FileList*, const Font&, int width, bool multipleFilesAllowed) const OVERRIDE; - - IntRect inflateRect(const IntRect&, const IntSize&, const int* margins, float zoomLevel = 1.0f) const; - - FloatRect convertToPaintingRect(const RenderObject* inputRenderer, const RenderObject* partRenderer, const FloatRect& inputRect, const IntRect&) const; - - // Get the control size based off the font. Used by some of the controls (like buttons). - NSControlSize controlSizeForFont(RenderStyle*) const; - NSControlSize controlSizeForSystemFont(RenderStyle*) const; - void setControlSize(NSCell*, const IntSize* sizes, const IntSize& minSize, float zoomLevel = 1.0f); - void setSizeFromFont(RenderStyle*, const IntSize* sizes) const; - IntSize sizeForFont(RenderStyle*, const IntSize* sizes) const; - IntSize sizeForSystemFont(RenderStyle*, const IntSize* sizes) const; - void setFontFromControlSize(StyleResolver*, RenderStyle*, NSControlSize) const; - - void updateCheckedState(NSCell*, const RenderObject*); - void updateEnabledState(NSCell*, const RenderObject*); - void updateFocusedState(NSCell*, const RenderObject*); - void updatePressedState(NSCell*, const RenderObject*); - // An optional hook for subclasses to update the control tint of NSCell. - virtual void updateActiveState(NSCell*, const RenderObject*) { } - - // Helpers for adjusting appearance and for painting - - void setPopupButtonCellState(const RenderObject*, const IntRect&); - const IntSize* popupButtonSizes() const; - const int* popupButtonMargins() const; - const int* popupButtonPadding(NSControlSize) const; - void paintMenuListButtonGradients(RenderObject*, const PaintInfo&, const IntRect&); - const IntSize* menuListSizes() const; - - const IntSize* searchFieldSizes() const; - const IntSize* cancelButtonSizes() const; - const IntSize* resultsButtonSizes() const; - void setSearchCellState(RenderObject*, const IntRect&); - void setSearchFieldSize(RenderStyle*) const; - - NSPopUpButtonCell* popupButton() const; - NSSearchFieldCell* search() const; - NSMenu* searchMenuTemplate() const; - NSSliderCell* sliderThumbHorizontal() const; - NSSliderCell* sliderThumbVertical() const; - NSTextFieldCell* textField() const; - -#if ENABLE(METER_ELEMENT) - NSLevelIndicatorStyle levelIndicatorStyleFor(ControlPart) const; - NSLevelIndicatorCell* levelIndicatorFor(const RenderMeter*) const; -#endif - -#if ENABLE(PROGRESS_ELEMENT) - int minimumProgressBarHeight(RenderStyle*) const; - const IntSize* progressBarSizes() const; - const int* progressBarMargins(NSControlSize) const; -#endif - -private: - mutable RetainPtr<NSPopUpButtonCell> m_popupButton; - mutable RetainPtr<NSSearchFieldCell> m_search; - mutable RetainPtr<NSMenu> m_searchMenuTemplate; - mutable RetainPtr<NSSliderCell> m_sliderThumbHorizontal; - mutable RetainPtr<NSSliderCell> m_sliderThumbVertical; - mutable RetainPtr<NSLevelIndicatorCell> m_levelIndicator; - mutable RetainPtr<NSTextFieldCell> m_textField; - - bool m_isSliderThumbHorizontalPressed; - bool m_isSliderThumbVerticalPressed; - - mutable HashMap<int, RGBA32> m_systemColorCache; - - RetainPtr<WebCoreRenderThemeNotificationObserver> m_notificationObserver; -}; - -} // namespace WebCore - -#endif // RenderThemeMacShared_h diff --git a/Source/WebCore/rendering/RenderThemeMacShared.mm b/Source/WebCore/rendering/RenderThemeMacShared.mm deleted file mode 100644 index e6074cc26..000000000 --- a/Source/WebCore/rendering/RenderThemeMacShared.mm +++ /dev/null @@ -1,1835 +0,0 @@ -/* - * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#import "config.h" -#import "RenderThemeMacShared.h" - -#import "BitmapImage.h" -#import "ColorMac.h" -#import "CSSValueList.h" -#import "CSSValueKeywords.h" -#import "Document.h" -#import "Element.h" -#import "FileList.h" -#import "FrameView.h" -#import "GraphicsContextCG.h" -#import "HTMLInputElement.h" -#import "HTMLMediaElement.h" -#import "HTMLNames.h" -#import "Image.h" -#import "ImageBuffer.h" -#import "LocalCurrentGraphicsContext.h" -#import "LocalizedStrings.h" -#import "MediaControlElements.h" -#import "PaintInfo.h" -#import "RenderMedia.h" -#import "RenderMediaControls.h" -#import "RenderSlider.h" -#import "RenderSnapshottedPlugIn.h" -#import "RenderView.h" -#import "SharedBuffer.h" -#import "StringTruncator.h" -#import "StyleResolver.h" -#import "TimeRanges.h" -#import "ThemeMac.h" -#import "WebCoreNSCellExtras.h" -#import "WebCoreSystemInterface.h" -#import "UserAgentStyleSheets.h" -#import <Carbon/Carbon.h> -#import <Cocoa/Cocoa.h> -#import <wtf/RetainPtr.h> -#import <wtf/StdLibExtras.h> -#import <math.h> - -#import "RenderProgress.h" - -#if ENABLE(METER_ELEMENT) -#include "RenderMeter.h" -#include "HTMLMeterElement.h" -#endif - -using namespace std; - -// The methods in this file are specific to the Mac OS X platform. - -// FIXME: The platform-independent code in this class should be factored out and merged with RenderThemeSafari. - -// We estimate the animation rate of a Mac OS X progress bar is 33 fps. -// Hard code the value here because we haven't found API for it. -const double progressAnimationFrameRate = 0.033; - -// Mac OS X progress bar animation seems to have 256 frames. -const double progressAnimationNumFrames = 256; - -@interface WebCoreRenderThemeNotificationObserver : NSObject -{ - WebCore::RenderTheme *_theme; -} - -- (id)initWithTheme:(WebCore::RenderTheme *)theme; -- (void)systemColorsDidChange:(NSNotification *)notification; - -@end - -@implementation WebCoreRenderThemeNotificationObserver - -- (id)initWithTheme:(WebCore::RenderTheme *)theme -{ - if (!(self = [super init])) - return nil; - - _theme = theme; - return self; -} - -- (void)systemColorsDidChange:(NSNotification *)unusedNotification -{ - ASSERT_UNUSED(unusedNotification, [[unusedNotification name] isEqualToString:NSSystemColorsDidChangeNotification]); - _theme->platformColorsDidChange(); -} - -@end - -@interface NSTextFieldCell (WKDetails) -- (CFDictionaryRef)_coreUIDrawOptionsWithFrame:(NSRect)cellFrame inView:(NSView *)controlView includeFocus:(BOOL)includeFocus; -@end - - -@interface WebCoreTextFieldCell : NSTextFieldCell -- (CFDictionaryRef)_coreUIDrawOptionsWithFrame:(NSRect)cellFrame inView:(NSView *)controlView includeFocus:(BOOL)includeFocus; -@end - -@implementation WebCoreTextFieldCell -- (CFDictionaryRef)_coreUIDrawOptionsWithFrame:(NSRect)cellFrame inView:(NSView *)controlView includeFocus:(BOOL)includeFocus -{ - // FIXME: This is a post-Lion-only workaround for <rdar://problem/11385461>. When that bug is resolved, we should remove this code. - CFMutableDictionaryRef coreUIDrawOptions = CFDictionaryCreateMutableCopy(NULL, 0, [super _coreUIDrawOptionsWithFrame:cellFrame inView:controlView includeFocus:includeFocus]); - CFDictionarySetValue(coreUIDrawOptions, @"borders only", kCFBooleanTrue); - return (CFDictionaryRef)[NSMakeCollectable(coreUIDrawOptions) autorelease]; -} -@end - -namespace WebCore { - -using namespace HTMLNames; - -enum { - topMargin, - rightMargin, - bottomMargin, - leftMargin -}; - -enum { - topPadding, - rightPadding, - bottomPadding, - leftPadding -}; - -RenderThemeMacShared::RenderThemeMacShared() - : m_isSliderThumbHorizontalPressed(false) - , m_isSliderThumbVerticalPressed(false) - , m_notificationObserver(AdoptNS, [[WebCoreRenderThemeNotificationObserver alloc] initWithTheme:this]) -{ - [[NSNotificationCenter defaultCenter] addObserver:m_notificationObserver.get() - selector:@selector(systemColorsDidChange:) - name:NSSystemColorsDidChangeNotification - object:nil]; -} - -RenderThemeMacShared::~RenderThemeMacShared() -{ - [[NSNotificationCenter defaultCenter] removeObserver:m_notificationObserver.get()]; -} - -Color RenderThemeMacShared::platformActiveSelectionBackgroundColor() const -{ - NSColor* color = [[NSColor selectedTextBackgroundColor] colorUsingColorSpaceName:NSDeviceRGBColorSpace]; - return Color(static_cast<int>(255.0 * [color redComponent]), static_cast<int>(255.0 * [color greenComponent]), static_cast<int>(255.0 * [color blueComponent])); -} - -Color RenderThemeMacShared::platformInactiveSelectionBackgroundColor() const -{ - NSColor* color = [[NSColor secondarySelectedControlColor] colorUsingColorSpaceName:NSDeviceRGBColorSpace]; - return Color(static_cast<int>(255.0 * [color redComponent]), static_cast<int>(255.0 * [color greenComponent]), static_cast<int>(255.0 * [color blueComponent])); -} - -Color RenderThemeMacShared::platformActiveListBoxSelectionBackgroundColor() const -{ - NSColor* color = [[NSColor alternateSelectedControlColor] colorUsingColorSpaceName:NSDeviceRGBColorSpace]; - return Color(static_cast<int>(255.0 * [color redComponent]), static_cast<int>(255.0 * [color greenComponent]), static_cast<int>(255.0 * [color blueComponent])); -} - -Color RenderThemeMacShared::platformActiveListBoxSelectionForegroundColor() const -{ - return Color::white; -} - -Color RenderThemeMacShared::platformInactiveListBoxSelectionForegroundColor() const -{ - return Color::black; -} - -Color RenderThemeMacShared::platformFocusRingColor() const -{ - if (usesTestModeFocusRingColor()) - return oldAquaFocusRingColor(); - - return systemColor(CSSValueWebkitFocusRingColor); -} - -Color RenderThemeMacShared::platformInactiveListBoxSelectionBackgroundColor() const -{ - return platformInactiveSelectionBackgroundColor(); -} - -static FontWeight toFontWeight(NSInteger appKitFontWeight) -{ - ASSERT(appKitFontWeight > 0 && appKitFontWeight < 15); - if (appKitFontWeight > 14) - appKitFontWeight = 14; - else if (appKitFontWeight < 1) - appKitFontWeight = 1; - - static FontWeight fontWeights[] = { - FontWeight100, - FontWeight100, - FontWeight200, - FontWeight300, - FontWeight400, - FontWeight500, - FontWeight600, - FontWeight600, - FontWeight700, - FontWeight800, - FontWeight800, - FontWeight900, - FontWeight900, - FontWeight900 - }; - return fontWeights[appKitFontWeight - 1]; -} - -void RenderThemeMacShared::systemFont(int cssValueId, FontDescription& fontDescription) const -{ - DEFINE_STATIC_LOCAL(FontDescription, systemFont, ()); - DEFINE_STATIC_LOCAL(FontDescription, smallSystemFont, ()); - DEFINE_STATIC_LOCAL(FontDescription, menuFont, ()); - DEFINE_STATIC_LOCAL(FontDescription, labelFont, ()); - DEFINE_STATIC_LOCAL(FontDescription, miniControlFont, ()); - DEFINE_STATIC_LOCAL(FontDescription, smallControlFont, ()); - DEFINE_STATIC_LOCAL(FontDescription, controlFont, ()); - - FontDescription* cachedDesc; - NSFont* font = nil; - switch (cssValueId) { - case CSSValueSmallCaption: - cachedDesc = &smallSystemFont; - if (!smallSystemFont.isAbsoluteSize()) - font = [NSFont systemFontOfSize:[NSFont smallSystemFontSize]]; - break; - case CSSValueMenu: - cachedDesc = &menuFont; - if (!menuFont.isAbsoluteSize()) - font = [NSFont menuFontOfSize:[NSFont systemFontSize]]; - break; - case CSSValueStatusBar: - cachedDesc = &labelFont; - if (!labelFont.isAbsoluteSize()) - font = [NSFont labelFontOfSize:[NSFont labelFontSize]]; - break; - case CSSValueWebkitMiniControl: - cachedDesc = &miniControlFont; - if (!miniControlFont.isAbsoluteSize()) - font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSMiniControlSize]]; - break; - case CSSValueWebkitSmallControl: - cachedDesc = &smallControlFont; - if (!smallControlFont.isAbsoluteSize()) - font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]]; - break; - case CSSValueWebkitControl: - cachedDesc = &controlFont; - if (!controlFont.isAbsoluteSize()) - font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSRegularControlSize]]; - break; - default: - cachedDesc = &systemFont; - if (!systemFont.isAbsoluteSize()) - font = [NSFont systemFontOfSize:[NSFont systemFontSize]]; - } - - if (font) { - NSFontManager *fontManager = [NSFontManager sharedFontManager]; - cachedDesc->setIsAbsoluteSize(true); - cachedDesc->setGenericFamily(FontDescription::NoFamily); - cachedDesc->firstFamily().setFamily([font familyName]); - cachedDesc->setSpecifiedSize([font pointSize]); - cachedDesc->setWeight(toFontWeight([fontManager weightOfFont:font])); - cachedDesc->setItalic([fontManager traitsOfFont:font] & NSItalicFontMask); - } - fontDescription = *cachedDesc; -} - -static RGBA32 convertNSColorToColor(NSColor *color) -{ - NSColor *colorInColorSpace = [color colorUsingColorSpaceName:NSDeviceRGBColorSpace]; - if (colorInColorSpace) { - static const double scaleFactor = nextafter(256.0, 0.0); - return makeRGB(static_cast<int>(scaleFactor * [colorInColorSpace redComponent]), - static_cast<int>(scaleFactor * [colorInColorSpace greenComponent]), - static_cast<int>(scaleFactor * [colorInColorSpace blueComponent])); - } - - // This conversion above can fail if the NSColor in question is an NSPatternColor - // (as many system colors are). These colors are actually a repeating pattern - // not just a solid color. To work around this we simply draw a 1x1 image of - // the color and use that pixel's color. It might be better to use an average of - // the colors in the pattern instead. - NSBitmapImageRep *offscreenRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil - pixelsWide:1 - pixelsHigh:1 - bitsPerSample:8 - samplesPerPixel:4 - hasAlpha:YES - isPlanar:NO - colorSpaceName:NSDeviceRGBColorSpace - bytesPerRow:4 - bitsPerPixel:32]; - - [NSGraphicsContext saveGraphicsState]; - [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:offscreenRep]]; - NSEraseRect(NSMakeRect(0, 0, 1, 1)); - [color drawSwatchInRect:NSMakeRect(0, 0, 1, 1)]; - [NSGraphicsContext restoreGraphicsState]; - - NSUInteger pixel[4]; - [offscreenRep getPixel:pixel atX:0 y:0]; - - [offscreenRep release]; - - return makeRGB(pixel[0], pixel[1], pixel[2]); -} - -static RGBA32 menuBackgroundColor() -{ - NSBitmapImageRep *offscreenRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil - pixelsWide:1 - pixelsHigh:1 - bitsPerSample:8 - samplesPerPixel:4 - hasAlpha:YES - isPlanar:NO - colorSpaceName:NSDeviceRGBColorSpace - bytesPerRow:4 - bitsPerPixel:32]; - - CGContextRef context = static_cast<CGContextRef>([[NSGraphicsContext graphicsContextWithBitmapImageRep:offscreenRep] graphicsPort]); - CGRect rect = CGRectMake(0, 0, 1, 1); - HIThemeMenuDrawInfo drawInfo; - drawInfo.version = 0; - drawInfo.menuType = kThemeMenuTypePopUp; - HIThemeDrawMenuBackground(&rect, &drawInfo, context, kHIThemeOrientationInverted); - - NSUInteger pixel[4]; - [offscreenRep getPixel:pixel atX:0 y:0]; - - [offscreenRep release]; - - return makeRGB(pixel[0], pixel[1], pixel[2]); -} - -void RenderThemeMacShared::platformColorsDidChange() -{ - m_systemColorCache.clear(); - RenderTheme::platformColorsDidChange(); -} - -Color RenderThemeMacShared::systemColor(int cssValueId) const -{ - { - HashMap<int, RGBA32>::iterator it = m_systemColorCache.find(cssValueId); - if (it != m_systemColorCache.end()) - return it->value; - } - - Color color; - switch (cssValueId) { - case CSSValueActiveborder: - color = convertNSColorToColor([NSColor keyboardFocusIndicatorColor]); - break; - case CSSValueActivecaption: - color = convertNSColorToColor([NSColor windowFrameTextColor]); - break; - case CSSValueAppworkspace: - color = convertNSColorToColor([NSColor headerColor]); - break; - case CSSValueBackground: - // Use theme independent default - break; - case CSSValueButtonface: - // We use this value instead of NSColor's controlColor to avoid website incompatibilities. - // We may want to change this to use the NSColor in future. - color = 0xFFC0C0C0; - break; - case CSSValueButtonhighlight: - color = convertNSColorToColor([NSColor controlHighlightColor]); - break; - case CSSValueButtonshadow: - color = convertNSColorToColor([NSColor controlShadowColor]); - break; - case CSSValueButtontext: - color = convertNSColorToColor([NSColor controlTextColor]); - break; - case CSSValueCaptiontext: - color = convertNSColorToColor([NSColor textColor]); - break; - case CSSValueGraytext: - color = convertNSColorToColor([NSColor disabledControlTextColor]); - break; - case CSSValueHighlight: - color = convertNSColorToColor([NSColor selectedTextBackgroundColor]); - break; - case CSSValueHighlighttext: - color = convertNSColorToColor([NSColor selectedTextColor]); - break; - case CSSValueInactiveborder: - color = convertNSColorToColor([NSColor controlBackgroundColor]); - break; - case CSSValueInactivecaption: - color = convertNSColorToColor([NSColor controlBackgroundColor]); - break; - case CSSValueInactivecaptiontext: - color = convertNSColorToColor([NSColor textColor]); - break; - case CSSValueInfobackground: - // There is no corresponding NSColor for this so we use a hard coded value. - color = 0xFFFBFCC5; - break; - case CSSValueInfotext: - color = convertNSColorToColor([NSColor textColor]); - break; - case CSSValueMenu: - color = menuBackgroundColor(); - break; - case CSSValueMenutext: - color = convertNSColorToColor([NSColor selectedMenuItemTextColor]); - break; - case CSSValueScrollbar: - color = convertNSColorToColor([NSColor scrollBarColor]); - break; - case CSSValueText: - color = convertNSColorToColor([NSColor textColor]); - break; - case CSSValueThreeddarkshadow: - color = convertNSColorToColor([NSColor controlDarkShadowColor]); - break; - case CSSValueThreedshadow: - color = convertNSColorToColor([NSColor shadowColor]); - break; - case CSSValueThreedface: - // We use this value instead of NSColor's controlColor to avoid website incompatibilities. - // We may want to change this to use the NSColor in future. - color = 0xFFC0C0C0; - break; - case CSSValueThreedhighlight: - color = convertNSColorToColor([NSColor highlightColor]); - break; - case CSSValueThreedlightshadow: - color = convertNSColorToColor([NSColor controlLightHighlightColor]); - break; - case CSSValueWebkitFocusRingColor: - color = convertNSColorToColor([NSColor keyboardFocusIndicatorColor]); - break; - case CSSValueWindow: - color = convertNSColorToColor([NSColor windowBackgroundColor]); - break; - case CSSValueWindowframe: - color = convertNSColorToColor([NSColor windowFrameColor]); - break; - case CSSValueWindowtext: - color = convertNSColorToColor([NSColor windowFrameTextColor]); - break; - } - - if (!color.isValid()) - color = RenderTheme::systemColor(cssValueId); - - if (color.isValid()) - m_systemColorCache.set(cssValueId, color.rgb()); - - return color; -} - -bool RenderThemeMacShared::usesTestModeFocusRingColor() const -{ - return WebCore::usesTestModeFocusRingColor(); -} - -bool RenderThemeMacShared::isControlStyled(const RenderStyle* style, const BorderData& border, - const FillLayer& background, const Color& backgroundColor) const -{ - if (style->appearance() == TextFieldPart || style->appearance() == TextAreaPart || style->appearance() == ListboxPart) - return style->border() != border; - - // FIXME: This is horrible, but there is not much else that can be done. Menu lists cannot draw properly when - // scaled. They can't really draw properly when transformed either. We can't detect the transform case at style - // adjustment time so that will just have to stay broken. We can however detect that we're zooming. If zooming - // is in effect we treat it like the control is styled. - if (style->appearance() == MenulistPart && style->effectiveZoom() != 1.0f) - return true; - - return RenderTheme::isControlStyled(style, border, background, backgroundColor); -} - -void RenderThemeMacShared::adjustRepaintRect(const RenderObject* o, IntRect& r) -{ - ControlPart part = o->style()->appearance(); - -#if USE(NEW_THEME) - switch (part) { - case CheckboxPart: - case RadioPart: - case PushButtonPart: - case SquareButtonPart: - case DefaultButtonPart: - case ButtonPart: - case InnerSpinButtonPart: - return RenderTheme::adjustRepaintRect(o, r); - default: - break; - } -#endif - - float zoomLevel = o->style()->effectiveZoom(); - - if (part == MenulistPart) { - setPopupButtonCellState(o, r); - IntSize size = popupButtonSizes()[[popupButton() controlSize]]; - size.setHeight(size.height() * zoomLevel); - size.setWidth(r.width()); - r = inflateRect(r, size, popupButtonMargins(), zoomLevel); - } -} - -IntRect RenderThemeMacShared::inflateRect(const IntRect& r, const IntSize& size, const int* margins, float zoomLevel) const -{ - // Only do the inflation if the available width/height are too small. Otherwise try to - // fit the glow/check space into the available box's width/height. - int widthDelta = r.width() - (size.width() + margins[leftMargin] * zoomLevel + margins[rightMargin] * zoomLevel); - int heightDelta = r.height() - (size.height() + margins[topMargin] * zoomLevel + margins[bottomMargin] * zoomLevel); - IntRect result(r); - if (widthDelta < 0) { - result.setX(result.x() - margins[leftMargin] * zoomLevel); - result.setWidth(result.width() - widthDelta); - } - if (heightDelta < 0) { - result.setY(result.y() - margins[topMargin] * zoomLevel); - result.setHeight(result.height() - heightDelta); - } - return result; -} - -FloatRect RenderThemeMacShared::convertToPaintingRect(const RenderObject* inputRenderer, const RenderObject* partRenderer, const FloatRect& inputRect, const IntRect& r) const -{ - FloatRect partRect(inputRect); - - // Compute an offset between the part renderer and the input renderer - FloatSize offsetFromInputRenderer; - const RenderObject* renderer = partRenderer; - while (renderer && renderer != inputRenderer) { - RenderObject* containingRenderer = renderer->container(); - offsetFromInputRenderer -= roundedIntSize(renderer->offsetFromContainer(containingRenderer, LayoutPoint())); - renderer = containingRenderer; - } - // If the input renderer was not a container, something went wrong - ASSERT(renderer == inputRenderer); - // Move the rect into partRenderer's coords - partRect.move(offsetFromInputRenderer); - // Account for the local drawing offset (tx, ty) - partRect.move(r.x(), r.y()); - - return partRect; -} - -void RenderThemeMacShared::updateCheckedState(NSCell* cell, const RenderObject* o) -{ - bool oldIndeterminate = [cell state] == NSMixedState; - bool indeterminate = isIndeterminate(o); - bool checked = isChecked(o); - - if (oldIndeterminate != indeterminate) { - [cell setState:indeterminate ? NSMixedState : (checked ? NSOnState : NSOffState)]; - return; - } - - bool oldChecked = [cell state] == NSOnState; - if (checked != oldChecked) - [cell setState:checked ? NSOnState : NSOffState]; -} - -void RenderThemeMacShared::updateEnabledState(NSCell* cell, const RenderObject* o) -{ - bool oldEnabled = [cell isEnabled]; - bool enabled = isEnabled(o); - if (enabled != oldEnabled) - [cell setEnabled:enabled]; -} - -void RenderThemeMacShared::updateFocusedState(NSCell* cell, const RenderObject* o) -{ - bool oldFocused = [cell showsFirstResponder]; - bool focused = isFocused(o) && o->style()->outlineStyleIsAuto(); - if (focused != oldFocused) - [cell setShowsFirstResponder:focused]; -} - -void RenderThemeMacShared::updatePressedState(NSCell* cell, const RenderObject* o) -{ - bool oldPressed = [cell isHighlighted]; - bool pressed = (o->node() && o->node()->active()); - if (pressed != oldPressed) - [cell setHighlighted:pressed]; -} - -bool RenderThemeMacShared::controlSupportsTints(const RenderObject* o) const -{ - // An alternate way to implement this would be to get the appropriate cell object - // and call the private _needRedrawOnWindowChangedKeyState method. An advantage of - // that would be that we would match AppKit behavior more closely, but a disadvantage - // would be that we would rely on an AppKit SPI method. - - if (!isEnabled(o)) - return false; - - // Checkboxes only have tint when checked. - if (o->style()->appearance() == CheckboxPart) - return isChecked(o); - - // For now assume other controls have tint if enabled. - return true; -} - -NSControlSize RenderThemeMacShared::controlSizeForFont(RenderStyle* style) const -{ - int fontSize = style->fontSize(); - if (fontSize >= 16) - return NSRegularControlSize; - if (fontSize >= 11) - return NSSmallControlSize; - return NSMiniControlSize; -} - -void RenderThemeMacShared::setControlSize(NSCell* cell, const IntSize* sizes, const IntSize& minSize, float zoomLevel) -{ - NSControlSize size; - if (minSize.width() >= static_cast<int>(sizes[NSRegularControlSize].width() * zoomLevel) && - minSize.height() >= static_cast<int>(sizes[NSRegularControlSize].height() * zoomLevel)) - size = NSRegularControlSize; - else if (minSize.width() >= static_cast<int>(sizes[NSSmallControlSize].width() * zoomLevel) && - minSize.height() >= static_cast<int>(sizes[NSSmallControlSize].height() * zoomLevel)) - size = NSSmallControlSize; - else - size = NSMiniControlSize; - if (size != [cell controlSize]) // Only update if we have to, since AppKit does work even if the size is the same. - [cell setControlSize:size]; -} - -IntSize RenderThemeMacShared::sizeForFont(RenderStyle* style, const IntSize* sizes) const -{ - if (style->effectiveZoom() != 1.0f) { - IntSize result = sizes[controlSizeForFont(style)]; - return IntSize(result.width() * style->effectiveZoom(), result.height() * style->effectiveZoom()); - } - return sizes[controlSizeForFont(style)]; -} - -IntSize RenderThemeMacShared::sizeForSystemFont(RenderStyle* style, const IntSize* sizes) const -{ - if (style->effectiveZoom() != 1.0f) { - IntSize result = sizes[controlSizeForSystemFont(style)]; - return IntSize(result.width() * style->effectiveZoom(), result.height() * style->effectiveZoom()); - } - return sizes[controlSizeForSystemFont(style)]; -} - -void RenderThemeMacShared::setSizeFromFont(RenderStyle* style, const IntSize* sizes) const -{ - // FIXME: Check is flawed, since it doesn't take min-width/max-width into account. - IntSize size = sizeForFont(style, sizes); - if (style->width().isIntrinsicOrAuto() && size.width() > 0) - style->setWidth(Length(size.width(), Fixed)); - if (style->height().isAuto() && size.height() > 0) - style->setHeight(Length(size.height(), Fixed)); -} - -void RenderThemeMacShared::setFontFromControlSize(StyleResolver*, RenderStyle* style, NSControlSize controlSize) const -{ - FontDescription fontDescription; - fontDescription.setIsAbsoluteSize(true); - fontDescription.setGenericFamily(FontDescription::SerifFamily); - - NSFont* font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:controlSize]]; - fontDescription.firstFamily().setFamily([font familyName]); - fontDescription.setComputedSize([font pointSize] * style->effectiveZoom()); - fontDescription.setSpecifiedSize([font pointSize] * style->effectiveZoom()); - - // Reset line height - style->setLineHeight(RenderStyle::initialLineHeight()); - - if (style->setFontDescription(fontDescription)) - style->font().update(0); -} - -NSControlSize RenderThemeMacShared::controlSizeForSystemFont(RenderStyle* style) const -{ - int fontSize = style->fontSize(); - if (fontSize >= [NSFont systemFontSizeForControlSize:NSRegularControlSize]) - return NSRegularControlSize; - if (fontSize >= [NSFont systemFontSizeForControlSize:NSSmallControlSize]) - return NSSmallControlSize; - return NSMiniControlSize; -} - -bool RenderThemeMacShared::paintTextField(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) -{ - LocalCurrentGraphicsContext localContext(paintInfo.context); - -#if __MAC_OS_X_VERSION_MIN_REQUIRED <= 1070 - bool useNSTextFieldCell = o->style()->hasAppearance() - && o->style()->visitedDependentColor(CSSPropertyBackgroundColor) == Color::white - && !o->style()->hasBackgroundImage(); - - // We do not use NSTextFieldCell to draw styled text fields on Lion and SnowLeopard because - // there are a number of bugs on those platforms that require NSTextFieldCell to be in charge - // of painting its own background. We need WebCore to paint styled backgrounds, so we'll use - // this WebCoreSystemInterface function instead. - if (!useNSTextFieldCell) { - wkDrawBezeledTextFieldCell(r, isEnabled(o) && !isReadOnlyControl(o)); - return false; - } -#endif - - NSTextFieldCell *textField = this->textField(); - - GraphicsContextStateSaver stateSaver(*paintInfo.context); - - [textField setEnabled:(isEnabled(o) && !isReadOnlyControl(o))]; - [textField drawWithFrame:NSRect(r) inView:documentViewFor(o)]; - - [textField setControlView:nil]; - - return false; -} - -void RenderThemeMacShared::adjustTextFieldStyle(StyleResolver*, RenderStyle*, Element*) const -{ -} - -bool RenderThemeMacShared::paintCapsLockIndicator(RenderObject*, const PaintInfo& paintInfo, const IntRect& r) -{ - if (paintInfo.context->paintingDisabled()) - return true; - - LocalCurrentGraphicsContext localContext(paintInfo.context); - wkDrawCapsLockIndicator(localContext.cgContext(), r); - - return false; -} - -bool RenderThemeMacShared::paintTextArea(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) -{ - LocalCurrentGraphicsContext localContext(paintInfo.context); - wkDrawBezeledTextArea(r, isEnabled(o) && !isReadOnlyControl(o)); - return false; -} - -void RenderThemeMacShared::adjustTextAreaStyle(StyleResolver*, RenderStyle*, Element*) const -{ -} - -const int* RenderThemeMacShared::popupButtonMargins() const -{ - static const int margins[3][4] = - { - { 0, 3, 1, 3 }, - { 0, 3, 2, 3 }, - { 0, 1, 0, 1 } - }; - return margins[[popupButton() controlSize]]; -} - -const IntSize* RenderThemeMacShared::popupButtonSizes() const -{ - static const IntSize sizes[3] = { IntSize(0, 21), IntSize(0, 18), IntSize(0, 15) }; - return sizes; -} - -const int* RenderThemeMacShared::popupButtonPadding(NSControlSize size) const -{ - static const int padding[3][4] = - { - { 2, 26, 3, 8 }, - { 2, 23, 3, 8 }, - { 2, 22, 3, 10 } - }; - return padding[size]; -} - -bool RenderThemeMacShared::paintMenuList(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) -{ - LocalCurrentGraphicsContext localContext(paintInfo.context); - setPopupButtonCellState(o, r); - - NSPopUpButtonCell* popupButton = this->popupButton(); - - float zoomLevel = o->style()->effectiveZoom(); - IntSize size = popupButtonSizes()[[popupButton controlSize]]; - size.setHeight(size.height() * zoomLevel); - size.setWidth(r.width()); - - // Now inflate it to account for the shadow. - IntRect inflatedRect = r; - if (r.width() >= minimumMenuListSize(o->style())) - inflatedRect = inflateRect(inflatedRect, size, popupButtonMargins(), zoomLevel); - - GraphicsContextStateSaver stateSaver(*paintInfo.context); - - // On Leopard, the cell will draw outside of the given rect, so we have to clip to the rect - paintInfo.context->clip(inflatedRect); - - if (zoomLevel != 1.0f) { - inflatedRect.setWidth(inflatedRect.width() / zoomLevel); - inflatedRect.setHeight(inflatedRect.height() / zoomLevel); - paintInfo.context->translate(inflatedRect.x(), inflatedRect.y()); - paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel)); - paintInfo.context->translate(-inflatedRect.x(), -inflatedRect.y()); - } - - NSView *view = documentViewFor(o); - [popupButton drawWithFrame:inflatedRect inView:view]; -#if !BUTTON_CELL_DRAW_WITH_FRAME_DRAWS_FOCUS_RING - if (isFocused(o) && o->style()->outlineStyleIsAuto()) - [popupButton _web_drawFocusRingWithFrame:inflatedRect inView:view]; -#endif - [popupButton setControlView:nil]; - - return false; -} - -#if ENABLE(METER_ELEMENT) - -IntSize RenderThemeMacShared::meterSizeForBounds(const RenderMeter* renderMeter, const IntRect& bounds) const -{ - if (NoControlPart == renderMeter->style()->appearance()) - return bounds.size(); - - NSLevelIndicatorCell* cell = levelIndicatorFor(renderMeter); - // Makes enough room for cell's intrinsic size. - NSSize cellSize = [cell cellSizeForBounds:IntRect(IntPoint(), bounds.size())]; - return IntSize(bounds.width() < cellSize.width ? cellSize.width : bounds.width(), - bounds.height() < cellSize.height ? cellSize.height : bounds.height()); -} - -bool RenderThemeMacShared::paintMeter(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) -{ - if (!renderObject->isMeter()) - return true; - - LocalCurrentGraphicsContext localContext(paintInfo.context); - - NSLevelIndicatorCell* cell = levelIndicatorFor(toRenderMeter(renderObject)); - GraphicsContextStateSaver stateSaver(*paintInfo.context); - - [cell drawWithFrame:rect inView:documentViewFor(renderObject)]; - [cell setControlView:nil]; - return false; -} - -bool RenderThemeMacShared::supportsMeter(ControlPart part) const -{ - switch (part) { - case RelevancyLevelIndicatorPart: - case DiscreteCapacityLevelIndicatorPart: - case RatingLevelIndicatorPart: - case MeterPart: - case ContinuousCapacityLevelIndicatorPart: - return true; - default: - return false; - } -} - -NSLevelIndicatorStyle RenderThemeMacShared::levelIndicatorStyleFor(ControlPart part) const -{ - switch (part) { - case RelevancyLevelIndicatorPart: - return NSRelevancyLevelIndicatorStyle; - case DiscreteCapacityLevelIndicatorPart: - return NSDiscreteCapacityLevelIndicatorStyle; - case RatingLevelIndicatorPart: - return NSRatingLevelIndicatorStyle; - case MeterPart: - case ContinuousCapacityLevelIndicatorPart: - default: - return NSContinuousCapacityLevelIndicatorStyle; - } - -} - -NSLevelIndicatorCell* RenderThemeMacShared::levelIndicatorFor(const RenderMeter* renderMeter) const -{ - RenderStyle* style = renderMeter->style(); - ASSERT(style->appearance() != NoControlPart); - - if (!m_levelIndicator) - m_levelIndicator.adoptNS([[NSLevelIndicatorCell alloc] initWithLevelIndicatorStyle:NSContinuousCapacityLevelIndicatorStyle]); - NSLevelIndicatorCell* cell = m_levelIndicator.get(); - - HTMLMeterElement* element = renderMeter->meterElement(); - double value = element->value(); - - // Because NSLevelIndicatorCell does not support optimum-in-the-middle type coloring, - // we explicitly control the color instead giving low and high value to NSLevelIndicatorCell as is. - switch (element->gaugeRegion()) { - case HTMLMeterElement::GaugeRegionOptimum: - // Make meter the green - [cell setWarningValue:value + 1]; - [cell setCriticalValue:value + 2]; - break; - case HTMLMeterElement::GaugeRegionSuboptimal: - // Make the meter yellow - [cell setWarningValue:value - 1]; - [cell setCriticalValue:value + 1]; - break; - case HTMLMeterElement::GaugeRegionEvenLessGood: - // Make the meter red - [cell setWarningValue:value - 2]; - [cell setCriticalValue:value - 1]; - break; - } - - [cell setLevelIndicatorStyle:levelIndicatorStyleFor(style->appearance())]; - [cell setBaseWritingDirection:style->isLeftToRightDirection() ? NSWritingDirectionLeftToRight : NSWritingDirectionRightToLeft]; - [cell setMinValue:element->min()]; - [cell setMaxValue:element->max()]; - RetainPtr<NSNumber> valueObject = [NSNumber numberWithDouble:value]; - [cell setObjectValue:valueObject.get()]; - - return cell; -} - -#endif - -#if ENABLE(PROGRESS_ELEMENT) -const IntSize* RenderThemeMacShared::progressBarSizes() const -{ - static const IntSize sizes[3] = { IntSize(0, 20), IntSize(0, 12), IntSize(0, 12) }; - return sizes; -} - -const int* RenderThemeMacShared::progressBarMargins(NSControlSize controlSize) const -{ - static const int margins[3][4] = - { - { 0, 0, 1, 0 }, - { 0, 0, 1, 0 }, - { 0, 0, 1, 0 }, - }; - return margins[controlSize]; -} - -int RenderThemeMacShared::minimumProgressBarHeight(RenderStyle* style) const -{ - return sizeForSystemFont(style, progressBarSizes()).height(); -} - -double RenderThemeMacShared::animationRepeatIntervalForProgressBar(RenderProgress*) const -{ - return progressAnimationFrameRate; -} - -double RenderThemeMacShared::animationDurationForProgressBar(RenderProgress*) const -{ - return progressAnimationNumFrames * progressAnimationFrameRate; -} - -void RenderThemeMacShared::adjustProgressBarStyle(StyleResolver*, RenderStyle*, Element*) const -{ -} - -bool RenderThemeMacShared::paintProgressBar(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) -{ - if (!renderObject->isProgress()) - return true; - - float zoomLevel = renderObject->style()->effectiveZoom(); - int controlSize = controlSizeForFont(renderObject->style()); - IntSize size = progressBarSizes()[controlSize]; - size.setHeight(size.height() * zoomLevel); - size.setWidth(rect.width()); - - // Now inflate it to account for the shadow. - IntRect inflatedRect = rect; - if (rect.height() <= minimumProgressBarHeight(renderObject->style())) - inflatedRect = inflateRect(inflatedRect, size, progressBarMargins(controlSize), zoomLevel); - - RenderProgress* renderProgress = toRenderProgress(renderObject); - HIThemeTrackDrawInfo trackInfo; - trackInfo.version = 0; - if (controlSize == NSRegularControlSize) - trackInfo.kind = renderProgress->position() < 0 ? kThemeLargeIndeterminateBar : kThemeLargeProgressBar; - else - trackInfo.kind = renderProgress->position() < 0 ? kThemeMediumIndeterminateBar : kThemeMediumProgressBar; - - trackInfo.bounds = IntRect(IntPoint(), inflatedRect.size()); - trackInfo.min = 0; - trackInfo.max = numeric_limits<SInt32>::max(); - trackInfo.value = lround(renderProgress->position() * nextafter(trackInfo.max, 0)); - trackInfo.trackInfo.progress.phase = lround(renderProgress->animationProgress() * nextafter(progressAnimationNumFrames, 0)); - trackInfo.attributes = kThemeTrackHorizontal; - trackInfo.enableState = isActive(renderObject) ? kThemeTrackActive : kThemeTrackInactive; - trackInfo.reserved = 0; - trackInfo.filler1 = 0; - - OwnPtr<ImageBuffer> imageBuffer = ImageBuffer::create(inflatedRect.size(), 1); - if (!imageBuffer) - return true; - - ContextContainer cgContextContainer(imageBuffer->context()); - CGContextRef cgContext = cgContextContainer.context(); - HIThemeDrawTrack(&trackInfo, 0, cgContext, kHIThemeOrientationNormal); - - GraphicsContextStateSaver stateSaver(*paintInfo.context); - - if (!renderProgress->style()->isLeftToRightDirection()) { - paintInfo.context->translate(2 * inflatedRect.x() + inflatedRect.width(), 0); - paintInfo.context->scale(FloatSize(-1, 1)); - } - - paintInfo.context->drawImageBuffer(imageBuffer.get(), ColorSpaceDeviceRGB, inflatedRect.location()); - return false; -} -#endif - -const float baseFontSize = 11.0f; -const float baseArrowHeight = 4.0f; -const float baseArrowWidth = 5.0f; -const float baseSpaceBetweenArrows = 2.0f; -const int arrowPaddingLeft = 6; -const int arrowPaddingRight = 6; -const int paddingBeforeSeparator = 4; -const int baseBorderRadius = 5; -const int styledPopupPaddingLeft = 8; -const int styledPopupPaddingTop = 1; -const int styledPopupPaddingBottom = 2; - -static void TopGradientInterpolate(void*, const CGFloat* inData, CGFloat* outData) -{ - static float dark[4] = { 1.0f, 1.0f, 1.0f, 0.4f }; - static float light[4] = { 1.0f, 1.0f, 1.0f, 0.15f }; - float a = inData[0]; - int i = 0; - for (i = 0; i < 4; i++) - outData[i] = (1.0f - a) * dark[i] + a * light[i]; -} - -static void BottomGradientInterpolate(void*, const CGFloat* inData, CGFloat* outData) -{ - static float dark[4] = { 1.0f, 1.0f, 1.0f, 0.0f }; - static float light[4] = { 1.0f, 1.0f, 1.0f, 0.3f }; - float a = inData[0]; - int i = 0; - for (i = 0; i < 4; i++) - outData[i] = (1.0f - a) * dark[i] + a * light[i]; -} - -static void MainGradientInterpolate(void*, const CGFloat* inData, CGFloat* outData) -{ - static float dark[4] = { 0.0f, 0.0f, 0.0f, 0.15f }; - static float light[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; - float a = inData[0]; - int i = 0; - for (i = 0; i < 4; i++) - outData[i] = (1.0f - a) * dark[i] + a * light[i]; -} - -static void TrackGradientInterpolate(void*, const CGFloat* inData, CGFloat* outData) -{ - static float dark[4] = { 0.0f, 0.0f, 0.0f, 0.678f }; - static float light[4] = { 0.0f, 0.0f, 0.0f, 0.13f }; - float a = inData[0]; - int i = 0; - for (i = 0; i < 4; i++) - outData[i] = (1.0f - a) * dark[i] + a * light[i]; -} - -void RenderThemeMacShared::paintMenuListButtonGradients(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) -{ - if (r.isEmpty()) - return; - - ContextContainer cgContextContainer(paintInfo.context); - CGContextRef context = cgContextContainer.context(); - - GraphicsContextStateSaver stateSaver(*paintInfo.context); - - RoundedRect border = o->style()->getRoundedBorderFor(r, o->view()); - int radius = border.radii().topLeft().width(); - - CGColorSpaceRef cspace = deviceRGBColorSpaceRef(); - - FloatRect topGradient(r.x(), r.y(), r.width(), r.height() / 2.0f); - struct CGFunctionCallbacks topCallbacks = { 0, TopGradientInterpolate, NULL }; - RetainPtr<CGFunctionRef> topFunction(AdoptCF, CGFunctionCreate(NULL, 1, NULL, 4, NULL, &topCallbacks)); - RetainPtr<CGShadingRef> topShading(AdoptCF, CGShadingCreateAxial(cspace, CGPointMake(topGradient.x(), topGradient.y()), CGPointMake(topGradient.x(), topGradient.maxY()), topFunction.get(), false, false)); - - FloatRect bottomGradient(r.x() + radius, r.y() + r.height() / 2.0f, r.width() - 2.0f * radius, r.height() / 2.0f); - struct CGFunctionCallbacks bottomCallbacks = { 0, BottomGradientInterpolate, NULL }; - RetainPtr<CGFunctionRef> bottomFunction(AdoptCF, CGFunctionCreate(NULL, 1, NULL, 4, NULL, &bottomCallbacks)); - RetainPtr<CGShadingRef> bottomShading(AdoptCF, CGShadingCreateAxial(cspace, CGPointMake(bottomGradient.x(), bottomGradient.y()), CGPointMake(bottomGradient.x(), bottomGradient.maxY()), bottomFunction.get(), false, false)); - - struct CGFunctionCallbacks mainCallbacks = { 0, MainGradientInterpolate, NULL }; - RetainPtr<CGFunctionRef> mainFunction(AdoptCF, CGFunctionCreate(NULL, 1, NULL, 4, NULL, &mainCallbacks)); - RetainPtr<CGShadingRef> mainShading(AdoptCF, CGShadingCreateAxial(cspace, CGPointMake(r.x(), r.y()), CGPointMake(r.x(), r.maxY()), mainFunction.get(), false, false)); - - RetainPtr<CGShadingRef> leftShading(AdoptCF, CGShadingCreateAxial(cspace, CGPointMake(r.x(), r.y()), CGPointMake(r.x() + radius, r.y()), mainFunction.get(), false, false)); - - RetainPtr<CGShadingRef> rightShading(AdoptCF, CGShadingCreateAxial(cspace, CGPointMake(r.maxX(), r.y()), CGPointMake(r.maxX() - radius, r.y()), mainFunction.get(), false, false)); - - { - GraphicsContextStateSaver stateSaver(*paintInfo.context); - CGContextClipToRect(context, r); - paintInfo.context->addRoundedRectClip(border); - context = cgContextContainer.context(); - CGContextDrawShading(context, mainShading.get()); - } - - { - GraphicsContextStateSaver stateSaver(*paintInfo.context); - CGContextClipToRect(context, topGradient); - paintInfo.context->addRoundedRectClip(RoundedRect(enclosingIntRect(topGradient), border.radii().topLeft(), border.radii().topRight(), IntSize(), IntSize())); - context = cgContextContainer.context(); - CGContextDrawShading(context, topShading.get()); - } - - if (!bottomGradient.isEmpty()) { - GraphicsContextStateSaver stateSaver(*paintInfo.context); - CGContextClipToRect(context, bottomGradient); - paintInfo.context->addRoundedRectClip(RoundedRect(enclosingIntRect(bottomGradient), IntSize(), IntSize(), border.radii().bottomLeft(), border.radii().bottomRight())); - context = cgContextContainer.context(); - CGContextDrawShading(context, bottomShading.get()); - } - - { - GraphicsContextStateSaver stateSaver(*paintInfo.context); - CGContextClipToRect(context, r); - paintInfo.context->addRoundedRectClip(border); - context = cgContextContainer.context(); - CGContextDrawShading(context, leftShading.get()); - CGContextDrawShading(context, rightShading.get()); - } -} - -bool RenderThemeMacShared::paintMenuListButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) -{ - IntRect bounds = IntRect(r.x() + o->style()->borderLeftWidth(), - r.y() + o->style()->borderTopWidth(), - r.width() - o->style()->borderLeftWidth() - o->style()->borderRightWidth(), - r.height() - o->style()->borderTopWidth() - o->style()->borderBottomWidth()); - // Draw the gradients to give the styled popup menu a button appearance - paintMenuListButtonGradients(o, paintInfo, bounds); - - // Since we actually know the size of the control here, we restrict the font scale to make sure the arrows will fit vertically in the bounds - float fontScale = min(o->style()->fontSize() / baseFontSize, bounds.height() / (baseArrowHeight * 2 + baseSpaceBetweenArrows)); - float centerY = bounds.y() + bounds.height() / 2.0f; - float arrowHeight = baseArrowHeight * fontScale; - float arrowWidth = baseArrowWidth * fontScale; - float leftEdge = bounds.maxX() - arrowPaddingRight * o->style()->effectiveZoom() - arrowWidth; - float spaceBetweenArrows = baseSpaceBetweenArrows * fontScale; - - if (bounds.width() < arrowWidth + arrowPaddingLeft * o->style()->effectiveZoom()) - return false; - - GraphicsContextStateSaver stateSaver(*paintInfo.context); - - paintInfo.context->setFillColor(o->style()->visitedDependentColor(CSSPropertyColor), o->style()->colorSpace()); - paintInfo.context->setStrokeStyle(NoStroke); - - FloatPoint arrow1[3]; - arrow1[0] = FloatPoint(leftEdge, centerY - spaceBetweenArrows / 2.0f); - arrow1[1] = FloatPoint(leftEdge + arrowWidth, centerY - spaceBetweenArrows / 2.0f); - arrow1[2] = FloatPoint(leftEdge + arrowWidth / 2.0f, centerY - spaceBetweenArrows / 2.0f - arrowHeight); - - // Draw the top arrow - paintInfo.context->drawConvexPolygon(3, arrow1, true); - - FloatPoint arrow2[3]; - arrow2[0] = FloatPoint(leftEdge, centerY + spaceBetweenArrows / 2.0f); - arrow2[1] = FloatPoint(leftEdge + arrowWidth, centerY + spaceBetweenArrows / 2.0f); - arrow2[2] = FloatPoint(leftEdge + arrowWidth / 2.0f, centerY + spaceBetweenArrows / 2.0f + arrowHeight); - - // Draw the bottom arrow - paintInfo.context->drawConvexPolygon(3, arrow2, true); - - Color leftSeparatorColor(0, 0, 0, 40); - Color rightSeparatorColor(255, 255, 255, 40); - - // FIXME: Should the separator thickness and space be scaled up by fontScale? - int separatorSpace = 2; // Deliberately ignores zoom since it looks nicer if it stays thin. - int leftEdgeOfSeparator = static_cast<int>(leftEdge - arrowPaddingLeft * o->style()->effectiveZoom()); // FIXME: Round? - - // Draw the separator to the left of the arrows - paintInfo.context->setStrokeThickness(1.0f); // Deliberately ignores zoom since it looks nicer if it stays thin. - paintInfo.context->setStrokeStyle(SolidStroke); - paintInfo.context->setStrokeColor(leftSeparatorColor, ColorSpaceDeviceRGB); - paintInfo.context->drawLine(IntPoint(leftEdgeOfSeparator, bounds.y()), - IntPoint(leftEdgeOfSeparator, bounds.maxY())); - - paintInfo.context->setStrokeColor(rightSeparatorColor, ColorSpaceDeviceRGB); - paintInfo.context->drawLine(IntPoint(leftEdgeOfSeparator + separatorSpace, bounds.y()), - IntPoint(leftEdgeOfSeparator + separatorSpace, bounds.maxY())); - return false; -} - -static const IntSize* menuListButtonSizes() -{ - static const IntSize sizes[3] = { IntSize(0, 21), IntSize(0, 18), IntSize(0, 15) }; - return sizes; -} - -void RenderThemeMacShared::adjustMenuListStyle(StyleResolver* styleResolver, RenderStyle* style, Element* e) const -{ - NSControlSize controlSize = controlSizeForFont(style); - - style->resetBorder(); - style->resetPadding(); - - // Height is locked to auto. - style->setHeight(Length(Auto)); - - // White-space is locked to pre - style->setWhiteSpace(PRE); - - // Set the foreground color to black or gray when we have the aqua look. - // Cast to RGB32 is to work around a compiler bug. - style->setColor(e && e->isEnabledFormControl() ? static_cast<RGBA32>(Color::black) : Color::darkGray); - - // Set the button's vertical size. - setSizeFromFont(style, menuListButtonSizes()); - - // Our font is locked to the appropriate system font size for the control. To clarify, we first use the CSS-specified font to figure out - // a reasonable control size, but once that control size is determined, we throw that font away and use the appropriate - // system font for the control size instead. - setFontFromControlSize(styleResolver, style, controlSize); - - style->setBoxShadow(nullptr); -} - -int RenderThemeMacShared::popupInternalPaddingLeft(RenderStyle* style) const -{ - if (style->appearance() == MenulistPart) - return popupButtonPadding(controlSizeForFont(style))[leftPadding] * style->effectiveZoom(); - if (style->appearance() == MenulistButtonPart) - return styledPopupPaddingLeft * style->effectiveZoom(); - return 0; -} - -int RenderThemeMacShared::popupInternalPaddingRight(RenderStyle* style) const -{ - if (style->appearance() == MenulistPart) - return popupButtonPadding(controlSizeForFont(style))[rightPadding] * style->effectiveZoom(); - if (style->appearance() == MenulistButtonPart) { - float fontScale = style->fontSize() / baseFontSize; - float arrowWidth = baseArrowWidth * fontScale; - return static_cast<int>(ceilf(arrowWidth + (arrowPaddingLeft + arrowPaddingRight + paddingBeforeSeparator) * style->effectiveZoom())); - } - return 0; -} - -int RenderThemeMacShared::popupInternalPaddingTop(RenderStyle* style) const -{ - if (style->appearance() == MenulistPart) - return popupButtonPadding(controlSizeForFont(style))[topPadding] * style->effectiveZoom(); - if (style->appearance() == MenulistButtonPart) - return styledPopupPaddingTop * style->effectiveZoom(); - return 0; -} - -int RenderThemeMacShared::popupInternalPaddingBottom(RenderStyle* style) const -{ - if (style->appearance() == MenulistPart) - return popupButtonPadding(controlSizeForFont(style))[bottomPadding] * style->effectiveZoom(); - if (style->appearance() == MenulistButtonPart) - return styledPopupPaddingBottom * style->effectiveZoom(); - return 0; -} - -void RenderThemeMacShared::adjustMenuListButtonStyle(StyleResolver*, RenderStyle* style, Element*) const -{ - float fontScale = style->fontSize() / baseFontSize; - - style->resetPadding(); - style->setBorderRadius(IntSize(int(baseBorderRadius + fontScale - 1), int(baseBorderRadius + fontScale - 1))); // FIXME: Round up? - - const int minHeight = 15; - style->setMinHeight(Length(minHeight, Fixed)); - - style->setLineHeight(RenderStyle::initialLineHeight()); -} - -void RenderThemeMacShared::setPopupButtonCellState(const RenderObject* o, const IntRect& r) -{ - NSPopUpButtonCell* popupButton = this->popupButton(); - - // Set the control size based off the rectangle we're painting into. - setControlSize(popupButton, popupButtonSizes(), r.size(), o->style()->effectiveZoom()); - - // Update the various states we respond to. - updateActiveState(popupButton, o); - updateCheckedState(popupButton, o); - updateEnabledState(popupButton, o); - updatePressedState(popupButton, o); -#if BUTTON_CELL_DRAW_WITH_FRAME_DRAWS_FOCUS_RING - updateFocusedState(popupButton, o); -#endif -} - -const IntSize* RenderThemeMacShared::menuListSizes() const -{ - static const IntSize sizes[3] = { IntSize(9, 0), IntSize(5, 0), IntSize(0, 0) }; - return sizes; -} - -int RenderThemeMacShared::minimumMenuListSize(RenderStyle* style) const -{ - return sizeForSystemFont(style, menuListSizes()).width(); -} - -const int trackWidth = 5; -const int trackRadius = 2; - -void RenderThemeMacShared::adjustSliderTrackStyle(StyleResolver*, RenderStyle* style, Element*) const -{ - style->setBoxShadow(nullptr); -} - -bool RenderThemeMacShared::paintSliderTrack(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) -{ - IntRect bounds = r; - float zoomLevel = o->style()->effectiveZoom(); - float zoomedTrackWidth = trackWidth * zoomLevel; - - if (o->style()->appearance() == SliderHorizontalPart || o->style()->appearance() == MediaSliderPart) { - bounds.setHeight(zoomedTrackWidth); - bounds.setY(r.y() + r.height() / 2 - zoomedTrackWidth / 2); - } else if (o->style()->appearance() == SliderVerticalPart) { - bounds.setWidth(zoomedTrackWidth); - bounds.setX(r.x() + r.width() / 2 - zoomedTrackWidth / 2); - } - - LocalCurrentGraphicsContext localContext(paintInfo.context); - CGContextRef context = localContext.cgContext(); - CGColorSpaceRef cspace = deviceRGBColorSpaceRef(); - -#if ENABLE(DATALIST_ELEMENT) - paintSliderTicks(o, paintInfo, r); -#endif - - GraphicsContextStateSaver stateSaver(*paintInfo.context); - CGContextClipToRect(context, bounds); - - struct CGFunctionCallbacks mainCallbacks = { 0, TrackGradientInterpolate, NULL }; - RetainPtr<CGFunctionRef> mainFunction(AdoptCF, CGFunctionCreate(NULL, 1, NULL, 4, NULL, &mainCallbacks)); - RetainPtr<CGShadingRef> mainShading; - if (o->style()->appearance() == SliderVerticalPart) - mainShading.adoptCF(CGShadingCreateAxial(cspace, CGPointMake(bounds.x(), bounds.maxY()), CGPointMake(bounds.maxX(), bounds.maxY()), mainFunction.get(), false, false)); - else - mainShading.adoptCF(CGShadingCreateAxial(cspace, CGPointMake(bounds.x(), bounds.y()), CGPointMake(bounds.x(), bounds.maxY()), mainFunction.get(), false, false)); - - IntSize radius(trackRadius, trackRadius); - paintInfo.context->addRoundedRectClip(RoundedRect(bounds, radius, radius, radius, radius)); - context = localContext.cgContext(); - CGContextDrawShading(context, mainShading.get()); - - return false; -} - -void RenderThemeMacShared::adjustSliderThumbStyle(StyleResolver* styleResolver, RenderStyle* style, Element* element) const -{ - RenderTheme::adjustSliderThumbStyle(styleResolver, style, element); - style->setBoxShadow(nullptr); -} - -const float verticalSliderHeightPadding = 0.1f; - -bool RenderThemeMacShared::paintSliderThumb(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) -{ - NSSliderCell* sliderThumbCell = o->style()->appearance() == SliderThumbVerticalPart - ? sliderThumbVertical() - : sliderThumbHorizontal(); - - LocalCurrentGraphicsContext localContext(paintInfo.context); - - // Update the various states we respond to. - updateActiveState(sliderThumbCell, o); - updateEnabledState(sliderThumbCell, o); - updateFocusedState(sliderThumbCell, (o->node() && o->node()->focusDelegate()->renderer()) ? o->node()->focusDelegate()->renderer() : o); - - // Update the pressed state using the NSCell tracking methods, since that's how NSSliderCell keeps track of it. - bool oldPressed; - if (o->style()->appearance() == SliderThumbVerticalPart) - oldPressed = m_isSliderThumbVerticalPressed; - else - oldPressed = m_isSliderThumbHorizontalPressed; - - bool pressed = isPressed(o); - - if (o->style()->appearance() == SliderThumbVerticalPart) - m_isSliderThumbVerticalPressed = pressed; - else - m_isSliderThumbHorizontalPressed = pressed; - - if (pressed != oldPressed) { - if (pressed) - [sliderThumbCell startTrackingAt:NSPoint() inView:nil]; - else - [sliderThumbCell stopTracking:NSPoint() at:NSPoint() inView:nil mouseIsUp:YES]; - } - - FloatRect bounds = r; - // Make the height of the vertical slider slightly larger so NSSliderCell will draw a vertical slider. - if (o->style()->appearance() == SliderThumbVerticalPart) - bounds.setHeight(bounds.height() + verticalSliderHeightPadding * o->style()->effectiveZoom()); - - GraphicsContextStateSaver stateSaver(*paintInfo.context); - float zoomLevel = o->style()->effectiveZoom(); - - FloatRect unzoomedRect = bounds; - if (zoomLevel != 1.0f) { - unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel); - unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel); - paintInfo.context->translate(unzoomedRect.x(), unzoomedRect.y()); - paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel)); - paintInfo.context->translate(-unzoomedRect.x(), -unzoomedRect.y()); - } - -#if PLATFORM(CHROMIUM) - paintInfo.context->translate(0, unzoomedRect.y()); - paintInfo.context->scale(FloatSize(1, -1)); - paintInfo.context->translate(0, -(unzoomedRect.y() + unzoomedRect.height())); -#endif - - [sliderThumbCell drawInteriorWithFrame:unzoomedRect inView:documentViewFor(o)]; - [sliderThumbCell setControlView:nil]; - - return false; -} - -bool RenderThemeMacShared::paintSearchField(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) -{ - LocalCurrentGraphicsContext localContext(paintInfo.context); - NSSearchFieldCell* search = this->search(); - - setSearchCellState(o, r); - - GraphicsContextStateSaver stateSaver(*paintInfo.context); - - float zoomLevel = o->style()->effectiveZoom(); - - IntRect unzoomedRect = r; - - if (zoomLevel != 1.0f) { - unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel); - unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel); - paintInfo.context->translate(unzoomedRect.x(), unzoomedRect.y()); - paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel)); - paintInfo.context->translate(-unzoomedRect.x(), -unzoomedRect.y()); - } - - // Set the search button to nil before drawing. Then reset it so we can draw it later. - [search setSearchButtonCell:nil]; - - [search drawWithFrame:NSRect(unzoomedRect) inView:documentViewFor(o)]; - - [search setControlView:nil]; - [search resetSearchButtonCell]; - - return false; -} - -void RenderThemeMacShared::setSearchCellState(RenderObject* o, const IntRect&) -{ - NSSearchFieldCell* search = this->search(); - - [search setControlSize:controlSizeForFont(o->style())]; - - // Update the various states we respond to. - updateActiveState(search, o); - updateEnabledState(search, o); - updateFocusedState(search, o); -} - -const IntSize* RenderThemeMacShared::searchFieldSizes() const -{ - static const IntSize sizes[3] = { IntSize(0, 22), IntSize(0, 19), IntSize(0, 17) }; - return sizes; -} - -void RenderThemeMacShared::setSearchFieldSize(RenderStyle* style) const -{ - // If the width and height are both specified, then we have nothing to do. - if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto()) - return; - - // Use the font size to determine the intrinsic width of the control. - setSizeFromFont(style, searchFieldSizes()); -} - -void RenderThemeMacShared::adjustSearchFieldStyle(StyleResolver* styleResolver, RenderStyle* style, Element*) const -{ - // Override border. - style->resetBorder(); - const short borderWidth = 2 * style->effectiveZoom(); - style->setBorderLeftWidth(borderWidth); - style->setBorderLeftStyle(INSET); - style->setBorderRightWidth(borderWidth); - style->setBorderRightStyle(INSET); - style->setBorderBottomWidth(borderWidth); - style->setBorderBottomStyle(INSET); - style->setBorderTopWidth(borderWidth); - style->setBorderTopStyle(INSET); - - // Override height. - style->setHeight(Length(Auto)); - setSearchFieldSize(style); - - // Override padding size to match AppKit text positioning. - const int padding = 1 * style->effectiveZoom(); - style->setPaddingLeft(Length(padding, Fixed)); - style->setPaddingRight(Length(padding, Fixed)); - style->setPaddingTop(Length(padding, Fixed)); - style->setPaddingBottom(Length(padding, Fixed)); - - NSControlSize controlSize = controlSizeForFont(style); - setFontFromControlSize(styleResolver, style, controlSize); - - style->setBoxShadow(nullptr); -} - -bool RenderThemeMacShared::paintSearchFieldCancelButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) -{ - Element* input = o->node()->shadowHost(); - if (!input) - input = toElement(o->node()); - - if (!input->renderer()->isBox()) - return false; - - LocalCurrentGraphicsContext localContext(paintInfo.context); - setSearchCellState(input->renderer(), r); - - NSSearchFieldCell* search = this->search(); - - if (input->isEnabledFormControl() && (input->isTextFormControl() && !static_cast<HTMLTextFormControlElement*>(input)->readOnly())) { - updateActiveState([search cancelButtonCell], o); - updatePressedState([search cancelButtonCell], o); - } - else if ([[search cancelButtonCell] isHighlighted]) - [[search cancelButtonCell] setHighlighted:NO]; - - GraphicsContextStateSaver stateSaver(*paintInfo.context); - - float zoomLevel = o->style()->effectiveZoom(); - - FloatRect localBounds = [search cancelButtonRectForBounds:NSRect(input->renderBox()->pixelSnappedBorderBoxRect())]; - -#if ENABLE(INPUT_SPEECH) - // Take care of cases where the cancel button was not aligned with the right border of the input element (for e.g. - // when speech input is enabled for the input element. - IntRect absBoundingBox = input->renderer()->absoluteBoundingBoxRect(); - int absRight = absBoundingBox.x() + absBoundingBox.width() - input->renderBox()->paddingRight() - input->renderBox()->borderRight(); - int spaceToRightOfCancelButton = absRight - (r.x() + r.width()); - localBounds.setX(localBounds.x() - spaceToRightOfCancelButton); -#endif - - localBounds = convertToPaintingRect(input->renderer(), o, localBounds, r); - - FloatRect unzoomedRect(localBounds); - if (zoomLevel != 1.0f) { - unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel); - unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel); - paintInfo.context->translate(unzoomedRect.x(), unzoomedRect.y()); - paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel)); - paintInfo.context->translate(-unzoomedRect.x(), -unzoomedRect.y()); - } - - [[search cancelButtonCell] drawWithFrame:unzoomedRect inView:documentViewFor(o)]; - [[search cancelButtonCell] setControlView:nil]; - return false; -} - -const IntSize* RenderThemeMacShared::cancelButtonSizes() const -{ - static const IntSize sizes[3] = { IntSize(16, 13), IntSize(13, 11), IntSize(13, 9) }; - return sizes; -} - -void RenderThemeMacShared::adjustSearchFieldCancelButtonStyle(StyleResolver*, RenderStyle* style, Element*) const -{ - IntSize size = sizeForSystemFont(style, cancelButtonSizes()); - style->setWidth(Length(size.width(), Fixed)); - style->setHeight(Length(size.height(), Fixed)); - style->setBoxShadow(nullptr); -} - -const IntSize* RenderThemeMacShared::resultsButtonSizes() const -{ - static const IntSize sizes[3] = { IntSize(19, 13), IntSize(17, 11), IntSize(17, 9) }; - return sizes; -} - -const int emptyResultsOffset = 9; -void RenderThemeMacShared::adjustSearchFieldDecorationStyle(StyleResolver*, RenderStyle* style, Element*) const -{ - IntSize size = sizeForSystemFont(style, resultsButtonSizes()); - style->setWidth(Length(size.width() - emptyResultsOffset, Fixed)); - style->setHeight(Length(size.height(), Fixed)); - style->setBoxShadow(nullptr); -} - -bool RenderThemeMacShared::paintSearchFieldDecoration(RenderObject*, const PaintInfo&, const IntRect&) -{ - return false; -} - -void RenderThemeMacShared::adjustSearchFieldResultsDecorationStyle(StyleResolver*, RenderStyle* style, Element*) const -{ - IntSize size = sizeForSystemFont(style, resultsButtonSizes()); - style->setWidth(Length(size.width(), Fixed)); - style->setHeight(Length(size.height(), Fixed)); - style->setBoxShadow(nullptr); -} - -bool RenderThemeMacShared::paintSearchFieldResultsDecoration(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) -{ - Node* input = o->node()->shadowHost(); - if (!input) - input = o->node(); - if (!input->renderer()->isBox()) - return false; - - LocalCurrentGraphicsContext localContext(paintInfo.context); - setSearchCellState(input->renderer(), r); - - NSSearchFieldCell* search = this->search(); - - if ([search searchMenuTemplate] != nil) - [search setSearchMenuTemplate:nil]; - - updateActiveState([search searchButtonCell], o); - - FloatRect localBounds = [search searchButtonRectForBounds:NSRect(input->renderBox()->pixelSnappedBorderBoxRect())]; - localBounds = convertToPaintingRect(input->renderer(), o, localBounds, r); - - [[search searchButtonCell] drawWithFrame:localBounds inView:documentViewFor(o)]; - [[search searchButtonCell] setControlView:nil]; - return false; -} - -const int resultsArrowWidth = 5; -void RenderThemeMacShared::adjustSearchFieldResultsButtonStyle(StyleResolver*, RenderStyle* style, Element*) const -{ - IntSize size = sizeForSystemFont(style, resultsButtonSizes()); - style->setWidth(Length(size.width() + resultsArrowWidth, Fixed)); - style->setHeight(Length(size.height(), Fixed)); - style->setBoxShadow(nullptr); -} - -bool RenderThemeMacShared::paintSearchFieldResultsButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) -{ - Node* input = o->node()->shadowHost(); - if (!input) - input = o->node(); - if (!input->renderer()->isBox()) - return false; - - LocalCurrentGraphicsContext localContext(paintInfo.context); - setSearchCellState(input->renderer(), r); - - NSSearchFieldCell* search = this->search(); - - updateActiveState([search searchButtonCell], o); - - if (![search searchMenuTemplate]) - [search setSearchMenuTemplate:searchMenuTemplate()]; - - GraphicsContextStateSaver stateSaver(*paintInfo.context); - float zoomLevel = o->style()->effectiveZoom(); - - FloatRect localBounds = [search searchButtonRectForBounds:NSRect(input->renderBox()->pixelSnappedBorderBoxRect())]; - localBounds = convertToPaintingRect(input->renderer(), o, localBounds, r); - - IntRect unzoomedRect(localBounds); - if (zoomLevel != 1.0f) { - unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel); - unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel); - paintInfo.context->translate(unzoomedRect.x(), unzoomedRect.y()); - paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel)); - paintInfo.context->translate(-unzoomedRect.x(), -unzoomedRect.y()); - } - - [[search searchButtonCell] drawWithFrame:unzoomedRect inView:documentViewFor(o)]; - [[search searchButtonCell] setControlView:nil]; - - return false; -} - -#if ENABLE(DATALIST_ELEMENT) -IntSize RenderThemeMacShared::sliderTickSize() const -{ - return IntSize(1, 3); -} - -int RenderThemeMacShared::sliderTickOffsetFromTrackCenter() const -{ - return -9; -} -#endif - -const int sliderThumbWidth = 15; -const int sliderThumbHeight = 15; - -void RenderThemeMacShared::adjustSliderThumbSize(RenderStyle* style, Element*) const -{ - float zoomLevel = style->effectiveZoom(); - if (style->appearance() == SliderThumbHorizontalPart || style->appearance() == SliderThumbVerticalPart) { - style->setWidth(Length(static_cast<int>(sliderThumbWidth * zoomLevel), Fixed)); - style->setHeight(Length(static_cast<int>(sliderThumbHeight * zoomLevel), Fixed)); - } - -#if ENABLE(VIDEO) - adjustMediaSliderThumbSize(style); -#endif -} - -bool RenderThemeMacShared::shouldShowPlaceholderWhenFocused() const -{ -#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 - return true; -#else - return false; -#endif -} - -NSPopUpButtonCell* RenderThemeMacShared::popupButton() const -{ - if (!m_popupButton) { - m_popupButton.adoptNS([[NSPopUpButtonCell alloc] initTextCell:@"" pullsDown:NO]); - [m_popupButton.get() setUsesItemFromMenu:NO]; - [m_popupButton.get() setFocusRingType:NSFocusRingTypeExterior]; - } - - return m_popupButton.get(); -} - -NSSearchFieldCell* RenderThemeMacShared::search() const -{ - if (!m_search) { - m_search.adoptNS([[NSSearchFieldCell alloc] initTextCell:@""]); - [m_search.get() setBezelStyle:NSTextFieldRoundedBezel]; - [m_search.get() setBezeled:YES]; - [m_search.get() setEditable:YES]; - [m_search.get() setFocusRingType:NSFocusRingTypeExterior]; - } - - return m_search.get(); -} - -NSMenu* RenderThemeMacShared::searchMenuTemplate() const -{ - if (!m_searchMenuTemplate) - m_searchMenuTemplate.adoptNS([[NSMenu alloc] initWithTitle:@""]); - - return m_searchMenuTemplate.get(); -} - -NSSliderCell* RenderThemeMacShared::sliderThumbHorizontal() const -{ - if (!m_sliderThumbHorizontal) { - m_sliderThumbHorizontal.adoptNS([[NSSliderCell alloc] init]); - [m_sliderThumbHorizontal.get() setSliderType:NSLinearSlider]; - [m_sliderThumbHorizontal.get() setControlSize:NSSmallControlSize]; - [m_sliderThumbHorizontal.get() setFocusRingType:NSFocusRingTypeExterior]; - } - - return m_sliderThumbHorizontal.get(); -} - -NSSliderCell* RenderThemeMacShared::sliderThumbVertical() const -{ - if (!m_sliderThumbVertical) { - m_sliderThumbVertical.adoptNS([[NSSliderCell alloc] init]); - [m_sliderThumbVertical.get() setSliderType:NSLinearSlider]; - [m_sliderThumbVertical.get() setControlSize:NSSmallControlSize]; - [m_sliderThumbVertical.get() setFocusRingType:NSFocusRingTypeExterior]; - } - - return m_sliderThumbVertical.get(); -} - -NSTextFieldCell* RenderThemeMacShared::textField() const -{ - if (!m_textField) { - m_textField.adoptNS([[WebCoreTextFieldCell alloc] initTextCell:@""]); - [m_textField.get() setBezeled:YES]; - [m_textField.get() setEditable:YES]; - [m_textField.get() setFocusRingType:NSFocusRingTypeExterior]; -#if __MAC_OS_X_VERSION_MIN_REQUIRED <= 1070 - [m_textField.get() setDrawsBackground:YES]; - [m_textField.get() setBackgroundColor:[NSColor whiteColor]]; -#else - // Post-Lion, WebCore can be in charge of paintinng the background thanks to - // the workaround in place for <rdar://problem/11385461>, which is implemented - // above as _coreUIDrawOptionsWithFrame. - [m_textField.get() setDrawsBackground:NO]; -#endif - } - - return m_textField.get(); -} - -String RenderThemeMacShared::fileListNameForWidth(const FileList* fileList, const Font& font, int width, bool multipleFilesAllowed) const -{ - if (width <= 0) - return String(); - - String strToTruncate; - if (fileList->isEmpty()) - strToTruncate = fileListDefaultLabel(multipleFilesAllowed); - else if (fileList->length() == 1) - strToTruncate = [[NSFileManager defaultManager] displayNameAtPath:(fileList->item(0)->path())]; - else - return StringTruncator::rightTruncate(multipleFileUploadText(fileList->length()), width, font, StringTruncator::EnableRoundingHacks); - - return StringTruncator::centerTruncate(strToTruncate, width, font, StringTruncator::EnableRoundingHacks); -} - -} // namespace WebCore diff --git a/Source/WebCore/rendering/RenderThemeSafari.cpp b/Source/WebCore/rendering/RenderThemeSafari.cpp index 21d978c8b..2ab016d84 100644 --- a/Source/WebCore/rendering/RenderThemeSafari.cpp +++ b/Source/WebCore/rendering/RenderThemeSafari.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008, 2009 Apple Inc. + * Copyright (C) 2007, 2008, 2009, 2013 Apple Inc. * Copyright (C) 2009 Kenneth Rohde Christiansen * * This library is free software; you can redistribute it and/or @@ -35,9 +35,11 @@ #include "GraphicsContextCG.h" #include "HTMLInputElement.h" #include "HTMLMediaElement.h" +#include "HTMLMeterElement.h" #include "HTMLNames.h" #include "PaintInfo.h" #include "RenderMediaControls.h" +#include "RenderMeter.h" #include "RenderSlider.h" #include "RenderView.h" #include "SoftLinking.h" @@ -170,7 +172,7 @@ Color RenderThemeSafari::platformFocusRingColor() const if (!focusRingColor.isValid()) { if (STCopyThemeColorPtr()) { - RetainPtr<CGColorRef> color(AdoptCF, STCopyThemeColorPtr()(stFocusRingColorID, SafariTheme::ActiveState)); + RetainPtr<CGColorRef> color = adoptCF(STCopyThemeColorPtr()(stFocusRingColorID, SafariTheme::ActiveState)); focusRingColor = makeRGBAFromCGColor(color.get()); } if (!focusRingColor.isValid()) @@ -187,7 +189,7 @@ static float systemFontSizeForControlSize(NSControlSize controlSize) return sizes[controlSize]; } -void RenderThemeSafari::systemFont(int propId, FontDescription& fontDescription) const +void RenderThemeSafari::systemFont(CSSValueID valueID, FontDescription& fontDescription) const { static FontDescription systemFont; static FontDescription smallSystemFont; @@ -199,47 +201,47 @@ void RenderThemeSafari::systemFont(int propId, FontDescription& fontDescription) FontDescription* cachedDesc; float fontSize = 0; - switch (propId) { - case CSSValueSmallCaption: - cachedDesc = &smallSystemFont; - if (!smallSystemFont.isAbsoluteSize()) - fontSize = systemFontSizeForControlSize(NSSmallControlSize); - break; - case CSSValueMenu: - cachedDesc = &menuFont; - if (!menuFont.isAbsoluteSize()) - fontSize = systemFontSizeForControlSize(NSRegularControlSize); - break; - case CSSValueStatusBar: - cachedDesc = &labelFont; - if (!labelFont.isAbsoluteSize()) - fontSize = 10.0f; - break; - case CSSValueWebkitMiniControl: - cachedDesc = &miniControlFont; - if (!miniControlFont.isAbsoluteSize()) - fontSize = systemFontSizeForControlSize(NSMiniControlSize); - break; - case CSSValueWebkitSmallControl: - cachedDesc = &smallControlFont; - if (!smallControlFont.isAbsoluteSize()) - fontSize = systemFontSizeForControlSize(NSSmallControlSize); - break; - case CSSValueWebkitControl: - cachedDesc = &controlFont; - if (!controlFont.isAbsoluteSize()) - fontSize = systemFontSizeForControlSize(NSRegularControlSize); - break; - default: - cachedDesc = &systemFont; - if (!systemFont.isAbsoluteSize()) - fontSize = 13.0f; + switch (valueID) { + case CSSValueSmallCaption: + cachedDesc = &smallSystemFont; + if (!smallSystemFont.isAbsoluteSize()) + fontSize = systemFontSizeForControlSize(NSSmallControlSize); + break; + case CSSValueMenu: + cachedDesc = &menuFont; + if (!menuFont.isAbsoluteSize()) + fontSize = systemFontSizeForControlSize(NSRegularControlSize); + break; + case CSSValueStatusBar: + cachedDesc = &labelFont; + if (!labelFont.isAbsoluteSize()) + fontSize = 10.0f; + break; + case CSSValueWebkitMiniControl: + cachedDesc = &miniControlFont; + if (!miniControlFont.isAbsoluteSize()) + fontSize = systemFontSizeForControlSize(NSMiniControlSize); + break; + case CSSValueWebkitSmallControl: + cachedDesc = &smallControlFont; + if (!smallControlFont.isAbsoluteSize()) + fontSize = systemFontSizeForControlSize(NSSmallControlSize); + break; + case CSSValueWebkitControl: + cachedDesc = &controlFont; + if (!controlFont.isAbsoluteSize()) + fontSize = systemFontSizeForControlSize(NSRegularControlSize); + break; + default: + cachedDesc = &systemFont; + if (!systemFont.isAbsoluteSize()) + fontSize = 13.0f; } if (fontSize) { cachedDesc->setIsAbsoluteSize(true); cachedDesc->setGenericFamily(FontDescription::NoFamily); - cachedDesc->firstFamily().setFamily("Lucida Grande"); + cachedDesc->setOneFamily("Lucida Grande"); cachedDesc->setSpecifiedSize(fontSize); cachedDesc->setWeight(FontWeightNormal); cachedDesc->setItalic(false); @@ -390,7 +392,7 @@ void RenderThemeSafari::setFontFromControlSize(StyleResolver* styleResolver, Ren fontDescription.setGenericFamily(FontDescription::SerifFamily); float fontSize = systemFontSizeForControlSize(controlSize); - fontDescription.firstFamily().setFamily("Lucida Grande"); + fontDescription.setOneFamily("Lucida Grande"); fontDescription.setComputedSize(fontSize); fontDescription.setSpecifiedSize(fontSize); @@ -760,44 +762,44 @@ void RenderThemeSafari::paintMenuListButtonGradients(RenderObject* o, const Pain FloatRect topGradient(r.x(), r.y(), r.width(), r.height() / 2.0f); struct CGFunctionCallbacks topCallbacks = { 0, TopGradientInterpolate, NULL }; - RetainPtr<CGFunctionRef> topFunction(AdoptCF, CGFunctionCreate(NULL, 1, NULL, 4, NULL, &topCallbacks)); - RetainPtr<CGShadingRef> topShading(AdoptCF, CGShadingCreateAxial(cspace, CGPointMake(topGradient.x(), topGradient.y()), CGPointMake(topGradient.x(), topGradient.maxY()), topFunction.get(), false, false)); + RetainPtr<CGFunctionRef> topFunction = adoptCF(CGFunctionCreate(NULL, 1, NULL, 4, NULL, &topCallbacks)); + RetainPtr<CGShadingRef> topShading = adoptCF(CGShadingCreateAxial(cspace, CGPointMake(topGradient.x(), topGradient.y()), CGPointMake(topGradient.x(), topGradient.maxY()), topFunction.get(), false, false)); FloatRect bottomGradient(r.x() + radius, r.y() + r.height() / 2.0f, r.width() - 2.0f * radius, r.height() / 2.0f); struct CGFunctionCallbacks bottomCallbacks = { 0, BottomGradientInterpolate, NULL }; - RetainPtr<CGFunctionRef> bottomFunction(AdoptCF, CGFunctionCreate(NULL, 1, NULL, 4, NULL, &bottomCallbacks)); - RetainPtr<CGShadingRef> bottomShading(AdoptCF, CGShadingCreateAxial(cspace, CGPointMake(bottomGradient.x(), bottomGradient.y()), CGPointMake(bottomGradient.x(), bottomGradient.maxY()), bottomFunction.get(), false, false)); + RetainPtr<CGFunctionRef> bottomFunction = adoptCF(CGFunctionCreate(NULL, 1, NULL, 4, NULL, &bottomCallbacks)); + RetainPtr<CGShadingRef> bottomShading = adoptCF(CGShadingCreateAxial(cspace, CGPointMake(bottomGradient.x(), bottomGradient.y()), CGPointMake(bottomGradient.x(), bottomGradient.maxY()), bottomFunction.get(), false, false)); struct CGFunctionCallbacks mainCallbacks = { 0, MainGradientInterpolate, NULL }; - RetainPtr<CGFunctionRef> mainFunction(AdoptCF, CGFunctionCreate(NULL, 1, NULL, 4, NULL, &mainCallbacks)); - RetainPtr<CGShadingRef> mainShading(AdoptCF, CGShadingCreateAxial(cspace, CGPointMake(r.x(), r.y()), CGPointMake(r.x(), r.maxY()), mainFunction.get(), false, false)); + RetainPtr<CGFunctionRef> mainFunction = adoptCF(CGFunctionCreate(NULL, 1, NULL, 4, NULL, &mainCallbacks)); + RetainPtr<CGShadingRef> mainShading = adoptCF(CGShadingCreateAxial(cspace, CGPointMake(r.x(), r.y()), CGPointMake(r.x(), r.maxY()), mainFunction.get(), false, false)); - RetainPtr<CGShadingRef> leftShading(AdoptCF, CGShadingCreateAxial(cspace, CGPointMake(r.x(), r.y()), CGPointMake(r.x() + radius, r.y()), mainFunction.get(), false, false)); + RetainPtr<CGShadingRef> leftShading = adoptCF(CGShadingCreateAxial(cspace, CGPointMake(r.x(), r.y()), CGPointMake(r.x() + radius, r.y()), mainFunction.get(), false, false)); - RetainPtr<CGShadingRef> rightShading(AdoptCF, CGShadingCreateAxial(cspace, CGPointMake(r.maxX(), r.y()), CGPointMake(r.maxX() - radius, r.y()), mainFunction.get(), false, false)); + RetainPtr<CGShadingRef> rightShading = adoptCF(CGShadingCreateAxial(cspace, CGPointMake(r.maxX(), r.y()), CGPointMake(r.maxX() - radius, r.y()), mainFunction.get(), false, false)); paintInfo.context->save(); CGContextClipToRect(context, bound.rect()); - paintInfo.context->addRoundedRectClip(bound); + paintInfo.context->clipRoundedRect(bound); CGContextDrawShading(context, mainShading.get()); paintInfo.context->restore(); paintInfo.context->save(); CGContextClipToRect(context, topGradient); - paintInfo.context->addRoundedRectClip(RoundedRect(enclosingIntRect(topGradient), bound.radii().topLeft(), bound.radii().topRight(), IntSize(), IntSize())); + paintInfo.context->clipRoundedRect(RoundedRect(enclosingIntRect(topGradient), bound.radii().topLeft(), bound.radii().topRight(), IntSize(), IntSize())); CGContextDrawShading(context, topShading.get()); paintInfo.context->restore(); if (!bottomGradient.isEmpty()) { paintInfo.context->save(); CGContextClipToRect(context, bottomGradient); - paintInfo.context->addRoundedRectClip(RoundedRect(enclosingIntRect(bottomGradient), IntSize(), IntSize(), bound.radii().bottomLeft(), bound.radii().bottomRight())); + paintInfo.context->clipRoundedRect(RoundedRect(enclosingIntRect(bottomGradient), IntSize(), IntSize(), bound.radii().bottomLeft(), bound.radii().bottomRight())); CGContextDrawShading(context, bottomShading.get()); paintInfo.context->restore(); } paintInfo.context->save(); CGContextClipToRect(context, bound.rect()); - paintInfo.context->addRoundedRectClip(bound); + paintInfo.context->clipRoundedRect(bound); CGContextDrawShading(context, leftShading.get()); CGContextDrawShading(context, rightShading.get()); paintInfo.context->restore(); @@ -874,7 +876,7 @@ void RenderThemeSafari::adjustMenuListStyle(StyleResolver* styleResolver, Render // Set the foreground color to black or gray when we have the aqua look. // Cast to RGB32 is to work around a compiler bug. - style->setColor(e && e->isEnabledFormControl() ? static_cast<RGBA32>(Color::black) : Color::darkGray); + style->setColor(e && !e->isDisabledFormControl() ? static_cast<RGBA32>(Color::black) : Color::darkGray); // Set the button's vertical size. setButtonSize(style); @@ -974,14 +976,14 @@ bool RenderThemeSafari::paintSliderTrack(RenderObject* o, const PaintInfo& paint CGContextClipToRect(context, bounds.rect()); struct CGFunctionCallbacks mainCallbacks = { 0, TrackGradientInterpolate, NULL }; - RetainPtr<CGFunctionRef> mainFunction(AdoptCF, CGFunctionCreate(NULL, 1, NULL, 4, NULL, &mainCallbacks)); + RetainPtr<CGFunctionRef> mainFunction = adoptCF(CGFunctionCreate(NULL, 1, NULL, 4, NULL, &mainCallbacks)); RetainPtr<CGShadingRef> mainShading; if (o->style()->appearance() == SliderVerticalPart) - mainShading.adoptCF(CGShadingCreateAxial(cspace, CGPointMake(bounds.rect().x(), bounds.rect().maxY()), CGPointMake(bounds.rect().maxX(), bounds.rect().maxY()), mainFunction.get(), false, false)); + mainShading = adoptCF(CGShadingCreateAxial(cspace, CGPointMake(bounds.rect().x(), bounds.rect().maxY()), CGPointMake(bounds.rect().maxX(), bounds.rect().maxY()), mainFunction.get(), false, false)); else - mainShading.adoptCF(CGShadingCreateAxial(cspace, CGPointMake(bounds.rect().x(), bounds.rect().y()), CGPointMake(bounds.rect().x(), bounds.rect().maxY()), mainFunction.get(), false, false)); + mainShading = adoptCF(CGShadingCreateAxial(cspace, CGPointMake(bounds.rect().x(), bounds.rect().y()), CGPointMake(bounds.rect().x(), bounds.rect().maxY()), mainFunction.get(), false, false)); - paintInfo.context->addRoundedRectClip(bounds); + paintInfo.context->clipRoundedRect(bounds); CGContextDrawShading(context, mainShading.get()); paintInfo.context->restore(); @@ -1202,6 +1204,52 @@ bool RenderThemeSafari::paintMediaSliderThumb(RenderObject* o, const PaintInfo& } #endif +#if ENABLE(METER_ELEMENT) +void RenderThemeSafari::adjustMeterStyle(StyleResolver*, RenderStyle* style, Element*) const +{ + style->setBoxShadow(nullptr); +} + +bool RenderThemeSafari::supportsMeter(ControlPart part) const +{ + switch (part) { + case MeterPart: + return true; + default: + return false; + } +} + +IntSize RenderThemeSafari::meterSizeForBounds(const RenderMeter*, const IntRect& bounds) const +{ + return bounds.size(); +} + +bool RenderThemeSafari::paintMeter(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) +{ + // NOTE: This routine is for testing only. It should be fleshed out with a real CG-based implementation. + // Right now it uses a slider, with the thumb positioned at the meter point. + if (!renderObject->isMeter()) + return true; + + HTMLMeterElement* element = toRenderMeter(renderObject)->meterElement(); + + int remaining = static_cast<int>((1.0 - element->valueRatio()) * static_cast<double>(rect.size().width())); + + // Draw the background + paintSliderTrack(renderObject, paintInfo, rect); + + // Draw the progress portion + IntRect completedRect(rect); + completedRect.contract(remaining, 0); + + paintSliderThumb(renderObject, paintInfo, completedRect); + + return true; +} + +#endif + } // namespace WebCore #endif // #if USE(SAFARI_THEME) diff --git a/Source/WebCore/rendering/RenderThemeSafari.h b/Source/WebCore/rendering/RenderThemeSafari.h index fe26f326b..457e0d365 100644 --- a/Source/WebCore/rendering/RenderThemeSafari.h +++ b/Source/WebCore/rendering/RenderThemeSafari.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008 Apple Inc. + * Copyright (C) 2007, 2008, 2013 Apple Inc. * Copyright (C) 2009 Kenneth Rohde Christiansen * * This library is free software; you can redistribute it and/or @@ -72,7 +72,7 @@ public: virtual Color platformFocusRingColor() const; // System fonts. - virtual void systemFont(int propId, FontDescription&) const; + virtual void systemFont(CSSValueID, FontDescription&) const; virtual int minimumMenuListSize(RenderStyle*) const; @@ -138,6 +138,13 @@ protected: virtual bool paintMediaSliderThumb(RenderObject*, const PaintInfo&, const IntRect&); #endif +#if ENABLE(METER_ELEMENT) + virtual IntSize meterSizeForBounds(const RenderMeter*, const IntRect&) const OVERRIDE; + virtual bool supportsMeter(ControlPart) const OVERRIDE; + virtual void adjustMeterStyle(StyleResolver*, RenderStyle*, Element*) const OVERRIDE; + virtual bool paintMeter(RenderObject*, const PaintInfo&, const IntRect&) OVERRIDE; +#endif + virtual bool shouldShowPlaceholderWhenFocused() const { return true; } private: diff --git a/Source/WebCore/rendering/RenderThemeWin.cpp b/Source/WebCore/rendering/RenderThemeWin.cpp index 49ab18606..ed91f5b14 100644 --- a/Source/WebCore/rendering/RenderThemeWin.cpp +++ b/Source/WebCore/rendering/RenderThemeWin.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007 Apple Inc. + * Copyright (C) 2006, 2007, 2013 Apple Inc. * Copyright (C) 2009 Kenneth Rohde Christiansen * * This library is free software; you can redistribute it and/or @@ -26,6 +26,7 @@ #include "Element.h" #include "FontMetrics.h" #include "Frame.h" +#include "FrameSelection.h" #include "GraphicsContext.h" #include "LocalWindowsContext.h" #include "PaintInfo.h" @@ -108,6 +109,36 @@ #define UPS_PRESSED 3 #define UPS_DISABLED 4 +// Progress bar parts +#define PP_BAR 1 +#define PP_BARVERT 2 +#define PP_CHUNK 3 +#define PP_CHUNKVERT 4 +#define PP_FILL 5 +#define PP_FILLVERT 6 +#define PP_PULSEOVERLAY 7 +#define PP_MOVEOVERLAY 8 +#define PP_PULSEOVERLAYVERT 9 +#define PP_MOVEOVERLAYVERT 10 +#define PP_TRANSPARENTBAR 11 +#define PP_TRANSPARENTBARVERT 12 + +// Progress bar states +#define PBBS_NORMAL 1 +#define PBBS_PARTIAL 2 +#define PBBVS_NORMAL 1 // Vertical +#define PBBVS_PARTIAL 2 + +// Progress bar fill states +#define PBFS_NORMAL 1 +#define PBFS_ERROR 2 +#define PBFS_PAUSED 3 +#define PBFS_PARTIAL 4 +#define PBFVS_NORMAL 1 // Vertical +#define PBFVS_ERROR 2 +#define PBFVS_PAUSED 3 +#define PBFVS_PARTIAL 4 + SOFT_LINK_LIBRARY(uxtheme) SOFT_LINK(uxtheme, OpenThemeData, HANDLE, WINAPI, (HWND hwnd, LPCWSTR pszClassList), (hwnd, pszClassList)) @@ -145,7 +176,7 @@ static bool gWebKitIsBeingUnloaded; static bool documentIsInApplicationChromeMode(const Document* document) { Settings* settings = document->settings(); - return settings && settings->inApplicationChromeMode(); + return settings && settings->applicationChromeMode(); } void RenderThemeWin::setWebKitIsBeingUnloaded() @@ -172,6 +203,7 @@ RenderThemeWin::RenderThemeWin() , m_menuListTheme(0) , m_sliderTheme(0) , m_spinButtonTheme(0) + , m_progressBarTheme(0) { haveTheme = uxthemeLibrary() && IsThemeActive(); } @@ -219,6 +251,13 @@ HANDLE RenderThemeWin::spinButtonTheme() const return m_spinButtonTheme; } +HANDLE RenderThemeWin::progressBarTheme() const +{ + if (haveTheme && !m_progressBarTheme) + m_progressBarTheme = OpenThemeData(0, L"Progress"); + return m_progressBarTheme; +} + void RenderThemeWin::close() { // This method will need to be called when the OS theme changes to flush our cached themes. @@ -232,7 +271,9 @@ void RenderThemeWin::close() CloseThemeData(m_sliderTheme); if (m_spinButtonTheme) CloseThemeData(m_spinButtonTheme); - m_buttonTheme = m_textFieldTheme = m_menuListTheme = m_sliderTheme = m_spinButtonTheme = 0; + if (m_progressBarTheme) + CloseThemeData(m_progressBarTheme); + m_buttonTheme = m_textFieldTheme = m_menuListTheme = m_sliderTheme = m_spinButtonTheme = m_progressBarTheme = 0; haveTheme = uxthemeLibrary() && IsThemeActive(); } @@ -285,7 +326,7 @@ static void fillFontDescription(FontDescription& fontDescription, LOGFONT& logFo { fontDescription.setIsAbsoluteSize(true); fontDescription.setGenericFamily(FontDescription::NoFamily); - fontDescription.firstFamily().setFamily(String(logFont.lfFaceName)); + fontDescription.setOneFamily(String(logFont.lfFaceName)); fontDescription.setSpecifiedSize(fontSize); fontDescription.setWeight(logFont.lfWeight >= 700 ? FontWeightBold : FontWeightNormal); // FIXME: Use real weight. fontDescription.setItalic(logFont.lfItalic); @@ -296,7 +337,7 @@ static void fillFontDescription(FontDescription& fontDescription, LOGFONT& logFo fillFontDescription(fontDescription, logFont, abs(logFont.lfHeight)); } -void RenderThemeWin::systemFont(int propId, FontDescription& fontDescription) const +void RenderThemeWin::systemFont(CSSValueID valueID, FontDescription& fontDescription) const { static FontDescription captionFont; static FontDescription controlFont; @@ -316,65 +357,65 @@ void RenderThemeWin::systemFont(int propId, FontDescription& fontDescription) co ::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0); } - switch (propId) { - case CSSValueIcon: { - if (!iconFont.isAbsoluteSize()) { + switch (valueID) { + case CSSValueIcon: { + if (!iconFont.isAbsoluteSize()) { + LOGFONT logFont; + ::SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(logFont), &logFont, 0); + fillFontDescription(iconFont, logFont); + } + fontDescription = iconFont; + break; + } + case CSSValueMenu: + if (!menuFont.isAbsoluteSize()) + fillFontDescription(menuFont, ncm.lfMenuFont); + fontDescription = menuFont; + break; + case CSSValueMessageBox: + if (!messageBoxFont.isAbsoluteSize()) + fillFontDescription(messageBoxFont, ncm.lfMessageFont); + fontDescription = messageBoxFont; + break; + case CSSValueStatusBar: + if (!statusBarFont.isAbsoluteSize()) + fillFontDescription(statusBarFont, ncm.lfStatusFont); + fontDescription = statusBarFont; + break; + case CSSValueCaption: + if (!captionFont.isAbsoluteSize()) + fillFontDescription(captionFont, ncm.lfCaptionFont); + fontDescription = captionFont; + break; + case CSSValueSmallCaption: + if (!smallCaptionFont.isAbsoluteSize()) + fillFontDescription(smallCaptionFont, ncm.lfSmCaptionFont); + fontDescription = smallCaptionFont; + break; + case CSSValueWebkitSmallControl: + case CSSValueWebkitMiniControl: // Just map to small. + case CSSValueWebkitControl: // Just map to small. + if (!controlFont.isAbsoluteSize()) { + HGDIOBJ hGDI = ::GetStockObject(DEFAULT_GUI_FONT); + if (hGDI) { LOGFONT logFont; - ::SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(logFont), &logFont, 0); - fillFontDescription(iconFont, logFont); + if (::GetObject(hGDI, sizeof(logFont), &logFont) > 0) + fillFontDescription(controlFont, logFont, defaultControlFontPixelSize); } - fontDescription = iconFont; - break; } - case CSSValueMenu: - if (!menuFont.isAbsoluteSize()) - fillFontDescription(menuFont, ncm.lfMenuFont); - fontDescription = menuFont; - break; - case CSSValueMessageBox: - if (!messageBoxFont.isAbsoluteSize()) - fillFontDescription(messageBoxFont, ncm.lfMessageFont); - fontDescription = messageBoxFont; - break; - case CSSValueStatusBar: - if (!statusBarFont.isAbsoluteSize()) - fillFontDescription(statusBarFont, ncm.lfStatusFont); - fontDescription = statusBarFont; - break; - case CSSValueCaption: - if (!captionFont.isAbsoluteSize()) - fillFontDescription(captionFont, ncm.lfCaptionFont); - fontDescription = captionFont; - break; - case CSSValueSmallCaption: - if (!smallCaptionFont.isAbsoluteSize()) - fillFontDescription(smallCaptionFont, ncm.lfSmCaptionFont); - fontDescription = smallCaptionFont; - break; - case CSSValueWebkitSmallControl: - case CSSValueWebkitMiniControl: // Just map to small. - case CSSValueWebkitControl: // Just map to small. - if (!controlFont.isAbsoluteSize()) { - HGDIOBJ hGDI = ::GetStockObject(DEFAULT_GUI_FONT); - if (hGDI) { - LOGFONT logFont; - if (::GetObject(hGDI, sizeof(logFont), &logFont) > 0) - fillFontDescription(controlFont, logFont, defaultControlFontPixelSize); - } - } - fontDescription = controlFont; - break; - default: { // Everything else uses the stock GUI font. - if (!systemFont.isAbsoluteSize()) { - HGDIOBJ hGDI = ::GetStockObject(DEFAULT_GUI_FONT); - if (hGDI) { - LOGFONT logFont; - if (::GetObject(hGDI, sizeof(logFont), &logFont) > 0) - fillFontDescription(systemFont, logFont); - } + fontDescription = controlFont; + break; + default: { // Everything else uses the stock GUI font. + if (!systemFont.isAbsoluteSize()) { + HGDIOBJ hGDI = ::GetStockObject(DEFAULT_GUI_FONT); + if (hGDI) { + LOGFONT logFont; + if (::GetObject(hGDI, sizeof(logFont), &logFont) > 0) + fillFontDescription(systemFont, logFont); } - fontDescription = systemFont; } + fontDescription = systemFont; + } } } @@ -522,6 +563,10 @@ ThemeData RenderThemeWin::getClassicThemeData(RenderObject* o, ControlSubPart su result.m_part = DFC_SCROLL; result.m_state = determineClassicState(o); break; + case MeterPart: + result.m_part = PP_BAR; + result.m_state = determineState(o); + break; case SearchFieldPart: case TextFieldPart: case TextAreaPart: @@ -583,6 +628,10 @@ ThemeData RenderThemeWin::getThemeData(RenderObject* o, ControlSubPart subPart) result.m_state = determineState(o); break; } + case MeterPart: + result.m_part = PP_BAR; + result.m_state = determineState(o); + break; case RadioPart: result.m_part = BP_RADIO; result.m_state = determineState(o); @@ -675,11 +724,8 @@ static void drawControl(GraphicsContext* context, RenderObject* o, HANDLE theme, } } - -#if !OS(WINCE) if (!alphaBlend && !context->isInTransparencyLayer()) DIBPixelData::setRGBABitmapAlpha(windowsContext.hdc(), r, 255); -#endif } bool RenderThemeWin::paintButton(RenderObject* o, const PaintInfo& i, const IntRect& r) @@ -988,42 +1034,42 @@ bool RenderThemeWin::paintSearchFieldResultsButton(RenderObject* o, const PaintI } // Map a CSSValue* system color to an index understood by GetSysColor -static int cssValueIdToSysColorIndex(int cssValueId) +static int cssValueIdToSysColorIndex(CSSValueID cssValueId) { switch (cssValueId) { - case CSSValueActiveborder: return COLOR_ACTIVEBORDER; - case CSSValueActivecaption: return COLOR_ACTIVECAPTION; - case CSSValueAppworkspace: return COLOR_APPWORKSPACE; - case CSSValueBackground: return COLOR_BACKGROUND; - case CSSValueButtonface: return COLOR_BTNFACE; - case CSSValueButtonhighlight: return COLOR_BTNHIGHLIGHT; - case CSSValueButtonshadow: return COLOR_BTNSHADOW; - case CSSValueButtontext: return COLOR_BTNTEXT; - case CSSValueCaptiontext: return COLOR_CAPTIONTEXT; - case CSSValueGraytext: return COLOR_GRAYTEXT; - case CSSValueHighlight: return COLOR_HIGHLIGHT; - case CSSValueHighlighttext: return COLOR_HIGHLIGHTTEXT; - case CSSValueInactiveborder: return COLOR_INACTIVEBORDER; - case CSSValueInactivecaption: return COLOR_INACTIVECAPTION; - case CSSValueInactivecaptiontext: return COLOR_INACTIVECAPTIONTEXT; - case CSSValueInfobackground: return COLOR_INFOBK; - case CSSValueInfotext: return COLOR_INFOTEXT; - case CSSValueMenu: return COLOR_MENU; - case CSSValueMenutext: return COLOR_MENUTEXT; - case CSSValueScrollbar: return COLOR_SCROLLBAR; - case CSSValueThreeddarkshadow: return COLOR_3DDKSHADOW; - case CSSValueThreedface: return COLOR_3DFACE; - case CSSValueThreedhighlight: return COLOR_3DHIGHLIGHT; - case CSSValueThreedlightshadow: return COLOR_3DLIGHT; - case CSSValueThreedshadow: return COLOR_3DSHADOW; - case CSSValueWindow: return COLOR_WINDOW; - case CSSValueWindowframe: return COLOR_WINDOWFRAME; - case CSSValueWindowtext: return COLOR_WINDOWTEXT; - default: return -1; // Unsupported CSSValue + case CSSValueActiveborder: return COLOR_ACTIVEBORDER; + case CSSValueActivecaption: return COLOR_ACTIVECAPTION; + case CSSValueAppworkspace: return COLOR_APPWORKSPACE; + case CSSValueBackground: return COLOR_BACKGROUND; + case CSSValueButtonface: return COLOR_BTNFACE; + case CSSValueButtonhighlight: return COLOR_BTNHIGHLIGHT; + case CSSValueButtonshadow: return COLOR_BTNSHADOW; + case CSSValueButtontext: return COLOR_BTNTEXT; + case CSSValueCaptiontext: return COLOR_CAPTIONTEXT; + case CSSValueGraytext: return COLOR_GRAYTEXT; + case CSSValueHighlight: return COLOR_HIGHLIGHT; + case CSSValueHighlighttext: return COLOR_HIGHLIGHTTEXT; + case CSSValueInactiveborder: return COLOR_INACTIVEBORDER; + case CSSValueInactivecaption: return COLOR_INACTIVECAPTION; + case CSSValueInactivecaptiontext: return COLOR_INACTIVECAPTIONTEXT; + case CSSValueInfobackground: return COLOR_INFOBK; + case CSSValueInfotext: return COLOR_INFOTEXT; + case CSSValueMenu: return COLOR_MENU; + case CSSValueMenutext: return COLOR_MENUTEXT; + case CSSValueScrollbar: return COLOR_SCROLLBAR; + case CSSValueThreeddarkshadow: return COLOR_3DDKSHADOW; + case CSSValueThreedface: return COLOR_3DFACE; + case CSSValueThreedhighlight: return COLOR_3DHIGHLIGHT; + case CSSValueThreedlightshadow: return COLOR_3DLIGHT; + case CSSValueThreedshadow: return COLOR_3DSHADOW; + case CSSValueWindow: return COLOR_WINDOW; + case CSSValueWindowframe: return COLOR_WINDOWFRAME; + case CSSValueWindowtext: return COLOR_WINDOWTEXT; + default: return -1; // Unsupported CSSValue } } -Color RenderThemeWin::systemColor(int cssValueId) const +Color RenderThemeWin::systemColor(CSSValueID cssValueId) const { int sysColorIndex = cssValueIdToSysColorIndex(cssValueId); if (sysColorIndex == -1) @@ -1049,12 +1095,7 @@ String RenderThemeWin::extraFullScreenStyleSheet() bool RenderThemeWin::supportsClosedCaptioning() const { - // We rely on QuickTime to render captions so only enable the button for a video element. -#if SAFARI_THEME_VERSION >= 4 return true; -#else - return false; -#endif } bool RenderThemeWin::paintMediaFullscreenButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) @@ -1127,7 +1168,54 @@ IntPoint RenderThemeWin::volumeSliderOffsetFromMuteButton(RenderBox* muteButtonB return RenderMediaControls::volumeSliderOffsetFromMuteButton(muteButtonBox, size); } +#endif + +#if ENABLE(METER_ELEMENT) +void RenderThemeWin::adjustMeterStyle(StyleResolver*, RenderStyle* style, Element*) const +{ + style->setBoxShadow(nullptr); +} + +bool RenderThemeWin::supportsMeter(ControlPart part) const +{ + switch (part) { + case MeterPart: + return true; + default: + return false; + } +} + +IntSize RenderThemeWin::meterSizeForBounds(const RenderMeter*, const IntRect& bounds) const +{ + return bounds.size(); +} + +bool RenderThemeWin::paintMeter(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) +{ + if (!renderObject->isMeter()) + return true; + + HTMLMeterElement* element = toRenderMeter(renderObject)->meterElement(); + + ThemeData theme = getThemeData(renderObject); + + int remaining = static_cast<int>((1.0 - element->valueRatio()) * static_cast<double>(rect.size().width())); + + // Draw the background + drawControl(paintInfo.context, renderObject, progressBarTheme(), theme, rect); + + // Draw the progress portion + IntRect completedRect(rect); + completedRect.contract(remaining, 0); + + theme.m_part = PP_FILL; + drawControl(paintInfo.context, renderObject, progressBarTheme(), theme, completedRect); + + return true; +} #endif + } diff --git a/Source/WebCore/rendering/RenderThemeWin.h b/Source/WebCore/rendering/RenderThemeWin.h index b97f30004..9514554fc 100644 --- a/Source/WebCore/rendering/RenderThemeWin.h +++ b/Source/WebCore/rendering/RenderThemeWin.h @@ -1,7 +1,7 @@ /* * This file is part of the WebKit project. * - * Copyright (C) 2006, 2008 Apple Computer, Inc. + * Copyright (C) 2006, 2008, 2013 Apple Computer, Inc. * Copyright (C) 2009 Kenneth Rohde Christiansen * * This library is free software; you can redistribute it and/or @@ -63,8 +63,8 @@ public: virtual Color platformInactiveSelectionForegroundColor() const; // System fonts. - virtual void systemFont(int propId, FontDescription&) const; - virtual Color systemColor(int cssValueId) const; + virtual void systemFont(CSSValueID, FontDescription&) const; + virtual Color systemColor(CSSValueID) const; virtual bool paintCheckbox(RenderObject* o, const PaintInfo& i, const IntRect& r) { return paintButton(o, i, r); } @@ -144,6 +144,13 @@ public: virtual IntPoint volumeSliderOffsetFromMuteButton(RenderBox*, const IntSize&) const OVERRIDE; #endif +#if ENABLE(METER_ELEMENT) + virtual IntSize meterSizeForBounds(const RenderMeter*, const IntRect&) const OVERRIDE; + virtual bool supportsMeter(ControlPart) const OVERRIDE; + virtual void adjustMeterStyle(StyleResolver*, RenderStyle*, Element*) const OVERRIDE; + virtual bool paintMeter(RenderObject*, const PaintInfo&, const IntRect&) OVERRIDE; +#endif + virtual bool shouldShowPlaceholderWhenFocused() const { return true; } private: @@ -154,7 +161,7 @@ private: }; RenderThemeWin(); - ~RenderThemeWin(); + virtual ~RenderThemeWin(); void addIntrinsicMargins(RenderStyle*) const; void close(); @@ -175,12 +182,14 @@ private: HANDLE menuListTheme() const; HANDLE sliderTheme() const; HANDLE spinButtonTheme() const; + HANDLE progressBarTheme() const; mutable HANDLE m_buttonTheme; mutable HANDLE m_textFieldTheme; mutable HANDLE m_menuListTheme; mutable HANDLE m_sliderTheme; mutable HANDLE m_spinButtonTheme; + mutable HANDLE m_progressBarTheme; }; }; diff --git a/Source/WebCore/rendering/RenderThemeWinCE.cpp b/Source/WebCore/rendering/RenderThemeWinCE.cpp index c5750f4bc..f32760df1 100644 --- a/Source/WebCore/rendering/RenderThemeWinCE.cpp +++ b/Source/WebCore/rendering/RenderThemeWinCE.cpp @@ -257,7 +257,7 @@ bool RenderThemeWinCE::paintMenuListButton(RenderObject* o, const PaintInfo& i, return true; } -void RenderThemeWinCE::systemFont(int propId, FontDescription& fontDescription) const +void RenderThemeWinCE::systemFont(CSSValueID, FontDescription& fontDescription) const { notImplemented(); } @@ -284,7 +284,7 @@ bool RenderThemeWinCE::supportsHover(const RenderStyle*) const } // Map a CSSValue* system color to an index understood by GetSysColor -static int cssValueIdToSysColorIndex(int cssValueId) +static int cssValueIdToSysColorIndex(CSSValueID cssValueId) { switch (cssValueId) { case CSSValueActiveborder: return COLOR_ACTIVEBORDER; @@ -319,7 +319,7 @@ static int cssValueIdToSysColorIndex(int cssValueId) } } -Color RenderThemeWinCE::systemColor(int cssValueId) const +Color RenderThemeWinCE::systemColor(CSSValueID cssValueId) const { int sysColorIndex = cssValueIdToSysColorIndex(cssValueId); if (sysColorIndex == -1) @@ -371,7 +371,7 @@ bool RenderThemeWinCE::paintSearchField(RenderObject* o, const PaintInfo& i, con bool RenderThemeWinCE::paintSearchFieldCancelButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) { - Color buttonColor = (o->node() && o->node()->active()) ? Color(138, 138, 138) : Color(186, 186, 186); + Color buttonColor = (o->node() && o->node()->isElementNode() && toElement(o->node())->active()) ? Color(138, 138, 138) : Color(186, 186, 186); IntSize cancelSize(10, 10); IntSize cancelRadius(cancelSize.width() / 2, cancelSize.height() / 2); @@ -379,7 +379,7 @@ bool RenderThemeWinCE::paintSearchFieldCancelButton(RenderObject* o, const Paint int y = r.y() + (r.height() - cancelSize.height()) / 2 + 1; IntRect cancelBounds(IntPoint(x, y), cancelSize); paintInfo.context->save(); - paintInfo.context->addRoundedRectClip(RoundedRect(cancelBounds, cancelRadius, cancelRadius, cancelRadius, cancelRadius)); + paintInfo.context->clipRoundedRect(RoundedRect(cancelBounds, cancelRadius, cancelRadius, cancelRadius, cancelRadius)); paintInfo.context->fillRect(cancelBounds, buttonColor, ColorSpaceDeviceRGB); // Draw the 'x' @@ -482,10 +482,10 @@ static HTMLMediaElement* mediaElementParent(Node* node) Node* mediaNode = node->shadowHost(); if (!mediaNode) mediaNode = node; - if (!mediaNode || !mediaNode->isElementNode() || !static_cast<Element*>(mediaNode)->isMediaElement()) + if (!mediaNode || !mediaNode->isElementNode() || !toElement(mediaNode)->isMediaElement()) return 0; - return static_cast<HTMLMediaElement*>(mediaNode); + return toHTMLMediaElement(mediaNode); } #endif diff --git a/Source/WebCore/rendering/RenderThemeWinCE.h b/Source/WebCore/rendering/RenderThemeWinCE.h index bde872c6c..c1ab7be82 100644 --- a/Source/WebCore/rendering/RenderThemeWinCE.h +++ b/Source/WebCore/rendering/RenderThemeWinCE.h @@ -62,8 +62,8 @@ namespace WebCore { virtual Color platformInactiveSelectionForegroundColor() const; // System fonts. - virtual void systemFont(int propId, FontDescription&) const; - virtual Color systemColor(int cssValueId) const; + virtual void systemFont(CSSValueID, FontDescription&) const; + virtual Color systemColor(CSSValueID) const; virtual bool paintCheckbox(RenderObject* o, const PaintInfo& i, const IntRect& r) { return paintButton(o, i, r); } diff --git a/Source/WebCore/rendering/RenderTreeAsText.cpp b/Source/WebCore/rendering/RenderTreeAsText.cpp index 5b4eb7358..dc6fcd47e 100644 --- a/Source/WebCore/rendering/RenderTreeAsText.cpp +++ b/Source/WebCore/rendering/RenderTreeAsText.cpp @@ -50,7 +50,6 @@ #include "RenderWidget.h" #include "StylePropertySet.h" #include <wtf/HexNumber.h> -#include <wtf/UnusedParam.h> #include <wtf/Vector.h> #include <wtf/unicode/CharacterNames.h> @@ -89,9 +88,15 @@ TextStream& operator<<(TextStream& ts, const IntPoint& p) return ts << "(" << p.x() << "," << p.y() << ")"; } +TextStream& operator<<(TextStream& ts, const LayoutRect& r) +{ + // FIXME: These should be printed as floats. Keeping them ints for consistency with previous test expectations. + return ts << pixelSnappedIntRect(r); +} + TextStream& operator<<(TextStream& ts, const LayoutPoint& p) { - // FIXME: These should be printed as floats. Keeping them ints for consistency with pervious test expectations. + // FIXME: These should be printed as floats. Keeping them ints for consistency with previous test expectations. return ts << "(" << p.x().toInt() << "," << p.y().toInt() << ")"; } @@ -168,7 +173,7 @@ static bool isEmptyOrUnstyledAppleStyleSpan(const Node* node) if (!node || !node->isHTMLElement() || !node->hasTagName(spanTag)) return false; - const HTMLElement* elem = static_cast<const HTMLElement*>(node); + const HTMLElement* elem = toHTMLElement(node); if (elem->getAttribute(classAttr) != "Apple-style-span") return false; @@ -221,6 +226,9 @@ void RenderTreeAsText::writeRenderObject(TextStream& ts, const RenderObject& o, if (o.node()) { String tagName = getTagName(o.node()); + // FIXME: Temporary hack to make tests pass by simulating the old generated content output. + if (o.isPseudoElement() || (o.parent() && o.parent()->isPseudoElement())) + tagName = emptyAtom; if (!tagName.isEmpty()) { ts << " {" << tagName << "}"; // flag empty or unstyled AppleStyleSpan because we never @@ -425,17 +433,16 @@ void RenderTreeAsText::writeRenderObject(TextStream& ts, const RenderObject& o, } if (behavior & RenderAsTextShowIDAndClass) { - if (Node* node = o.node()) { - if (node->hasID()) - ts << " id=\"" + static_cast<Element*>(node)->getIdAttribute() + "\""; + if (Element* element = o.node() && o.node()->isElementNode() ? toElement(o.node()) : 0) { + if (element->hasID()) + ts << " id=\"" + element->getIdAttribute() + "\""; - if (node->hasClass()) { + if (element->hasClass()) { ts << " class=\""; - StyledElement* styledElement = static_cast<StyledElement*>(node); - for (size_t i = 0; i < styledElement->classNames().size(); ++i) { + for (size_t i = 0; i < element->classNames().size(); ++i) { if (i > 0) ts << " "; - ts << styledElement->classNames()[i]; + ts << element->classNames()[i]; } ts << "\""; } @@ -579,7 +586,7 @@ void write(TextStream& ts, const RenderObject& o, int indent, RenderAsTextBehavi if (o.isWidget()) { Widget* widget = toRenderWidget(&o)->widget(); if (widget && widget->isFrameView()) { - FrameView* view = static_cast<FrameView*>(widget); + FrameView* view = toFrameView(widget); RenderView* root = view->frame()->contentRenderer(); if (root) { view->layout(); @@ -665,8 +672,8 @@ static void writeRenderRegionList(const RenderRegionList& flowThreadRegionList, String tagName = getTagName(renderRegion->generatingNode()); if (!tagName.isEmpty()) ts << " {" << tagName << "}"; - if (renderRegion->generatingNode()->isElementNode() && renderRegion->generatingNode()->hasID()) { - Element* element = static_cast<Element*>(renderRegion->generatingNode()); + if (renderRegion->generatingNode()->isElementNode() && toElement(renderRegion->generatingNode())->hasID()) { + Element* element = toElement(renderRegion->generatingNode()); ts << " #" << element->idForStyleResolution(); } if (renderRegion->hasCustomRegionStyle()) @@ -712,6 +719,12 @@ static void writeRenderNamedFlowThreads(TextStream& ts, RenderView* renderView, } } +static LayoutSize maxLayoutOverflow(const RenderBox* box) +{ + LayoutRect overflowRect = box->layoutOverflowRect(); + return LayoutSize(overflowRect.maxX(), overflowRect.maxY()); +} + static void writeLayers(TextStream& ts, const RenderLayer* rootLayer, RenderLayer* l, const LayoutRect& paintRect, int indent, RenderAsTextBehavior behavior) { @@ -720,7 +733,7 @@ static void writeLayers(TextStream& ts, const RenderLayer* rootLayer, RenderLaye if (rootLayer == l) { paintDirtyRect.setWidth(max<LayoutUnit>(paintDirtyRect.width(), rootLayer->renderBox()->layoutOverflowRect().maxX())); paintDirtyRect.setHeight(max<LayoutUnit>(paintDirtyRect.height(), rootLayer->renderBox()->layoutOverflowRect().maxY())); - l->setSize(l->size().expandedTo(pixelSnappedIntSize(l->renderBox()->maxLayoutOverflow(), LayoutPoint(0, 0)))); + l->setSize(l->size().expandedTo(pixelSnappedIntSize(maxLayoutOverflow(l->renderBox()), LayoutPoint(0, 0)))); } // Calculate the clip rects we should use. @@ -763,14 +776,22 @@ static void writeLayers(TextStream& ts, const RenderLayer* rootLayer, RenderLaye } if (Vector<RenderLayer*>* posList = l->posZOrderList()) { - int currIndent = indent; - if (behavior & RenderAsTextShowLayerNesting) { - writeIndent(ts, indent); - ts << " positive z-order list(" << posList->size() << ")\n"; - ++currIndent; - } + size_t layerCount = 0; for (unsigned i = 0; i != posList->size(); ++i) - writeLayers(ts, rootLayer, posList->at(i), paintDirtyRect, currIndent, behavior); + if (!posList->at(i)->isOutOfFlowRenderFlowThread()) + ++layerCount; + if (layerCount) { + int currIndent = indent; + if (behavior & RenderAsTextShowLayerNesting) { + writeIndent(ts, indent); + ts << " positive z-order list(" << layerCount << ")\n"; + ++currIndent; + } + for (unsigned i = 0; i != posList->size(); ++i) { + if (!posList->at(i)->isOutOfFlowRenderFlowThread()) + writeLayers(ts, rootLayer, posList->at(i), paintDirtyRect, currIndent, behavior); + } + } } // Altough the RenderFlowThread requires a layer, it is not collected by its parent, @@ -788,7 +809,7 @@ static String nodePosition(Node* node) Element* body = node->document()->body(); Node* parent; for (Node* n = node; n; n = parent) { - parent = n->parentOrHostNode(); + parent = n->parentOrShadowHostNode(); if (n != node) result.appendLiteral(" of "); if (parent) { @@ -821,7 +842,7 @@ static void writeSelection(TextStream& ts, const RenderObject* o) if (!n || !n->isDocumentNode()) return; - Document* doc = static_cast<Document*>(n); + Document* doc = toDocument(n); Frame* frame = doc->frame(); if (!frame) return; @@ -898,12 +919,10 @@ String counterValueForElement(Element* element) TextStream stream; bool isFirstCounter = true; // The counter renderers should be children of :before or :after pseudo-elements. - if (RenderObject* renderer = element->renderer()) { - if (RenderObject* pseudoElement = renderer->beforePseudoElementRenderer()) - writeCounterValuesFromChildren(stream, pseudoElement, isFirstCounter); - if (RenderObject* pseudoElement = renderer->afterPseudoElementRenderer()) - writeCounterValuesFromChildren(stream, pseudoElement, isFirstCounter); - } + if (RenderObject* before = element->pseudoElementRenderer(BEFORE)) + writeCounterValuesFromChildren(stream, before, isFirstCounter); + if (RenderObject* after = element->pseudoElementRenderer(AFTER)) + writeCounterValuesFromChildren(stream, after, isFirstCounter); return stream.release(); } diff --git a/Source/WebCore/rendering/RenderTreeAsText.h b/Source/WebCore/rendering/RenderTreeAsText.h index 3a83849d5..39061a0ff 100644 --- a/Source/WebCore/rendering/RenderTreeAsText.h +++ b/Source/WebCore/rendering/RenderTreeAsText.h @@ -38,6 +38,7 @@ class Frame; class IntPoint; class IntRect; class LayoutPoint; +class LayoutRect; class RenderObject; class TextStream; @@ -71,6 +72,7 @@ static void writeRenderObject(TextStream& ts, const RenderObject& o, RenderAsTex TextStream& operator<<(TextStream&, const IntPoint&); TextStream& operator<<(TextStream&, const IntRect&); TextStream& operator<<(TextStream&, const LayoutPoint&); +TextStream& operator<<(TextStream&, const LayoutRect&); TextStream& operator<<(TextStream&, const FloatPoint&); TextStream& operator<<(TextStream&, const FloatSize&); diff --git a/Source/WebCore/rendering/RenderVideo.cpp b/Source/WebCore/rendering/RenderVideo.cpp index 11269e252..61ec6f244 100644 --- a/Source/WebCore/rendering/RenderVideo.cpp +++ b/Source/WebCore/rendering/RenderVideo.cpp @@ -38,6 +38,7 @@ #include "Page.h" #include "PaintInfo.h" #include "RenderView.h" +#include <wtf/StackStats.h> #if ENABLE(FULLSCREEN_API) #include "RenderFullScreen.h" @@ -229,8 +230,7 @@ void RenderVideo::layout() HTMLVideoElement* RenderVideo::videoElement() const { - ASSERT(node()->hasTagName(videoTag)); - return static_cast<HTMLVideoElement*>(node()); + return toHTMLVideoElement(node()); } void RenderVideo::updateFromElement() @@ -262,9 +262,9 @@ void RenderVideo::updatePlayer() mediaPlayer->setVisible(true); } -LayoutUnit RenderVideo::computeReplacedLogicalWidth(bool includeMaxWidth) const +LayoutUnit RenderVideo::computeReplacedLogicalWidth(ShouldComputePreferred shouldComputePreferred) const { - return RenderReplaced::computeReplacedLogicalWidth(includeMaxWidth); + return RenderReplaced::computeReplacedLogicalWidth(shouldComputePreferred); } LayoutUnit RenderVideo::computeReplacedLogicalHeight() const @@ -338,6 +338,14 @@ LayoutUnit RenderVideo::offsetHeight() const } #endif +bool RenderVideo::foregroundIsKnownToBeOpaqueInRect(const LayoutRect& localRect, unsigned maxDepthToTest) const +{ + if (videoElement()->shouldDisplayPosterImage()) + return RenderImage::foregroundIsKnownToBeOpaqueInRect(localRect, maxDepthToTest); + + return videoBox().contains(enclosingIntRect(localRect)); +} + } // namespace WebCore #endif diff --git a/Source/WebCore/rendering/RenderVideo.h b/Source/WebCore/rendering/RenderVideo.h index 78f855068..4a256d188 100644 --- a/Source/WebCore/rendering/RenderVideo.h +++ b/Source/WebCore/rendering/RenderVideo.h @@ -70,7 +70,7 @@ private: virtual void layout(); - virtual LayoutUnit computeReplacedLogicalWidth(bool includeMaxWidth = true) const; + virtual LayoutUnit computeReplacedLogicalWidth(ShouldComputePreferred = ComputeActual) const OVERRIDE; virtual LayoutUnit computeReplacedLogicalHeight() const; virtual LayoutUnit minimumReplacedHeight() const OVERRIDE; @@ -83,12 +83,14 @@ private: void updatePlayer(); + virtual bool foregroundIsKnownToBeOpaqueInRect(const LayoutRect& localRect, unsigned maxDepthToTest) const OVERRIDE; + LayoutSize m_cachedImageSize; }; inline RenderVideo* toRenderVideo(RenderObject* object) { - ASSERT(!object || object->isVideo()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isVideo()); return static_cast<RenderVideo*>(object); } diff --git a/Source/WebCore/rendering/RenderView.cpp b/Source/WebCore/rendering/RenderView.cpp index bdb9c8069..251659fc1 100644 --- a/Source/WebCore/rendering/RenderView.cpp +++ b/Source/WebCore/rendering/RenderView.cpp @@ -27,19 +27,23 @@ #include "FloatQuad.h" #include "FlowThreadController.h" #include "Frame.h" +#include "FrameSelection.h" #include "FrameView.h" #include "GraphicsContext.h" #include "HTMLFrameOwnerElement.h" +#include "HTMLIFrameElement.h" #include "HitTestResult.h" #include "Page.h" #include "RenderGeometryMap.h" #include "RenderLayer.h" +#include "RenderLayerBacking.h" #include "RenderNamedFlowThread.h" #include "RenderSelectionInfo.h" #include "RenderWidget.h" #include "RenderWidgetProtector.h" #include "StyleInheritedData.h" #include "TransformState.h" +#include <wtf/StackStats.h> #if USE(ACCELERATED_COMPOSITING) #include "RenderLayerCompositor.h" @@ -51,9 +55,9 @@ namespace WebCore { -RenderView::RenderView(Node* node, FrameView* view) - : RenderBlock(node) - , m_frameView(view) +RenderView::RenderView(Document* document) + : RenderBlock(document) + , m_frameView(document->view()) , m_selectionStart(0) , m_selectionEnd(0) , m_selectionStartPos(-1) @@ -65,12 +69,8 @@ RenderView::RenderView(Node* node, FrameView* view) , m_layoutStateDisableCount(0) , m_renderQuoteHead(0) , m_renderCounterCount(0) - , m_layoutPhase(RenderViewNormalLayout) + , m_selectionWasCaret(false) { - // Clear our anonymous bit, set because RenderObject assumes - // any renderer with document as the node is anonymous. - setIsAnonymous(false); - // init RenderObject attributes setInline(false); @@ -79,7 +79,7 @@ RenderView::RenderView(Node* node, FrameView* view) setPreferredLogicalWidthsDirty(true, MarkOnlyThis); - setPositioned(true); // to 0,0 :) + setPositionState(AbsolutePosition); // to 0,0 :) } RenderView::~RenderView() @@ -93,12 +93,21 @@ bool RenderView::hitTest(const HitTestRequest& request, HitTestResult& result) bool RenderView::hitTest(const HitTestRequest& request, const HitTestLocation& location, HitTestResult& result) { - bool inside = layer()->hitTest(request, location, result); + if (layer()->hitTest(request, location, result)) + return true; - // Next set up the correct :hover/:active state along the new chain. - document()->updateHoverActiveState(request, result); + // FIXME: Consider if this test should be done unconditionally. + if (request.allowsFrameScrollbars() && m_frameView) { + // ScrollView scrollbars are not the same as RenderLayer scrollbars tested by RenderLayer::hitTestOverflowControls, + // so we need to test ScrollView scrollbars separately here. + Scrollbar* frameScrollbar = m_frameView->scrollbarAtPoint(location.roundedPoint()); + if (frameScrollbar) { + result.setScrollbar(frameScrollbar); + return true; + } + } - return inside; + return false; } void RenderView::computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit, LogicalExtentComputedValues& computedValues) const @@ -112,19 +121,12 @@ void RenderView::updateLogicalWidth() setLogicalWidth(viewLogicalWidth()); } -void RenderView::computePreferredLogicalWidths() -{ - ASSERT(preferredLogicalWidthsDirty()); - - RenderBlock::computePreferredLogicalWidths(); -} - -LayoutUnit RenderView::availableLogicalHeight() const +LayoutUnit RenderView::availableLogicalHeight(AvailableLogicalHeightType heightType) const { // If we have columns, then the available logical height is reduced to the column height. if (hasColumns()) return columnInfo()->columnHeight(); - return RenderBlock::availableLogicalHeight(); + return RenderBlock::availableLogicalHeight(heightType); } bool RenderView::isChildAllowed(RenderObject* child, RenderStyle*) const @@ -154,6 +156,110 @@ void RenderView::checkLayoutState(const LayoutState& state) } #endif +static RenderBox* enclosingSeamlessRenderer(Document* doc) +{ + if (!doc) + return 0; + Element* ownerElement = doc->seamlessParentIFrame(); + if (!ownerElement) + return 0; + return ownerElement->renderBox(); +} + +void RenderView::addChild(RenderObject* newChild, RenderObject* beforeChild) +{ + // Seamless iframes are considered part of an enclosing render flow thread from the parent document. This is necessary for them to look + // up regions in the parent document during layout. + if (newChild && !newChild->isRenderFlowThread()) { + RenderBox* seamlessBox = enclosingSeamlessRenderer(document()); + if (seamlessBox && seamlessBox->flowThreadContainingBlock()) + newChild->setFlowThreadState(seamlessBox->flowThreadState()); + } + RenderBlock::addChild(newChild, beforeChild); +} + +bool RenderView::initializeLayoutState(LayoutState& state) +{ + bool isSeamlessAncestorInFlowThread = false; + + // FIXME: May be better to push a clip and avoid issuing offscreen repaints. + state.m_clipped = false; + + // Check the writing mode of the seamless ancestor. It has to match our document's writing mode, or we won't inherit any + // pagination information. + RenderBox* seamlessAncestor = enclosingSeamlessRenderer(document()); + LayoutState* seamlessLayoutState = seamlessAncestor ? seamlessAncestor->view()->layoutState() : 0; + bool shouldInheritPagination = seamlessLayoutState && !m_pageLogicalHeight && seamlessAncestor->style()->writingMode() == style()->writingMode(); + + state.m_pageLogicalHeight = shouldInheritPagination ? seamlessLayoutState->m_pageLogicalHeight : m_pageLogicalHeight; + state.m_pageLogicalHeightChanged = shouldInheritPagination ? seamlessLayoutState->m_pageLogicalHeightChanged : m_pageLogicalHeightChanged; + state.m_isPaginated = state.m_pageLogicalHeight; + if (state.m_isPaginated && shouldInheritPagination) { + // Set up the correct pagination offset. We can use a negative offset in order to push the top of the RenderView into its correct place + // on a page. We can take the iframe's offset from the logical top of the first page and make the negative into the pagination offset within the child + // view. + bool isFlipped = seamlessAncestor->style()->isFlippedBlocksWritingMode(); + LayoutSize layoutOffset = seamlessLayoutState->layoutOffset(); + LayoutSize iFrameOffset(layoutOffset.width() + seamlessAncestor->x() + (!isFlipped ? seamlessAncestor->borderLeft() + seamlessAncestor->paddingLeft() : + seamlessAncestor->borderRight() + seamlessAncestor->paddingRight()), + layoutOffset.height() + seamlessAncestor->y() + (!isFlipped ? seamlessAncestor->borderTop() + seamlessAncestor->paddingTop() : + seamlessAncestor->borderBottom() + seamlessAncestor->paddingBottom())); + + LayoutSize offsetDelta = seamlessLayoutState->m_pageOffset - iFrameOffset; + state.m_pageOffset = offsetDelta; + + // Set the current render flow thread to point to our ancestor. This will allow the seamless document to locate the correct + // regions when doing a layout. + if (seamlessAncestor->flowThreadContainingBlock()) { + flowThreadController()->setCurrentRenderFlowThread(seamlessAncestor->view()->flowThreadController()->currentRenderFlowThread()); + isSeamlessAncestorInFlowThread = true; + } + } + + // FIXME: We need to make line grids and exclusions work with seamless iframes as well here. Basically all layout state information needs + // to propagate here and not just pagination information. + return isSeamlessAncestorInFlowThread; +} + +// The algorithm below assumes this is a full layout. In case there are previously computed values for regions, supplemental steps are taken +// to ensure the results are the same as those obtained from a full layout (i.e. the auto-height regions from all the flows are marked as needing +// layout). +// 1. The flows are laid out from the outer flow to the inner flow. This successfully computes the outer non-auto-height regions size so the +// inner flows have the necessary information to correctly fragment the content. +// 2. The flows are laid out from the inner flow to the outer flow. After an inner flow is laid out it goes into the constrained layout phase +// and marks the auto-height regions they need layout. This means the outer flows will relayout if they depend on regions with auto-height regions +// belonging to inner flows. This step will correctly set the computedAutoHeight for the auto-height regions. It's possible for non-auto-height +// regions to relayout if they depend on auto-height regions. This will invalidate the inner flow threads and mark them as needing layout. +// 3. The last step is to do one last layout if there are pathological dependencies between non-auto-height regions and auto-height regions +// as detected in the previous step. +void RenderView::layoutContentInAutoLogicalHeightRegions(const LayoutState& state) +{ + // We need to invalidate all the flows with auto-height regions if one such flow needs layout. + // If none is found we do a layout a check back again afterwards. + if (!flowThreadController()->updateFlowThreadsNeedingLayout()) { + // Do a first layout of the content. In some cases more layouts are not needed (e.g. only flows with non-auto-height regions have changed). + layoutContent(state); + + // If we find no named flow needing a two step layout after the first layout, exit early. + // Otherwise, initiate the two step layout algorithm and recompute all the flows. + if (!flowThreadController()->updateFlowThreadsNeedingTwoStepLayout()) + return; + } + + // Layout to recompute all the named flows with auto-height regions. + layoutContent(state); + + // Propagate the computed auto-height values upwards. + // Non-auto-height regions may invalidate the flow thread because they depended on auto-height regions, but that's ok. + flowThreadController()->updateFlowThreadsIntoConstrainedPhase(); + + // Do one last layout that should update the auto-height regions found in the main flow + // and solve pathological dependencies between regions (e.g. a non-auto-height region depending + // on an auto-height one). + if (needsLayout()) + layoutContent(state); +} + void RenderView::layout() { StackStats::LayoutCheckPoint layoutCheckPoint; @@ -168,10 +274,17 @@ void RenderView::layout() if (relayoutChildren) { setChildNeedsLayout(true, MarkOnlyThis); for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { - if ((child->isBox() && toRenderBox(child)->hasRelativeLogicalHeight()) + if ((child->isBox() && (toRenderBox(child)->hasRelativeLogicalHeight() || toRenderBox(child)->hasViewportPercentageLogicalHeight())) || child->style()->logicalHeight().isPercent() || child->style()->logicalMinHeight().isPercent() - || child->style()->logicalMaxHeight().isPercent()) + || child->style()->logicalMaxHeight().isPercent() + || child->style()->logicalHeight().isViewportPercentage() + || child->style()->logicalMinHeight().isViewportPercentage() + || child->style()->logicalMaxHeight().isViewportPercentage() +#if ENABLE(SVG) + || child->isSVGRoot() +#endif + ) child->setChildNeedsLayout(true, MarkOnlyThis); } } @@ -181,33 +294,37 @@ void RenderView::layout() return; LayoutState state; - // FIXME: May be better to push a clip and avoid issuing offscreen repaints. - state.m_clipped = false; - state.m_pageLogicalHeight = m_pageLogicalHeight; - state.m_pageLogicalHeightChanged = m_pageLogicalHeightChanged; - state.m_isPaginated = state.m_pageLogicalHeight; + bool isSeamlessAncestorInFlowThread = initializeLayoutState(state); + m_pageLogicalHeightChanged = false; m_layoutState = &state; - m_layoutPhase = RenderViewNormalLayout; - bool needsTwoPassLayoutForAutoLogicalHeightRegions = hasRenderNamedFlowThreads() && flowThreadController()->hasAutoLogicalHeightRegions(); - - if (needsTwoPassLayoutForAutoLogicalHeightRegions) - flowThreadController()->resetRegionsOverrideLogicalContentHeight(); - - layoutContent(state); - - if (needsTwoPassLayoutForAutoLogicalHeightRegions) { - m_layoutPhase = ConstrainedFlowThreadsLayoutInAutoLogicalHeightRegions; - flowThreadController()->markAutoLogicalHeightRegionsForLayout(); + if (checkTwoPassLayoutForAutoHeightRegions()) + layoutContentInAutoLogicalHeightRegions(state); + else layoutContent(state); - } #ifndef NDEBUG checkLayoutState(state); #endif m_layoutState = 0; setNeedsLayout(false); + + if (isSeamlessAncestorInFlowThread) + flowThreadController()->setCurrentRenderFlowThread(0); +} + +LayoutUnit RenderView::pageOrViewLogicalHeight() const +{ + if (document()->printing()) + return pageLogicalHeight(); + + if (hasColumns() && !style()->hasInlineColumnAxis()) { + if (int pageLength = frameView()->pagination().pageLength) + return pageLength; + } + + return viewLogicalHeight(); } void RenderView::mapLocalToContainer(const RenderLayerModelObject* repaintContainer, TransformState& transformState, MapCoordinatesFlags mode, bool* wasFixed) const @@ -294,7 +411,7 @@ void RenderView::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) ASSERT(LayoutPoint(IntPoint(paintOffset.x(), paintOffset.y())) == paintOffset); // This avoids painting garbage between columns if there is a column gap. - if (m_frameView && m_frameView->pagination().mode != Pagination::Unpaginated) + if (m_frameView && m_frameView->pagination().mode != Pagination::Unpaginated && paintInfo.shouldPaintWithinRoot(this)) paintInfo.context->fillRect(paintInfo.rect, m_frameView->baseBackgroundColor(), ColorSpaceDeviceRGB); paintObject(paintInfo, paintOffset); @@ -328,6 +445,9 @@ static inline bool rendererObscuresBackground(RenderObject* rootObject) void RenderView::paintBoxDecorations(PaintInfo& paintInfo, const LayoutPoint&) { + if (!paintInfo.shouldPaintWithinRoot(this)) + return; + // Check to see if we are enclosed by a layer that requires complex painting rules. If so, we cannot blit // when scrolling, and we need to use slow repaints. Examples of layers that require this are transparent layers, // layers with reflections, or transformed layers. @@ -354,6 +474,9 @@ void RenderView::paintBoxDecorations(PaintInfo& paintInfo, const LayoutPoint&) if (document()->ownerElement() || !view()) return; + if (paintInfo.skipRootBackground()) + return; + bool rootFillsViewport = false; bool rootObscuresBackground = false; Node* documentElement = document()->documentElement(); @@ -452,6 +575,14 @@ void RenderView::repaintViewAndCompositedLayers() #endif } +LayoutRect RenderView::visualOverflowRect() const +{ + if (m_frameView->paintsEntireContents()) + return layoutOverflowRect(); + + return RenderBlock::visualOverflowRect(); +} + void RenderView::computeRectForRepaint(const RenderLayerModelObject* repaintContainer, LayoutRect& rect, bool fixed) const { // If a container was specified, and was not 0 or the RenderView, @@ -533,7 +664,7 @@ IntRect RenderView::selectionBounds(bool clipToVisibleContent) const // RenderSelectionInfo::rect() is in the coordinates of the repaintContainer, so map to page coordinates. LayoutRect currRect = info->rect(); if (RenderLayerModelObject* repaintContainer = info->repaintContainer()) { - FloatQuad absQuad = repaintContainer->localToAbsoluteQuad(FloatRect(currRect), SnapOffsetForTransforms); + FloatQuad absQuad = repaintContainer->localToAbsoluteQuad(FloatRect(currRect)); currRect = absQuad.enclosingBoundingBox(); } selRect.unite(currRect); @@ -541,6 +672,30 @@ IntRect RenderView::selectionBounds(bool clipToVisibleContent) const return pixelSnappedIntRect(selRect); } +void RenderView::repaintSelection() const +{ + document()->updateStyleIfNeeded(); + + HashSet<RenderBlock*> processedBlocks; + + RenderObject* end = rendererAfterPosition(m_selectionEnd, m_selectionEndPos); + for (RenderObject* o = m_selectionStart; o && o != end; o = o->nextInPreOrder()) { + if (!o->canBeSelectionLeaf() && o != m_selectionStart && o != m_selectionEnd) + continue; + if (o->selectionState() == SelectionNone) + continue; + + RenderSelectionInfo(o, true).repaint(); + + // Blocks are responsible for painting line gaps and margin gaps. They must be examined as well. + for (RenderBlock* block = o->containingBlock(); block && !block->isRenderView(); block = block->containingBlock()) { + if (!processedBlocks.add(block).isNewEntry) + break; + RenderSelectionInfo(block, true).repaint(); + } + } +} + #if USE(ACCELERATED_COMPOSITING) // Compositing layer dimensions take outline size into account, so we have to recompute layer // bounds when it changes. @@ -563,9 +718,14 @@ void RenderView::setSelection(RenderObject* start, int startPos, RenderObject* e if ((start && !end) || (end && !start)) return; + bool caretChanged = m_selectionWasCaret != view()->frame()->selection()->isCaret(); + m_selectionWasCaret = view()->frame()->selection()->isCaret(); // Just return if the selection hasn't changed. if (m_selectionStart == start && m_selectionStartPos == startPos && - m_selectionEnd == end && m_selectionEndPos == endPos) + m_selectionEnd == end && m_selectionEndPos == endPos && !caretChanged) + return; + + if ((start && end) && (start->flowThreadContainingBlock() != end->flowThreadContainingBlock())) return; // Record the old selected objects. These will be used later @@ -819,6 +979,16 @@ IntRect RenderView::unscaledDocumentRect() const return pixelSnappedIntRect(overflowRect); } +bool RenderView::rootBackgroundIsEntirelyFixed() const +{ + RenderObject* rootObject = document()->documentElement() ? document()->documentElement()->renderer() : 0; + if (!rootObject) + return false; + + RenderObject* rootRenderer = rootObject->rendererForRootBackground(); + return rootRenderer->hasEntirelyFixedBackground(); +} + LayoutRect RenderView::backgroundRect(RenderBox* backgroundRenderer) const { if (!hasColumns()) @@ -864,12 +1034,6 @@ int RenderView::viewWidth() const int RenderView::viewLogicalHeight() const { int height = style()->isHorizontalWritingMode() ? viewHeight() : viewWidth(); - - if (hasColumns() && !style()->hasInlineColumnAxis()) { - if (int pageLength = m_frameView->pagination().pageLength) - height = pageLength; - } - return height; } @@ -884,6 +1048,7 @@ void RenderView::pushLayoutState(RenderObject* root) ASSERT(m_layoutStateDisableCount == 0); ASSERT(m_layoutState == 0); + pushLayoutStateForCurrentFlowThread(root); m_layoutState = new (renderArena()) LayoutState(root); } @@ -956,19 +1121,11 @@ RenderLayerCompositor* RenderView::compositor() } #endif -void RenderView::didMoveOnscreen() -{ -#if USE(ACCELERATED_COMPOSITING) - if (m_compositor) - m_compositor->didMoveOnscreen(); -#endif -} - -void RenderView::willMoveOffscreen() +void RenderView::setIsInWindow(bool isInWindow) { #if USE(ACCELERATED_COMPOSITING) if (m_compositor) - m_compositor->willMoveOffscreen(); + m_compositor->setIsInWindow(isInWindow); #endif } @@ -993,6 +1150,11 @@ bool RenderView::hasRenderNamedFlowThreads() const return m_flowThreadController && m_flowThreadController->hasRenderNamedFlowThreads(); } +bool RenderView::checkTwoPassLayoutForAutoHeightRegions() const +{ + return hasRenderNamedFlowThreads() && m_flowThreadController->hasFlowThreadsWithAutoLogicalHeightRegions(); +} + FlowThreadController* RenderView::flowThreadController() { if (!m_flowThreadController) @@ -1001,6 +1163,30 @@ FlowThreadController* RenderView::flowThreadController() return m_flowThreadController.get(); } +void RenderView::pushLayoutStateForCurrentFlowThread(const RenderObject* object) +{ + if (!m_flowThreadController) + return; + + RenderFlowThread* currentFlowThread = m_flowThreadController->currentRenderFlowThread(); + if (!currentFlowThread) + return; + + currentFlowThread->pushFlowThreadLayoutState(object); +} + +void RenderView::popLayoutStateForCurrentFlowThread() +{ + if (!m_flowThreadController) + return; + + RenderFlowThread* currentFlowThread = m_flowThreadController->currentRenderFlowThread(); + if (!currentFlowThread) + return; + + currentFlowThread->popFlowThreadLayoutState(); +} + RenderBlock::IntervalArena* RenderView::intervalArena() { if (!m_intervalArena) @@ -1008,4 +1194,42 @@ RenderBlock::IntervalArena* RenderView::intervalArena() return m_intervalArena.get(); } +FragmentationDisabler::FragmentationDisabler(RenderObject* root) +{ + RenderView* renderView = root->view(); + ASSERT(renderView); + + LayoutState* layoutState = renderView->layoutState(); + + m_root = root; + m_fragmenting = layoutState && layoutState->isPaginated(); + m_flowThreadState = m_root->flowThreadState(); +#ifndef NDEBUG + m_layoutState = layoutState; +#endif + + if (layoutState) + layoutState->m_isPaginated = false; + + if (m_flowThreadState != RenderObject::NotInsideFlowThread) + m_root->setFlowThreadStateIncludingDescendants(RenderObject::NotInsideFlowThread); +} + +FragmentationDisabler::~FragmentationDisabler() +{ + RenderView* renderView = m_root->view(); + ASSERT(renderView); + + LayoutState* layoutState = renderView->layoutState(); +#ifndef NDEBUG + ASSERT(m_layoutState == layoutState); +#endif + + if (layoutState) + layoutState->m_isPaginated = m_fragmenting; + + if (m_flowThreadState != RenderObject::NotInsideFlowThread) + m_root->setFlowThreadStateIncludingDescendants(m_flowThreadState); +} + } // namespace WebCore diff --git a/Source/WebCore/rendering/RenderView.h b/Source/WebCore/rendering/RenderView.h index 99ab1c1a3..09466dec7 100644 --- a/Source/WebCore/rendering/RenderView.h +++ b/Source/WebCore/rendering/RenderView.h @@ -31,8 +31,8 @@ namespace WebCore { class FlowThreadController; -class RenderWidget; class RenderQuote; +class RenderWidget; #if USE(ACCELERATED_COMPOSITING) class RenderLayerCompositor; @@ -44,7 +44,7 @@ class CustomFilterGlobalContext; class RenderView : public RenderBlock { public: - RenderView(Node*, FrameView*); + explicit RenderView(Document*); virtual ~RenderView(); bool hitTest(const HitTestRequest&, HitTestResult&); @@ -61,11 +61,8 @@ public: virtual void layout() OVERRIDE; virtual void updateLogicalWidth() OVERRIDE; virtual void computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues&) const OVERRIDE; - // FIXME: This override is not needed and should be removed - // it only exists to make computePreferredLogicalWidths public. - virtual void computePreferredLogicalWidths() OVERRIDE; - virtual LayoutUnit availableLogicalHeight() const OVERRIDE; + virtual LayoutUnit availableLogicalHeight(AvailableLogicalHeightType) const OVERRIDE; // The same as the FrameView's layoutHeight/layoutWidth but with null check guards. int viewHeight() const; @@ -77,6 +74,7 @@ public: FrameView* frameView() const { return m_frameView; } + virtual LayoutRect visualOverflowRect() const OVERRIDE; virtual void computeRectForRepaint(const RenderLayerModelObject* repaintContainer, LayoutRect&, bool fixed = false) const OVERRIDE; void repaintViewRectangle(const LayoutRect&, bool immediate = false) const; // Repaint the view, and all composited layers that intersect the given absolute rectangle. @@ -95,6 +93,7 @@ public: RenderObject* selectionEnd() const { return m_selectionEnd; } IntRect selectionBounds(bool clipToVisibleContent = true) const; void selectionStartEnd(int& startPos, int& endPos) const; + void repaintSelection() const; bool printing() const; @@ -161,14 +160,15 @@ public: virtual void updateHitTestResult(HitTestResult&, const LayoutPoint&); - unsigned pageLogicalHeight() const { return m_pageLogicalHeight; } - void setPageLogicalHeight(unsigned height) + LayoutUnit pageLogicalHeight() const { return m_pageLogicalHeight; } + void setPageLogicalHeight(LayoutUnit height) { if (m_pageLogicalHeight != height) { m_pageLogicalHeight = height; m_pageLogicalHeightChanged = true; } } + LayoutUnit pageOrViewLogicalHeight() const; // FIXME: These functions are deprecated. No code should be added that uses these. int bestTruncatedAt() const { return m_legacyPrinting.m_bestTruncatedAt; } @@ -185,10 +185,8 @@ public: void setPrintRect(const IntRect& r) { m_legacyPrinting.m_printRect = r; } // End deprecated functions. - // Notifications that this view became visible in a window, or will be - // removed from the window. - void didMoveOnscreen(); - void willMoveOffscreen(); + // Notification that this view moved into or out of a native window. + void setIsInWindow(bool); #if USE(ACCELERATED_COMPOSITING) RenderLayerCompositor* compositor(); @@ -204,13 +202,13 @@ public: IntRect documentRect() const; + // Renderer that paints the root background has background-images which all have background-attachment: fixed. + bool rootBackgroundIsEntirelyFixed() const; + bool hasRenderNamedFlowThreads() const; + bool checkTwoPassLayoutForAutoHeightRegions() const; FlowThreadController* flowThreadController(); - enum RenderViewLayoutPhase { RenderViewNormalLayout, ConstrainedFlowThreadsLayoutInAutoLogicalHeightRegions }; - bool normalLayoutPhase() const { return m_layoutPhase == RenderViewNormalLayout; } - bool constrainedFlowThreadsLayoutPhase() const { return m_layoutPhase == ConstrainedFlowThreadsLayoutInAutoLogicalHeightRegions; } - void styleDidChange(StyleDifference, const RenderStyle* oldStyle); IntervalArena* intervalArena(); @@ -227,30 +225,37 @@ public: void addRenderCounter() { m_renderCounterCount++; } void removeRenderCounter() { ASSERT(m_renderCounterCount > 0); m_renderCounterCount--; } bool hasRenderCounters() { return m_renderCounterCount; } + + virtual void addChild(RenderObject* newChild, RenderObject* beforeChild = 0) OVERRIDE; + + IntRect pixelSnappedLayoutOverflowRect() const { return pixelSnappedIntRect(layoutOverflowRect()); } protected: - virtual void mapLocalToContainer(const RenderLayerModelObject* repaintContainer, TransformState&, MapCoordinatesFlags = ApplyContainerFlip | SnapOffsetForTransforms, bool* wasFixed = 0) const OVERRIDE; + virtual void mapLocalToContainer(const RenderLayerModelObject* repaintContainer, TransformState&, MapCoordinatesFlags = ApplyContainerFlip, bool* wasFixed = 0) const OVERRIDE; virtual const RenderObject* pushMappingToContainer(const RenderLayerModelObject* ancestorToStopAt, RenderGeometryMap&) const OVERRIDE; virtual void mapAbsoluteToLocalPoint(MapCoordinatesFlags, TransformState&) const; virtual bool requiresColumns(int desiredColumnCount) const OVERRIDE; - + private: + bool initializeLayoutState(LayoutState&); + virtual void calcColumnWidth() OVERRIDE; virtual ColumnInfo::PaginationUnit paginationUnit() const OVERRIDE; bool shouldRepaint(const LayoutRect&) const; // These functions may only be accessed by LayoutStateMaintainer. - void pushLayoutState(RenderFlowThread*, bool regionsChanged); bool pushLayoutState(RenderBox* renderer, const LayoutSize& offset, LayoutUnit pageHeight = 0, bool pageHeightChanged = false, ColumnInfo* colInfo = 0) { // We push LayoutState even if layoutState is disabled because it stores layoutDelta too. - if (!doingFullRepaint() || m_layoutState->isPaginated() || renderer->hasColumns() || renderer->inRenderFlowThread() + if (!doingFullRepaint() || m_layoutState->isPaginated() || renderer->hasColumns() || renderer->flowThreadContainingBlock() || m_layoutState->lineGrid() || (renderer->style()->lineGrid() != RenderStyle::initialLineGrid() && renderer->isBlockFlow()) -#if ENABLE(CSS_EXCLUSIONS) - || (renderer->isRenderBlock() && toRenderBlock(renderer)->exclusionShapeInsideInfo()) +#if ENABLE(CSS_SHAPES) + || (renderer->isRenderBlock() && toRenderBlock(renderer)->shapeInsideInfo()) + || (m_layoutState->shapeInsideInfo() && renderer->isRenderBlock() && !toRenderBlock(renderer)->allowsShapeInsideInfoSharing()) #endif ) { + pushLayoutStateForCurrentFlowThread(renderer); m_layoutState = new (renderArena()) LayoutState(m_layoutState, renderer, offset, pageHeight, pageHeightChanged, colInfo); return true; } @@ -262,6 +267,7 @@ private: LayoutState* state = m_layoutState; m_layoutState = state->m_next; state->destroy(renderArena()); + popLayoutStateForCurrentFlowThread(); } // Suspends the LayoutState optimization. Used under transforms that cannot be represented by @@ -273,12 +279,16 @@ private: void enableLayoutState() { ASSERT(m_layoutStateDisableCount > 0); m_layoutStateDisableCount--; } void layoutContent(const LayoutState&); + void layoutContentInAutoLogicalHeightRegions(const LayoutState&); #ifndef NDEBUG void checkLayoutState(const LayoutState&); #endif size_t getRetainedWidgets(Vector<RenderWidget*>&); void releaseWidgets(Vector<RenderWidget*>&); + + void pushLayoutStateForCurrentFlowThread(const RenderObject*); + void popLayoutStateForCurrentFlowThread(); friend class LayoutStateMaintainer; friend class LayoutStateDisabler; @@ -317,7 +327,7 @@ protected: private: bool shouldUsePrintingLayout() const; - unsigned m_pageLogicalHeight; + LayoutUnit m_pageLogicalHeight; bool m_pageLogicalHeightChanged; LayoutState* m_layoutState; unsigned m_layoutStateDisableCount; @@ -332,18 +342,19 @@ private: RenderQuote* m_renderQuoteHead; unsigned m_renderCounterCount; - RenderViewLayoutPhase m_layoutPhase; + + bool m_selectionWasCaret; }; inline RenderView* toRenderView(RenderObject* object) { - ASSERT(!object || object->isRenderView()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isRenderView()); return static_cast<RenderView*>(object); } inline const RenderView* toRenderView(const RenderObject* object) { - ASSERT(!object || object->isRenderView()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isRenderView()); return static_cast<const RenderView*>(object); } @@ -438,6 +449,20 @@ private: RenderView* m_view; }; +class FragmentationDisabler { + WTF_MAKE_NONCOPYABLE(FragmentationDisabler); +public: + FragmentationDisabler(RenderObject* root); + ~FragmentationDisabler(); +private: + RenderObject* m_root; + RenderObject::FlowThreadState m_flowThreadState; + bool m_fragmenting; +#ifndef NDEBUG + LayoutState* m_layoutState; +#endif +}; + } // namespace WebCore #endif // RenderView_h diff --git a/Source/WebCore/rendering/RenderWidget.cpp b/Source/WebCore/rendering/RenderWidget.cpp index 977751a73..271b8cfe3 100644 --- a/Source/WebCore/rendering/RenderWidget.cpp +++ b/Source/WebCore/rendering/RenderWidget.cpp @@ -32,6 +32,7 @@ #include "RenderLayer.h" #include "RenderView.h" #include "RenderWidgetProtector.h" +#include <wtf/StackStats.h> #if USE(ACCELERATED_COMPOSITING) #include "RenderLayerBacking.h" @@ -57,8 +58,8 @@ WidgetHierarchyUpdatesSuspensionScope::WidgetToParentMap& WidgetHierarchyUpdates void WidgetHierarchyUpdatesSuspensionScope::moveWidgets() { - WidgetToParentMap map = widgetNewParentMap(); - widgetNewParentMap().clear(); + WidgetToParentMap map; + widgetNewParentMap().swap(map); WidgetToParentMap::iterator end = map.end(); for (WidgetToParentMap::iterator it = map.begin(); it != end; ++it) { Widget* child = it->key.get(); @@ -85,10 +86,10 @@ static void moveWidgetToParentSoon(Widget* child, FrameView* parent) WidgetHierarchyUpdatesSuspensionScope::scheduleWidgetToMove(child, parent); } -RenderWidget::RenderWidget(Node* node) - : RenderReplaced(node) +RenderWidget::RenderWidget(Element* element) + : RenderReplaced(element) , m_widget(0) - , m_frameView(node->document()->view()) + , m_frameView(element->document()->view()) // Reference counting is used to prevent the widget from being // destroyed while inside the Widget code, which might not be // able to handle that. @@ -102,9 +103,9 @@ void RenderWidget::willBeDestroyed() if (RenderView* v = view()) v->removeWidget(this); - if (AXObjectCache::accessibilityEnabled()) { - document()->axObjectCache()->childrenChanged(this->parent()); - document()->axObjectCache()->remove(this); + if (AXObjectCache* cache = document()->existingAXObjectCache()) { + cache->childrenChanged(this->parent()); + cache->remove(this); } setWidget(0); @@ -119,7 +120,7 @@ void RenderWidget::destroy() // Grab the arena from node()->document()->renderArena() before clearing the node pointer. // Clear the node before deref-ing, as this may be deleted when deref is called. RenderArena* arena = renderArena(); - setNode(0); + clearNode(); deref(arena); } @@ -143,8 +144,9 @@ bool RenderWidget::setWidgetGeometry(const LayoutRect& frame) return false; IntRect clipRect = roundedIntRect(enclosingLayer()->childrenClipRect()); + IntRect newFrame = roundedIntRect(frame); bool clipChanged = m_clipRect != clipRect; - bool boundsChanged = m_widget->frameRect() != frame; + bool boundsChanged = m_widget->frameRect() != newFrame; if (!boundsChanged && !clipChanged) return false; @@ -153,7 +155,10 @@ bool RenderWidget::setWidgetGeometry(const LayoutRect& frame) RenderWidgetProtector protector(this); RefPtr<Node> protectedNode(node()); - m_widget->setFrameRect(roundedIntRect(frame)); + m_widget->setFrameRect(newFrame); + + if (clipChanged && !boundsChanged) + m_widget->clipRectChanged(); #if USE(ACCELERATED_COMPOSITING) if (hasLayer() && layer()->isComposited()) @@ -169,7 +174,7 @@ bool RenderWidget::updateWidgetGeometry() if (!m_widget->transformsAffectFrameRect()) return setWidgetGeometry(absoluteContentBox()); - LayoutRect absoluteContentBox(localToAbsoluteQuad(FloatQuad(contentBox), SnapOffsetForTransforms).boundingBox()); + LayoutRect absoluteContentBox(localToAbsoluteQuad(FloatQuad(contentBox)).boundingBox()); if (m_widget->isFrameView()) { contentBox.setLocation(absoluteContentBox.location()); return setWidgetGeometry(contentBox); @@ -234,6 +239,39 @@ void RenderWidget::notifyWidget(WidgetNotification notification) m_widget->notifyWidget(notification); } +void RenderWidget::paintContents(PaintInfo& paintInfo, const LayoutPoint& paintOffset) +{ + LayoutPoint adjustedPaintOffset = paintOffset + location(); + + // Tell the widget to paint now. This is the only time the widget is allowed + // to paint itself. That way it will composite properly with z-indexed layers. + IntPoint widgetLocation = m_widget->frameRect().location(); + IntPoint paintLocation(roundToInt(adjustedPaintOffset.x() + borderLeft() + paddingLeft()), + roundToInt(adjustedPaintOffset.y() + borderTop() + paddingTop())); + IntRect paintRect = paintInfo.rect; + + IntSize widgetPaintOffset = paintLocation - widgetLocation; + // When painting widgets into compositing layers, tx and ty are relative to the enclosing compositing layer, + // not the root. In this case, shift the CTM and adjust the paintRect to be root-relative to fix plug-in drawing. + if (!widgetPaintOffset.isZero()) { + paintInfo.context->translate(widgetPaintOffset); + paintRect.move(-widgetPaintOffset); + } + m_widget->paint(paintInfo.context, paintRect); + + if (!widgetPaintOffset.isZero()) + paintInfo.context->translate(-widgetPaintOffset); + + if (m_widget->isFrameView()) { + FrameView* frameView = toFrameView(m_widget.get()); + bool runOverlapTests = !frameView->useSlowRepaintsIfNotOverlapped() || frameView->hasCompositedContentIncludingDescendants(); + if (paintInfo.overlapTestRequests && runOverlapTests) { + ASSERT(!paintInfo.overlapTestRequests->contains(this)); + paintInfo.overlapTestRequests->set(this, m_widget->frameRect()); + } + } +} + void RenderWidget::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { if (!shouldPaint(paintInfo, paintOffset)) @@ -250,7 +288,7 @@ void RenderWidget::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) } if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && hasOutline()) - paintOutline(paintInfo.context, LayoutRect(adjustedPaintOffset, size())); + paintOutline(paintInfo, LayoutRect(adjustedPaintOffset, size())); if (!m_frameView || paintInfo.phase != PaintPhaseForeground) return; @@ -273,35 +311,8 @@ void RenderWidget::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) clipRoundedInnerRect(paintInfo.context, borderRect, roundedInnerRect); } - if (m_widget) { - // Tell the widget to paint now. This is the only time the widget is allowed - // to paint itself. That way it will composite properly with z-indexed layers. - IntPoint widgetLocation = m_widget->frameRect().location(); - IntPoint paintLocation(roundToInt(adjustedPaintOffset.x() + borderLeft() + paddingLeft()), - roundToInt(adjustedPaintOffset.y() + borderTop() + paddingTop())); - IntRect paintRect = paintInfo.rect; - - IntSize widgetPaintOffset = paintLocation - widgetLocation; - // When painting widgets into compositing layers, tx and ty are relative to the enclosing compositing layer, - // not the root. In this case, shift the CTM and adjust the paintRect to be root-relative to fix plug-in drawing. - if (!widgetPaintOffset.isZero()) { - paintInfo.context->translate(widgetPaintOffset); - paintRect.move(-widgetPaintOffset); - } - m_widget->paint(paintInfo.context, paintRect); - - if (!widgetPaintOffset.isZero()) - paintInfo.context->translate(-widgetPaintOffset); - - if (m_widget->isFrameView()) { - FrameView* frameView = static_cast<FrameView*>(m_widget.get()); - bool runOverlapTests = !frameView->useSlowRepaintsIfNotOverlapped() || frameView->hasCompositedContentIncludingDescendants(); - if (paintInfo.overlapTestRequests && runOverlapTests) { - ASSERT(!paintInfo.overlapTestRequests->contains(this)); - paintInfo.overlapTestRequests->set(this, m_widget->frameRect()); - } - } - } + if (m_widget) + paintContents(paintInfo, paintOffset); if (style()->hasBorderRadius()) paintInfo.context->restore(); @@ -311,13 +322,16 @@ void RenderWidget::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) // FIXME: selectionRect() is in absolute, not painting coordinates. paintInfo.context->fillRect(pixelSnappedIntRect(selectionRect()), selectionBackgroundColor(), style()->colorSpace()); } + + if (hasLayer() && layer()->canResize()) + layer()->paintResizer(paintInfo.context, roundedIntPoint(adjustedPaintOffset), paintInfo.rect); } void RenderWidget::setOverlapTestResult(bool isOverlapped) { ASSERT(m_widget); ASSERT(m_widget->isFrameView()); - static_cast<FrameView*>(m_widget.get())->setIsOverlapped(isOverlapped); + toFrameView(m_widget.get())->setIsOverlapped(isOverlapped); } void RenderWidget::deref(RenderArena *arena) @@ -336,7 +350,7 @@ void RenderWidget::updateWidgetPosition() // if the frame bounds got changed, or if view needs layout (possibly indicating // content size is wrong) we have to do a layout to set the right widget size if (m_widget && m_widget->isFrameView()) { - FrameView* frameView = static_cast<FrameView*>(m_widget.get()); + FrameView* frameView = toFrameView(m_widget.get()); // Check the frame's page to make sure that the frame isn't in the process of being destroyed. if ((boundsChanged || frameView->needsLayout()) && frameView->frame()->page()) frameView->layout(); diff --git a/Source/WebCore/rendering/RenderWidget.h b/Source/WebCore/rendering/RenderWidget.h index d20123a68..ef8f3b741 100644 --- a/Source/WebCore/rendering/RenderWidget.h +++ b/Source/WebCore/rendering/RenderWidget.h @@ -73,7 +73,7 @@ public: void deref(RenderArena*); protected: - RenderWidget(Node*); + RenderWidget(Element*); FrameView* frameView() const { return m_frameView; } @@ -85,6 +85,8 @@ protected: virtual CursorDirective getCursor(const LayoutPoint&, Cursor&) const; virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction) OVERRIDE; + virtual void paintContents(PaintInfo&, const LayoutPoint&); + private: virtual bool isWidget() const { return true; } @@ -104,13 +106,13 @@ private: inline RenderWidget* toRenderWidget(RenderObject* object) { - ASSERT(!object || object->isWidget()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isWidget()); return static_cast<RenderWidget*>(object); } inline const RenderWidget* toRenderWidget(const RenderObject* object) { - ASSERT(!object || object->isWidget()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isWidget()); return static_cast<const RenderWidget*>(object); } diff --git a/Source/WebCore/rendering/RenderWidgetProtector.h b/Source/WebCore/rendering/RenderWidgetProtector.h index 8bf6ac9bb..35c75199d 100644 --- a/Source/WebCore/rendering/RenderWidgetProtector.h +++ b/Source/WebCore/rendering/RenderWidgetProtector.h @@ -33,7 +33,7 @@ namespace WebCore { class RenderWidgetProtector { WTF_MAKE_NONCOPYABLE(RenderWidgetProtector); public: - RenderWidgetProtector(RenderWidget* object) + explicit RenderWidgetProtector(RenderWidget* object) : m_object(object) , m_arena(object->ref()) { diff --git a/Source/WebCore/rendering/RenderingAllInOne.cpp b/Source/WebCore/rendering/RenderingAllInOne.cpp index 3e155dba3..29555bf14 100644 --- a/Source/WebCore/rendering/RenderingAllInOne.cpp +++ b/Source/WebCore/rendering/RenderingAllInOne.cpp @@ -34,6 +34,7 @@ #include "FixedTableLayout.cpp" #include "HitTestingTransformState.cpp" #include "HitTestResult.cpp" +#include "HitTestLocation.cpp" #include "InlineBox.cpp" #include "InlineFlowBox.cpp" #include "InlineTextBox.cpp" @@ -74,6 +75,7 @@ #include "RenderListMarker.cpp" #include "RenderMarquee.cpp" #include "RenderMedia.cpp" +#include "RenderMediaControlElements.cpp" #include "RenderMediaControls.cpp" #include "RenderMenuList.cpp" #include "RenderMeter.cpp" @@ -108,6 +110,7 @@ #include "RenderTextControlMultiLine.cpp" #include "RenderTextControlSingleLine.cpp" #include "RenderTextFragment.cpp" +#include "RenderTextTrackCue.cpp" #include "RenderTheme.cpp" #if PLATFORM(WIN) #include "RenderThemeWin.cpp" @@ -120,3 +123,4 @@ #include "RootInlineBox.cpp" #include "ScrollBehavior.cpp" #include "break_lines.cpp" + diff --git a/Source/WebCore/rendering/RootInlineBox.cpp b/Source/WebCore/rendering/RootInlineBox.cpp index fb1ab2aa6..fc8ef94c1 100644 --- a/Source/WebCore/rendering/RootInlineBox.cpp +++ b/Source/WebCore/rendering/RootInlineBox.cpp @@ -29,6 +29,7 @@ #include "GraphicsContext.h" #include "HitTestResult.h" #include "InlineTextBox.h" +#include "LogicalSelectionOffsetCaches.h" #include "Page.h" #include "PaintInfo.h" #include "RenderArena.h" @@ -42,6 +43,13 @@ using namespace std; namespace WebCore { +struct SameSizeAsRootInlineBox : public InlineFlowBox { + unsigned variables[5]; + void* pointers[4]; +}; + +COMPILE_ASSERT(sizeof(RootInlineBox) == sizeof(SameSizeAsRootInlineBox), RootInlineBox_should_stay_small); + typedef WTF::HashMap<const RootInlineBox*, EllipsisBox*> EllipsisBoxMap; static EllipsisBoxMap* gEllipsisBoxMap = 0; @@ -180,7 +188,7 @@ void RootInlineBox::addHighlightOverflow() // Highlight acts as a selection inflation. FloatRect rootRect(0, selectionTop(), logicalWidth(), selectionHeight()); - IntRect inflatedRect = enclosingIntRect(page->chrome()->client()->customHighlightRect(renderer()->node(), renderer()->style()->highlight(), rootRect)); + IntRect inflatedRect = enclosingIntRect(page->chrome().client()->customHighlightRect(renderer()->node(), renderer()->style()->highlight(), rootRect)); setOverflowFromLogicalRects(inflatedRect, inflatedRect, lineTop(), lineBottom()); } @@ -198,9 +206,9 @@ void RootInlineBox::paintCustomHighlight(PaintInfo& paintInfo, const LayoutPoint // Get the inflated rect so that we can properly hit test. FloatRect rootRect(paintOffset.x() + x(), paintOffset.y() + selectionTop(), logicalWidth(), selectionHeight()); - FloatRect inflatedRect = page->chrome()->client()->customHighlightRect(renderer()->node(), highlightType, rootRect); + FloatRect inflatedRect = page->chrome().client()->customHighlightRect(renderer()->node(), highlightType, rootRect); if (inflatedRect.intersects(paintInfo.rect)) - page->chrome()->client()->paintCustomHighlight(renderer()->node(), highlightType, rootRect, rootRect, false, true); + page->chrome().client()->paintCustomHighlight(renderer()->node(), highlightType, rootRect, rootRect, false, true); } #endif @@ -250,28 +258,27 @@ void RootInlineBox::childRemoved(InlineBox* box) } } +RenderRegion* RootInlineBox::containingRegion() const +{ + RenderRegion* region = m_fragmentationData ? m_fragmentationData->m_containingRegion : 0; + +#ifndef NDEBUG + if (region) { + RenderFlowThread* flowThread = block()->flowThreadContainingBlock(); + const RenderRegionList& regionList = flowThread->renderRegionList(); + ASSERT(regionList.contains(region)); + } +#endif + + return region; +} + void RootInlineBox::setContainingRegion(RenderRegion* region) { ASSERT(!isDirty()); - ASSERT(block()->inRenderFlowThread()); + ASSERT(block()->flowThreadContainingBlock()); LineFragmentationData* fragmentationData = ensureLineFragmentationData(); fragmentationData->m_containingRegion = region; - fragmentationData->m_hasContainingRegion = !!region; -} - -RootInlineBox::LineFragmentationData* RootInlineBox::LineFragmentationData::sanitize(const RenderBlock* block) -{ - ASSERT(block->inRenderFlowThread()); - if (!m_containingRegion) - return this; - - RenderFlowThread* flowThread = block->enclosingRenderFlowThread(); - const RenderRegionList& regionList = flowThread->renderRegionList(); - // For pointer types the hash function is |safeToCompareToEmptyOrDeleted|. There shouldn't be any problems if m_containingRegion was deleted. - if (!regionList.contains(m_containingRegion)) - m_containingRegion = 0; - - return this; } LayoutUnit RootInlineBox::alignBoxesInBlockDirection(LayoutUnit heightOfBlock, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache& verticalPositionCache) @@ -335,6 +342,15 @@ LayoutUnit RootInlineBox::alignBoxesInBlockDirection(LayoutUnit heightOfBlock, G return heightOfBlock + maxHeight; } +#if ENABLE(CSS3_TEXT) +float RootInlineBox::maxLogicalTop() const +{ + float maxLogicalTop = 0; + computeMaxLogicalTop(maxLogicalTop); + return maxLogicalTop; +} +#endif // CSS3_TEXT + LayoutUnit RootInlineBox::beforeAnnotationsAdjustment() const { LayoutUnit result = 0; @@ -414,7 +430,7 @@ LayoutUnit RootInlineBox::lineSnapAdjustment(LayoutUnit delta) const if (layoutState->isPaginated() && layoutState->pageLogicalHeight()) { pageLogicalTop = block()->pageLogicalTopForOffset(lineTopWithLeading() + delta); if (pageLogicalTop > firstLineTopWithLeading) - firstTextTop = pageLogicalTop + lineGridBox->logicalTop() - lineGrid->borderBefore() - lineGrid->paddingBefore() + lineGridPaginationOrigin; + firstTextTop = pageLogicalTop + lineGridBox->logicalTop() - lineGrid->borderAndPaddingBefore() + lineGridPaginationOrigin; } if (block()->style()->lineSnap() == LineSnapContain) { @@ -457,8 +473,8 @@ LayoutUnit RootInlineBox::lineSnapAdjustment(LayoutUnit delta) const return lineSnapAdjustment(newPageLogicalTop - (blockOffset + lineTopWithLeading())); } -GapRects RootInlineBox::lineSelectionGap(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, - LayoutUnit selTop, LayoutUnit selHeight, const PaintInfo* paintInfo) +GapRects RootInlineBox::lineSelectionGap(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, + LayoutUnit selTop, LayoutUnit selHeight, const LogicalSelectionOffsetCaches& cache, const PaintInfo* paintInfo) { RenderObject::SelectionState lineState = selectionState(); @@ -469,12 +485,14 @@ GapRects RootInlineBox::lineSelectionGap(RenderBlock* rootBlock, const LayoutPoi InlineBox* firstBox = firstSelectedBox(); InlineBox* lastBox = lastSelectedBox(); - if (leftGap) - result.uniteLeft(block()->logicalLeftSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, - firstBox->parent()->renderer(), firstBox->logicalLeft(), selTop, selHeight, paintInfo)); - if (rightGap) - result.uniteRight(block()->logicalRightSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, - lastBox->parent()->renderer(), lastBox->logicalRight(), selTop, selHeight, paintInfo)); + if (leftGap) { + result.uniteLeft(block()->logicalLeftSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, firstBox->parent()->renderer(), firstBox->logicalLeft(), + selTop, selHeight, cache, paintInfo)); + } + if (rightGap) { + result.uniteRight(block()->logicalRightSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastBox->parent()->renderer(), lastBox->logicalRight(), + selTop, selHeight, cache, paintInfo)); + } // When dealing with bidi text, a non-contiguous selection region is possible. // e.g. The logical text aaaAAAbbb (capitals denote RTL text and non-capitals LTR) is layed out @@ -563,7 +581,7 @@ LayoutUnit RootInlineBox::selectionTop() const if (renderer()->style()->isFlippedLinesWritingMode()) return selectionTop; - LayoutUnit prevBottom = prevRootBox() ? prevRootBox()->selectionBottom() : block()->borderBefore() + block()->paddingBefore(); + LayoutUnit prevBottom = prevRootBox() ? prevRootBox()->selectionBottom() : block()->borderAndPaddingBefore(); if (prevBottom < selectionTop && block()->containsFloats()) { // This line has actually been moved further down, probably from a large line-height, but possibly because the // line was forced to clear floats. If so, let's check the offsets, and only be willing to use the previous @@ -831,8 +849,8 @@ void RootInlineBox::ascentAndDescentForBox(InlineBox* box, GlyphOverflowAndFallb } if (includeFontForBox(box) && !setUsedFont) { - int fontAscent = box->renderer()->style(isFirstLineStyle())->fontMetrics().ascent(); - int fontDescent = box->renderer()->style(isFirstLineStyle())->fontMetrics().descent(); + int fontAscent = box->renderer()->style(isFirstLineStyle())->fontMetrics().ascent(baselineType()); + int fontDescent = box->renderer()->style(isFirstLineStyle())->fontMetrics().descent(baselineType()); setAscentAndDescent(ascent, descent, fontAscent, fontDescent, ascentDescentSet); affectsAscent = fontAscent - box->logicalTop() > 0; affectsDescent = fontDescent + box->logicalTop() > 0; @@ -842,16 +860,16 @@ void RootInlineBox::ascentAndDescentForBox(InlineBox* box, GlyphOverflowAndFallb setAscentAndDescent(ascent, descent, glyphOverflow->top, glyphOverflow->bottom, ascentDescentSet); affectsAscent = glyphOverflow->top - box->logicalTop() > 0; affectsDescent = glyphOverflow->bottom + box->logicalTop() > 0; - glyphOverflow->top = min(glyphOverflow->top, max(0, glyphOverflow->top - box->renderer()->style(isFirstLineStyle())->fontMetrics().ascent())); - glyphOverflow->bottom = min(glyphOverflow->bottom, max(0, glyphOverflow->bottom - box->renderer()->style(isFirstLineStyle())->fontMetrics().descent())); + glyphOverflow->top = min(glyphOverflow->top, max(0, glyphOverflow->top - box->renderer()->style(isFirstLineStyle())->fontMetrics().ascent(baselineType()))); + glyphOverflow->bottom = min(glyphOverflow->bottom, max(0, glyphOverflow->bottom - box->renderer()->style(isFirstLineStyle())->fontMetrics().descent(baselineType()))); } if (includeMarginForBox(box)) { - LayoutUnit ascentWithMargin = box->renderer()->style(isFirstLineStyle())->fontMetrics().ascent(); - LayoutUnit descentWithMargin = box->renderer()->style(isFirstLineStyle())->fontMetrics().descent(); + LayoutUnit ascentWithMargin = box->renderer()->style(isFirstLineStyle())->fontMetrics().ascent(baselineType()); + LayoutUnit descentWithMargin = box->renderer()->style(isFirstLineStyle())->fontMetrics().descent(baselineType()); if (box->parent() && !box->renderer()->isText()) { - ascentWithMargin += box->boxModelObject()->borderBefore() + box->boxModelObject()->paddingBefore() + box->boxModelObject()->marginBefore(); - descentWithMargin += box->boxModelObject()->borderAfter() + box->boxModelObject()->paddingAfter() + box->boxModelObject()->marginAfter(); + ascentWithMargin += box->boxModelObject()->borderAndPaddingBefore() + box->boxModelObject()->marginBefore(); + descentWithMargin += box->boxModelObject()->borderAndPaddingAfter() + box->boxModelObject()->marginAfter(); } setAscentAndDescent(ascent, descent, ascentWithMargin, descentWithMargin, ascentDescentSet); diff --git a/Source/WebCore/rendering/RootInlineBox.h b/Source/WebCore/rendering/RootInlineBox.h index e7c3e54b6..05b173f56 100644 --- a/Source/WebCore/rendering/RootInlineBox.h +++ b/Source/WebCore/rendering/RootInlineBox.h @@ -28,6 +28,7 @@ namespace WebCore { class EllipsisBox; class HitTestResult; +class LogicalSelectionOffsetCaches; class RenderRegion; struct BidiStatus; @@ -35,18 +36,18 @@ struct GapRects; class RootInlineBox : public InlineFlowBox { public: - RootInlineBox(RenderBlock* block); + explicit RootInlineBox(RenderBlock*); - virtual void destroy(RenderArena*); + virtual void destroy(RenderArena*) FINAL; - virtual bool isRootInlineBox() const { return true; } + virtual bool isRootInlineBox() const FINAL { return true; } void detachEllipsisBox(RenderArena*); RootInlineBox* nextRootBox() const { return static_cast<RootInlineBox*>(m_nextLineBox); } RootInlineBox* prevRootBox() const { return static_cast<RootInlineBox*>(m_prevLineBox); } - virtual void adjustPosition(float dx, float dy); + virtual void adjustPosition(float dx, float dy) FINAL; LayoutUnit lineTop() const { return m_lineTop; } LayoutUnit lineBottom() const { return m_lineBottom; } @@ -63,8 +64,7 @@ public: LayoutUnit paginatedLineWidth() const { return m_fragmentationData ? m_fragmentationData->m_paginatedLineWidth : LayoutUnit(0); } void setPaginatedLineWidth(LayoutUnit width) { ensureLineFragmentationData()->m_paginatedLineWidth = width; } - RenderRegion* containingRegion() const { return m_fragmentationData ? m_fragmentationData->sanitize(block())->m_containingRegion : 0; } - bool hasContainingRegion() const { return m_fragmentationData ? m_fragmentationData->m_hasContainingRegion : false; } + RenderRegion* containingRegion() const; void setContainingRegion(RenderRegion*); LayoutUnit selectionTop() const; @@ -85,7 +85,7 @@ public: m_lineBottomWithLeading = bottomWithLeading; } - virtual RenderLineBoxList* rendererLineBoxes() const; + virtual RenderLineBoxList* rendererLineBoxes() const FINAL; RenderObject* lineBreakObj() const { return m_lineBreakObj; } BidiStatus lineBreakBidiStatus() const; @@ -103,19 +103,19 @@ public: // Return the truncatedWidth, the width of the truncated text + ellipsis. float placeEllipsis(const AtomicString& ellipsisStr, bool ltr, float blockLeftEdge, float blockRightEdge, float ellipsisWidth, InlineBox* markupBox = 0); // Return the position of the EllipsisBox or -1. - virtual float placeEllipsisBox(bool ltr, float blockLeftEdge, float blockRightEdge, float ellipsisWidth, float &truncatedWidth, bool& foundBox) OVERRIDE; + virtual float placeEllipsisBox(bool ltr, float blockLeftEdge, float blockRightEdge, float ellipsisWidth, float &truncatedWidth, bool& foundBox) OVERRIDE FINAL; using InlineBox::hasEllipsisBox; EllipsisBox* ellipsisBox() const; void paintEllipsisBox(PaintInfo&, const LayoutPoint&, LayoutUnit lineTop, LayoutUnit lineBottom) const; - virtual void clearTruncation() OVERRIDE; + virtual void clearTruncation() OVERRIDE FINAL; bool isHyphenated() const; - virtual int baselinePosition(FontBaseline baselineType) const; - virtual LayoutUnit lineHeight() const; + virtual int baselinePosition(FontBaseline baselineType) const FINAL; + virtual LayoutUnit lineHeight() const FINAL; #if PLATFORM(MAC) void addHighlightOverflow(); @@ -123,16 +123,17 @@ public: #endif virtual void paint(PaintInfo&, const LayoutPoint&, LayoutUnit lineTop, LayoutUnit lineBottom); - virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, LayoutUnit lineTop, LayoutUnit lineBottom) OVERRIDE; + virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, LayoutUnit lineTop, LayoutUnit lineBottom) OVERRIDE FINAL; using InlineBox::hasSelectedChildren; using InlineBox::setHasSelectedChildren; - virtual RenderObject::SelectionState selectionState(); + virtual RenderObject::SelectionState selectionState() FINAL; InlineBox* firstSelectedBox(); InlineBox* lastSelectedBox(); - GapRects lineSelectionGap(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, LayoutUnit selTop, LayoutUnit selHeight, const PaintInfo*); + GapRects lineSelectionGap(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, + LayoutUnit selTop, LayoutUnit selHeight, const LogicalSelectionOffsetCaches&, const PaintInfo*); RenderBlock* block() const; @@ -150,9 +151,9 @@ public: Vector<RenderBox*>* floatsPtr() { ASSERT(!isDirty()); return m_floats.get(); } - virtual void extractLineBoxFromRenderObject(); - virtual void attachLineBoxToRenderObject(); - virtual void removeLineBoxFromRenderObject(); + virtual void extractLineBoxFromRenderObject() FINAL; + virtual void attachLineBoxToRenderObject() FINAL; + virtual void removeLineBoxFromRenderObject() FINAL; FontBaseline baselineType() const { return static_cast<FontBaseline>(m_baselineType); } @@ -187,6 +188,11 @@ public: return InlineFlowBox::logicalBottomLayoutOverflow(lineBottom()); } +#if ENABLE(CSS3_TEXT) + // Used to calculate the underline offset for TextUnderlinePositionUnder. + float maxLogicalTop() const; +#endif // CSS3_TEXT + Node* getLogicalStartBoxWithNode(InlineBox*&) const; Node* getLogicalEndBoxWithNode(InlineBox*&) const; @@ -229,21 +235,16 @@ private: , m_paginationStrut(0) , m_paginatedLineWidth(0) , m_isFirstAfterPageBreak(false) - , m_hasContainingRegion(false) { } - LineFragmentationData* sanitize(const RenderBlock*); - // It should not be assumed the |containingRegion| is always valid. - // It can also be 0 if the flow has no region chain or an invalid pointer if the region is no longer in the chain. - // Use |sanitize| to filter an invalid region. + // It can also be 0 if the flow has no region chain. RenderRegion* m_containingRegion; LayoutUnit m_paginationStrut; LayoutUnit m_paginatedLineWidth; - unsigned m_isFirstAfterPageBreak : 1; - unsigned m_hasContainingRegion : 1; // We need to keep this to differentiate between the case of a void region and an invalid region. + bool m_isFirstAfterPageBreak; }; OwnPtr<LineFragmentationData> m_fragmentationData; diff --git a/Source/WebCore/rendering/TableLayout.h b/Source/WebCore/rendering/TableLayout.h index 750768347..67472b97c 100644 --- a/Source/WebCore/rendering/TableLayout.h +++ b/Source/WebCore/rendering/TableLayout.h @@ -31,18 +31,21 @@ class RenderTable; class TableLayout { WTF_MAKE_NONCOPYABLE(TableLayout); WTF_MAKE_FAST_ALLOCATED; public: - TableLayout(RenderTable* table) + explicit TableLayout(RenderTable* table) : m_table(table) { } virtual ~TableLayout() { } - virtual void computePreferredLogicalWidths(LayoutUnit& minWidth, LayoutUnit& maxWidth) = 0; + virtual void computeIntrinsicLogicalWidths(LayoutUnit& minWidth, LayoutUnit& maxWidth) = 0; + virtual void applyPreferredLogicalWidthQuirks(LayoutUnit& minWidth, LayoutUnit& maxWidth) const = 0; virtual void layout() = 0; protected: - const static int tableMaxWidth = 15000; + // FIXME: Once we enable SATURATED_LAYOUT_ARITHMETHIC, this should just be LayoutUnit::nearlyMax(). + // Until then though, using nearlyMax causes overflow in some tests, so we just pick a large number. + const static int tableMaxWidth = 1000000; RenderTable* m_table; }; diff --git a/Source/WebCore/rendering/TextAutosizer.cpp b/Source/WebCore/rendering/TextAutosizer.cpp index 358840dc4..903398c97 100644 --- a/Source/WebCore/rendering/TextAutosizer.cpp +++ b/Source/WebCore/rendering/TextAutosizer.cpp @@ -25,7 +25,9 @@ #include "TextAutosizer.h" #include "Document.h" +#include "HTMLElement.h" #include "InspectorInstrumentation.h" +#include "IntSize.h" #include "RenderObject.h" #include "RenderStyle.h" #include "RenderText.h" @@ -34,14 +36,52 @@ #include "StyleInheritedData.h" #include <algorithm> +#include <wtf/StdLibExtras.h> +#include <wtf/Vector.h> namespace WebCore { +using namespace HTMLNames; + struct TextAutosizingWindowInfo { IntSize windowSize; IntSize minLayoutSize; }; +// Represents cluster related data. Instances should not persist between calls to processSubtree. +struct TextAutosizingClusterInfo { + explicit TextAutosizingClusterInfo(RenderBlock* root) + : root(root) + , blockContainingAllText(0) + , maxAllowedDifferenceFromTextWidth(150) + { + } + + RenderBlock* root; + const RenderBlock* blockContainingAllText; + + // Upper limit on the difference between the width of the cluster's block containing all + // text and that of a narrow child before the child becomes a separate cluster. + float maxAllowedDifferenceFromTextWidth; + + // Descendants of the cluster that are narrower than the block containing all text and must be + // processed together. + Vector<TextAutosizingClusterInfo> narrowDescendants; +}; + + +static const Vector<QualifiedName>& formInputTags() +{ + // Returns the tags for the form input elements. + DEFINE_STATIC_LOCAL(Vector<QualifiedName>, formInputTags, ()); + if (formInputTags.isEmpty()) { + formInputTags.append(inputTag); + formInputTags.append(buttonTag); + formInputTags.append(selectTag); + } + return formInputTags; +} + TextAutosizer::TextAutosizer(Document* document) : m_document(document) { @@ -51,6 +91,16 @@ TextAutosizer::~TextAutosizer() { } +void TextAutosizer::recalculateMultipliers() +{ + RenderObject* renderer = m_document->renderer(); + while (renderer) { + if (renderer->style() && renderer->style()->textAutosizingMultiplier() != 1) + setMultiplier(renderer, 1); + renderer = renderer->nextInPreOrder(); + } +} + bool TextAutosizer::processSubtree(RenderObject* layoutRoot) { // FIXME: Text Autosizing should only be enabled when m_document->page()->mainFrame()->view()->useFixedLayout() @@ -66,9 +116,7 @@ bool TextAutosizer::processSubtree(RenderObject* layoutRoot) windowInfo.windowSize = m_document->settings()->textAutosizingWindowSizeOverride(); if (windowInfo.windowSize.isEmpty()) { bool includeScrollbars = !InspectorInstrumentation::shouldApplyScreenWidthOverride(mainFrame); - windowInfo.windowSize = mainFrame->view()->visibleContentRect(includeScrollbars).size(); - if (!m_document->settings()->applyPageScaleFactorInCompositor()) - windowInfo.windowSize.scale(1 / m_document->page()->deviceScaleFactor()); + windowInfo.windowSize = mainFrame->view()->unscaledVisibleContentSize(includeScrollbars ? ScrollableArea::IncludeScrollbars : ScrollableArea::ExcludeScrollbars); } // Largest area of block that can be visible at once (assuming the main @@ -85,80 +133,96 @@ bool TextAutosizer::processSubtree(RenderObject* layoutRoot) container = container->containingBlock(); RenderBlock* cluster = container; - while (cluster && (!isAutosizingContainer(cluster) || !isAutosizingCluster(cluster))) + while (cluster && (!isAutosizingContainer(cluster) || !isIndependentDescendant(cluster))) cluster = cluster->containingBlock(); - processCluster(cluster, container, layoutRoot, windowInfo); + TextAutosizingClusterInfo clusterInfo(cluster); + processCluster(clusterInfo, container, layoutRoot, windowInfo); return true; } -void TextAutosizer::processCluster(RenderBlock* cluster, RenderBlock* container, RenderObject* subtreeRoot, const TextAutosizingWindowInfo& windowInfo) +float TextAutosizer::clusterMultiplier(WritingMode writingMode, const TextAutosizingWindowInfo& windowInfo, float textWidth) const { - ASSERT(isAutosizingCluster(cluster)); - - // Many pages set a max-width on their content. So especially for the - // RenderView, instead of just taking the width of |cluster| we find - // the lowest common ancestor of the first and last descendant text node of - // the cluster (i.e. the deepest wrapper block that contains all the text), - // and use its width instead. - const RenderBlock* lowestCommonAncestor = findDeepestBlockContainingAllText(cluster); - float commonAncestorWidth = lowestCommonAncestor->contentLogicalWidth(); - - float multiplier = 1; - if (clusterShouldBeAutosized(lowestCommonAncestor, commonAncestorWidth)) { - int logicalWindowWidth = cluster->isHorizontalWritingMode() ? windowInfo.windowSize.width() : windowInfo.windowSize.height(); - int logicalLayoutWidth = cluster->isHorizontalWritingMode() ? windowInfo.minLayoutSize.width() : windowInfo.minLayoutSize.height(); - // Ignore box width in excess of the layout width, to avoid extreme multipliers. - float logicalClusterWidth = std::min<float>(commonAncestorWidth, logicalLayoutWidth); - - multiplier = logicalClusterWidth / logicalWindowWidth; - multiplier *= m_document->settings()->textAutosizingFontScaleFactor(); - multiplier = std::max(1.0f, multiplier); - } + int logicalWindowWidth = isHorizontalWritingMode(writingMode) ? windowInfo.windowSize.width() : windowInfo.windowSize.height(); + int logicalLayoutWidth = isHorizontalWritingMode(writingMode) ? windowInfo.minLayoutSize.width() : windowInfo.minLayoutSize.height(); + // Ignore box width in excess of the layout width, to avoid extreme multipliers. + float logicalClusterWidth = std::min<float>(textWidth, logicalLayoutWidth); + + float multiplier = logicalClusterWidth / logicalWindowWidth; + multiplier *= m_document->settings()->textAutosizingFontScaleFactor(); + return std::max(1.0f, multiplier); +} + +void TextAutosizer::processClusterInternal(TextAutosizingClusterInfo& clusterInfo, RenderBlock* container, RenderObject* subtreeRoot, const TextAutosizingWindowInfo& windowInfo, float multiplier) +{ + processContainer(multiplier, container, clusterInfo, subtreeRoot, windowInfo); - processContainer(multiplier, container, subtreeRoot, windowInfo); + Vector<Vector<TextAutosizingClusterInfo> > narrowDescendantsGroups; + getNarrowDescendantsGroupedByWidth(clusterInfo, narrowDescendantsGroups); + for (size_t i = 0; i < narrowDescendantsGroups.size(); ++i) + processCompositeCluster(narrowDescendantsGroups[i], windowInfo); } -static bool contentHeightIsConstrained(const RenderBlock* container) +void TextAutosizer::processCluster(TextAutosizingClusterInfo& clusterInfo, RenderBlock* container, RenderObject* subtreeRoot, const TextAutosizingWindowInfo& windowInfo) { - // FIXME: Propagate constrainedness down the tree, to avoid inefficiently walking back up from each box. - // FIXME: This code needs to take into account vertical writing modes. - // FIXME: Consider additional heuristics, such as ignoring fixed heights if the content is already overflowing before autosizing kicks in. - for (; container; container = container->containingBlock()) { - RenderStyle* style = container->style(); - if (style->overflowY() >= OSCROLL) - return false; - if (style->height().isSpecified() || style->maxHeight().isSpecified()) { - // Some sites (e.g. wikipedia) set their html and/or body elements to height:100%, - // without intending to constrain the height of the content within them. - return !container->isRoot() && !container->isBody(); - } - if (container->isFloatingOrOutOfFlowPositioned()) - return false; + // Many pages set a max-width on their content. So especially for the RenderView, instead of + // just taking the width of |cluster| we find the lowest common ancestor of the first and last + // descendant text node of the cluster (i.e. the deepest wrapper block that contains all the + // text), and use its width instead. + clusterInfo.blockContainingAllText = findDeepestBlockContainingAllText(clusterInfo.root); + float textWidth = clusterInfo.blockContainingAllText->contentLogicalWidth(); + float multiplier = 1.0; + if (clusterShouldBeAutosized(clusterInfo, textWidth)) + multiplier = clusterMultiplier(clusterInfo.root->style()->writingMode(), windowInfo, textWidth); + processClusterInternal(clusterInfo, container, subtreeRoot, windowInfo, multiplier); +} + +void TextAutosizer::processCompositeCluster(Vector<TextAutosizingClusterInfo>& clusterInfos, const TextAutosizingWindowInfo& windowInfo) +{ + if (clusterInfos.isEmpty()) + return; + + float maxTextWidth = 0; + for (size_t i = 0; i < clusterInfos.size(); ++i) { + TextAutosizingClusterInfo& clusterInfo = clusterInfos[i]; + clusterInfo.blockContainingAllText = findDeepestBlockContainingAllText(clusterInfo.root); + maxTextWidth = max<float>(maxTextWidth, clusterInfo.blockContainingAllText->contentLogicalWidth()); + } + + float multiplier = 1.0; + if (compositeClusterShouldBeAutosized(clusterInfos, maxTextWidth)) + multiplier = clusterMultiplier(clusterInfos[0].root->style()->writingMode(), windowInfo, maxTextWidth); + for (size_t i = 0; i < clusterInfos.size(); ++i) { + ASSERT(clusterInfos[i].root->style()->writingMode() == clusterInfos[0].root->style()->writingMode()); + processClusterInternal(clusterInfos[i], clusterInfos[i].root, clusterInfos[i].root, windowInfo, multiplier); } - return false; } -void TextAutosizer::processContainer(float multiplier, RenderBlock* container, RenderObject* subtreeRoot, const TextAutosizingWindowInfo& windowInfo) +void TextAutosizer::processContainer(float multiplier, RenderBlock* container, TextAutosizingClusterInfo& clusterInfo, RenderObject* subtreeRoot, const TextAutosizingWindowInfo& windowInfo) { ASSERT(isAutosizingContainer(container)); - float localMultiplier = contentHeightIsConstrained(container) ? 1 : multiplier; + float localMultiplier = containerShouldBeAutosized(container) ? multiplier: 1; RenderObject* descendant = nextInPreOrderSkippingDescendantsOfContainers(subtreeRoot, subtreeRoot); while (descendant) { if (descendant->isText()) { - if (localMultiplier != descendant->style()->textAutosizingMultiplier()) { + if (localMultiplier != 1 && descendant->style()->textAutosizingMultiplier() == 1) { setMultiplier(descendant, localMultiplier); setMultiplier(descendant->parent(), localMultiplier); // Parent does line spacing. } // FIXME: Increase list marker size proportionately. } else if (isAutosizingContainer(descendant)) { RenderBlock* descendantBlock = toRenderBlock(descendant); - if (isAutosizingCluster(descendantBlock)) - processCluster(descendantBlock, descendantBlock, descendantBlock, windowInfo); - else - processContainer(multiplier, descendantBlock, descendantBlock, windowInfo); + TextAutosizingClusterInfo descendantClusterInfo(descendantBlock); + if (isWiderDescendant(descendantBlock, clusterInfo) || isIndependentDescendant(descendantBlock)) + processCluster(descendantClusterInfo, descendantBlock, descendantBlock, windowInfo); + else if (isNarrowDescendant(descendantBlock, clusterInfo)) { + // Narrow descendants are processed together later to be able to apply the same multiplier + // to each of them if necessary. + clusterInfo.narrowDescendants.append(descendantClusterInfo); + } else + processContainer(multiplier, descendantBlock, clusterInfo, descendantBlock, windowInfo); } descendant = nextInPreOrderSkippingDescendantsOfContainers(descendant, subtreeRoot); } @@ -204,21 +268,71 @@ bool TextAutosizer::isAutosizingContainer(const RenderObject* renderer) // "Autosizing containers" are the smallest unit for which we can // enable/disable Text Autosizing. // - Must not be inline, as different multipliers on one line looks terrible. + // Exceptions are inline-block and alike elements (inline-table, -webkit-inline-*), + // as they often contain entire multi-line columns of text. // - Must not be list items, as items in the same list should look consistent (*). - // * except for those list items positioned out of the list's flow. - return renderer->isRenderBlock() - && !renderer->isInline() - && (!renderer->isListItem() || renderer->isOutOfFlowPositioned()); + // - Must not be normal list items, as items in the same list should look + // consistent, unless they are floating or position:absolute/fixed. + if (!renderer->isRenderBlock() || (renderer->isInline() && !renderer->style()->isDisplayReplacedType())) + return false; + if (renderer->isListItem()) + return renderer->isFloating() || renderer->isOutOfFlowPositioned(); + // Avoid creating containers for text within text controls, buttons, or <select> buttons. + Node* parentNode = renderer->parent() ? renderer->parent()->generatingNode() : 0; + if (parentNode && parentNode->isElementNode() && formInputTags().contains(toElement(parentNode)->tagQName())) + return false; + + return true; +} + +bool TextAutosizer::isNarrowDescendant(const RenderBlock* renderer, TextAutosizingClusterInfo& parentClusterInfo) +{ + ASSERT(isAutosizingContainer(renderer)); + + // Autosizing containers that are significantly narrower than the |blockContainingAllText| of + // their enclosing cluster may be acting as separate columns, hence must be autosized + // separately. For example the 2nd div in: + // <body> + // <div style="float: right; width: 50%"></div> + // <div style="width: 50%"></div> + // <body> + // is the left column, and should be autosized differently from the body. + // If however the container is only narrower by 150px or less, it's considered part of + // the enclosing cluster. This 150px limit is adjusted whenever a descendant container is + // less than 50px narrower than the current limit. + const float differenceFromMaxWidthDifference = 50; + float contentWidth = renderer->contentLogicalWidth(); + float clusterTextWidth = parentClusterInfo.blockContainingAllText->contentLogicalWidth(); + float widthDifference = clusterTextWidth - contentWidth; + + if (widthDifference - parentClusterInfo.maxAllowedDifferenceFromTextWidth > differenceFromMaxWidthDifference) + return true; + + parentClusterInfo.maxAllowedDifferenceFromTextWidth = std::max(widthDifference, parentClusterInfo.maxAllowedDifferenceFromTextWidth); + return false; +} + +bool TextAutosizer::isWiderDescendant(const RenderBlock* renderer, const TextAutosizingClusterInfo& parentClusterInfo) +{ + ASSERT(isAutosizingContainer(renderer)); + + // Autosizing containers that are wider than the |blockContainingAllText| of their enclosing + // cluster are treated the same way as autosizing clusters to be autosized separately. + float contentWidth = renderer->contentLogicalWidth(); + float clusterTextWidth = parentClusterInfo.blockContainingAllText->contentLogicalWidth(); + return contentWidth > clusterTextWidth; } -bool TextAutosizer::isAutosizingCluster(const RenderBlock* renderer) +bool TextAutosizer::isIndependentDescendant(const RenderBlock* renderer) { + ASSERT(isAutosizingContainer(renderer)); + // "Autosizing clusters" are special autosizing containers within which we // want to enforce a uniform text size multiplier, in the hopes of making // the major sections of the page look internally consistent. - // All their descendents (including other autosizing containers) must share + // All their descendants (including other autosizing containers) must share // the same multiplier, except for subtrees which are themselves clusters, - // and some of their descendent containers might not be autosized at all + // and some of their descendant containers might not be autosized at all // (for example if their height is constrained). // Additionally, clusterShouldBeAutosized requires each cluster to contain a // minimum amount of text, without which it won't be autosized. @@ -227,13 +341,11 @@ bool TextAutosizer::isAutosizingCluster(const RenderBlock* renderer) // block formatting contexts (http://w3.org/TR/css3-box/#flow-root), since // flow roots correspond to box containers that behave somewhat // independently from their parent (for example they don't overlap floats). - // The definition of a flow flow root also conveniently includes most of the + // The definition of a flow root also conveniently includes most of the // ways that a box and its children can have significantly different width // from the box's parent (we want to avoid having significantly different // width blocks within a cluster, since the narrower blocks would end up // larger than would otherwise be necessary). - ASSERT(isAutosizingContainer(renderer)); - return renderer->isRenderView() || renderer->isFloating() || renderer->isOutOfFlowPositioned() @@ -241,13 +353,120 @@ bool TextAutosizer::isAutosizingCluster(const RenderBlock* renderer) || renderer->isTableCaption() || renderer->isFlexibleBoxIncludingDeprecated() || renderer->hasColumns() - || renderer->containingBlock()->isHorizontalWritingMode() != renderer->isHorizontalWritingMode(); + || renderer->containingBlock()->isHorizontalWritingMode() != renderer->isHorizontalWritingMode() + || renderer->style()->isDisplayReplacedType(); // FIXME: Tables need special handling to multiply all their columns by // the same amount even if they're different widths; so do hasColumns() - // renderers, and probably flexboxes... + // containers, and probably flexboxes... +} + +bool TextAutosizer::isAutosizingCluster(const RenderBlock* renderer, TextAutosizingClusterInfo& parentClusterInfo) +{ + ASSERT(isAutosizingContainer(renderer)); + + return isNarrowDescendant(renderer, parentClusterInfo) + || isWiderDescendant(renderer, parentClusterInfo) + || isIndependentDescendant(renderer); +} + +bool TextAutosizer::containerShouldBeAutosized(const RenderBlock* container) +{ + if (containerContainsOneOfTags(container, formInputTags())) + return false; + + if (containerIsRowOfLinks(container)) + return false; + + // Don't autosize block-level text that can't wrap (as it's likely to + // expand sideways and break the page's layout). + if (!container->style()->autoWrap()) + return false; + + return !contentHeightIsConstrained(container); +} + +bool TextAutosizer::containerContainsOneOfTags(const RenderBlock* container, const Vector<QualifiedName>& tags) +{ + const RenderObject* renderer = container; + while (renderer) { + const Node* rendererNode = renderer->node(); + if (rendererNode && rendererNode->isElementNode()) { + if (tags.contains(toElement(rendererNode)->tagQName())) + return true; + } + renderer = nextInPreOrderSkippingDescendantsOfContainers(renderer, container); + } + + return false; +} + +bool TextAutosizer::containerIsRowOfLinks(const RenderObject* container) +{ + // A "row of links" is a container for which holds: + // 1. it should not contain non-link text elements longer than 3 characters + // 2. it should contain min. 3 inline links and all links should + // have the same specified font size + // 3. it should not contain <br> elements + // 4. it should contain only inline elements unless they are containers, + // children of link elements or children of sub-containers. + int linkCount = 0; + RenderObject* renderer = container->nextInPreOrder(container); + float matchingFontSize = -1; + + while (renderer) { + if (!isAutosizingContainer(renderer)) { + if (renderer->isText() && toRenderText(renderer)->text()->stripWhiteSpace()->length() > 3) + return false; + if (!renderer->isInline()) + return false; + if (renderer->isBR()) + return false; + } + if (renderer->style()->isLink()) { + if (matchingFontSize < 0) + matchingFontSize = renderer->style()->specifiedFontSize(); + else { + if (matchingFontSize != renderer->style()->specifiedFontSize()) + return false; + } + + linkCount++; + // Skip traversing descendants of the link. + renderer = renderer->nextInPreOrderAfterChildren(container); + } else + renderer = nextInPreOrderSkippingDescendantsOfContainers(renderer, container); + } + + return (linkCount >= 3); +} + +bool TextAutosizer::contentHeightIsConstrained(const RenderBlock* container) +{ + // FIXME: Propagate constrainedness down the tree, to avoid inefficiently walking back up from each box. + // FIXME: This code needs to take into account vertical writing modes. + // FIXME: Consider additional heuristics, such as ignoring fixed heights if the content is already overflowing before autosizing kicks in. + for (; container; container = container->containingBlock()) { + RenderStyle* style = container->style(); + if (style->overflowY() >= OSCROLL) + return false; + if (style->height().isSpecified() || style->maxHeight().isSpecified()) { + // Some sites (e.g. wikipedia) set their html and/or body elements to height:100%, + // without intending to constrain the height of the content within them. + return !container->isRoot() && !container->isBody(); + } + if (container->isFloatingOrOutOfFlowPositioned()) + return false; + } + return false; +} + +bool TextAutosizer::clusterShouldBeAutosized(TextAutosizingClusterInfo& clusterInfo, float blockWidth) +{ + Vector<TextAutosizingClusterInfo> clusterInfos(1, clusterInfo); + return compositeClusterShouldBeAutosized(clusterInfos, blockWidth); } -bool TextAutosizer::clusterShouldBeAutosized(const RenderBlock* lowestCommonAncestor, float commonAncestorWidth) +bool TextAutosizer::compositeClusterShouldBeAutosized(Vector<TextAutosizingClusterInfo>& clusterInfos, float blockWidth) { // Don't autosize clusters that contain less than 4 lines of text (in // practice less lines are required, since measureDescendantTextWidth @@ -259,18 +478,20 @@ bool TextAutosizer::clusterShouldBeAutosized(const RenderBlock* lowestCommonAnce // if a cluster contains very few lines of text then it's ok to have to zoom // in and pan from side to side to read each line, since if there are very // few lines of text you'll only need to pan across once or twice. + float totalTextWidth = 0; const float minLinesOfText = 4; - float minTextWidth = commonAncestorWidth * minLinesOfText; - float textWidth = 0; - measureDescendantTextWidth(lowestCommonAncestor, minTextWidth, textWidth); - if (textWidth >= minTextWidth) - return true; + float minTextWidth = blockWidth * minLinesOfText; + for (size_t i = 0; i < clusterInfos.size(); ++i) { + measureDescendantTextWidth(clusterInfos[i].blockContainingAllText, clusterInfos[i], minTextWidth, totalTextWidth); + if (totalTextWidth >= minTextWidth) + return true; + } return false; } -void TextAutosizer::measureDescendantTextWidth(const RenderBlock* container, float minTextWidth, float& textWidth) +void TextAutosizer::measureDescendantTextWidth(const RenderBlock* container, TextAutosizingClusterInfo& clusterInfo, float minTextWidth, float& textWidth) { - bool skipLocalText = contentHeightIsConstrained(container); + bool skipLocalText = !containerShouldBeAutosized(container); RenderObject* descendant = nextInPreOrderSkippingDescendantsOfContainers(container, container); while (descendant) { @@ -278,8 +499,8 @@ void TextAutosizer::measureDescendantTextWidth(const RenderBlock* container, flo textWidth += toRenderText(descendant)->renderedTextLength() * descendant->style()->specifiedFontSize(); } else if (isAutosizingContainer(descendant)) { RenderBlock* descendantBlock = toRenderBlock(descendant); - if (!isAutosizingCluster(descendantBlock)) - measureDescendantTextWidth(descendantBlock, minTextWidth, textWidth); + if (!isAutosizingCluster(descendantBlock, clusterInfo)) + measureDescendantTextWidth(descendantBlock, clusterInfo, minTextWidth, textWidth); } if (textWidth >= minTextWidth) return; @@ -290,23 +511,12 @@ void TextAutosizer::measureDescendantTextWidth(const RenderBlock* container, flo RenderObject* TextAutosizer::nextInPreOrderSkippingDescendantsOfContainers(const RenderObject* current, const RenderObject* stayWithin) { if (current == stayWithin || !isAutosizingContainer(current)) - for (RenderObject* child = current->firstChild(); child; child = child->nextSibling()) - return child; - - for (const RenderObject* ancestor = current; ancestor; ancestor = ancestor->parent()) { - if (ancestor == stayWithin) - return 0; - for (RenderObject* sibling = ancestor->nextSibling(); sibling; sibling = sibling->nextSibling()) - return sibling; - } - - return 0; + return current->nextInPreOrder(stayWithin); + return current->nextInPreOrderAfterChildren(stayWithin); } const RenderBlock* TextAutosizer::findDeepestBlockContainingAllText(const RenderBlock* cluster) { - ASSERT(isAutosizingCluster(cluster)); - size_t firstDepth = 0; const RenderObject* firstTextLeaf = findFirstTextLeafNotInCluster(cluster, firstDepth, FirstToLast); if (!firstTextLeaf) @@ -355,7 +565,7 @@ const RenderObject* TextAutosizer::findFirstTextLeafNotInCluster(const RenderObj ++depth; const RenderObject* child = (direction == FirstToLast) ? parent->firstChild() : parent->lastChild(); while (child) { - if (!isAutosizingContainer(child) || !isAutosizingCluster(toRenderBlock(child))) { + if (!isAutosizingContainer(child) || !isIndependentDescendant(toRenderBlock(child))) { const RenderObject* leaf = findFirstTextLeafNotInCluster(child, depth, direction); if (leaf) return leaf; @@ -367,6 +577,43 @@ const RenderObject* TextAutosizer::findFirstTextLeafNotInCluster(const RenderObj return 0; } +namespace { + +// Compares the width of the specified cluster's roots in descending order. +bool clusterWiderThanComparisonFn(const TextAutosizingClusterInfo& first, const TextAutosizingClusterInfo& second) +{ + return first.root->contentLogicalWidth() > second.root->contentLogicalWidth(); +} + +} // namespace + +void TextAutosizer::getNarrowDescendantsGroupedByWidth(const TextAutosizingClusterInfo& parentClusterInfo, Vector<Vector<TextAutosizingClusterInfo> >& groups) +{ + ASSERT(parentClusterInfo.blockContainingAllText); + ASSERT(groups.isEmpty()); + + Vector<TextAutosizingClusterInfo> clusterInfos(parentClusterInfo.narrowDescendants); + if (clusterInfos.isEmpty()) + return; + + std::sort(clusterInfos.begin(), clusterInfos.end(), &clusterWiderThanComparisonFn); + groups.grow(1); + + // If the width difference between two consecutive elements of |clusterInfos| is greater than + // this empirically determined value, the next element should start a new group. + const float maxWidthDifferenceWithinGroup = 100; + for (size_t i = 0; i < clusterInfos.size(); ++i) { + groups.last().append(clusterInfos[i]); + + if (i + 1 < clusterInfos.size()) { + float currentWidth = clusterInfos[i].root->contentLogicalWidth(); + float nextWidth = clusterInfos[i + 1].root->contentLogicalWidth(); + if (currentWidth - nextWidth > maxWidthDifferenceWithinGroup) + groups.grow(groups.size() + 1); + } + } +} + } // namespace WebCore #endif // ENABLE(TEXT_AUTOSIZING) diff --git a/Source/WebCore/rendering/TextAutosizer.h b/Source/WebCore/rendering/TextAutosizer.h index a709b1168..f1f675385 100644 --- a/Source/WebCore/rendering/TextAutosizer.h +++ b/Source/WebCore/rendering/TextAutosizer.h @@ -28,19 +28,19 @@ #if ENABLE(TEXT_AUTOSIZING) -#include "IntSize.h" +#include "HTMLNames.h" +#include "WritingMode.h" #include <wtf/Noncopyable.h> #include <wtf/PassOwnPtr.h> -#include <wtf/PassRefPtr.h> namespace WebCore { class Document; class RenderBlock; class RenderObject; -class RenderStyle; class RenderText; struct TextAutosizingWindowInfo; +struct TextAutosizingClusterInfo; class TextAutosizer { WTF_MAKE_NONCOPYABLE(TextAutosizer); @@ -51,6 +51,7 @@ public: virtual ~TextAutosizer(); bool processSubtree(RenderObject* layoutRoot); + void recalculateMultipliers(); static float computeAutosizedFontSize(float specifiedSize, float multiplier); @@ -62,28 +63,43 @@ private: explicit TextAutosizer(Document*); - void processCluster(RenderBlock* cluster, RenderBlock* container, RenderObject* subtreeRoot, const TextAutosizingWindowInfo&); - void processContainer(float multiplier, RenderBlock* container, RenderObject* subtreeRoot, const TextAutosizingWindowInfo&); + float clusterMultiplier(WritingMode, const TextAutosizingWindowInfo&, float textWidth) const; + + void processClusterInternal(TextAutosizingClusterInfo&, RenderBlock* container, RenderObject* subtreeRoot, const TextAutosizingWindowInfo&, float multiplier); + void processCluster(TextAutosizingClusterInfo&, RenderBlock* container, RenderObject* subtreeRoot, const TextAutosizingWindowInfo&); + void processCompositeCluster(Vector<TextAutosizingClusterInfo>&, const TextAutosizingWindowInfo&); + void processContainer(float multiplier, RenderBlock* container, TextAutosizingClusterInfo&, RenderObject* subtreeRoot, const TextAutosizingWindowInfo&); void setMultiplier(RenderObject*, float); static bool isAutosizingContainer(const RenderObject*); - static bool isAutosizingCluster(const RenderBlock*); - - static bool clusterShouldBeAutosized(const RenderBlock* lowestCommonAncestor, float commonAncestorWidth); - static void measureDescendantTextWidth(const RenderBlock* container, float minTextWidth, float& textWidth); + static bool isNarrowDescendant(const RenderBlock*, TextAutosizingClusterInfo& parentClusterInfo); + static bool isWiderDescendant(const RenderBlock*, const TextAutosizingClusterInfo& parentClusterInfo); + static bool isIndependentDescendant(const RenderBlock*); + static bool isAutosizingCluster(const RenderBlock*, TextAutosizingClusterInfo& parentClusterInfo); + + static bool containerShouldBeAutosized(const RenderBlock* container); + static bool containerContainsOneOfTags(const RenderBlock* cluster, const Vector<QualifiedName>& tags); + static bool containerIsRowOfLinks(const RenderObject* container); + static bool contentHeightIsConstrained(const RenderBlock* container); + static bool clusterShouldBeAutosized(TextAutosizingClusterInfo&, float blockWidth); + static bool compositeClusterShouldBeAutosized(Vector<TextAutosizingClusterInfo>&, float blockWidth); + static void measureDescendantTextWidth(const RenderBlock* container, TextAutosizingClusterInfo&, float minTextWidth, float& textWidth); // Use to traverse the tree of descendants, excluding descendants of containers (but returning the containers themselves). - static RenderObject* nextInPreOrderSkippingDescendantsOfContainers(const RenderObject* current, const RenderObject* stayWithin); + static RenderObject* nextInPreOrderSkippingDescendantsOfContainers(const RenderObject*, const RenderObject* stayWithin); - // Finds the lowest common ancestor of the first and the last descendant - // text node (excluding those belonging to other autosizing clusters). static const RenderBlock* findDeepestBlockContainingAllText(const RenderBlock* cluster); // Depending on the traversal direction specified, finds the first or the last leaf text node child that doesn't // belong to any cluster. static const RenderObject* findFirstTextLeafNotInCluster(const RenderObject*, size_t& depth, TraversalDirection); + // Returns groups of narrow descendants of a given autosizing cluster. The groups are combined + // by the difference between the width of the descendant and the width of the parent cluster's + // |blockContainingAllText|. + static void getNarrowDescendantsGroupedByWidth(const TextAutosizingClusterInfo& parentClusterInfo, Vector<Vector<TextAutosizingClusterInfo> >&); + Document* m_document; }; diff --git a/Source/WebCore/rendering/TrailingFloatsRootInlineBox.h b/Source/WebCore/rendering/TrailingFloatsRootInlineBox.h index 960ca8a29..1f9150d1c 100644 --- a/Source/WebCore/rendering/TrailingFloatsRootInlineBox.h +++ b/Source/WebCore/rendering/TrailingFloatsRootInlineBox.h @@ -30,7 +30,7 @@ namespace WebCore { -class TrailingFloatsRootInlineBox : public RootInlineBox { +class TrailingFloatsRootInlineBox FINAL : public RootInlineBox { public: TrailingFloatsRootInlineBox(RenderBlock* block) : RootInlineBox(block) diff --git a/Source/WebCore/rendering/break_lines.cpp b/Source/WebCore/rendering/break_lines.cpp index 10f8b2be2..04b040d34 100644 --- a/Source/WebCore/rendering/break_lines.cpp +++ b/Source/WebCore/rendering/break_lines.cpp @@ -153,8 +153,9 @@ static inline int nextBreakablePosition(LazyLineBreakIterator& lazyBreakIterator int len = static_cast<int>(length); int nextBreak = -1; - CharacterType lastLastCh = pos > 1 ? str[pos - 2] : 0; - CharacterType lastCh = pos > 0 ? str[pos - 1] : 0; + CharacterType lastLastCh = pos > 1 ? str[pos - 2] : static_cast<CharacterType>(lazyBreakIterator.secondToLastCharacter()); + CharacterType lastCh = pos > 0 ? str[pos - 1] : static_cast<CharacterType>(lazyBreakIterator.lastCharacter()); + unsigned priorContextLength = lazyBreakIterator.priorContextLength(); for (int i = pos; i < len; i++) { CharacterType ch = str[i]; @@ -162,10 +163,16 @@ static inline int nextBreakablePosition(LazyLineBreakIterator& lazyBreakIterator return i; if (needsLineBreakIterator<treatNoBreakSpaceAsBreak>(ch) || needsLineBreakIterator<treatNoBreakSpaceAsBreak>(lastCh)) { - if (nextBreak < i && i) { - TextBreakIterator* breakIterator = lazyBreakIterator.get(); - if (breakIterator) - nextBreak = textBreakFollowing(breakIterator, i - 1); + if (nextBreak < i) { + // Don't break if positioned at start of primary context and there is no prior context. + if (i || priorContextLength) { + TextBreakIterator* breakIterator = lazyBreakIterator.get(priorContextLength); + if (breakIterator) { + nextBreak = textBreakFollowing(breakIterator, i - 1 + priorContextLength); + if (nextBreak >= 0) + nextBreak -= priorContextLength; + } + } } if (i == nextBreak && !isBreakableSpace<treatNoBreakSpaceAsBreak>(lastCh)) return i; diff --git a/Source/WebCore/rendering/mathml/RenderMathMLBlock.cpp b/Source/WebCore/rendering/mathml/RenderMathMLBlock.cpp index c23a9de20..025842c39 100644 --- a/Source/WebCore/rendering/mathml/RenderMathMLBlock.cpp +++ b/Source/WebCore/rendering/mathml/RenderMathMLBlock.cpp @@ -33,6 +33,7 @@ #include "GraphicsContext.h" #include "MathMLNames.h" #include "RenderView.h" +#include <wtf/text/StringBuilder.h> #if ENABLE(DEBUG_MATH_LAYOUT) #include "PaintInfo.h" @@ -42,13 +43,9 @@ namespace WebCore { using namespace MathMLNames; -RenderMathMLBlock::RenderMathMLBlock(Node* container) +RenderMathMLBlock::RenderMathMLBlock(Element* container) : RenderFlexibleBox(container) , m_ignoreInAccessibilityTree(false) - , m_intrinsicPaddingBefore(0) - , m_intrinsicPaddingAfter(0) - , m_intrinsicPaddingStart(0) - , m_intrinsicPaddingEnd(0) , m_preferredLogicalHeight(preferredLogicalHeightUnset) { } @@ -58,90 +55,6 @@ bool RenderMathMLBlock::isChildAllowed(RenderObject* child, RenderStyle*) const return child->node() && child->node()->nodeType() == Node::ELEMENT_NODE; } -LayoutUnit RenderMathMLBlock::paddingTop() const -{ - LayoutUnit result = computedCSSPaddingTop(); - switch (style()->writingMode()) { - case TopToBottomWritingMode: - return result + m_intrinsicPaddingBefore; - case BottomToTopWritingMode: - return result + m_intrinsicPaddingAfter; - case LeftToRightWritingMode: - case RightToLeftWritingMode: - return result + (style()->isLeftToRightDirection() ? m_intrinsicPaddingStart : m_intrinsicPaddingEnd); - } - ASSERT_NOT_REACHED(); - return result; -} - -LayoutUnit RenderMathMLBlock::paddingBottom() const -{ - LayoutUnit result = computedCSSPaddingBottom(); - switch (style()->writingMode()) { - case TopToBottomWritingMode: - return result + m_intrinsicPaddingAfter; - case BottomToTopWritingMode: - return result + m_intrinsicPaddingBefore; - case LeftToRightWritingMode: - case RightToLeftWritingMode: - return result + (style()->isLeftToRightDirection() ? m_intrinsicPaddingEnd : m_intrinsicPaddingStart); - } - ASSERT_NOT_REACHED(); - return result; -} - -LayoutUnit RenderMathMLBlock::paddingLeft() const -{ - LayoutUnit result = computedCSSPaddingLeft(); - switch (style()->writingMode()) { - case LeftToRightWritingMode: - return result + m_intrinsicPaddingBefore; - case RightToLeftWritingMode: - return result + m_intrinsicPaddingAfter; - case TopToBottomWritingMode: - case BottomToTopWritingMode: - return result + (style()->isLeftToRightDirection() ? m_intrinsicPaddingStart : m_intrinsicPaddingEnd); - } - ASSERT_NOT_REACHED(); - return result; -} - -LayoutUnit RenderMathMLBlock::paddingRight() const -{ - LayoutUnit result = computedCSSPaddingRight(); - switch (style()->writingMode()) { - case RightToLeftWritingMode: - return result + m_intrinsicPaddingBefore; - case LeftToRightWritingMode: - return result + m_intrinsicPaddingAfter; - case TopToBottomWritingMode: - case BottomToTopWritingMode: - return result + (style()->isLeftToRightDirection() ? m_intrinsicPaddingEnd : m_intrinsicPaddingStart); - } - ASSERT_NOT_REACHED(); - return result; -} - -LayoutUnit RenderMathMLBlock::paddingBefore() const -{ - return computedCSSPaddingBefore() + m_intrinsicPaddingBefore; -} - -LayoutUnit RenderMathMLBlock::paddingAfter() const -{ - return computedCSSPaddingAfter() + m_intrinsicPaddingAfter; -} - -LayoutUnit RenderMathMLBlock::paddingStart() const -{ - return computedCSSPaddingStart() + m_intrinsicPaddingStart; -} - -LayoutUnit RenderMathMLBlock::paddingEnd() const -{ - return computedCSSPaddingEnd() + m_intrinsicPaddingEnd; -} - void RenderMathMLBlock::computePreferredLogicalWidths() { ASSERT(preferredLogicalWidthsDirty()); @@ -152,7 +65,8 @@ void RenderMathMLBlock::computePreferredLogicalWidths() RenderMathMLBlock* RenderMathMLBlock::createAnonymousMathMLBlock(EDisplay display) { RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyleWithDisplay(style(), display); - RenderMathMLBlock* newBlock = new (renderArena()) RenderMathMLBlock(document() /* is anonymous */); + RenderMathMLBlock* newBlock = new (renderArena()) RenderMathMLBlock(0); + newBlock->setDocumentForAnonymous(document()); newBlock->setStyle(newStyle.release()); return newBlock; } @@ -163,10 +77,13 @@ static const int cLargeLogicalWidth = 15000; void RenderMathMLBlock::computeChildrenPreferredLogicalHeights() { ASSERT(needsLayout()); - + + // This is ugly, but disable fragmentation when computing the preferred heights. + FragmentationDisabler fragmentationDisabler(this); + // Ensure a full repaint will happen after layout finishes. setNeedsLayout(true, MarkOnlyThis); - + RenderView* renderView = view(); bool hadLayoutState = renderView->layoutState(); if (!hadLayoutState) @@ -271,6 +188,179 @@ void RenderMathMLBlock::paint(PaintInfo& info, const LayoutPoint& paintOffset) } #endif // ENABLE(DEBUG_MATH_LAYOUT) +// +// The MathML specification says: +// (http://www.w3.org/TR/MathML/chapter2.html#fund.units) +// +// "Most presentation elements have attributes that accept values representing +// lengths to be used for size, spacing or similar properties. The syntax of a +// length is specified as +// +// number | number unit | namedspace +// +// There should be no space between the number and the unit of a length." +// +// "A trailing '%' represents a percent of the default value. The default +// value, or how it is obtained, is listed in the table of attributes for each +// element. [...] A number without a unit is intepreted as a multiple of the +// default value." +// +// "The possible units in MathML are: +// +// Unit Description +// em an em (font-relative unit traditionally used for horizontal lengths) +// ex an ex (font-relative unit traditionally used for vertical lengths) +// px pixels, or size of a pixel in the current display +// in inches (1 inch = 2.54 centimeters) +// cm centimeters +// mm millimeters +// pt points (1 point = 1/72 inch) +// pc picas (1 pica = 12 points) +// % percentage of default value" +// +// The numbers are defined that way: +// - unsigned-number: "a string of decimal digits with up to one decimal point +// (U+002E), representing a non-negative terminating decimal number (a type of +// rational number)" +// - number: "an optional prefix of '-' (U+002D), followed by an unsigned +// number, representing a terminating decimal number (a type of rational +// number)" +// +bool parseMathMLLength(const String& string, LayoutUnit& lengthValue, const RenderStyle* style, bool allowNegative) +{ + String s = string.simplifyWhiteSpace(); + + int stringLength = s.length(); + if (!stringLength) + return false; + + if (parseMathMLNamedSpace(s, lengthValue, style, allowNegative)) + return true; + + StringBuilder number; + String unit; + + // This verifies whether the negative sign is there. + int i = 0; + UChar c = s[0]; + if (c == '-') { + number.append(c); + i++; + } + + // This gathers up characters that make up the number. + bool gotDot = false; + for ( ; i < stringLength; i++) { + c = s[i]; + // The string is invalid if it contains two dots. + if (gotDot && c == '.') + return false; + if (c == '.') + gotDot = true; + else if (!isASCIIDigit(c)) { + unit = s.substring(i, stringLength - i); + // Some authors leave blanks before the unit, but that shouldn't + // be allowed, so don't simplifyWhitespace on 'unit'. + break; + } + number.append(c); + } + + // Convert number to floating point + bool ok; + float floatValue = number.toString().toFloat(&ok); + if (!ok) + return false; + if (floatValue < 0 && !allowNegative) + return false; + + if (unit.isEmpty()) { + // no explicit unit, this is a number that will act as a multiplier + lengthValue *= floatValue; + return true; + } + if (unit == "%") { + lengthValue *= floatValue / 100; + return true; + } + if (unit == "em") { + lengthValue = floatValue * style->font().size(); + return true; + } + if (unit == "ex") { + lengthValue = floatValue * style->fontMetrics().xHeight(); + return true; + } + if (unit == "px") { + lengthValue = floatValue; + return true; + } + if (unit == "pt") { + lengthValue = 4 * (floatValue / 3); + return true; + } + if (unit == "pc") { + lengthValue = 16 * floatValue; + return true; + } + if (unit == "in") { + lengthValue = 96 * floatValue; + return true; + } + if (unit == "cm") { + lengthValue = 96 * (floatValue / 2.54); + return true; + } + if (unit == "mm") { + lengthValue = 96 * (floatValue / 25.4); + return true; + } + + // unexpected unit + return false; +} + +bool parseMathMLNamedSpace(const String& string, LayoutUnit& lengthValue, const RenderStyle* style, bool allowNegative) +{ + float length = 0; + // See if it is one of the namedspaces (ranging -7/18em, -6/18, ... 7/18em) + if (string == "veryverythinmathspace") + length = 1; + else if (string == "verythinmathspace") + length = 2; + else if (string == "thinmathspace") + length = 3; + else if (string == "mediummathspace") + length = 4; + else if (string == "thickmathspace") + length = 5; + else if (string == "verythickmathspace") + length = 6; + else if (string == "veryverythickmathspace") + length = 7; + else if (allowNegative) { + if (string == "negativeveryverythinmathspace") + length = -1; + else if (string == "negativeverythinmathspace") + length = -2; + else if (string == "negativethinmathspace") + length = -3; + else if (string == "negativemediummathspace") + length = -4; + else if (string == "negativethickmathspace") + length = -5; + else if (string == "negativeverythickmathspace") + length = -6; + else if (string == "negativeveryverythickmathspace") + length = -7; + } + if (length) { + lengthValue = length * style->font().size() / 18; + return true; + } + return false; +} + int RenderMathMLTable::firstLineBoxBaseline() const { // In legal MathML, we'll have a MathML parent. That RenderFlexibleBox parent will use our firstLineBoxBaseline() for baseline alignment, per diff --git a/Source/WebCore/rendering/mathml/RenderMathMLBlock.h b/Source/WebCore/rendering/mathml/RenderMathMLBlock.h index 51e80ec0a..4c82632d7 100644 --- a/Source/WebCore/rendering/mathml/RenderMathMLBlock.h +++ b/Source/WebCore/rendering/mathml/RenderMathMLBlock.h @@ -41,7 +41,8 @@ class RenderMathMLOperator; class RenderMathMLBlock : public RenderFlexibleBox { public: - RenderMathMLBlock(Node* container); + RenderMathMLBlock(Element* container); + virtual bool isChildAllowed(RenderObject*, RenderStyle*) const; virtual bool isRenderMathMLBlock() const { return true; } @@ -51,6 +52,7 @@ public: virtual bool isRenderMathMLFenced() const { return false; } virtual bool isRenderMathMLFraction() const { return false; } virtual bool isRenderMathMLRoot() const { return false; } + virtual bool isRenderMathMLSpace() const { return false; } virtual bool isRenderMathMLSquareRoot() const { return false; } virtual bool isRenderMathMLSubSup() const { return false; } virtual bool isRenderMathMLUnderOver() const { return false; } @@ -63,15 +65,6 @@ public: // FIXME: We don't yet handle all the cases in the MathML spec. See // https://bugs.webkit.org/show_bug.cgi?id=78617. virtual RenderMathMLOperator* unembellishedOperator() { return 0; } - - virtual LayoutUnit paddingTop() const OVERRIDE; - virtual LayoutUnit paddingBottom() const OVERRIDE; - virtual LayoutUnit paddingLeft() const OVERRIDE; - virtual LayoutUnit paddingRight() const OVERRIDE; - virtual LayoutUnit paddingBefore() const OVERRIDE; - virtual LayoutUnit paddingAfter() const OVERRIDE; - virtual LayoutUnit paddingStart() const OVERRIDE; - virtual LayoutUnit paddingEnd() const OVERRIDE; // A MathML element's preferred logical widths often depend on its children's preferred heights, not just their widths. // This is due to operator stretching and other layout fine tuning. We define an element's preferred height to be its @@ -106,24 +99,19 @@ protected: // This can only be called after children have been sized by computeChildrenPreferredLogicalHeights(). static LayoutUnit preferredLogicalHeightAfterSizing(RenderObject* child); - int m_intrinsicPaddingBefore; - int m_intrinsicPaddingAfter; - int m_intrinsicPaddingStart; - int m_intrinsicPaddingEnd; - // m_preferredLogicalHeight is dirty if it's < 0 or preferredLogicalWidthsDirty(). LayoutUnit m_preferredLogicalHeight; }; inline RenderMathMLBlock* toRenderMathMLBlock(RenderObject* object) { - ASSERT(!object || object->isRenderMathMLBlock()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isRenderMathMLBlock()); return static_cast<RenderMathMLBlock*>(object); } inline const RenderMathMLBlock* toRenderMathMLBlock(const RenderObject* object) { - ASSERT(!object || object->isRenderMathMLBlock()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isRenderMathMLBlock()); return static_cast<const RenderMathMLBlock*>(object); } @@ -132,7 +120,7 @@ void toRenderMathMLBlock(const RenderMathMLBlock*); class RenderMathMLTable : public RenderTable { public: - explicit RenderMathMLTable(Node* node) : RenderTable(node) { } + explicit RenderMathMLTable(Element* element) : RenderTable(element) { } virtual int firstLineBoxBaseline() const OVERRIDE; @@ -140,6 +128,9 @@ private: virtual const char* renderName() const OVERRIDE { return "RenderMathMLTable"; } }; +// Parsing functions for MathML Length values +bool parseMathMLLength(const String&, LayoutUnit&, const RenderStyle*, bool allowNegative = true); +bool parseMathMLNamedSpace(const String&, LayoutUnit&, const RenderStyle*, bool allowNegative = true); } #endif // ENABLE(MATHML) diff --git a/Source/WebCore/rendering/mathml/RenderMathMLFenced.cpp b/Source/WebCore/rendering/mathml/RenderMathMLFenced.cpp index 96f6cbabc..c408aad9b 100644 --- a/Source/WebCore/rendering/mathml/RenderMathMLFenced.cpp +++ b/Source/WebCore/rendering/mathml/RenderMathMLFenced.cpp @@ -55,7 +55,7 @@ RenderMathMLFenced::RenderMathMLFenced(Element* element) void RenderMathMLFenced::updateFromElement() { - Element* fenced = static_cast<Element*>(node()); + Element* fenced = toElement(node()); // FIXME: Handle open/close values with more than one character (they should be treated like text). AtomicString openValue = fenced->getAttribute(MathMLNames::openAttr); @@ -89,7 +89,7 @@ RenderMathMLOperator* RenderMathMLFenced::createMathMLOperator(UChar uChar, Rend newStyle->setMarginEnd(Length((operatorType == RenderMathMLOperator::Fence ? gFenceMarginEms : gSeparatorMarginEndEms) * style()->fontSize(), Fixed)); if (operatorType == RenderMathMLOperator::Fence) newStyle->setMarginStart(Length(gFenceMarginEms * style()->fontSize(), Fixed)); - RenderMathMLOperator* newOperator = new (renderArena()) RenderMathMLOperator(node() /* "almost anonymous" */, uChar); + RenderMathMLOperator* newOperator = new (renderArena()) RenderMathMLOperator(toElement(node()), uChar); newOperator->setOperatorType(operatorType); newOperator->setStyle(newStyle.release()); return newOperator; diff --git a/Source/WebCore/rendering/mathml/RenderMathMLFraction.cpp b/Source/WebCore/rendering/mathml/RenderMathMLFraction.cpp index 5603eb558..e5c92a62d 100644 --- a/Source/WebCore/rendering/mathml/RenderMathMLFraction.cpp +++ b/Source/WebCore/rendering/mathml/RenderMathMLFraction.cpp @@ -63,14 +63,13 @@ void RenderMathMLFraction::updateFromElement() if (isEmpty()) return; - Element* fraction = static_cast<Element*>(node()); + Element* fraction = toElement(node()); RenderObject* numeratorWrapper = firstChild(); RenderObject* denominatorWrapper = numeratorWrapper->nextSibling(); if (!denominatorWrapper) return; - // FIXME: parse units String thickness = fraction->getAttribute(MathMLNames::linethicknessAttr); m_lineThickness = gLineMedium; if (equalIgnoringCase(thickness, "thin")) @@ -79,8 +78,12 @@ void RenderMathMLFraction::updateFromElement() m_lineThickness = gLineMedium; else if (equalIgnoringCase(thickness, "thick")) m_lineThickness = gLineThick; - else if (equalIgnoringCase(thickness, "0")) - m_lineThickness = 0; + else { + // This function parses the thickness attribute using gLineMedium as + // the default value. If the parsing fails, m_lineThickness will not be + // modified i.e. the default value will be used. + parseMathMLLength(thickness, m_lineThickness, style(), false); + } // Update the style for the padding of the denominator for the line thickness lastChild()->style()->setPaddingTop(Length(static_cast<int>(m_lineThickness), Fixed)); @@ -140,7 +143,7 @@ void RenderMathMLFraction::layout() void RenderMathMLFraction::paint(PaintInfo& info, const LayoutPoint& paintOffset) { RenderMathMLBlock::paint(info, paintOffset); - if (info.context->paintingDisabled() || info.phase != PaintPhaseForeground) + if (info.context->paintingDisabled() || info.phase != PaintPhaseForeground || style()->visibility() != VISIBLE) return; RenderBox* denominatorWrapper = lastChildBox(); diff --git a/Source/WebCore/rendering/mathml/RenderMathMLFraction.h b/Source/WebCore/rendering/mathml/RenderMathMLFraction.h index 1ec4a0634..df5308163 100644 --- a/Source/WebCore/rendering/mathml/RenderMathMLFraction.h +++ b/Source/WebCore/rendering/mathml/RenderMathMLFraction.h @@ -42,6 +42,7 @@ public: virtual RenderMathMLOperator* unembellishedOperator(); virtual int firstLineBoxBaseline() const OVERRIDE; + float lineThickness() const { return m_lineThickness; } virtual void paint(PaintInfo&, const LayoutPoint&); protected: virtual void layout(); @@ -53,8 +54,20 @@ private: virtual const char* renderName() const { return "RenderMathMLFraction"; } - float m_lineThickness; + LayoutUnit m_lineThickness; }; + +inline RenderMathMLFraction* toRenderMathMLFraction(RenderObject* object) +{ + ASSERT_WITH_SECURITY_IMPLICATION(!object || (object->isRenderMathMLBlock() && toRenderMathMLBlock(object)->isRenderMathMLFraction())); + return static_cast<RenderMathMLFraction*>(object); +} + +inline const RenderMathMLFraction* toRenderMathMLFraction(const RenderObject* object) +{ + ASSERT_WITH_SECURITY_IMPLICATION(!object || (object->isRenderMathMLBlock() && toRenderMathMLBlock(object)->isRenderMathMLFraction())); + return static_cast<const RenderMathMLFraction*>(object); +} } diff --git a/Source/WebCore/rendering/mathml/RenderMathMLOperator.cpp b/Source/WebCore/rendering/mathml/RenderMathMLOperator.cpp index bde8baf64..0db64555f 100644 --- a/Source/WebCore/rendering/mathml/RenderMathMLOperator.cpp +++ b/Source/WebCore/rendering/mathml/RenderMathMLOperator.cpp @@ -47,8 +47,8 @@ RenderMathMLOperator::RenderMathMLOperator(Element* element) { } -RenderMathMLOperator::RenderMathMLOperator(Node* node, UChar operatorChar) - : RenderMathMLBlock(node) +RenderMathMLOperator::RenderMathMLOperator(Element* element, UChar operatorChar) + : RenderMathMLBlock(element) , m_stretchHeight(0) , m_operator(convertHyphenMinusToMinusSign(operatorChar)) , m_operatorType(Default) @@ -83,11 +83,16 @@ void RenderMathMLOperator::styleDidChange(StyleDifference diff, const RenderStyl void RenderMathMLOperator::computePreferredLogicalWidths() { ASSERT(preferredLogicalWidthsDirty()); + +#ifndef NDEBUG + // FIXME: Remove this once mathml stops modifying the render tree here. + SetLayoutNeededForbiddenScope layoutForbiddenScope(this, false); +#endif // Check for an uninitialized operator. if (!firstChild()) updateFromElement(); - + RenderMathMLBlock::computePreferredLogicalWidths(); } @@ -146,8 +151,8 @@ void RenderMathMLOperator::updateFromElement() bool stretchDisabled = false; // We may need the element later if we can't stretch. - if (node()->nodeType() == Node::ELEMENT_NODE) { - if (Element* mo = static_cast<Element*>(node())) { + if (node()->isElementNode()) { + if (Element* mo = toElement(node())) { AtomicString stretchyAttr = mo->getAttribute(MathMLNames::stretchyAttr); stretchDisabled = equalIgnoringCase(stretchyAttr, "false"); @@ -202,7 +207,7 @@ void RenderMathMLOperator::updateFromElement() // Either stretch is disabled or we don't have a stretchable character over the minimum height if (stretchDisabled || !shouldStack) { m_isStacked = false; - RenderBlock* container = new (renderArena()) RenderMathMLBlock(node()); + RenderBlock* container = new (renderArena()) RenderMathMLBlock(toElement(node())); // This container doesn't offer any useful information to accessibility. toRenderMathMLBlock(container)->setIgnoreInAccessibilityTree(true); @@ -227,8 +232,8 @@ void RenderMathMLOperator::updateFromElement() RenderText* text = 0; if (m_operator) text = new (renderArena()) RenderText(node(), StringImpl::create(&m_operator, 1)); - else if (node()->nodeType() == Node::ELEMENT_NODE) - if (Element* mo = static_cast<Element*>(node())) + else if (node()->isElementNode()) + if (Element* mo = toElement(node())) text = new (renderArena()) RenderText(node(), mo->textContent().replace(hyphenMinus, minusSign).impl()); // If we can't figure out the text, leave it blank. if (text) { @@ -296,7 +301,7 @@ PassRefPtr<RenderStyle> RenderMathMLOperator::createStackableStyle(int maxHeight RenderBlock* RenderMathMLOperator::createGlyph(UChar glyph, int maxHeightForRenderer, int charRelative) { - RenderBlock* container = new (renderArena()) RenderMathMLBlock(node()); + RenderBlock* container = new (renderArena()) RenderMathMLBlock(toElement(node())); toRenderMathMLBlock(container)->setIgnoreInAccessibilityTree(true); container->setStyle(createStackableStyle(maxHeightForRenderer)); addChild(container); diff --git a/Source/WebCore/rendering/mathml/RenderMathMLOperator.h b/Source/WebCore/rendering/mathml/RenderMathMLOperator.h index 75bb941de..747f070d9 100644 --- a/Source/WebCore/rendering/mathml/RenderMathMLOperator.h +++ b/Source/WebCore/rendering/mathml/RenderMathMLOperator.h @@ -36,7 +36,8 @@ namespace WebCore { class RenderMathMLOperator : public RenderMathMLBlock { public: RenderMathMLOperator(Element*); - RenderMathMLOperator(Node*, UChar operatorChar); + RenderMathMLOperator(Element*, UChar operatorChar); + virtual bool isRenderMathMLOperator() const { return true; } virtual bool isChildAllowed(RenderObject*, RenderStyle*) const; @@ -71,13 +72,13 @@ private: inline RenderMathMLOperator* toRenderMathMLOperator(RenderMathMLBlock* block) { - ASSERT(!block || block->isRenderMathMLOperator()); + ASSERT_WITH_SECURITY_IMPLICATION(!block || block->isRenderMathMLOperator()); return static_cast<RenderMathMLOperator*>(block); } inline const RenderMathMLOperator* toRenderMathMLOperator(const RenderMathMLBlock* block) { - ASSERT(!block || block->isRenderMathMLOperator()); + ASSERT_WITH_SECURITY_IMPLICATION(!block || block->isRenderMathMLOperator()); return static_cast<const RenderMathMLOperator*>(block); } diff --git a/Source/WebCore/rendering/mathml/RenderMathMLRoot.cpp b/Source/WebCore/rendering/mathml/RenderMathMLRoot.cpp index cbbf251c0..4918e8696 100644 --- a/Source/WebCore/rendering/mathml/RenderMathMLRoot.cpp +++ b/Source/WebCore/rendering/mathml/RenderMathMLRoot.cpp @@ -68,9 +68,97 @@ const float gRadicalThickLineThicknessEms = 0.1f; RenderMathMLRoot::RenderMathMLRoot(Element* element) : RenderMathMLBlock(element) + , m_intrinsicPaddingBefore(0) + , m_intrinsicPaddingAfter(0) + , m_intrinsicPaddingStart(0) + , m_intrinsicPaddingEnd(0) { } +LayoutUnit RenderMathMLRoot::paddingTop() const +{ + LayoutUnit result = computedCSSPaddingTop(); + switch (style()->writingMode()) { + case TopToBottomWritingMode: + return result + m_intrinsicPaddingBefore; + case BottomToTopWritingMode: + return result + m_intrinsicPaddingAfter; + case LeftToRightWritingMode: + case RightToLeftWritingMode: + return result + (style()->isLeftToRightDirection() ? m_intrinsicPaddingStart : m_intrinsicPaddingEnd); + } + ASSERT_NOT_REACHED(); + return result; +} + +LayoutUnit RenderMathMLRoot::paddingBottom() const +{ + LayoutUnit result = computedCSSPaddingBottom(); + switch (style()->writingMode()) { + case TopToBottomWritingMode: + return result + m_intrinsicPaddingAfter; + case BottomToTopWritingMode: + return result + m_intrinsicPaddingBefore; + case LeftToRightWritingMode: + case RightToLeftWritingMode: + return result + (style()->isLeftToRightDirection() ? m_intrinsicPaddingEnd : m_intrinsicPaddingStart); + } + ASSERT_NOT_REACHED(); + return result; +} + +LayoutUnit RenderMathMLRoot::paddingLeft() const +{ + LayoutUnit result = computedCSSPaddingLeft(); + switch (style()->writingMode()) { + case LeftToRightWritingMode: + return result + m_intrinsicPaddingBefore; + case RightToLeftWritingMode: + return result + m_intrinsicPaddingAfter; + case TopToBottomWritingMode: + case BottomToTopWritingMode: + return result + (style()->isLeftToRightDirection() ? m_intrinsicPaddingStart : m_intrinsicPaddingEnd); + } + ASSERT_NOT_REACHED(); + return result; +} + +LayoutUnit RenderMathMLRoot::paddingRight() const +{ + LayoutUnit result = computedCSSPaddingRight(); + switch (style()->writingMode()) { + case RightToLeftWritingMode: + return result + m_intrinsicPaddingBefore; + case LeftToRightWritingMode: + return result + m_intrinsicPaddingAfter; + case TopToBottomWritingMode: + case BottomToTopWritingMode: + return result + (style()->isLeftToRightDirection() ? m_intrinsicPaddingEnd : m_intrinsicPaddingStart); + } + ASSERT_NOT_REACHED(); + return result; +} + +LayoutUnit RenderMathMLRoot::paddingBefore() const +{ + return computedCSSPaddingBefore() + m_intrinsicPaddingBefore; +} + +LayoutUnit RenderMathMLRoot::paddingAfter() const +{ + return computedCSSPaddingAfter() + m_intrinsicPaddingAfter; +} + +LayoutUnit RenderMathMLRoot::paddingStart() const +{ + return computedCSSPaddingStart() + m_intrinsicPaddingStart; +} + +LayoutUnit RenderMathMLRoot::paddingEnd() const +{ + return computedCSSPaddingEnd() + m_intrinsicPaddingEnd; +} + void RenderMathMLRoot::addChild(RenderObject* newChild, RenderObject* beforeChild) { // Insert an implicit <mrow> for <mroot> as well as <msqrt>, to ensure firstChild() will have a box @@ -99,6 +187,11 @@ void RenderMathMLRoot::computePreferredLogicalWidths() { ASSERT(preferredLogicalWidthsDirty() && needsLayout()); +#ifndef NDEBUG + // FIXME: Remove this once mathml stops modifying the render tree here. + SetLayoutNeededForbiddenScope layoutForbiddenScope(this, false); +#endif + computeChildrenPreferredLogicalHeights(); int baseHeight = firstChild() ? roundToInt(preferredLogicalHeightAfterSizing(firstChild())) : style()->fontSize(); @@ -131,7 +224,7 @@ void RenderMathMLRoot::computePreferredLogicalWidths() m_indexTop = - rootExtraTop; } else m_intrinsicPaddingStart = frontWidth; - + RenderMathMLBlock::computePreferredLogicalWidths(); // Shrink our logical width to its probable value now without triggering unnecessary relayout of our children. @@ -158,7 +251,7 @@ void RenderMathMLRoot::paint(PaintInfo& info, const LayoutPoint& paintOffset) { RenderMathMLBlock::paint(info, paintOffset); - if (info.context->paintingDisabled()) + if (info.context->paintingDisabled() || style()->visibility() != VISIBLE) return; IntPoint adjustedPaintOffset = roundedIntPoint(paintOffset + location() + contentBoxRect().location()); diff --git a/Source/WebCore/rendering/mathml/RenderMathMLRoot.h b/Source/WebCore/rendering/mathml/RenderMathMLRoot.h index 99cd36594..f8ecb9607 100644 --- a/Source/WebCore/rendering/mathml/RenderMathMLRoot.h +++ b/Source/WebCore/rendering/mathml/RenderMathMLRoot.h @@ -36,7 +36,16 @@ namespace WebCore { class RenderMathMLRoot : public RenderMathMLBlock { public: RenderMathMLRoot(Element*); - + + virtual LayoutUnit paddingTop() const OVERRIDE; + virtual LayoutUnit paddingBottom() const OVERRIDE; + virtual LayoutUnit paddingLeft() const OVERRIDE; + virtual LayoutUnit paddingRight() const OVERRIDE; + virtual LayoutUnit paddingBefore() const OVERRIDE; + virtual LayoutUnit paddingAfter() const OVERRIDE; + virtual LayoutUnit paddingStart() const OVERRIDE; + virtual LayoutUnit paddingEnd() const OVERRIDE; + virtual void addChild(RenderObject* newChild, RenderObject* beforeChild = 0) OVERRIDE; protected: @@ -52,7 +61,11 @@ private: // This may return 0 for a non-MathML index (which won't occur in valid MathML). RenderBoxModelObject* index() const; - + + int m_intrinsicPaddingBefore; + int m_intrinsicPaddingAfter; + int m_intrinsicPaddingStart; + int m_intrinsicPaddingEnd; int m_overbarLeftPointShift; int m_indexTop; }; diff --git a/Source/WebCore/rendering/mathml/RenderMathMLRow.cpp b/Source/WebCore/rendering/mathml/RenderMathMLRow.cpp index 52bb6feae..25fb75b7c 100644 --- a/Source/WebCore/rendering/mathml/RenderMathMLRow.cpp +++ b/Source/WebCore/rendering/mathml/RenderMathMLRow.cpp @@ -36,8 +36,8 @@ namespace WebCore { using namespace MathMLNames; -RenderMathMLRow::RenderMathMLRow(Node* node) - : RenderMathMLBlock(node) +RenderMathMLRow::RenderMathMLRow(Element* element) + : RenderMathMLBlock(element) { } @@ -45,7 +45,8 @@ RenderMathMLRow::RenderMathMLRow(Node* node) RenderMathMLRow* RenderMathMLRow::createAnonymousWithParentRenderer(const RenderObject* parent) { RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyleWithDisplay(parent->style(), FLEX); - RenderMathMLRow* newMRow = new (parent->renderArena()) RenderMathMLRow(parent->document() /* is anonymous */); + RenderMathMLRow* newMRow = new (parent->renderArena()) RenderMathMLRow(0); + newMRow->setDocumentForAnonymous(parent->document()); newMRow->setStyle(newStyle.release()); return newMRow; } @@ -53,7 +54,12 @@ RenderMathMLRow* RenderMathMLRow::createAnonymousWithParentRenderer(const Render void RenderMathMLRow::computePreferredLogicalWidths() { ASSERT(preferredLogicalWidthsDirty() && needsLayout()); - + +#ifndef NDEBUG + // FIXME: Remove this once mathml stops modifying the render tree here. + SetLayoutNeededForbiddenScope layoutForbiddenScope(this, false); +#endif + computeChildrenPreferredLogicalHeights(); int stretchLogicalHeight = 0; for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { @@ -76,7 +82,7 @@ void RenderMathMLRow::computePreferredLogicalWidths() renderMo->stretchToHeight(stretchLogicalHeight); } } - + RenderMathMLBlock::computePreferredLogicalWidths(); // Shrink our logical width to its probable value now without triggering unnecessary relayout of our children. diff --git a/Source/WebCore/rendering/mathml/RenderMathMLRow.h b/Source/WebCore/rendering/mathml/RenderMathMLRow.h index 37f64a1db..9c1a1dc35 100644 --- a/Source/WebCore/rendering/mathml/RenderMathMLRow.h +++ b/Source/WebCore/rendering/mathml/RenderMathMLRow.h @@ -34,7 +34,8 @@ namespace WebCore { class RenderMathMLRow : public RenderMathMLBlock { public: - RenderMathMLRow(Node*); + RenderMathMLRow(Element*); + static RenderMathMLRow* createAnonymousWithParentRenderer(const RenderObject*); virtual bool isRenderMathMLRow() const { return true; } diff --git a/Source/WebCore/rendering/mathml/RenderMathMLSpace.cpp b/Source/WebCore/rendering/mathml/RenderMathMLSpace.cpp new file mode 100644 index 000000000..3de35ffbe --- /dev/null +++ b/Source/WebCore/rendering/mathml/RenderMathMLSpace.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2013 The MathJax Consortium. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "RenderMathMLSpace.h" + +#if ENABLE(MATHML) + +#include "GraphicsContext.h" +#include "MathMLNames.h" +#include "PaintInfo.h" + +namespace WebCore { + +using namespace MathMLNames; + +RenderMathMLSpace::RenderMathMLSpace(Element* element) + : RenderMathMLBlock(element) + , m_width(0) + , m_height(0) + , m_depth(0) +{ +} + +void RenderMathMLSpace::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const +{ + minLogicalWidth = m_width; + maxLogicalWidth = m_width; +} + +void RenderMathMLSpace::updateFromElement() +{ + Element* space = toElement(node()); + + // This parses the mspace attributes, using 0 as the default values. + m_width = 0; + m_height = 0; + m_depth = 0; + parseMathMLLength(space->getAttribute(MathMLNames::widthAttr), m_width, style()); + parseMathMLLength(space->getAttribute(MathMLNames::heightAttr), m_height, style()); + parseMathMLLength(space->getAttribute(MathMLNames::depthAttr), m_depth, style()); + + // FIXME: Negative width values should be accepted. + if (m_width < 0) + m_width = 0; + + // If the total height is negative, set vertical dimensions to 0. + if (m_height + m_depth < 0) { + m_height = 0; + m_depth = 0; + } + + setNeedsLayoutAndPrefWidthsRecalc(); +} + +void RenderMathMLSpace::updateLogicalWidth() +{ + setLogicalWidth(m_width); +} + +void RenderMathMLSpace::updateLogicalHeight() +{ + setLogicalHeight(m_height + m_depth); +} + +void RenderMathMLSpace::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) +{ + RenderMathMLBlock::styleDidChange(diff, oldStyle); + updateFromElement(); +} + +int RenderMathMLSpace::firstLineBoxBaseline() const +{ + return m_height; +} + +} + +#endif diff --git a/Source/WebCore/rendering/mathml/RenderMathMLSpace.h b/Source/WebCore/rendering/mathml/RenderMathMLSpace.h new file mode 100644 index 000000000..46e6a04c2 --- /dev/null +++ b/Source/WebCore/rendering/mathml/RenderMathMLSpace.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2013 The MathJax Consortium. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef RenderMathMLSpace_h +#define RenderMathMLSpace_h + +#if ENABLE(MATHML) + +#include "RenderMathMLBlock.h" + +namespace WebCore { + +class RenderMathMLSpace : public RenderMathMLBlock { +public: + explicit RenderMathMLSpace(Element*); + + virtual int firstLineBoxBaseline() const OVERRIDE; + virtual void updateLogicalWidth() OVERRIDE; + virtual void updateLogicalHeight() OVERRIDE; + +private: + virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle) OVERRIDE; + virtual const char* renderName() const OVERRIDE { return isAnonymous() ? "RenderMathMLSpace (anonymous)" : "RenderMathMLSpace"; } + + virtual bool isRenderMathMLSpace() const OVERRIDE { return true; } + + virtual bool isChildAllowed(RenderObject*, RenderStyle*) const OVERRIDE { return false; } + virtual void computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const OVERRIDE; + + virtual void updateFromElement() OVERRIDE; + + LayoutUnit m_width; + LayoutUnit m_height; + LayoutUnit m_depth; +}; + +inline RenderMathMLSpace* toRenderMathMLSpace(RenderMathMLBlock* block) +{ + ASSERT_WITH_SECURITY_IMPLICATION(!block || block->isRenderMathMLSpace()); + return static_cast<RenderMathMLSpace*>(block); +} + +inline const RenderMathMLSpace* toRenderMathMLSpace(const RenderMathMLBlock* block) +{ + ASSERT_WITH_SECURITY_IMPLICATION(!block || block->isRenderMathMLSpace()); + return static_cast<const RenderMathMLSpace*>(block); +} + +// This will catch anyone doing an unnecessary cast. +void toRenderMathMLSpace(const RenderMathMLSpace*); +} + +#endif // ENABLE(MATHML) +#endif // RenderMathMLSpace_h diff --git a/Source/WebCore/rendering/mathml/RenderMathMLSubSup.cpp b/Source/WebCore/rendering/mathml/RenderMathMLSubSup.cpp index aee1c3934..d07fc8b8d 100644 --- a/Source/WebCore/rendering/mathml/RenderMathMLSubSup.cpp +++ b/Source/WebCore/rendering/mathml/RenderMathMLSubSup.cpp @@ -113,37 +113,45 @@ RenderMathMLOperator* RenderMathMLSubSup::unembellishedOperator() void RenderMathMLSubSup::layout() { RenderMathMLBlock::layout(); - + RenderMathMLBlock* baseWrapper = toRenderMathMLBlock(firstChild()); if (!baseWrapper || !m_scripts) return; RenderBox* base = baseWrapper->firstChildBox(); if (!base) return; - + // Our layout rules include: Don't let the superscript go below the "axis" (half x-height above the // baseline), or the subscript above the axis. Also, don't let the superscript's top edge be // below the base's top edge, or the subscript's bottom edge above the base's bottom edge. // // FIXME: Check any subscriptshift or superscriptshift attributes, and maybe use more sophisticated // heuristics from TeX or elsewhere. See https://bugs.webkit.org/show_bug.cgi?id=79274#c5. - + LayoutUnit baseHeight = base->logicalHeight(); LayoutUnit baseBaseline = base->firstLineBoxBaseline(); if (baseBaseline == -1) baseBaseline = baseHeight; LayoutUnit axis = style()->fontMetrics().xHeight() / 2; int fontSize = style()->fontSize(); - + + ASSERT(baseWrapper->style()->hasOneRef()); + bool needsSecondLayout = false; + if (RenderBox* superscript = m_kind == Sub ? 0 : m_scripts->lastChildBox()) { LayoutUnit superscriptHeight = superscript->logicalHeight(); LayoutUnit superscriptBaseline = superscript->firstLineBoxBaseline(); if (superscriptBaseline == -1) superscriptBaseline = superscriptHeight; LayoutUnit minBaseline = max<LayoutUnit>(fontSize / 3 + 1 + superscriptBaseline, superscriptHeight + axis); - baseWrapper->style()->setPaddingTop(Length(max<LayoutUnit>(minBaseline - baseBaseline, 0), Fixed)); + + Length newPadding = Length(max<LayoutUnit>(minBaseline - baseBaseline, 0), Fixed); + if (newPadding != baseWrapper->style()->paddingTop()) { + baseWrapper->style()->setPaddingTop(newPadding); + needsSecondLayout = true; + } } - + if (RenderBox* subscript = m_kind == Super ? 0 : m_scripts->firstChildBox()) { LayoutUnit subscriptHeight = subscript->logicalHeight(); LayoutUnit subscriptBaseline = subscript->firstLineBoxBaseline(); @@ -152,12 +160,20 @@ void RenderMathMLSubSup::layout() LayoutUnit baseExtendUnderBaseline = baseHeight - baseBaseline; LayoutUnit subscriptUnderItsBaseline = subscriptHeight - subscriptBaseline; LayoutUnit minExtendUnderBaseline = max<LayoutUnit>(fontSize / 5 + 1 + subscriptUnderItsBaseline, subscriptHeight - axis); - baseWrapper->style()->setPaddingBottom(Length(max<LayoutUnit>(minExtendUnderBaseline - baseExtendUnderBaseline, 0), Fixed)); + + Length newPadding = Length(max<LayoutUnit>(minExtendUnderBaseline - baseExtendUnderBaseline, 0), Fixed); + if (newPadding != baseWrapper->style()->paddingBottom()) { + baseWrapper->style()->setPaddingBottom(newPadding); + needsSecondLayout = true; + } } - - setChildNeedsLayout(true, MarkOnlyThis); - baseWrapper->setNeedsLayout(true, MarkOnlyThis); - + + if (!needsSecondLayout) + return; + + setNeedsLayout(true, MarkOnlyThis); + baseWrapper->setChildNeedsLayout(true, MarkOnlyThis); + RenderMathMLBlock::layout(); } diff --git a/Source/WebCore/rendering/shapes/PolygonShape.cpp b/Source/WebCore/rendering/shapes/PolygonShape.cpp new file mode 100644 index 000000000..e2124fd56 --- /dev/null +++ b/Source/WebCore/rendering/shapes/PolygonShape.cpp @@ -0,0 +1,512 @@ +/* + * Copyright (C) 2012 Adobe Systems Incorporated. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "PolygonShape.h" + +#include <wtf/MathExtras.h> + +namespace WebCore { + +enum EdgeIntersectionType { + Normal, + VertexMinY, + VertexMaxY, + VertexYBoth +}; + +struct EdgeIntersection { + const FloatPolygonEdge* edge; + FloatPoint point; + EdgeIntersectionType type; +}; + +static inline float leftSide(const FloatPoint& vertex1, const FloatPoint& vertex2, const FloatPoint& point) +{ + return ((point.x() - vertex1.x()) * (vertex2.y() - vertex1.y())) - ((vertex2.x() - vertex1.x()) * (point.y() - vertex1.y())); +} + +static inline bool isReflexVertex(const FloatPoint& prevVertex, const FloatPoint& vertex, const FloatPoint& nextVertex) +{ + return leftSide(prevVertex, nextVertex, vertex) < 0; +} + +static bool computeXIntersection(const FloatPolygonEdge* edgePointer, float y, EdgeIntersection& result) +{ + const FloatPolygonEdge& edge = *edgePointer; + + if (edge.minY() > y || edge.maxY() < y) + return false; + + const FloatPoint& vertex1 = edge.vertex1(); + const FloatPoint& vertex2 = edge.vertex2(); + float dy = vertex2.y() - vertex1.y(); + + float intersectionX; + EdgeIntersectionType intersectionType; + + if (!dy) { + intersectionType = VertexYBoth; + intersectionX = edge.minX(); + } else if (y == edge.minY()) { + intersectionType = VertexMinY; + intersectionX = (vertex1.y() < vertex2.y()) ? vertex1.x() : vertex2.x(); + } else if (y == edge.maxY()) { + intersectionType = VertexMaxY; + intersectionX = (vertex1.y() > vertex2.y()) ? vertex1.x() : vertex2.x(); + } else { + intersectionType = Normal; + intersectionX = ((y - vertex1.y()) * (vertex2.x() - vertex1.x()) / dy) + vertex1.x(); + } + + result.edge = edgePointer; + result.type = intersectionType; + result.point.set(intersectionX, y); + + return true; +} + +static inline FloatSize inwardEdgeNormal(const FloatPolygonEdge& edge) +{ + FloatSize edgeDelta = edge.vertex2() - edge.vertex1(); + if (!edgeDelta.width()) + return FloatSize((edgeDelta.height() > 0 ? -1 : 1), 0); + if (!edgeDelta.height()) + return FloatSize(0, (edgeDelta.width() > 0 ? 1 : -1)); + float edgeLength = edgeDelta.diagonalLength(); + return FloatSize(-edgeDelta.height() / edgeLength, edgeDelta.width() / edgeLength); +} + +static inline FloatSize outwardEdgeNormal(const FloatPolygonEdge& edge) +{ + return -inwardEdgeNormal(edge); +} + +static inline void appendArc(Vector<FloatPoint>& vertices, const FloatPoint& arcCenter, float arcRadius, const FloatPoint& startArcVertex, const FloatPoint& endArcVertex, bool padding) +{ + float startAngle = atan2(startArcVertex.y() - arcCenter.y(), startArcVertex.x() - arcCenter.x()); + float endAngle = atan2(endArcVertex.y() - arcCenter.y(), endArcVertex.x() - arcCenter.x()); + const float twoPI = piFloat * 2; + if (startAngle < 0) + startAngle += twoPI; + if (endAngle < 0) + endAngle += twoPI; + float angle = (startAngle > endAngle) ? (startAngle - endAngle) : (startAngle + twoPI - endAngle); + const float arcSegmentCount = 6; // An even number so that one arc vertex will be eactly arcRadius from arcCenter. + float arcSegmentAngle = ((padding) ? -angle : twoPI - angle) / arcSegmentCount; + + vertices.append(startArcVertex); + for (unsigned i = 1; i < arcSegmentCount; ++i) { + float angle = startAngle + arcSegmentAngle * i; + vertices.append(arcCenter + FloatPoint(cos(angle) * arcRadius, sin(angle) * arcRadius)); + } + vertices.append(endArcVertex); +} + +static inline void snapVerticesToLayoutUnitGrid(Vector<FloatPoint>& vertices) +{ + for (unsigned i = 0; i < vertices.size(); ++i) + vertices[i].set(LayoutUnit(vertices[i].x()).toFloat(), LayoutUnit(vertices[i].y()).toFloat()); +} + +static inline PassOwnPtr<FloatPolygon> computeShapePaddingBounds(const FloatPolygon& polygon, float padding, WindRule fillRule) +{ + OwnPtr<Vector<FloatPoint> > paddedVertices = adoptPtr(new Vector<FloatPoint>()); + FloatPoint intersection; + + for (unsigned i = 0; i < polygon.numberOfEdges(); ++i) { + const FloatPolygonEdge& thisEdge = polygon.edgeAt(i); + const FloatPolygonEdge& prevEdge = thisEdge.previousEdge(); + OffsetPolygonEdge thisOffsetEdge(thisEdge, inwardEdgeNormal(thisEdge) * padding); + OffsetPolygonEdge prevOffsetEdge(prevEdge, inwardEdgeNormal(prevEdge) * padding); + + if (prevOffsetEdge.intersection(thisOffsetEdge, intersection)) + paddedVertices->append(intersection); + else if (isReflexVertex(prevEdge.vertex1(), thisEdge.vertex1(), thisEdge.vertex2())) + appendArc(*paddedVertices, thisEdge.vertex1(), padding, prevOffsetEdge.vertex2(), thisOffsetEdge.vertex1(), true); + } + + snapVerticesToLayoutUnitGrid(*paddedVertices); + return adoptPtr(new FloatPolygon(paddedVertices.release(), fillRule)); +} + +static inline PassOwnPtr<FloatPolygon> computeShapeMarginBounds(const FloatPolygon& polygon, float margin, WindRule fillRule) +{ + OwnPtr<Vector<FloatPoint> > marginVertices = adoptPtr(new Vector<FloatPoint>()); + FloatPoint intersection; + + for (unsigned i = 0; i < polygon.numberOfEdges(); ++i) { + const FloatPolygonEdge& thisEdge = polygon.edgeAt(i); + const FloatPolygonEdge& prevEdge = thisEdge.previousEdge(); + OffsetPolygonEdge thisOffsetEdge(thisEdge, outwardEdgeNormal(thisEdge) * margin); + OffsetPolygonEdge prevOffsetEdge(prevEdge, outwardEdgeNormal(prevEdge) * margin); + + if (prevOffsetEdge.intersection(thisOffsetEdge, intersection)) + marginVertices->append(intersection); + else + appendArc(*marginVertices, thisEdge.vertex1(), margin, prevOffsetEdge.vertex2(), thisOffsetEdge.vertex1(), false); + } + + snapVerticesToLayoutUnitGrid(*marginVertices); + return adoptPtr(new FloatPolygon(marginVertices.release(), fillRule)); +} + +const FloatPolygon& PolygonShape::shapePaddingBounds() const +{ + ASSERT(shapePadding() >= 0); + if (!shapePadding()) + return m_polygon; + + if (!m_paddingBounds) + m_paddingBounds = computeShapePaddingBounds(m_polygon, shapePadding(), m_polygon.fillRule()); + + return *m_paddingBounds; +} + +const FloatPolygon& PolygonShape::shapeMarginBounds() const +{ + ASSERT(shapeMargin() >= 0); + if (!shapeMargin()) + return m_polygon; + + if (!m_marginBounds) + m_marginBounds = computeShapeMarginBounds(m_polygon, shapeMargin(), m_polygon.fillRule()); + + return *m_marginBounds; +} + +static inline bool getVertexIntersectionVertices(const EdgeIntersection& intersection, FloatPoint& prevVertex, FloatPoint& thisVertex, FloatPoint& nextVertex) +{ + if (intersection.type != VertexMinY && intersection.type != VertexMaxY) + return false; + + ASSERT(intersection.edge && intersection.edge->polygon()); + const FloatPolygon& polygon = *(intersection.edge->polygon()); + const FloatPolygonEdge& thisEdge = *(intersection.edge); + + if ((intersection.type == VertexMinY && (thisEdge.vertex1().y() < thisEdge.vertex2().y())) + || (intersection.type == VertexMaxY && (thisEdge.vertex1().y() > thisEdge.vertex2().y()))) { + prevVertex = polygon.vertexAt(thisEdge.previousEdge().vertexIndex1()); + thisVertex = polygon.vertexAt(thisEdge.vertexIndex1()); + nextVertex = polygon.vertexAt(thisEdge.vertexIndex2()); + } else { + prevVertex = polygon.vertexAt(thisEdge.vertexIndex1()); + thisVertex = polygon.vertexAt(thisEdge.vertexIndex2()); + nextVertex = polygon.vertexAt(thisEdge.nextEdge().vertexIndex2()); + } + + return true; +} + +static inline bool appendIntervalX(float x, bool inside, Vector<ShapeInterval>& result) +{ + if (!inside) + result.append(ShapeInterval(x)); + else + result[result.size() - 1].x2 = x; + + return !inside; +} + +static bool compareEdgeIntersectionX(const EdgeIntersection& intersection1, const EdgeIntersection& intersection2) +{ + float x1 = intersection1.point.x(); + float x2 = intersection2.point.x(); + return (x1 == x2) ? intersection1.type < intersection2.type : x1 < x2; +} + +static void computeXIntersections(const FloatPolygon& polygon, float y, bool isMinY, Vector<ShapeInterval>& result) +{ + Vector<const FloatPolygonEdge*> edges; + if (!polygon.overlappingEdges(y, y, edges)) + return; + + Vector<EdgeIntersection> intersections; + EdgeIntersection intersection; + for (unsigned i = 0; i < edges.size(); ++i) { + if (computeXIntersection(edges[i], y, intersection) && intersection.type != VertexYBoth) + intersections.append(intersection); + } + + if (intersections.size() < 2) + return; + + std::sort(intersections.begin(), intersections.end(), WebCore::compareEdgeIntersectionX); + + unsigned index = 0; + int windCount = 0; + bool inside = false; + + while (index < intersections.size()) { + const EdgeIntersection& thisIntersection = intersections[index]; + if (index + 1 < intersections.size()) { + const EdgeIntersection& nextIntersection = intersections[index + 1]; + if ((thisIntersection.point.x() == nextIntersection.point.x()) && (thisIntersection.type == VertexMinY || thisIntersection.type == VertexMaxY)) { + if (thisIntersection.type == nextIntersection.type) { + // Skip pairs of intersections whose types are VertexMaxY,VertexMaxY and VertexMinY,VertexMinY. + index += 2; + } else { + // Replace pairs of intersections whose types are VertexMinY,VertexMaxY or VertexMaxY,VertexMinY with one intersection. + ++index; + } + continue; + } + } + + const FloatPolygonEdge& thisEdge = *thisIntersection.edge; + bool evenOddCrossing = !windCount; + + if (polygon.fillRule() == RULE_EVENODD) { + windCount += (thisEdge.vertex2().y() > thisEdge.vertex1().y()) ? 1 : -1; + evenOddCrossing = evenOddCrossing || !windCount; + } + + if (evenOddCrossing) { + bool edgeCrossing = thisIntersection.type == Normal; + if (!edgeCrossing) { + FloatPoint prevVertex; + FloatPoint thisVertex; + FloatPoint nextVertex; + + if (getVertexIntersectionVertices(thisIntersection, prevVertex, thisVertex, nextVertex)) { + if (nextVertex.y() == y) + edgeCrossing = (isMinY) ? prevVertex.y() > y : prevVertex.y() < y; + else if (prevVertex.y() == y) + edgeCrossing = (isMinY) ? nextVertex.y() > y : nextVertex.y() < y; + else + edgeCrossing = true; + } + } + if (edgeCrossing) + inside = appendIntervalX(thisIntersection.point.x(), inside, result); + } + + ++index; + } +} + +static void computeOverlappingEdgeXProjections(const FloatPolygon& polygon, float y1, float y2, Vector<ShapeInterval>& result) +{ + Vector<const FloatPolygonEdge*> edges; + if (!polygon.overlappingEdges(y1, y2, edges)) + return; + + EdgeIntersection intersection; + for (unsigned i = 0; i < edges.size(); ++i) { + const FloatPolygonEdge *edge = edges[i]; + float x1; + float x2; + + if (edge->minY() < y1) { + computeXIntersection(edge, y1, intersection); + x1 = intersection.point.x(); + } else + x1 = (edge->vertex1().y() < edge->vertex2().y()) ? edge->vertex1().x() : edge->vertex2().x(); + + if (edge->maxY() > y2) { + computeXIntersection(edge, y2, intersection); + x2 = intersection.point.x(); + } else + x2 = (edge->vertex1().y() > edge->vertex2().y()) ? edge->vertex1().x() : edge->vertex2().x(); + + if (x1 > x2) + std::swap(x1, x2); + + if (x2 > x1) + result.append(ShapeInterval(x1, x2)); + } + + sortShapeIntervals(result); +} + +void PolygonShape::getExcludedIntervals(LayoutUnit logicalTop, LayoutUnit logicalHeight, SegmentList& result) const +{ + const FloatPolygon& polygon = shapeMarginBounds(); + if (polygon.isEmpty()) + return; + + float y1 = logicalTop; + float y2 = logicalTop + logicalHeight; + + Vector<ShapeInterval> y1XIntervals, y2XIntervals; + computeXIntersections(polygon, y1, true, y1XIntervals); + computeXIntersections(polygon, y2, false, y2XIntervals); + + Vector<ShapeInterval> mergedIntervals; + mergeShapeIntervals(y1XIntervals, y2XIntervals, mergedIntervals); + + Vector<ShapeInterval> edgeIntervals; + computeOverlappingEdgeXProjections(polygon, y1, y2, edgeIntervals); + + Vector<ShapeInterval> excludedIntervals; + mergeShapeIntervals(mergedIntervals, edgeIntervals, excludedIntervals); + + for (unsigned i = 0; i < excludedIntervals.size(); ++i) { + ShapeInterval interval = excludedIntervals[i]; + result.append(LineSegment(interval.x1, interval.x2)); + } +} + +void PolygonShape::getIncludedIntervals(LayoutUnit logicalTop, LayoutUnit logicalHeight, SegmentList& result) const +{ + const FloatPolygon& polygon = shapePaddingBounds(); + if (polygon.isEmpty()) + return; + + float y1 = logicalTop; + float y2 = logicalTop + logicalHeight; + + Vector<ShapeInterval> y1XIntervals, y2XIntervals; + computeXIntersections(polygon, y1, true, y1XIntervals); + computeXIntersections(polygon, y2, false, y2XIntervals); + + Vector<ShapeInterval> commonIntervals; + intersectShapeIntervals(y1XIntervals, y2XIntervals, commonIntervals); + + Vector<ShapeInterval> edgeIntervals; + computeOverlappingEdgeXProjections(polygon, y1, y2, edgeIntervals); + + Vector<ShapeInterval> includedIntervals; + subtractShapeIntervals(commonIntervals, edgeIntervals, includedIntervals); + + for (unsigned i = 0; i < includedIntervals.size(); ++i) { + ShapeInterval interval = includedIntervals[i]; + result.append(LineSegment(interval.x1, interval.x2)); + } +} + +static inline bool firstFitRectInPolygon(const FloatPolygon& polygon, const FloatRect& rect, unsigned offsetEdgeIndex1, unsigned offsetEdgeIndex2) +{ + Vector<const FloatPolygonEdge*> edges; + if (!polygon.overlappingEdges(rect.y(), rect.maxY(), edges)) + return true; + + for (unsigned i = 0; i < edges.size(); ++i) { + const FloatPolygonEdge* edge = edges[i]; + if (edge->edgeIndex() != offsetEdgeIndex1 && edge->edgeIndex() != offsetEdgeIndex2 && edge->overlapsRect(rect)) + return false; + } + + return true; +} + +static inline bool aboveOrToTheLeft(const FloatRect& r1, const FloatRect& r2) +{ + if (r1.y() < r2.y()) + return true; + if (r1.y() == r2.y()) + return r1.x() < r2.x(); + return false; +} + +bool PolygonShape::firstIncludedIntervalLogicalTop(LayoutUnit minLogicalIntervalTop, const LayoutSize& minLogicalIntervalSize, LayoutUnit& result) const +{ + float minIntervalTop = minLogicalIntervalTop; + float minIntervalHeight = minLogicalIntervalSize.height(); + float minIntervalWidth = minLogicalIntervalSize.width(); + + const FloatPolygon& polygon = shapePaddingBounds(); + const FloatRect boundingBox = polygon.boundingBox(); + if (minIntervalWidth > boundingBox.width()) + return false; + + float minY = std::max(boundingBox.y(), minIntervalTop); + float maxY = minY + minIntervalHeight; + + if (maxY > boundingBox.maxY()) + return false; + + Vector<const FloatPolygonEdge*> edges; + polygon.overlappingEdges(minIntervalTop, boundingBox.maxY(), edges); + + float dx = minIntervalWidth / 2; + float dy = minIntervalHeight / 2; + Vector<OffsetPolygonEdge> offsetEdges; + + for (unsigned i = 0; i < edges.size(); ++i) { + const FloatPolygonEdge& edge = *(edges[i]); + const FloatPoint& vertex0 = edge.previousEdge().vertex1(); + const FloatPoint& vertex1 = edge.vertex1(); + const FloatPoint& vertex2 = edge.vertex2(); + Vector<OffsetPolygonEdge> offsetEdgeBuffer; + + if (vertex2.y() > vertex1.y() ? vertex2.x() >= vertex1.x() : vertex1.x() >= vertex2.x()) { + offsetEdgeBuffer.append(OffsetPolygonEdge(edge, FloatSize(dx, -dy))); + offsetEdgeBuffer.append(OffsetPolygonEdge(edge, FloatSize(-dx, dy))); + } else { + offsetEdgeBuffer.append(OffsetPolygonEdge(edge, FloatSize(dx, dy))); + offsetEdgeBuffer.append(OffsetPolygonEdge(edge, FloatSize(-dx, -dy))); + } + + if (isReflexVertex(vertex0, vertex1, vertex2)) { + if (vertex2.x() <= vertex1.x() && vertex0.x() <= vertex1.x()) + offsetEdgeBuffer.append(OffsetPolygonEdge(vertex1, FloatSize(dx, -dy), FloatSize(dx, dy))); + else if (vertex2.x() >= vertex1.x() && vertex0.x() >= vertex1.x()) + offsetEdgeBuffer.append(OffsetPolygonEdge(vertex1, FloatSize(-dx, -dy), FloatSize(-dx, dy))); + if (vertex2.y() <= vertex1.y() && vertex0.y() <= vertex1.y()) + offsetEdgeBuffer.append(OffsetPolygonEdge(vertex1, FloatSize(-dx, dy), FloatSize(dx, dy))); + else if (vertex2.y() >= vertex1.y() && vertex0.y() >= vertex1.y()) + offsetEdgeBuffer.append(OffsetPolygonEdge(vertex1, FloatSize(-dx, -dy), FloatSize(dx, -dy))); + } + + for (unsigned j = 0; j < offsetEdgeBuffer.size(); ++j) + if (offsetEdgeBuffer[j].maxY() >= minY) + offsetEdges.append(offsetEdgeBuffer[j]); + } + + offsetEdges.append(OffsetPolygonEdge(polygon, minIntervalTop, FloatSize(0, dy))); + + FloatPoint offsetEdgesIntersection; + FloatRect firstFitRect; + bool firstFitFound = false; + + for (unsigned i = 0; i < offsetEdges.size() - 1; ++i) { + for (unsigned j = i + 1; j < offsetEdges.size(); ++j) { + if (offsetEdges[i].intersection(offsetEdges[j], offsetEdgesIntersection)) { + FloatPoint potentialFirstFitLocation(offsetEdgesIntersection.x() - dx, offsetEdgesIntersection.y() - dy); + FloatRect potentialFirstFitRect(potentialFirstFitLocation, minLogicalIntervalSize); + if ((offsetEdges[i].basis() == OffsetPolygonEdge::LineTop + || offsetEdges[j].basis() == OffsetPolygonEdge::LineTop + || potentialFirstFitLocation.y() >= minIntervalTop) + && (!firstFitFound || aboveOrToTheLeft(potentialFirstFitRect, firstFitRect)) + && polygon.contains(offsetEdgesIntersection) + && firstFitRectInPolygon(polygon, potentialFirstFitRect, offsetEdges[i].edgeIndex(), offsetEdges[j].edgeIndex())) { + firstFitFound = true; + firstFitRect = potentialFirstFitRect; + } + } + } + } + + if (firstFitFound) + result = ceiledLayoutUnit(firstFitRect.y()); + return firstFitFound; +} + +} // namespace WebCore diff --git a/Source/WebCore/rendering/shapes/PolygonShape.h b/Source/WebCore/rendering/shapes/PolygonShape.h new file mode 100644 index 000000000..628ac3add --- /dev/null +++ b/Source/WebCore/rendering/shapes/PolygonShape.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2012 Adobe Systems Incorporated. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PolygonShape_h +#define PolygonShape_h + +#include "FloatPolygon.h" +#include "Shape.h" +#include "ShapeInterval.h" + +namespace WebCore { + +class OffsetPolygonEdge : public VertexPair { +public: + enum Basis { + Edge, + Vertex, + LineTop + }; + + OffsetPolygonEdge(const FloatPolygonEdge& edge, const FloatSize& offset) + : m_vertex1(edge.vertex1() + offset) + , m_vertex2(edge.vertex2() + offset) + , m_edgeIndex(edge.edgeIndex()) + , m_basis(Edge) + { + } + + OffsetPolygonEdge(const FloatPoint& reflexVertex, const FloatSize& offset1, const FloatSize& offset2) + : m_vertex1(reflexVertex + offset1) + , m_vertex2(reflexVertex + offset2) + , m_edgeIndex(-1) + , m_basis(Vertex) + { + } + + OffsetPolygonEdge(const FloatPolygon& polygon, float minLogicalIntervalTop, const FloatSize& offset) + : m_vertex1(FloatPoint(polygon.boundingBox().x(), minLogicalIntervalTop) + offset) + , m_vertex2(FloatPoint(polygon.boundingBox().maxX(), minLogicalIntervalTop) + offset) + , m_edgeIndex(-1) + , m_basis(LineTop) + { + } + + virtual const FloatPoint& vertex1() const OVERRIDE { return m_vertex1; } + virtual const FloatPoint& vertex2() const OVERRIDE { return m_vertex2; } + int edgeIndex() const { return m_edgeIndex; } + Basis basis() const { return m_basis; } + +private: + FloatPoint m_vertex1; + FloatPoint m_vertex2; + int m_edgeIndex; + Basis m_basis; +}; + +class PolygonShape : public Shape { + WTF_MAKE_NONCOPYABLE(PolygonShape); +public: + PolygonShape(PassOwnPtr<Vector<FloatPoint> > vertices, WindRule fillRule) + : Shape() + , m_polygon(vertices, fillRule) + , m_marginBounds(nullptr) + , m_paddingBounds(nullptr) + { + } + + virtual LayoutRect shapeMarginLogicalBoundingBox() const OVERRIDE { return static_cast<LayoutRect>(shapeMarginBounds().boundingBox()); } + virtual LayoutRect shapePaddingLogicalBoundingBox() const OVERRIDE { return static_cast<LayoutRect>(shapePaddingBounds().boundingBox()); } + virtual bool isEmpty() const OVERRIDE { return m_polygon.isEmpty(); } + virtual void getExcludedIntervals(LayoutUnit logicalTop, LayoutUnit logicalHeight, SegmentList&) const OVERRIDE; + virtual void getIncludedIntervals(LayoutUnit logicalTop, LayoutUnit logicalHeight, SegmentList&) const OVERRIDE; + virtual bool firstIncludedIntervalLogicalTop(LayoutUnit minLogicalIntervalTop, const LayoutSize& minLogicalIntervalSize, LayoutUnit&) const OVERRIDE; + +private: + const FloatPolygon& shapeMarginBounds() const; + const FloatPolygon& shapePaddingBounds() const; + + FloatPolygon m_polygon; + mutable OwnPtr<FloatPolygon> m_marginBounds; + mutable OwnPtr<FloatPolygon> m_paddingBounds; +}; + +} // namespace WebCore + +#endif // PolygonShape_h diff --git a/Source/WebCore/rendering/shapes/RectangleShape.cpp b/Source/WebCore/rendering/shapes/RectangleShape.cpp new file mode 100644 index 000000000..7527fcfa7 --- /dev/null +++ b/Source/WebCore/rendering/shapes/RectangleShape.cpp @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2012 Adobe Systems Incorporated. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "RectangleShape.h" + +#include <wtf/MathExtras.h> + +namespace WebCore { + +static inline float ellipseXIntercept(float y, float rx, float ry) +{ + ASSERT(ry > 0); + return rx * sqrt(1 - (y * y) / (ry * ry)); +} + +static inline float ellipseYIntercept(float x, float rx, float ry) +{ + ASSERT(rx > 0); + return ry * sqrt(1 - (x * x) / (rx * rx)); +} + +FloatRoundedRect FloatRoundedRect::paddingBounds(float padding) const +{ + ASSERT(padding >= 0); + if (!padding || isEmpty()) + return *this; + + float boundsX = x() + std::min(width() / 2, padding); + float boundsY = y() + std::min(height() / 2, padding); + float boundsWidth = std::max(0.0f, width() - padding * 2); + float boundsHeight = std::max(0.0f, height() - padding * 2); + float boundsRadiusX = std::max(0.0f, rx() - padding); + float boundsRadiusY = std::max(0.0f, ry() - padding); + return FloatRoundedRect(FloatRect(boundsX, boundsY, boundsWidth, boundsHeight), FloatSize(boundsRadiusX, boundsRadiusY)); +} + +FloatRoundedRect FloatRoundedRect::marginBounds(float margin) const +{ + ASSERT(margin >= 0); + if (!margin) + return *this; + + float boundsX = x() - margin; + float boundsY = y() - margin; + float boundsWidth = width() + margin * 2; + float boundsHeight = height() + margin * 2; + float boundsRadiusX = rx() + margin; + float boundsRadiusY = ry() + margin; + return FloatRoundedRect(FloatRect(boundsX, boundsY, boundsWidth, boundsHeight), FloatSize(boundsRadiusX, boundsRadiusY)); +} + +FloatPoint FloatRoundedRect::cornerInterceptForWidth(float widthAtIntercept) const +{ + float xi = (width() - widthAtIntercept) / 2; + float yi = ry() - ellipseYIntercept(rx() - xi, rx(), ry()); + return FloatPoint(xi, yi); +} + +FloatRoundedRect RectangleShape::shapePaddingBounds() const +{ + if (!m_haveInitializedPaddingBounds) { + m_haveInitializedPaddingBounds = true; + m_paddingBounds = m_bounds.paddingBounds(shapePadding()); + } + return m_paddingBounds; +} + +FloatRoundedRect RectangleShape::shapeMarginBounds() const +{ + if (!m_haveInitializedMarginBounds) { + m_haveInitializedMarginBounds = true; + m_marginBounds = m_bounds.marginBounds(shapeMargin()); + } + return m_marginBounds; +} + +void RectangleShape::getExcludedIntervals(LayoutUnit logicalTop, LayoutUnit logicalHeight, SegmentList& result) const +{ + const FloatRoundedRect& bounds = shapeMarginBounds(); + if (bounds.isEmpty()) + return; + + float y1 = logicalTop; + float y2 = logicalTop + logicalHeight; + + if (y2 < bounds.y() || y1 >= bounds.maxY()) + return; + + float x1 = bounds.x(); + float x2 = bounds.maxX(); + + if (bounds.ry() > 0) { + if (y2 < bounds.y() + bounds.ry()) { + float yi = y2 - bounds.y() - bounds.ry(); + float xi = ellipseXIntercept(yi, bounds.rx(), bounds.ry()); + x1 = bounds.x() + bounds.rx() - xi; + x2 = bounds.maxX() - bounds.rx() + xi; + } else if (y1 > bounds.maxY() - bounds.ry()) { + float yi = y1 - (bounds.maxY() - bounds.ry()); + float xi = ellipseXIntercept(yi, bounds.rx(), bounds.ry()); + x1 = bounds.x() + bounds.rx() - xi; + x2 = bounds.maxX() - bounds.rx() + xi; + } + } + + result.append(LineSegment(x1, x2)); +} + +void RectangleShape::getIncludedIntervals(LayoutUnit logicalTop, LayoutUnit logicalHeight, SegmentList& result) const +{ + const FloatRoundedRect& bounds = shapePaddingBounds(); + if (bounds.isEmpty()) + return; + + float y1 = logicalTop; + float y2 = logicalTop + logicalHeight; + + if (y1 < bounds.y() || y2 > bounds.maxY()) + return; + + float x1 = bounds.x(); + float x2 = bounds.maxX(); + + if (bounds.ry() > 0) { + bool y1InterceptsCorner = y1 < bounds.y() + bounds.ry(); + bool y2InterceptsCorner = y2 > bounds.maxY() - bounds.ry(); + float xi = 0; + + if (y1InterceptsCorner && y2InterceptsCorner) { + if (y1 < bounds.height() + 2 * bounds.y() - y2) { + float yi = y1 - bounds.y() - bounds.ry(); + xi = ellipseXIntercept(yi, bounds.rx(), bounds.ry()); + } else { + float yi = y2 - (bounds.maxY() - bounds.ry()); + xi = ellipseXIntercept(yi, bounds.rx(), bounds.ry()); + } + } else if (y1InterceptsCorner) { + float yi = y1 - bounds.y() - bounds.ry(); + xi = ellipseXIntercept(yi, bounds.rx(), bounds.ry()); + } else if (y2InterceptsCorner) { + float yi = y2 - (bounds.maxY() - bounds.ry()); + xi = ellipseXIntercept(yi, bounds.rx(), bounds.ry()); + } + + if (y1InterceptsCorner || y2InterceptsCorner) { + x1 = bounds.x() + bounds.rx() - xi; + x2 = bounds.maxX() - bounds.rx() + xi; + } + } + + result.append(LineSegment(x1, x2)); +} + +bool RectangleShape::firstIncludedIntervalLogicalTop(LayoutUnit minLogicalIntervalTop, const LayoutSize& minLogicalIntervalSize, LayoutUnit& result) const +{ + float minIntervalTop = minLogicalIntervalTop; + float minIntervalHeight = minLogicalIntervalSize.height(); + float minIntervalWidth = minLogicalIntervalSize.width(); + + const FloatRoundedRect& bounds = shapePaddingBounds(); + if (bounds.isEmpty() || minIntervalWidth > bounds.width()) + return false; + + float minY = std::max(bounds.y(), minIntervalTop); + float maxY = minY + minIntervalHeight; + + if (maxY > bounds.maxY()) + return false; + + bool intervalOverlapsMinCorner = minY < bounds.y() + bounds.ry(); + bool intervalOverlapsMaxCorner = maxY > bounds.maxY() - bounds.ry(); + + if (!intervalOverlapsMinCorner && !intervalOverlapsMaxCorner) { + result = minY; + return true; + } + + float centerY = bounds.y() + bounds.height() / 2; + bool minCornerDefinesX = fabs(centerY - minY) > fabs(centerY - maxY); + bool intervalFitsWithinCorners = minIntervalWidth + 2 * bounds.rx() <= bounds.width(); + FloatPoint cornerIntercept = bounds.cornerInterceptForWidth(minIntervalWidth); + + if (intervalOverlapsMinCorner && (!intervalOverlapsMaxCorner || minCornerDefinesX)) { + if (intervalFitsWithinCorners || bounds.y() + cornerIntercept.y() < minY) { + result = minY; + return true; + } + if (minIntervalHeight < bounds.height() - (2 * cornerIntercept.y())) { + result = ceiledLayoutUnit(bounds.y() + cornerIntercept.y()); + return true; + } + } + + if (intervalOverlapsMaxCorner && (!intervalOverlapsMinCorner || !minCornerDefinesX)) { + if (intervalFitsWithinCorners || minY <= bounds.maxY() - cornerIntercept.y() - minIntervalHeight) { + result = minY; + return true; + } + } + + return false; +} + +} // namespace WebCore diff --git a/Source/WebCore/rendering/shapes/RectangleShape.h b/Source/WebCore/rendering/shapes/RectangleShape.h new file mode 100644 index 000000000..e9ecf0504 --- /dev/null +++ b/Source/WebCore/rendering/shapes/RectangleShape.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2012 Adobe Systems Incorporated. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef RectangleShape_h +#define RectangleShape_h + +#include "FloatPoint.h" +#include "FloatRect.h" +#include "FloatSize.h" +#include "Shape.h" +#include <wtf/Assertions.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class FloatRoundedRect : public FloatRect { +public: + FloatRoundedRect() { } + FloatRoundedRect(const FloatRect& bounds, const FloatSize& radii) + : FloatRect(bounds) + , m_radii(radii) + { + } + + float rx() const { return m_radii.width(); } + float ry() const { return m_radii.height(); } + FloatRoundedRect marginBounds(float margin) const; + FloatRoundedRect paddingBounds(float padding) const; + FloatPoint cornerInterceptForWidth(float width) const; + +private: + FloatSize m_radii; +}; + +class RectangleShape : public Shape { +public: + RectangleShape(const FloatRect& bounds, const FloatSize& radii) + : Shape() + , m_bounds(bounds, radii) + , m_haveInitializedMarginBounds(false) + , m_haveInitializedPaddingBounds(false) + { + } + + virtual LayoutRect shapeMarginLogicalBoundingBox() const OVERRIDE { return static_cast<LayoutRect>(shapeMarginBounds()); } + virtual LayoutRect shapePaddingLogicalBoundingBox() const OVERRIDE { return static_cast<LayoutRect>(shapePaddingBounds()); } + virtual bool isEmpty() const OVERRIDE { return m_bounds.isEmpty(); } + virtual void getExcludedIntervals(LayoutUnit logicalTop, LayoutUnit logicalHeight, SegmentList&) const OVERRIDE; + virtual void getIncludedIntervals(LayoutUnit logicalTop, LayoutUnit logicalHeight, SegmentList&) const OVERRIDE; + virtual bool firstIncludedIntervalLogicalTop(LayoutUnit minLogicalIntervalTop, const LayoutSize& minLogicalIntervalSize, LayoutUnit&) const OVERRIDE; + +private: + FloatRoundedRect shapeMarginBounds() const; + FloatRoundedRect shapePaddingBounds() const; + + FloatRoundedRect m_bounds; + mutable FloatRoundedRect m_marginBounds; + mutable FloatRoundedRect m_paddingBounds; + mutable bool m_haveInitializedMarginBounds : 1; + mutable bool m_haveInitializedPaddingBounds : 1; +}; + +} // namespace WebCore + +#endif // RectangleShape_h diff --git a/Source/WebCore/rendering/shapes/Shape.cpp b/Source/WebCore/rendering/shapes/Shape.cpp new file mode 100644 index 000000000..810ac5544 --- /dev/null +++ b/Source/WebCore/rendering/shapes/Shape.cpp @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2012 Adobe Systems Incorporated. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "Shape.h" + +#include "BasicShapeFunctions.h" +#include "FloatSize.h" +#include "LengthFunctions.h" +#include "PolygonShape.h" +#include "RectangleShape.h" +#include "WindRule.h" +#include <wtf/MathExtras.h> +#include <wtf/OwnPtr.h> +#include <wtf/PassOwnPtr.h> + +namespace WebCore { + +static PassOwnPtr<Shape> createRectangleShape(const FloatRect& bounds, const FloatSize& radii) +{ + ASSERT(bounds.width() >= 0 && bounds.height() >= 0 && radii.width() >= 0 && radii.height() >= 0); + return adoptPtr(new RectangleShape(bounds, radii)); +} + +static PassOwnPtr<Shape> createShapeCircle(const FloatPoint& center, float radius) +{ + ASSERT(radius >= 0); + return adoptPtr(new RectangleShape(FloatRect(center.x() - radius, center.y() - radius, radius*2, radius*2), FloatSize(radius, radius))); +} + +static PassOwnPtr<Shape> createShapeEllipse(const FloatPoint& center, const FloatSize& radii) +{ + ASSERT(radii.width() >= 0 && radii.height() >= 0); + return adoptPtr(new RectangleShape(FloatRect(center.x() - radii.width(), center.y() - radii.height(), radii.width()*2, radii.height()*2), radii)); +} + +static PassOwnPtr<Shape> createPolygonShape(PassOwnPtr<Vector<FloatPoint> > vertices, WindRule fillRule) +{ + return adoptPtr(new PolygonShape(vertices, fillRule)); +} + +static inline FloatRect physicalRectToLogical(const FloatRect& rect, float logicalBoxHeight, WritingMode writingMode) +{ + if (isHorizontalWritingMode(writingMode)) + return rect; + if (isFlippedBlocksWritingMode(writingMode)) + return FloatRect(rect.y(), logicalBoxHeight - rect.maxX(), rect.height(), rect.width()); + return rect.transposedRect(); +} + +static inline FloatPoint physicalPointToLogical(const FloatPoint& point, float logicalBoxHeight, WritingMode writingMode) +{ + if (isHorizontalWritingMode(writingMode)) + return point; + if (isFlippedBlocksWritingMode(writingMode)) + return FloatPoint(point.y(), logicalBoxHeight - point.x()); + return point.transposedPoint(); +} + +static inline FloatSize physicalSizeToLogical(const FloatSize& size, WritingMode writingMode) +{ + if (isHorizontalWritingMode(writingMode)) + return size; + return size.transposedSize(); +} + +static inline void ensureRadiiDoNotOverlap(FloatRect &bounds, FloatSize &radii) +{ + float widthRatio = bounds.width() / (2 * radii.width()); + float heightRatio = bounds.height() / (2 * radii.height()); + float reductionRatio = std::min<float>(widthRatio, heightRatio); + if (reductionRatio < 1) { + radii.setWidth(reductionRatio * radii.width()); + radii.setHeight(reductionRatio * radii.height()); + } +} + +PassOwnPtr<Shape> Shape::createShape(const BasicShape* basicShape, const LayoutSize& logicalBoxSize, WritingMode writingMode, Length margin, Length padding) +{ + ASSERT(basicShape); + + bool horizontalWritingMode = isHorizontalWritingMode(writingMode); + float boxWidth = horizontalWritingMode ? logicalBoxSize.width() : logicalBoxSize.height(); + float boxHeight = horizontalWritingMode ? logicalBoxSize.height() : logicalBoxSize.width(); + OwnPtr<Shape> shape; + + switch (basicShape->type()) { + + case BasicShape::BasicShapeRectangleType: { + const BasicShapeRectangle* rectangle = static_cast<const BasicShapeRectangle*>(basicShape); + FloatRect bounds( + floatValueForLength(rectangle->x(), boxWidth), + floatValueForLength(rectangle->y(), boxHeight), + floatValueForLength(rectangle->width(), boxWidth), + floatValueForLength(rectangle->height(), boxHeight)); + FloatSize cornerRadii( + floatValueForLength(rectangle->cornerRadiusX(), boxWidth), + floatValueForLength(rectangle->cornerRadiusY(), boxHeight)); + ensureRadiiDoNotOverlap(bounds, cornerRadii); + FloatRect logicalBounds = physicalRectToLogical(bounds, logicalBoxSize.height(), writingMode); + + shape = createRectangleShape(logicalBounds, physicalSizeToLogical(cornerRadii, writingMode)); + break; + } + + case BasicShape::BasicShapeCircleType: { + const BasicShapeCircle* circle = static_cast<const BasicShapeCircle*>(basicShape); + float centerX = floatValueForLength(circle->centerX(), boxWidth); + float centerY = floatValueForLength(circle->centerY(), boxHeight); + float radius = floatValueForLength(circle->radius(), std::min(boxHeight, boxWidth)); + FloatPoint logicalCenter = physicalPointToLogical(FloatPoint(centerX, centerY), logicalBoxSize.height(), writingMode); + + shape = createShapeCircle(logicalCenter, radius); + break; + } + + case BasicShape::BasicShapeEllipseType: { + const BasicShapeEllipse* ellipse = static_cast<const BasicShapeEllipse*>(basicShape); + float centerX = floatValueForLength(ellipse->centerX(), boxWidth); + float centerY = floatValueForLength(ellipse->centerY(), boxHeight); + float radiusX = floatValueForLength(ellipse->radiusX(), boxWidth); + float radiusY = floatValueForLength(ellipse->radiusY(), boxHeight); + FloatPoint logicalCenter = physicalPointToLogical(FloatPoint(centerX, centerY), logicalBoxSize.height(), writingMode); + FloatSize logicalRadii = physicalSizeToLogical(FloatSize(radiusX, radiusY), writingMode); + + shape = createShapeEllipse(logicalCenter, logicalRadii); + break; + } + + case BasicShape::BasicShapePolygonType: { + const BasicShapePolygon* polygon = static_cast<const BasicShapePolygon*>(basicShape); + const Vector<Length>& values = polygon->values(); + size_t valuesSize = values.size(); + ASSERT(!(valuesSize % 2)); + OwnPtr<Vector<FloatPoint> > vertices = adoptPtr(new Vector<FloatPoint>(valuesSize / 2)); + for (unsigned i = 0; i < valuesSize; i += 2) { + FloatPoint vertex( + floatValueForLength(values.at(i), boxWidth), + floatValueForLength(values.at(i + 1), boxHeight)); + (*vertices)[i / 2] = physicalPointToLogical(vertex, logicalBoxSize.height(), writingMode); + } + + shape = createPolygonShape(vertices.release(), polygon->windRule()); + break; + } + + case BasicShape::BasicShapeInsetRectangleType: { + const BasicShapeInsetRectangle* rectangle = static_cast<const BasicShapeInsetRectangle*>(basicShape); + float left = floatValueForLength(rectangle->left(), boxWidth); + float top = floatValueForLength(rectangle->top(), boxHeight); + FloatRect bounds( + left, + top, + boxWidth - left - floatValueForLength(rectangle->right(), boxWidth), + boxHeight - top - floatValueForLength(rectangle->bottom(), boxHeight)); + FloatSize cornerRadii( + floatValueForLength(rectangle->cornerRadiusX(), boxWidth), + floatValueForLength(rectangle->cornerRadiusY(), boxHeight)); + ensureRadiiDoNotOverlap(bounds, cornerRadii); + FloatRect logicalBounds = physicalRectToLogical(bounds, logicalBoxSize.height(), writingMode); + + shape = createRectangleShape(logicalBounds, physicalSizeToLogical(cornerRadii, writingMode)); + break; + } + + default: + ASSERT_NOT_REACHED(); + } + + shape->m_writingMode = writingMode; + shape->m_margin = floatValueForLength(margin, 0); + shape->m_padding = floatValueForLength(padding, 0); + + return shape.release(); +} + +} // namespace WebCore diff --git a/Source/WebCore/rendering/ExclusionShape.h b/Source/WebCore/rendering/shapes/Shape.h index 075c002c7..0d9ecfd72 100644 --- a/Source/WebCore/rendering/ExclusionShape.h +++ b/Source/WebCore/rendering/shapes/Shape.h @@ -27,11 +27,11 @@ * OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ExclusionShape_h -#define ExclusionShape_h +#ifndef Shape_h +#define Shape_h #include "BasicShapes.h" -#include "FloatRect.h" +#include "LayoutRect.h" #include "WritingMode.h" #include <wtf/PassOwnPtr.h> #include <wtf/Vector.h> @@ -45,8 +45,8 @@ struct LineSegment { { } - float logicalLeft; - float logicalRight; + LayoutUnit logicalLeft; + LayoutUnit logicalRight; }; typedef Vector<LineSegment> SegmentList; @@ -57,29 +57,29 @@ typedef Vector<LineSegment> SegmentList; // computed segments are returned as pairs of logical X coordinates. The BasicShape itself is defined in // physical coordinates. -class ExclusionShape { +class Shape { public: - static PassOwnPtr<ExclusionShape> createExclusionShape(const BasicShape*, float logicalBoxWidth, float logicalBoxHeight, WritingMode); + static PassOwnPtr<Shape> createShape(const BasicShape*, const LayoutSize& logicalBoxSize, WritingMode, Length margin, Length padding); - virtual ~ExclusionShape() { } + virtual ~Shape() { } - virtual FloatRect shapeLogicalBoundingBox() const = 0; + virtual LayoutRect shapeMarginLogicalBoundingBox() const = 0; + virtual LayoutRect shapePaddingLogicalBoundingBox() const = 0; virtual bool isEmpty() const = 0; - virtual void getIncludedIntervals(float logicalTop, float logicalHeight, SegmentList&) const = 0; - virtual void getExcludedIntervals(float logicalTop, float logicalHeight, SegmentList&) const = 0; + virtual void getIncludedIntervals(LayoutUnit logicalTop, LayoutUnit logicalHeight, SegmentList&) const = 0; + virtual void getExcludedIntervals(LayoutUnit logicalTop, LayoutUnit logicalHeight, SegmentList&) const = 0; + virtual bool firstIncludedIntervalLogicalTop(LayoutUnit minLogicalIntervalTop, const LayoutSize& minLogicalIntervalSize, LayoutUnit& result) const = 0; protected: - float minYForLogicalLine(float logicalTop, float logicalHeight) const { return (m_writingMode == RightToLeftWritingMode) ? m_logicalBoxHeight - logicalTop - logicalHeight : logicalTop; } - float maxYForLogicalLine(float logicalTop, float logicalHeight) const { return (m_writingMode == RightToLeftWritingMode) ? m_logicalBoxHeight - logicalTop : logicalTop + logicalHeight; } - FloatRect internalToLogicalBoundingBox(FloatRect r) const { return (m_writingMode == RightToLeftWritingMode) ? FloatRect(r.x(), m_logicalBoxHeight - r.maxY(), r.width(), r.height()) : r; } + float shapeMargin() const { return m_margin; } + float shapePadding() const { return m_padding; } private: WritingMode m_writingMode; - float m_logicalBoxWidth; - float m_logicalBoxHeight; - FloatRect m_boundingBox; + float m_margin; + float m_padding; }; } // namespace WebCore -#endif // ExclusionShape_h +#endif // Shape_h diff --git a/Source/WebCore/rendering/shapes/ShapeInfo.cpp b/Source/WebCore/rendering/shapes/ShapeInfo.cpp new file mode 100644 index 000000000..e11558321 --- /dev/null +++ b/Source/WebCore/rendering/shapes/ShapeInfo.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2012 Adobe Systems Incorporated. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "config.h" +#include "ShapeInfo.h" + +#if ENABLE(CSS_SHAPES) + +#include "RenderBlock.h" +#include "RenderBox.h" +#include "RenderRegion.h" +#include "RenderStyle.h" +#include "Shape.h" + +namespace WebCore { +template<class RenderType, ShapeValue* (RenderStyle::*shapeGetter)() const, void (Shape::*intervalGetter)(LayoutUnit, LayoutUnit, SegmentList&) const> +const Shape* ShapeInfo<RenderType, shapeGetter, intervalGetter>::computedShape() const +{ + if (Shape* shape = m_shape.get()) + return shape; + + ShapeValue* shapeValue = (m_renderer->style()->*shapeGetter)(); + BasicShape* shape = (shapeValue && shapeValue->type() == ShapeValue::Shape) ? shapeValue->shape() : 0; + + ASSERT(shape); + + m_shape = Shape::createShape(shape, LayoutSize(m_shapeLogicalWidth, m_shapeLogicalHeight), m_renderer->style()->writingMode(), m_renderer->style()->shapeMargin(), m_renderer->style()->shapePadding()); + ASSERT(m_shape); + return m_shape.get(); +} + +template<class RenderType, ShapeValue* (RenderStyle::*shapeGetter)() const, void (Shape::*intervalGetter)(LayoutUnit, LayoutUnit, SegmentList&) const> +bool ShapeInfo<RenderType, shapeGetter, intervalGetter>::computeSegmentsForLine(LayoutUnit lineTop, LayoutUnit lineHeight) +{ + ASSERT(lineHeight >= 0); + m_shapeLineTop = lineTop - logicalTopOffset(); + m_lineHeight = lineHeight; + m_segments.clear(); + + if (lineOverlapsShapeBounds()) + (computedShape()->*intervalGetter)(m_shapeLineTop, std::min(m_lineHeight, shapeLogicalBottom() - lineTop), m_segments); + + LayoutUnit logicalLeftOffset = this->logicalLeftOffset(); + for (size_t i = 0; i < m_segments.size(); i++) { + m_segments[i].logicalLeft += logicalLeftOffset; + m_segments[i].logicalRight += logicalLeftOffset; + } + + return m_segments.size(); +} + +template class ShapeInfo<RenderBlock, &RenderStyle::resolvedShapeInside, &Shape::getIncludedIntervals>; +template class ShapeInfo<RenderBox, &RenderStyle::shapeOutside, &Shape::getExcludedIntervals>; +} +#endif diff --git a/Source/WebCore/rendering/shapes/ShapeInfo.h b/Source/WebCore/rendering/shapes/ShapeInfo.h new file mode 100644 index 000000000..04ee471a8 --- /dev/null +++ b/Source/WebCore/rendering/shapes/ShapeInfo.h @@ -0,0 +1,131 @@ +/* +* Copyright (C) 2012 Adobe Systems Incorporated. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* 1. Redistributions of source code must retain the above +* copyright notice, this list of conditions and the following +* disclaimer. +* 2. Redistributions in binary form must reproduce the above +* copyright notice, this list of conditions and the following +* disclaimer in the documentation and/or other materials +* provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +* SUCH DAMAGE. +*/ + +#ifndef ShapeInfo_h +#define ShapeInfo_h + +#if ENABLE(CSS_SHAPES) + +#include "FloatRect.h" +#include "LayoutUnit.h" +#include "RenderStyle.h" +#include "Shape.h" +#include "ShapeValue.h" +#include <wtf/OwnPtr.h> +#include <wtf/Vector.h> + +namespace WebCore { + +template<class KeyType, class InfoType> +class MappedInfo { +public: + static InfoType* ensureInfo(const KeyType* key) + { + InfoMap& infoMap = MappedInfo<KeyType, InfoType>::infoMap(); + if (InfoType* info = infoMap.get(key)) + return info; + typename InfoMap::AddResult result = infoMap.add(key, InfoType::createInfo(key)); + return result.iterator->value.get(); + } + static void removeInfo(const KeyType* key) { infoMap().remove(key); } + static InfoType* info(const KeyType* key) { return infoMap().get(key); } +private: + typedef HashMap<const KeyType*, OwnPtr<InfoType> > InfoMap; + static InfoMap& infoMap() + { + DEFINE_STATIC_LOCAL(InfoMap, staticInfoMap, ()); + return staticInfoMap; + } +}; + +template<class RenderType, ShapeValue* (RenderStyle::*shapeGetter)() const, void (Shape::*intervalGetter)(LayoutUnit, LayoutUnit, SegmentList&) const> +class ShapeInfo { + WTF_MAKE_FAST_ALLOCATED; +public: + virtual ~ShapeInfo() { } + + void setShapeSize(LayoutUnit logicalWidth, LayoutUnit logicalHeight) + { + if (m_renderer->style()->boxSizing() == CONTENT_BOX) { + logicalWidth -= m_renderer->borderAndPaddingLogicalWidth(); + logicalHeight -= m_renderer->borderAndPaddingLogicalHeight(); + } + + if (m_shapeLogicalWidth == logicalWidth && m_shapeLogicalHeight == logicalHeight) + return; + dirtyShapeSize(); + m_shapeLogicalWidth = logicalWidth; + m_shapeLogicalHeight = logicalHeight; + } + + virtual bool computeSegmentsForLine(LayoutUnit lineTop, LayoutUnit lineHeight); + void clearSegments() { m_segments.clear(); } + + LayoutUnit shapeLogicalTop() const { return computedShapeLogicalBoundingBox().y() + logicalTopOffset(); } + LayoutUnit shapeLogicalBottom() const { return computedShapeLogicalBoundingBox().maxY() + logicalTopOffset(); } + LayoutUnit shapeLogicalLeft() const { return computedShapeLogicalBoundingBox().x() + logicalLeftOffset(); } + LayoutUnit shapeLogicalRight() const { return computedShapeLogicalBoundingBox().maxX() + logicalLeftOffset(); } + LayoutUnit shapeLogicalWidth() const { return computedShapeLogicalBoundingBox().width(); } + LayoutUnit shapeLogicalHeight() const { return computedShapeLogicalBoundingBox().height(); } + + LayoutUnit logicalLineTop() const { return m_shapeLineTop + logicalTopOffset(); } + LayoutUnit logicalLineBottom() const { return m_shapeLineTop + m_lineHeight + logicalTopOffset(); } + + LayoutUnit shapeContainingBlockHeight() const { return (m_renderer->style()->boxSizing() == CONTENT_BOX) ? (m_shapeLogicalHeight + m_renderer->borderAndPaddingLogicalHeight()) : m_shapeLogicalHeight; } + + bool lineOverlapsShapeBounds() const { return logicalLineTop() < shapeLogicalBottom() && shapeLogicalTop() <= logicalLineBottom(); } + + void dirtyShapeSize() { m_shape.clear(); } + bool shapeSizeDirty() { return !m_shape.get(); } + const RenderType* owner() const { return m_renderer; } + +protected: + ShapeInfo(const RenderType* renderer): m_renderer(renderer) { } + + const Shape* computedShape() const; + virtual LayoutRect computedShapeLogicalBoundingBox() const = 0; + + LayoutUnit logicalTopOffset() const { return m_renderer->style()->boxSizing() == CONTENT_BOX ? m_renderer->borderAndPaddingBefore() : LayoutUnit(); }; + LayoutUnit logicalLeftOffset() const { return (m_renderer->style()->boxSizing() == CONTENT_BOX && !m_renderer->isRenderRegion()) ? m_renderer->borderAndPaddingStart() : LayoutUnit(); } + + LayoutUnit m_shapeLineTop; + LayoutUnit m_lineHeight; + SegmentList m_segments; + + const RenderType* m_renderer; + +private: + mutable OwnPtr<Shape> m_shape; + + LayoutUnit m_shapeLogicalWidth; + LayoutUnit m_shapeLogicalHeight; +}; +} +#endif +#endif diff --git a/Source/WebCore/rendering/shapes/ShapeInsideInfo.cpp b/Source/WebCore/rendering/shapes/ShapeInsideInfo.cpp new file mode 100644 index 000000000..d1295d03e --- /dev/null +++ b/Source/WebCore/rendering/shapes/ShapeInsideInfo.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2012 Adobe Systems Incorporated. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "config.h" +#include "ShapeInsideInfo.h" + +#if ENABLE(CSS_SHAPES) + +#include "InlineIterator.h" +#include "RenderBlock.h" + +namespace WebCore { + +LineSegmentRange::LineSegmentRange(const InlineIterator& start, const InlineIterator& end) + : start(start.root(), start.object(), start.offset()) + , end(end.root(), end.object(), end.offset()) + { + } + +bool ShapeInsideInfo::isEnabledFor(const RenderBlock* renderer) +{ + ShapeValue* shapeValue = renderer->style()->resolvedShapeInside(); + if (!shapeValue || shapeValue->type() != ShapeValue::Shape) + return false; + + BasicShape* shape = shapeValue->shape(); + return shape && shape->type() != BasicShape::BasicShapeInsetRectangleType; +} + +bool ShapeInsideInfo::adjustLogicalLineTop(float minSegmentWidth) +{ + const Shape* shape = computedShape(); + if (!shape || m_lineHeight <= 0 || logicalLineTop() > shapeLogicalBottom()) + return false; + + LayoutUnit newLineTop; + if (shape->firstIncludedIntervalLogicalTop(m_shapeLineTop, LayoutSize(minSegmentWidth, m_lineHeight), newLineTop)) { + if (newLineTop > m_shapeLineTop) { + m_shapeLineTop = newLineTop; + return true; + } + } + + return false; +} + +} +#endif diff --git a/Source/WebCore/rendering/shapes/ShapeInsideInfo.h b/Source/WebCore/rendering/shapes/ShapeInsideInfo.h new file mode 100644 index 000000000..c006b3645 --- /dev/null +++ b/Source/WebCore/rendering/shapes/ShapeInsideInfo.h @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2012 Adobe Systems Incorporated. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef ShapeInsideInfo_h +#define ShapeInsideInfo_h + +#if ENABLE(CSS_SHAPES) + +#include "ShapeInfo.h" +#include <wtf/PassOwnPtr.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class InlineIterator; +class RenderBlock; +class RenderObject; + +struct LineSegmentIterator { + RenderObject* root; + RenderObject* object; + unsigned offset; + LineSegmentIterator(RenderObject* root, RenderObject* object, unsigned offset) + : root(root) + , object(object) + , offset(offset) + { + } +}; + +struct LineSegmentRange { + LineSegmentIterator start; + LineSegmentIterator end; + LineSegmentRange(const InlineIterator& start, const InlineIterator& end); +}; + +typedef Vector<LineSegmentRange> SegmentRangeList; + +class ShapeInsideInfo : public ShapeInfo<RenderBlock, &RenderStyle::resolvedShapeInside, &Shape::getIncludedIntervals> { +public: + static PassOwnPtr<ShapeInsideInfo> createInfo(const RenderBlock* renderer) { return adoptPtr(new ShapeInsideInfo(renderer)); } + + static bool isEnabledFor(const RenderBlock* renderer); + + virtual bool computeSegmentsForLine(LayoutUnit lineTop, LayoutUnit lineHeight) OVERRIDE + { + m_segmentRanges.clear(); + return ShapeInfo<RenderBlock, &RenderStyle::resolvedShapeInside, &Shape::getIncludedIntervals>::computeSegmentsForLine(lineTop, lineHeight); + } + + bool hasSegments() const + { + return lineOverlapsShapeBounds() && m_segments.size(); + } + const SegmentList& segments() const + { + ASSERT(hasSegments()); + return m_segments; + } + SegmentRangeList& segmentRanges() { return m_segmentRanges; } + const SegmentRangeList& segmentRanges() const { return m_segmentRanges; } + const LineSegment* currentSegment() const + { + if (!hasSegments()) + return 0; + ASSERT(m_segmentRanges.size() < m_segments.size()); + return &m_segments[m_segmentRanges.size()]; + } + bool adjustLogicalLineTop(float minSegmentWidth); + + void setNeedsLayout(bool value) { m_needsLayout = value; } + bool needsLayout() { return m_needsLayout; } + +protected: + virtual LayoutRect computedShapeLogicalBoundingBox() const OVERRIDE { return computedShape()->shapePaddingLogicalBoundingBox(); } + +private: + ShapeInsideInfo(const RenderBlock* renderer) + : ShapeInfo<RenderBlock, &RenderStyle::resolvedShapeInside, &Shape::getIncludedIntervals> (renderer) + , m_needsLayout(false) + { } + + SegmentRangeList m_segmentRanges; + bool m_needsLayout:1; +}; + +} +#endif +#endif diff --git a/Source/WebCore/rendering/ExclusionInterval.cpp b/Source/WebCore/rendering/shapes/ShapeInterval.cpp index 4e0ef7a98..b558038b2 100644 --- a/Source/WebCore/rendering/ExclusionInterval.cpp +++ b/Source/WebCore/rendering/shapes/ShapeInterval.cpp @@ -28,20 +28,20 @@ */ #include "config.h" -#include "ExclusionInterval.h" +#include "ShapeInterval.h" #include <wtf/MathExtras.h> namespace WebCore { struct IntervalX1Comparator { - bool operator() (const ExclusionInterval& i1, const ExclusionInterval& i2) const + bool operator() (const ShapeInterval& i1, const ShapeInterval& i2) const { return i1.x1 < i2.x1; } }; -bool ExclusionInterval::intersect(const ExclusionInterval& i, ExclusionInterval& rv) const +bool ShapeInterval::intersect(const ShapeInterval& i, ShapeInterval& rv) const { if (x2 < i.x1 || x1 > i.x2) return false; @@ -50,20 +50,20 @@ bool ExclusionInterval::intersect(const ExclusionInterval& i, ExclusionInterval& return true; } -void sortExclusionIntervals(Vector<ExclusionInterval>& v) +void sortShapeIntervals(Vector<ShapeInterval>& v) { std::sort(v.begin(), v.end(), IntervalX1Comparator()); } -void mergeExclusionIntervals(const Vector<ExclusionInterval>& v1, const Vector<ExclusionInterval>& v2, Vector<ExclusionInterval>& rv) +void mergeShapeIntervals(const Vector<ShapeInterval>& v1, const Vector<ShapeInterval>& v2, Vector<ShapeInterval>& rv) { if (!v1.size()) rv.appendRange(v2.begin(), v2.end()); else if (!v2.size()) rv.appendRange(v1.begin(), v1.end()); else { - Vector<ExclusionInterval> v(v1.size() + v2.size()); - ExclusionInterval* interval = 0; + Vector<ShapeInterval> v(v1.size() + v2.size()); + ShapeInterval* interval = 0; std::merge(v1.begin(), v1.end(), v2.begin(), v2.end(), v.begin(), IntervalX1Comparator()); @@ -83,7 +83,7 @@ void mergeExclusionIntervals(const Vector<ExclusionInterval>& v1, const Vector<E } } -void intersectExclusionIntervals(const Vector<ExclusionInterval>& v1, const Vector<ExclusionInterval>& v2, Vector<ExclusionInterval>& rv) +void intersectShapeIntervals(const Vector<ShapeInterval>& v1, const Vector<ShapeInterval>& v2, Vector<ShapeInterval>& rv) { size_t v1Size = v1.size(); size_t v2Size = v2.size(); @@ -91,13 +91,13 @@ void intersectExclusionIntervals(const Vector<ExclusionInterval>& v1, const Vect if (!v1Size || !v2Size) return; - ExclusionInterval interval; + ShapeInterval interval; bool overlap = false; size_t i1 = 0; size_t i2 = 0; while (i1 < v1Size && i2 < v2Size) { - ExclusionInterval v12; + ShapeInterval v12; if (v1[i1].intersect(v2[i2], v12)) { if (!overlap || !v12.intersect(interval, interval)) { if (overlap) @@ -124,7 +124,7 @@ void intersectExclusionIntervals(const Vector<ExclusionInterval>& v1, const Vect rv.append(interval); } -void subtractExclusionIntervals(const Vector<ExclusionInterval>& v1, const Vector<ExclusionInterval>& v2, Vector<ExclusionInterval>& rv) +void subtractShapeIntervals(const Vector<ShapeInterval>& v1, const Vector<ShapeInterval>& v2, Vector<ShapeInterval>& rv) { size_t v1Size = v1.size(); size_t v2Size = v2.size(); @@ -139,8 +139,8 @@ void subtractExclusionIntervals(const Vector<ExclusionInterval>& v1, const Vecto rv.appendRange(v1.begin(), v1.end()); while (i1 < rv.size() && i2 < v2Size) { - ExclusionInterval& interval1 = rv[i1]; - const ExclusionInterval& interval2 = v2[i2]; + ShapeInterval& interval1 = rv[i1]; + const ShapeInterval& interval2 = v2[i2]; if (interval2.x1 <= interval1.x1 && interval2.x2 >= interval1.x2) rv.remove(i1); @@ -149,7 +149,7 @@ void subtractExclusionIntervals(const Vector<ExclusionInterval>& v1, const Vecto else if (interval2.x1 > interval1.x2) i1 += 1; else if (interval2.x1 > interval1.x1 && interval2.x2 < interval1.x2) { - rv.insert(i1, ExclusionInterval(interval1.x1, interval2.x1)); + rv.insert(i1, ShapeInterval(interval1.x1, interval2.x1)); interval1.x1 = interval2.x2; i2 += 1; } else if (interval2.x1 <= interval1.x1) { diff --git a/Source/WebCore/rendering/ExclusionInterval.h b/Source/WebCore/rendering/shapes/ShapeInterval.h index 5d31feb18..0ff14d8a5 100644 --- a/Source/WebCore/rendering/ExclusionInterval.h +++ b/Source/WebCore/rendering/shapes/ShapeInterval.h @@ -27,32 +27,32 @@ * OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ExclusionInterval_h -#define ExclusionInterval_h +#ifndef ShapeInterval_h +#define ShapeInterval_h #include <wtf/Vector.h> namespace WebCore { -struct ExclusionInterval { +struct ShapeInterval { public: float x1; float x2; - ExclusionInterval(float x1 = 0, float x2 = 0) + ShapeInterval(float x1 = 0, float x2 = 0) : x1(x1) , x2(x2) { } - bool intersect(const ExclusionInterval&, ExclusionInterval&) const; + bool intersect(const ShapeInterval&, ShapeInterval&) const; }; -void sortExclusionIntervals(Vector<ExclusionInterval>&); -void mergeExclusionIntervals(const Vector<ExclusionInterval>&, const Vector<ExclusionInterval>&, Vector<ExclusionInterval>&); -void intersectExclusionIntervals(const Vector<ExclusionInterval>&, const Vector<ExclusionInterval>&, Vector<ExclusionInterval>&); -void subtractExclusionIntervals(const Vector<ExclusionInterval>&, const Vector<ExclusionInterval>&, Vector<ExclusionInterval>&); +void sortShapeIntervals(Vector<ShapeInterval>&); +void mergeShapeIntervals(const Vector<ShapeInterval>&, const Vector<ShapeInterval>&, Vector<ShapeInterval>&); +void intersectShapeIntervals(const Vector<ShapeInterval>&, const Vector<ShapeInterval>&, Vector<ShapeInterval>&); +void subtractShapeIntervals(const Vector<ShapeInterval>&, const Vector<ShapeInterval>&, Vector<ShapeInterval>&); } // namespace WebCore -#endif // ExclusionInterval_h +#endif // ShapeInterval_h diff --git a/Source/WebCore/rendering/shapes/ShapeOutsideInfo.cpp b/Source/WebCore/rendering/shapes/ShapeOutsideInfo.cpp new file mode 100644 index 000000000..9ffe7dc5b --- /dev/null +++ b/Source/WebCore/rendering/shapes/ShapeOutsideInfo.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2012 Adobe Systems Incorporated. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(CSS_SHAPES) + +#include "ShapeOutsideInfo.h" + +#include "RenderBox.h" + +namespace WebCore { +bool ShapeOutsideInfo::isEnabledFor(const RenderBox* box) +{ + ShapeValue* value = box->style()->shapeOutside(); + return box->isFloatingWithShapeOutside() && value->type() == ShapeValue::Shape && value->shape(); +} + +bool ShapeOutsideInfo::computeSegmentsForContainingBlockLine(LayoutUnit lineTop, LayoutUnit floatTop, LayoutUnit lineHeight) +{ + LayoutUnit lineTopInShapeCoordinates = lineTop - floatTop + logicalTopOffset(); + return computeSegmentsForLine(lineTopInShapeCoordinates, lineHeight); +} + +bool ShapeOutsideInfo::computeSegmentsForLine(LayoutUnit lineTop, LayoutUnit lineHeight) +{ + if (shapeSizeDirty() || m_lineTop != lineTop || m_lineHeight != lineHeight) { + if (ShapeInfo<RenderBox, &RenderStyle::shapeOutside, &Shape::getExcludedIntervals>::computeSegmentsForLine(lineTop, lineHeight)) { + m_leftSegmentMarginBoxDelta = m_segments[0].logicalLeft + m_renderer->marginStart(); + m_rightSegmentMarginBoxDelta = m_segments[m_segments.size()-1].logicalRight - m_renderer->logicalWidth() - m_renderer->marginEnd(); + } else { + m_leftSegmentMarginBoxDelta = m_renderer->logicalWidth() + m_renderer->marginStart(); + m_rightSegmentMarginBoxDelta = -m_renderer->logicalWidth() - m_renderer->marginEnd(); + } + m_lineTop = lineTop; + } + + return m_segments.size(); +} + +} +#endif diff --git a/Source/WebCore/rendering/shapes/ShapeOutsideInfo.h b/Source/WebCore/rendering/shapes/ShapeOutsideInfo.h new file mode 100644 index 000000000..685b0f94c --- /dev/null +++ b/Source/WebCore/rendering/shapes/ShapeOutsideInfo.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2012 Adobe Systems Incorporated. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef ShapeOutsideInfo_h +#define ShapeOutsideInfo_h + +#if ENABLE(CSS_SHAPES) + +#include "LayoutSize.h" +#include "ShapeInfo.h" + +namespace WebCore { + +class RenderBox; + +class ShapeOutsideInfo : public ShapeInfo<RenderBox, &RenderStyle::shapeOutside, &Shape::getExcludedIntervals>, public MappedInfo<RenderBox, ShapeOutsideInfo> { +public: + LayoutUnit leftSegmentMarginBoxDelta() const { return m_leftSegmentMarginBoxDelta; } + LayoutUnit rightSegmentMarginBoxDelta() const { return m_rightSegmentMarginBoxDelta; } + + bool computeSegmentsForContainingBlockLine(LayoutUnit lineTop, LayoutUnit floatTop, LayoutUnit lineHeight); + virtual bool computeSegmentsForLine(LayoutUnit lineTop, LayoutUnit lineHeight) OVERRIDE; + + static PassOwnPtr<ShapeOutsideInfo> createInfo(const RenderBox* renderer) { return adoptPtr(new ShapeOutsideInfo(renderer)); } + static bool isEnabledFor(const RenderBox*); + +protected: + virtual LayoutRect computedShapeLogicalBoundingBox() const OVERRIDE { return computedShape()->shapeMarginLogicalBoundingBox(); } + +private: + ShapeOutsideInfo(const RenderBox* renderer) : ShapeInfo<RenderBox, &RenderStyle::shapeOutside, &Shape::getExcludedIntervals>(renderer) { } + + LayoutUnit m_leftSegmentMarginBoxDelta; + LayoutUnit m_rightSegmentMarginBoxDelta; + LayoutUnit m_lineTop; +}; + +} +#endif +#endif diff --git a/Source/WebCore/rendering/style/BasicShapes.cpp b/Source/WebCore/rendering/style/BasicShapes.cpp index b21358fdc..b5927f924 100644 --- a/Source/WebCore/rendering/style/BasicShapes.cpp +++ b/Source/WebCore/rendering/style/BasicShapes.cpp @@ -30,6 +30,7 @@ #include "config.h" #include "BasicShapes.h" + #include "FloatRect.h" #include "LengthFunctions.h" #include "Path.h" @@ -43,7 +44,7 @@ bool BasicShape::canBlend(const BasicShape* other) const return false; // Just polygons with same number of vertices can be animated. - if (type() == BasicShape::BASIC_SHAPE_POLYGON + if (type() == BasicShape::BasicShapePolygonType && static_cast<const BasicShapePolygon*>(this)->values().size() != static_cast<const BasicShapePolygon*>(other)->values().size()) return false; @@ -53,12 +54,18 @@ bool BasicShape::canBlend(const BasicShape* other) const void BasicShapeRectangle::path(Path& path, const FloatRect& boundingBox) { ASSERT(path.isEmpty()); - path.addRoundedRect(FloatRect(floatValueForLength(m_x, boundingBox.width()) + boundingBox.x(), - floatValueForLength(m_y, boundingBox.height()) + boundingBox.y(), - floatValueForLength(m_width, boundingBox.width()), - floatValueForLength(m_height, boundingBox.height())), - FloatSize(m_cornerRadiusX.isUndefined() ? 0 : floatValueForLength(m_cornerRadiusX, boundingBox.width()), - m_cornerRadiusY.isUndefined() ? 0 : floatValueForLength(m_cornerRadiusY, boundingBox.height()))); + path.addRoundedRect( + FloatRect( + floatValueForLength(m_x, boundingBox.width()) + boundingBox.x(), + floatValueForLength(m_y, boundingBox.height()) + boundingBox.y(), + floatValueForLength(m_width, boundingBox.width()), + floatValueForLength(m_height, boundingBox.height()) + ), + FloatSize( + floatValueForLength(m_cornerRadiusX, boundingBox.width()), + floatValueForLength(m_cornerRadiusY, boundingBox.height()) + ) + ); } PassRefPtr<BasicShape> BasicShapeRectangle::blend(const BasicShape* other, double progress) const @@ -71,10 +78,8 @@ PassRefPtr<BasicShape> BasicShapeRectangle::blend(const BasicShape* other, doubl result->setY(m_y.blend(o->y(), progress)); result->setWidth(m_width.blend(o->width(), progress)); result->setHeight(m_height.blend(o->height(), progress)); - if (!m_cornerRadiusX.isUndefined() && !o->cornerRadiusX().isUndefined()) - result->setCornerRadiusX(m_cornerRadiusX.blend(o->cornerRadiusX(), progress)); - if (!m_cornerRadiusY.isUndefined() && !o->cornerRadiusY().isUndefined()) - result->setCornerRadiusY(m_cornerRadiusY.blend(o->cornerRadiusY(), progress)); + result->setCornerRadiusX(m_cornerRadiusX.blend(o->cornerRadiusX(), progress)); + result->setCornerRadiusY(m_cornerRadiusY.blend(o->cornerRadiusY(), progress)); return result.release(); } @@ -85,10 +90,12 @@ void BasicShapeCircle::path(Path& path, const FloatRect& boundingBox) float centerX = floatValueForLength(m_centerX, boundingBox.width()); float centerY = floatValueForLength(m_centerY, boundingBox.height()); float radius = floatValueForLength(m_radius, diagonal); - path.addEllipse(FloatRect(centerX - radius + boundingBox.x(), - centerY - radius + boundingBox.y(), - radius * 2, - radius * 2)); + path.addEllipse(FloatRect( + centerX - radius + boundingBox.x(), + centerY - radius + boundingBox.y(), + radius * 2, + radius * 2 + )); } PassRefPtr<BasicShape> BasicShapeCircle::blend(const BasicShape* other, double progress) const @@ -110,10 +117,12 @@ void BasicShapeEllipse::path(Path& path, const FloatRect& boundingBox) float centerY = floatValueForLength(m_centerY, boundingBox.height()); float radiusX = floatValueForLength(m_radiusX, boundingBox.width()); float radiusY = floatValueForLength(m_radiusY, boundingBox.height()); - path.addEllipse(FloatRect(centerX - radiusX + boundingBox.x(), - centerY - radiusY + boundingBox.y(), - radiusX * 2, - radiusY * 2)); + path.addEllipse(FloatRect( + centerX - radiusX + boundingBox.x(), + centerY - radiusY + boundingBox.y(), + radiusX * 2, + radiusY * 2 + )); } PassRefPtr<BasicShape> BasicShapeEllipse::blend(const BasicShape* other, double progress) const @@ -169,4 +178,38 @@ PassRefPtr<BasicShape> BasicShapePolygon::blend(const BasicShape* other, double return result.release(); } + +void BasicShapeInsetRectangle::path(Path& path, const FloatRect& boundingBox) +{ + ASSERT(path.isEmpty()); + float left = floatValueForLength(m_left, boundingBox.width()); + float top = floatValueForLength(m_top, boundingBox.height()); + path.addRoundedRect( + FloatRect( + left + boundingBox.x(), + top + boundingBox.y(), + std::max<float>(boundingBox.width() - left - floatValueForLength(m_right, boundingBox.width()), 0), + std::max<float>(boundingBox.height() - top - floatValueForLength(m_bottom, boundingBox.height()), 0) + ), + FloatSize( + floatValueForLength(m_cornerRadiusX, boundingBox.width()), + floatValueForLength(m_cornerRadiusY, boundingBox.height()) + ) + ); +} + +PassRefPtr<BasicShape> BasicShapeInsetRectangle::blend(const BasicShape* other, double progress) const +{ + ASSERT(type() == other->type()); + + const BasicShapeInsetRectangle* o = static_cast<const BasicShapeInsetRectangle*>(other); + RefPtr<BasicShapeInsetRectangle> result = BasicShapeInsetRectangle::create(); + result->setTop(m_top.blend(o->top(), progress)); + result->setRight(m_right.blend(o->right(), progress)); + result->setBottom(m_bottom.blend(o->bottom(), progress)); + result->setLeft(m_left.blend(o->left(), progress)); + result->setCornerRadiusX(m_cornerRadiusX.blend(o->cornerRadiusX(), progress)); + result->setCornerRadiusY(m_cornerRadiusY.blend(o->cornerRadiusY(), progress)); + return result.release(); +} } diff --git a/Source/WebCore/rendering/style/BasicShapes.h b/Source/WebCore/rendering/style/BasicShapes.h index 171d9c1f6..ec027fe91 100644 --- a/Source/WebCore/rendering/style/BasicShapes.h +++ b/Source/WebCore/rendering/style/BasicShapes.h @@ -46,10 +46,11 @@ public: virtual ~BasicShape() { } enum Type { - BASIC_SHAPE_RECTANGLE = 1, - BASIC_SHAPE_CIRCLE = 2, - BASIC_SHAPE_ELLIPSE = 3, - BASIC_SHAPE_POLYGON = 4 + BasicShapeRectangleType = 1, + BasicShapeCircleType = 2, + BasicShapeEllipseType = 3, + BasicShapePolygonType = 4, + BasicShapeInsetRectangleType = 5 }; bool canBlend(const BasicShape*) const; @@ -78,18 +79,23 @@ public: void setY(Length y) { m_y = y; } void setWidth(Length width) { m_width = width; } void setHeight(Length height) { m_height = height; } - void setCornerRadiusX(Length radiusX) { m_cornerRadiusX = radiusX; } - void setCornerRadiusY(Length radiusY) { m_cornerRadiusY = radiusY; } + void setCornerRadiusX(Length radiusX) + { + ASSERT(!radiusX.isUndefined()); + m_cornerRadiusX = radiusX; + } + void setCornerRadiusY(Length radiusY) + { + ASSERT(!radiusY.isUndefined()); + m_cornerRadiusY = radiusY; + } virtual void path(Path&, const FloatRect&) OVERRIDE; virtual PassRefPtr<BasicShape> blend(const BasicShape*, double) const OVERRIDE; - virtual Type type() const { return BASIC_SHAPE_RECTANGLE; } + virtual Type type() const { return BasicShapeRectangleType; } private: - BasicShapeRectangle() - : m_cornerRadiusX(Undefined) - , m_cornerRadiusY(Undefined) - { } + BasicShapeRectangle() { } Length m_y; Length m_x; @@ -114,7 +120,7 @@ public: virtual void path(Path&, const FloatRect&) OVERRIDE; virtual PassRefPtr<BasicShape> blend(const BasicShape*, double) const OVERRIDE; - virtual Type type() const { return BASIC_SHAPE_CIRCLE; } + virtual Type type() const { return BasicShapeCircleType; } private: BasicShapeCircle() { } @@ -140,7 +146,7 @@ public: virtual void path(Path&, const FloatRect&) OVERRIDE; virtual PassRefPtr<BasicShape> blend(const BasicShape*, double) const OVERRIDE; - virtual Type type() const { return BASIC_SHAPE_ELLIPSE; } + virtual Type type() const { return BasicShapeEllipseType; } private: BasicShapeEllipse() { } @@ -166,7 +172,7 @@ public: virtual WindRule windRule() const { return m_windRule; } - virtual Type type() const { return BASIC_SHAPE_POLYGON; } + virtual Type type() const { return BasicShapePolygonType; } private: BasicShapePolygon() : m_windRule(RULE_NONZERO) @@ -175,5 +181,46 @@ private: WindRule m_windRule; Vector<Length> m_values; }; + +class BasicShapeInsetRectangle : public BasicShape { +public: + static PassRefPtr<BasicShapeInsetRectangle> create() { return adoptRef(new BasicShapeInsetRectangle); } + + Length top() const { return m_top; } + Length right() const { return m_right; } + Length bottom() const { return m_bottom; } + Length left() const { return m_left; } + Length cornerRadiusX() const { return m_cornerRadiusX; } + Length cornerRadiusY() const { return m_cornerRadiusY; } + + void setTop(Length top) { m_top = top; } + void setRight(Length right) { m_right = right; } + void setBottom(Length bottom) { m_bottom = bottom; } + void setLeft(Length left) { m_left = left; } + void setCornerRadiusX(Length radiusX) + { + ASSERT(!radiusX.isUndefined()); + m_cornerRadiusX = radiusX; + } + void setCornerRadiusY(Length radiusY) + { + ASSERT(!radiusY.isUndefined()); + m_cornerRadiusY = radiusY; + } + + virtual void path(Path&, const FloatRect&) OVERRIDE; + virtual PassRefPtr<BasicShape> blend(const BasicShape*, double) const OVERRIDE; + + virtual Type type() const { return BasicShapeInsetRectangleType; } +private: + BasicShapeInsetRectangle() { } + + Length m_right; + Length m_top; + Length m_bottom; + Length m_left; + Length m_cornerRadiusX; + Length m_cornerRadiusY; +}; } #endif diff --git a/Source/WebCore/rendering/style/ContentData.cpp b/Source/WebCore/rendering/style/ContentData.cpp index 9cac254ce..7a115b915 100644 --- a/Source/WebCore/rendering/style/ContentData.cpp +++ b/Source/WebCore/rendering/style/ContentData.cpp @@ -69,13 +69,8 @@ PassOwnPtr<ContentData> ContentData::clone() const RenderObject* ImageContentData::createRenderer(Document* doc, RenderStyle* pseudoStyle) const { - RenderImage* image = new (doc->renderArena()) RenderImage(doc); - // Images are special and must inherit the pseudoStyle so the width and height of - // the pseudo element don't change the size of the image. In all other cases we - // can just share the style. - RefPtr<RenderStyle> style = RenderStyle::create(); - style->inheritFrom(pseudoStyle); - image->setStyle(style.release()); + RenderImage* image = RenderImage::createAnonymous(doc); + image->setPseudoStyle(pseudoStyle); if (m_image) image->setImageResource(RenderImageResourceStyleImage::create(m_image.get())); else @@ -86,21 +81,21 @@ RenderObject* ImageContentData::createRenderer(Document* doc, RenderStyle* pseud RenderObject* TextContentData::createRenderer(Document* doc, RenderStyle* pseudoStyle) const { RenderObject* renderer = new (doc->renderArena()) RenderTextFragment(doc, m_text.impl()); - renderer->setStyle(pseudoStyle); + renderer->setPseudoStyle(pseudoStyle); return renderer; } RenderObject* CounterContentData::createRenderer(Document* doc, RenderStyle* pseudoStyle) const { RenderObject* renderer = new (doc->renderArena()) RenderCounter(doc, *m_counter); - renderer->setStyle(pseudoStyle); + renderer->setPseudoStyle(pseudoStyle); return renderer; } RenderObject* QuoteContentData::createRenderer(Document* doc, RenderStyle* pseudoStyle) const { RenderObject* renderer = new (doc->renderArena()) RenderQuote(doc, m_quote); - renderer->setStyle(pseudoStyle); + renderer->setPseudoStyle(pseudoStyle); return renderer; } diff --git a/Source/WebCore/rendering/style/CursorData.h b/Source/WebCore/rendering/style/CursorData.h index 6d0a273f6..f8f779a28 100644 --- a/Source/WebCore/rendering/style/CursorData.h +++ b/Source/WebCore/rendering/style/CursorData.h @@ -51,6 +51,7 @@ public: StyleImage* image() const { return m_image.get(); } void setImage(PassRefPtr<StyleImage> image) { m_image = image; } + // Hot spot in the image in logical pixels. const IntPoint& hotSpot() const { return m_hotSpot; } private: diff --git a/Source/WebCore/rendering/style/DataRef.h b/Source/WebCore/rendering/style/DataRef.h index b4d4a7970..c8d8072cb 100644 --- a/Source/WebCore/rendering/style/DataRef.h +++ b/Source/WebCore/rendering/style/DataRef.h @@ -62,14 +62,6 @@ public: return m_data != o.m_data && *m_data != *o.m_data; } - // Template helps us to write the implementation without MemoryInstrumentation.h include. - template<typename MemoryObjectInfo> - void reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const - { - typename MemoryObjectInfo::ClassInfo info(memoryObjectInfo, this); - info.addMember(m_data); - } - private: RefPtr<T> m_data; }; diff --git a/Source/WebCore/rendering/style/FillLayer.cpp b/Source/WebCore/rendering/style/FillLayer.cpp index edd3d9a60..b11e24ccd 100644 --- a/Source/WebCore/rendering/style/FillLayer.cpp +++ b/Source/WebCore/rendering/style/FillLayer.cpp @@ -34,7 +34,8 @@ struct SameSizeAsFillLayer { LengthSize m_sizeLength; - unsigned m_bitfields; + unsigned m_bitfields: 32; + unsigned m_bitfields2: 1; }; COMPILE_ASSERT(sizeof(FillLayer) == sizeof(SameSizeAsFillLayer), FillLayer_should_stay_small); @@ -51,7 +52,8 @@ FillLayer::FillLayer(EFillLayerType type) , m_repeatX(FillLayer::initialFillRepeatX(type)) , m_repeatY(FillLayer::initialFillRepeatY(type)) , m_composite(FillLayer::initialFillComposite(type)) - , m_sizeType(SizeNone) + , m_sizeType(FillLayer::initialFillSizeType(type)) + , m_blendMode(FillLayer::initialFillBlendMode(type)) , m_imageSet(false) , m_attachmentSet(false) , m_clipSet(false) @@ -60,7 +62,11 @@ FillLayer::FillLayer(EFillLayerType type) , m_repeatYSet(false) , m_xPosSet(false) , m_yPosSet(false) + , m_backgroundOriginSet(false) + , m_backgroundXOrigin(LeftEdge) + , m_backgroundYOrigin(TopEdge) , m_compositeSet(type == MaskFillLayer) + , m_blendModeSet(false) , m_type(type) { } @@ -78,6 +84,7 @@ FillLayer::FillLayer(const FillLayer& o) , m_repeatY(o.m_repeatY) , m_composite(o.m_composite) , m_sizeType(o.m_sizeType) + , m_blendMode(o.m_blendMode) , m_imageSet(o.m_imageSet) , m_attachmentSet(o.m_attachmentSet) , m_clipSet(o.m_clipSet) @@ -86,7 +93,11 @@ FillLayer::FillLayer(const FillLayer& o) , m_repeatYSet(o.m_repeatYSet) , m_xPosSet(o.m_xPosSet) , m_yPosSet(o.m_yPosSet) + , m_backgroundOriginSet(o.m_backgroundOriginSet) + , m_backgroundXOrigin(o.m_backgroundXOrigin) + , m_backgroundYOrigin(o.m_backgroundYOrigin) , m_compositeSet(o.m_compositeSet) + , m_blendModeSet(o.m_blendModeSet) , m_type(o.m_type) { } @@ -106,10 +117,14 @@ FillLayer& FillLayer::operator=(const FillLayer& o) m_image = o.m_image; m_xPosition = o.m_xPosition; m_yPosition = o.m_yPosition; + m_backgroundXOrigin = o.m_backgroundXOrigin; + m_backgroundYOrigin = o.m_backgroundYOrigin; + m_backgroundOriginSet = o.m_backgroundOriginSet; m_sizeLength = o.m_sizeLength; m_attachment = o.m_attachment; m_clip = o.m_clip; m_composite = o.m_composite; + m_blendMode = o.m_blendMode; m_origin = o.m_origin; m_repeatX = o.m_repeatX; m_repeatY = o.m_repeatY; @@ -119,6 +134,7 @@ FillLayer& FillLayer::operator=(const FillLayer& o) m_attachmentSet = o.m_attachmentSet; m_clipSet = o.m_clipSet; m_compositeSet = o.m_compositeSet; + m_blendModeSet = o.m_blendModeSet; m_originSet = o.m_originSet; m_repeatXSet = o.m_repeatXSet; m_repeatYSet = o.m_repeatYSet; @@ -134,11 +150,12 @@ bool FillLayer::operator==(const FillLayer& o) const { // We do not check the "isSet" booleans for each property, since those are only used during initial construction // to propagate patterns into layers. All layer comparisons happen after values have all been filled in anyway. - return StyleImage::imagesEquivalent(m_image.get(), o.m_image.get()) && m_xPosition == o.m_xPosition && m_yPosition == o.m_yPosition && - m_attachment == o.m_attachment && m_clip == o.m_clip && - m_composite == o.m_composite && m_origin == o.m_origin && m_repeatX == o.m_repeatX && - m_repeatY == o.m_repeatY && m_sizeType == o.m_sizeType && m_sizeLength == o.m_sizeLength && - m_type == o.m_type && ((m_next && o.m_next) ? *m_next == *o.m_next : m_next == o.m_next); + return StyleImage::imagesEquivalent(m_image.get(), o.m_image.get()) && m_xPosition == o.m_xPosition && m_yPosition == o.m_yPosition + && m_backgroundXOrigin == o.m_backgroundXOrigin && m_backgroundYOrigin == o.m_backgroundYOrigin + && m_attachment == o.m_attachment && m_clip == o.m_clip && m_composite == o.m_composite + && m_blendMode == o.m_blendMode && m_origin == o.m_origin && m_repeatX == o.m_repeatX + && m_repeatY == o.m_repeatY && m_sizeType == o.m_sizeType && m_sizeLength == o.m_sizeLength + && m_type == o.m_type && ((m_next && o.m_next) ? *m_next == *o.m_next : m_next == o.m_next); } void FillLayer::fillUnsetProperties() @@ -149,6 +166,10 @@ void FillLayer::fillUnsetProperties() // We need to fill in the remaining values with the pattern specified. for (FillLayer* pattern = this; curr; curr = curr->next()) { curr->m_xPosition = pattern->m_xPosition; + if (pattern->isBackgroundOriginSet()) { + curr->m_backgroundXOrigin = pattern->m_backgroundXOrigin; + curr->m_backgroundYOrigin = pattern->m_backgroundYOrigin; + } pattern = pattern->next(); if (pattern == curr || !pattern) pattern = this; @@ -160,12 +181,16 @@ void FillLayer::fillUnsetProperties() // We need to fill in the remaining values with the pattern specified. for (FillLayer* pattern = this; curr; curr = curr->next()) { curr->m_yPosition = pattern->m_yPosition; + if (pattern->isBackgroundOriginSet()) { + curr->m_backgroundXOrigin = pattern->m_backgroundXOrigin; + curr->m_backgroundYOrigin = pattern->m_backgroundYOrigin; + } pattern = pattern->next(); if (pattern == curr || !pattern) pattern = this; } } - + for (curr = this; curr && curr->isAttachmentSet(); curr = curr->next()) { } if (curr && curr != this) { // We need to fill in the remaining values with the pattern specified. @@ -199,6 +224,17 @@ void FillLayer::fillUnsetProperties() } } + for (curr = this; curr && curr->isBlendModeSet(); curr = curr->next()) { } + if (curr && curr != this) { + // We need to fill in the remaining values with the pattern specified. + for (FillLayer* pattern = this; curr; curr = curr->next()) { + curr->m_blendMode = pattern->m_blendMode; + pattern = pattern->next(); + if (pattern == curr || !pattern) + pattern = this; + } + } + for (curr = this; curr && curr->isOriginSet(); curr = curr->next()) { } if (curr && curr != this) { // We need to fill in the remaining values with the pattern specified. @@ -258,6 +294,33 @@ void FillLayer::cullEmptyLayers() } } +static EFillBox clipMax(EFillBox clipA, EFillBox clipB) +{ + if (clipA == BorderFillBox || clipB == BorderFillBox) + return BorderFillBox; + if (clipA == PaddingFillBox || clipB == PaddingFillBox) + return PaddingFillBox; + if (clipA == ContentFillBox || clipB == ContentFillBox) + return ContentFillBox; + return TextFillBox; +} + +void FillLayer::computeClipMax() const +{ + if (m_next) { + m_next->computeClipMax(); + m_clipMax = clipMax(clip(), m_next->clip()); + } else + m_clipMax = m_clip; +} + +bool FillLayer::clipOccludesNextLayers(bool firstLayer) const +{ + if (firstLayer) + computeClipMax(); + return m_clip == m_clipMax; +} + bool FillLayer::containsImage(StyleImage* s) const { if (!s) @@ -288,8 +351,11 @@ bool FillLayer::hasOpaqueImage(const RenderObject* renderer) const if (m_composite == CompositeClear || m_composite == CompositeCopy) return true; + if (m_blendMode != BlendModeNormal) + return false; + if (m_composite == CompositeSourceOver) - return !m_image->hasAlpha(renderer); + return m_image->knownToBeOpaque(renderer); return false; } diff --git a/Source/WebCore/rendering/style/FillLayer.h b/Source/WebCore/rendering/style/FillLayer.h index 525f533ab..040a3f125 100644 --- a/Source/WebCore/rendering/style/FillLayer.h +++ b/Source/WebCore/rendering/style/FillLayer.h @@ -68,12 +68,15 @@ public: StyleImage* image() const { return m_image.get(); } Length xPosition() const { return m_xPosition; } Length yPosition() const { return m_yPosition; } + BackgroundEdgeOrigin backgroundXOrigin() const { return static_cast<BackgroundEdgeOrigin>(m_backgroundXOrigin); } + BackgroundEdgeOrigin backgroundYOrigin() const { return static_cast<BackgroundEdgeOrigin>(m_backgroundYOrigin); } EFillAttachment attachment() const { return static_cast<EFillAttachment>(m_attachment); } EFillBox clip() const { return static_cast<EFillBox>(m_clip); } EFillBox origin() const { return static_cast<EFillBox>(m_origin); } EFillRepeat repeatX() const { return static_cast<EFillRepeat>(m_repeatX); } EFillRepeat repeatY() const { return static_cast<EFillRepeat>(m_repeatY); } CompositeOperator composite() const { return static_cast<CompositeOperator>(m_composite); } + BlendMode blendMode() const { return static_cast<BlendMode>(m_blendMode); } LengthSize sizeLength() const { return m_sizeLength; } EFillSizeType sizeType() const { return static_cast<EFillSizeType>(m_sizeType); } FillSize size() const { return FillSize(static_cast<EFillSizeType>(m_sizeType), m_sizeLength); } @@ -84,36 +87,51 @@ public: bool isImageSet() const { return m_imageSet; } bool isXPositionSet() const { return m_xPosSet; } bool isYPositionSet() const { return m_yPosSet; } + bool isBackgroundOriginSet() const { return m_backgroundOriginSet; } bool isAttachmentSet() const { return m_attachmentSet; } bool isClipSet() const { return m_clipSet; } bool isOriginSet() const { return m_originSet; } bool isRepeatXSet() const { return m_repeatXSet; } bool isRepeatYSet() const { return m_repeatYSet; } bool isCompositeSet() const { return m_compositeSet; } + bool isBlendModeSet() const { return m_blendModeSet; } bool isSizeSet() const { return m_sizeType != SizeNone; } void setImage(PassRefPtr<StyleImage> i) { m_image = i; m_imageSet = true; } void setXPosition(Length l) { m_xPosition = l; m_xPosSet = true; } void setYPosition(Length l) { m_yPosition = l; m_yPosSet = true; } + void setBackgroundXOrigin(BackgroundEdgeOrigin o) { m_backgroundXOrigin = o; m_backgroundOriginSet = true; } + void setBackgroundYOrigin(BackgroundEdgeOrigin o) { m_backgroundYOrigin = o; m_backgroundOriginSet = true; } void setAttachment(EFillAttachment attachment) { m_attachment = attachment; m_attachmentSet = true; } void setClip(EFillBox b) { m_clip = b; m_clipSet = true; } void setOrigin(EFillBox b) { m_origin = b; m_originSet = true; } void setRepeatX(EFillRepeat r) { m_repeatX = r; m_repeatXSet = true; } void setRepeatY(EFillRepeat r) { m_repeatY = r; m_repeatYSet = true; } void setComposite(CompositeOperator c) { m_composite = c; m_compositeSet = true; } + void setBlendMode(BlendMode b) { m_blendMode = b; m_blendModeSet = true; } void setSizeType(EFillSizeType b) { m_sizeType = b; } void setSizeLength(LengthSize l) { m_sizeLength = l; } void setSize(FillSize f) { m_sizeType = f.type; m_sizeLength = f.size; } void clearImage() { m_image.clear(); m_imageSet = false; } - void clearXPosition() { m_xPosSet = false; } - void clearYPosition() { m_yPosSet = false; } + void clearXPosition() + { + m_xPosSet = false; + m_backgroundOriginSet = false; + } + void clearYPosition() + { + m_yPosSet = false; + m_backgroundOriginSet = false; + } + void clearAttachment() { m_attachmentSet = false; } void clearClip() { m_clipSet = false; } void clearOrigin() { m_originSet = false; } void clearRepeatX() { m_repeatXSet = false; } void clearRepeatY() { m_repeatYSet = false; } void clearComposite() { m_compositeSet = false; } + void clearBlendMode() { m_blendModeSet = false; } void clearSize() { m_sizeType = SizeNone; } void setNext(FillLayer* n) { if (m_next != n) { delete m_next; m_next = n; } } @@ -146,6 +164,7 @@ public: bool hasOpaqueImage(const RenderObject*) const; bool hasRepeatXY() const; + bool clipOccludesNextLayers(bool firstLayer) const; EFillLayerType type() const { return static_cast<EFillLayerType>(m_type); } @@ -158,9 +177,10 @@ public: static EFillRepeat initialFillRepeatX(EFillLayerType) { return RepeatFill; } static EFillRepeat initialFillRepeatY(EFillLayerType) { return RepeatFill; } static CompositeOperator initialFillComposite(EFillLayerType) { return CompositeSourceOver; } - static EFillSizeType initialFillSizeType(EFillLayerType) { return SizeLength; } + static BlendMode initialFillBlendMode(EFillLayerType) { return BlendModeNormal; } + static EFillSizeType initialFillSizeType(EFillLayerType) { return SizeNone; } static LengthSize initialFillSizeLength(EFillLayerType) { return LengthSize(); } - static FillSize initialFillSize(EFillLayerType) { return FillSize(); } + static FillSize initialFillSize(EFillLayerType type) { return FillSize(initialFillSizeType(type), initialFillSizeLength(type)); } static Length initialFillXPosition(EFillLayerType) { return Length(0.0, Percent); } static Length initialFillYPosition(EFillLayerType) { return Length(0.0, Percent); } static StyleImage* initialFillImage(EFillLayerType) { return 0; } @@ -168,6 +188,8 @@ public: private: friend class RenderStyle; + void computeClipMax() const; + FillLayer() { } FillLayer* m_next; @@ -186,6 +208,7 @@ private: unsigned m_repeatY : 3; // EFillRepeat unsigned m_composite : 4; // CompositeOperator unsigned m_sizeType : 2; // EFillSizeType + unsigned m_blendMode : 5; // BlendMode unsigned m_imageSet : 1; unsigned m_attachmentSet : 1; @@ -195,9 +218,15 @@ private: unsigned m_repeatYSet : 1; unsigned m_xPosSet : 1; unsigned m_yPosSet : 1; + unsigned m_backgroundOriginSet : 1; + unsigned m_backgroundXOrigin : 2; // BackgroundEdgeOrigin + unsigned m_backgroundYOrigin : 2; // BackgroundEdgeOrigin unsigned m_compositeSet : 1; + unsigned m_blendModeSet : 1; unsigned m_type : 1; // EFillLayerType + + mutable unsigned m_clipMax : 2; // EFillBox, maximum m_clip value from this to bottom layer }; } // namespace WebCore diff --git a/Source/WebCore/rendering/style/GridTrackSize.h b/Source/WebCore/rendering/style/GridTrackSize.h index 1bc1d06a5..72f64a687 100644 --- a/Source/WebCore/rendering/style/GridTrackSize.h +++ b/Source/WebCore/rendering/style/GridTrackSize.h @@ -36,40 +36,77 @@ namespace WebCore { enum GridTrackSizeType { - LengthTrackSizing + LengthTrackSizing, + MinMaxTrackSizing }; class GridTrackSize { public: - GridTrackSize() + GridTrackSize(LengthType type = Undefined) : m_type(LengthTrackSizing) - , m_length(Undefined) + , m_minTrackBreadth(type) + , m_maxTrackBreadth(type) { } const Length& length() const { ASSERT(m_type == LengthTrackSizing); - ASSERT(!m_length.isUndefined()); - return m_length; + ASSERT(!m_minTrackBreadth.isUndefined()); + ASSERT(m_minTrackBreadth == m_maxTrackBreadth); + return m_minTrackBreadth; } void setLength(const Length& length) { m_type = LengthTrackSizing; - m_length = length; + m_minTrackBreadth = length; + m_maxTrackBreadth = length; + } + + const Length& minTrackBreadth() const + { + ASSERT(!m_minTrackBreadth.isUndefined()); + if (m_minTrackBreadth.isAuto()) { + DEFINE_STATIC_LOCAL(Length, minContent, (MinContent)); + return minContent; + } + return m_minTrackBreadth; + } + + const Length& maxTrackBreadth() const + { + ASSERT(!m_maxTrackBreadth.isUndefined()); + if (m_maxTrackBreadth.isAuto()) { + DEFINE_STATIC_LOCAL(Length, maxContent, (MaxContent)); + return maxContent; + } + return m_maxTrackBreadth; + } + + void setMinMax(const Length& minTrackBreadth, const Length& maxTrackBreadth) + { + m_type = MinMaxTrackSizing; + m_minTrackBreadth = minTrackBreadth; + m_maxTrackBreadth = maxTrackBreadth; } GridTrackSizeType type() const { return m_type; } bool operator==(const GridTrackSize& other) const { - return m_type == other.m_type && m_length == other.m_length; + return m_type == other.m_type && m_minTrackBreadth == other.m_minTrackBreadth && m_maxTrackBreadth == other.m_maxTrackBreadth; } + bool hasMinOrMaxContentMinTrackBreadth() const { return minTrackBreadth().isMinContent() || minTrackBreadth().isMaxContent(); } + bool hasMaxContentMinTrackBreadth() const { return minTrackBreadth().isMaxContent(); } + bool hasMinOrMaxContentMaxTrackBreadth() const { return maxTrackBreadth().isMinContent() || maxTrackBreadth().isMaxContent(); } + bool hasMaxContentMaxTrackBreadth() const { return maxTrackBreadth().isMaxContent(); } + private: GridTrackSizeType m_type; - Length m_length; + Length m_minTrackBreadth; + Length m_maxTrackBreadth; }; } // namespace WebCore diff --git a/Source/WebCore/rendering/style/NinePieceImage.cpp b/Source/WebCore/rendering/style/NinePieceImage.cpp index 18a107e8a..a463d815b 100644 --- a/Source/WebCore/rendering/style/NinePieceImage.cpp +++ b/Source/WebCore/rendering/style/NinePieceImage.cpp @@ -2,7 +2,7 @@ * Copyright (C) 2000 Lars Knoll (knoll@kde.org) * (C) 2000 Antti Koivisto (koivisto@kde.org) * (C) 2000 Dirk Mueller (mueller@kde.org) - * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2005, 2006, 2007, 2008, 2013 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -26,16 +26,63 @@ namespace WebCore { -bool NinePieceImageData::operator==(const NinePieceImageData& o) const +static DataRef<NinePieceImageData>& defaultData() { - return StyleImage::imagesEquivalent(m_image.get(), o.m_image.get()) && m_imageSlices == o.m_imageSlices && m_fill == o.m_fill - && m_borderSlices == o.m_borderSlices && m_outset == o.m_outset && m_horizontalRule == o.m_horizontalRule && m_verticalRule == o.m_verticalRule; + static DataRef<NinePieceImageData>* data = new DataRef<NinePieceImageData>; + if (!data->get()) + data->init(); + return *data; } -const NinePieceImageData& NinePieceImage::defaultData() +NinePieceImage::NinePieceImage() + : m_data(defaultData()) { - DEFINE_STATIC_LOCAL(NinePieceImageData, data, ()); - return data; +} + +NinePieceImage::NinePieceImage(PassRefPtr<StyleImage> image, LengthBox imageSlices, bool fill, LengthBox borderSlices, LengthBox outset, ENinePieceImageRule horizontalRule, ENinePieceImageRule verticalRule) +{ + m_data.init(); + m_data.access()->image = image; + m_data.access()->imageSlices = imageSlices; + m_data.access()->borderSlices = borderSlices; + m_data.access()->outset = outset; + m_data.access()->fill = fill; + m_data.access()->horizontalRule = horizontalRule; + m_data.access()->verticalRule = verticalRule; +} + +NinePieceImageData::NinePieceImageData() + : fill(false) + , horizontalRule(StretchImageRule) + , verticalRule(StretchImageRule) + , image(0) + , imageSlices(Length(100, Percent), Length(100, Percent), Length(100, Percent), Length(100, Percent)) + , borderSlices(Length(1, Relative), Length(1, Relative), Length(1, Relative), Length(1, Relative)) + , outset(0) +{ +} + +NinePieceImageData::NinePieceImageData(const NinePieceImageData& other) + : RefCounted<NinePieceImageData>() + , fill(other.fill) + , horizontalRule(other.horizontalRule) + , verticalRule(other.verticalRule) + , image(other.image) + , imageSlices(other.imageSlices) + , borderSlices(other.borderSlices) + , outset(other.outset) +{ +} + +bool NinePieceImageData::operator==(const NinePieceImageData& other) const +{ + return StyleImage::imagesEquivalent(image.get(), other.image.get()) + && imageSlices == other.imageSlices + && fill == other.fill + && borderSlices == other.borderSlices + && outset == other.outset + && horizontalRule == other.horizontalRule + && verticalRule == other.verticalRule; } } diff --git a/Source/WebCore/rendering/style/NinePieceImage.h b/Source/WebCore/rendering/style/NinePieceImage.h index fd007cea1..0f3b339ea 100644 --- a/Source/WebCore/rendering/style/NinePieceImage.h +++ b/Source/WebCore/rendering/style/NinePieceImage.h @@ -2,7 +2,7 @@ * Copyright (C) 2000 Lars Knoll (knoll@kde.org) * (C) 2000 Antti Koivisto (koivisto@kde.org) * (C) 2000 Dirk Mueller (mueller@kde.org) - * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2005, 2006, 2007, 2008, 2013 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -24,6 +24,7 @@ #ifndef NinePieceImage_h #define NinePieceImage_h +#include "DataRef.h" #include "LayoutUnit.h" #include "LengthBox.h" #include "StyleImage.h" @@ -34,149 +35,95 @@ enum ENinePieceImageRule { StretchImageRule, RoundImageRule, SpaceImageRule, RepeatImageRule }; -class NinePieceImageData { - WTF_MAKE_FAST_ALLOCATED; +class NinePieceImageData : public RefCounted<NinePieceImageData> { public: - NinePieceImageData() - : m_image(0) - , m_imageSlices(Length(100, Percent), Length(100, Percent), Length(100, Percent), Length(100, Percent)) - , m_borderSlices(Length(1, Relative), Length(1, Relative), Length(1, Relative), Length(1, Relative)) - , m_outset(0) - , m_fill(false) - , m_horizontalRule(StretchImageRule) - , m_verticalRule(StretchImageRule) - { - } - - NinePieceImageData(PassRefPtr<StyleImage> image, LengthBox imageSlices, bool fill, LengthBox borderSlices, LengthBox outset, ENinePieceImageRule h, ENinePieceImageRule v) - : m_image(image) - , m_imageSlices(imageSlices) - , m_borderSlices(borderSlices) - , m_outset(outset) - , m_fill(fill) - , m_horizontalRule(h) - , m_verticalRule(v) - { - } + static PassRefPtr<NinePieceImageData> create() { return adoptRef(new NinePieceImageData); } + PassRefPtr<NinePieceImageData> copy() const { return adoptRef(new NinePieceImageData(*this)); } bool operator==(const NinePieceImageData&) const; bool operator!=(const NinePieceImageData& o) const { return !(*this == o); } - RefPtr<StyleImage> m_image; - LengthBox m_imageSlices; - LengthBox m_borderSlices; - LengthBox m_outset; - bool m_fill : 1; - unsigned m_horizontalRule : 2; // ENinePieceImageRule - unsigned m_verticalRule : 2; // ENinePieceImageRule + bool fill : 1; + unsigned horizontalRule : 2; // ENinePieceImageRule + unsigned verticalRule : 2; // ENinePieceImageRule + RefPtr<StyleImage> image; + LengthBox imageSlices; + LengthBox borderSlices; + LengthBox outset; + +private: + NinePieceImageData(); + NinePieceImageData(const NinePieceImageData&); }; class NinePieceImage { public: - NinePieceImage() { } - - NinePieceImage(PassRefPtr<StyleImage> image, LengthBox imageSlices, bool fill, LengthBox borderSlices, LengthBox outset, ENinePieceImageRule h, ENinePieceImageRule v) - : m_data(adoptPtr(new NinePieceImageData(image, imageSlices, fill, borderSlices, outset, h, v))) - { } + NinePieceImage(); + NinePieceImage(PassRefPtr<StyleImage>, LengthBox imageSlices, bool fill, LengthBox borderSlices, LengthBox outset, ENinePieceImageRule horizontalRule, ENinePieceImageRule verticalRule); - NinePieceImage(const NinePieceImage& other) - { - *this = other; - } - - void operator=(const NinePieceImage& other) - { - if (!other.m_data) { - m_data.clear(); - return; - } - const NinePieceImageData& otherData = other.data(); - m_data = adoptPtr(new NinePieceImageData(otherData.m_image, otherData.m_imageSlices, otherData.m_fill, otherData.m_borderSlices, otherData.m_outset, static_cast<ENinePieceImageRule>(otherData.m_horizontalRule), static_cast<ENinePieceImageRule>(otherData.m_verticalRule))); - } - - bool operator==(const NinePieceImage& other) const { return data() == other.data(); } - bool operator!=(const NinePieceImage& other) const { return !(*this == other); } + bool operator==(const NinePieceImage& other) const { return m_data == other.m_data; } + bool operator!=(const NinePieceImage& other) const { return m_data != other.m_data; } - bool hasImage() const { return m_data && m_data->m_image; } - StyleImage* image() const { return m_data ? m_data->m_image.get() : 0; } - void setImage(PassRefPtr<StyleImage> image) { ensureData(); m_data->m_image = image; } + bool hasImage() const { return m_data->image; } + StyleImage* image() const { return m_data->image.get(); } + void setImage(PassRefPtr<StyleImage> image) { m_data.access()->image = image; } - const LengthBox& imageSlices() const { return data().m_imageSlices; } - void setImageSlices(const LengthBox& slices) { ensureData(); m_data->m_imageSlices = slices; } + const LengthBox& imageSlices() const { return m_data->imageSlices; } + void setImageSlices(const LengthBox& slices) { m_data.access()->imageSlices = slices; } - bool fill() const { return data().m_fill; } - void setFill(bool fill) { ensureData(); m_data->m_fill = fill; } + bool fill() const { return m_data->fill; } + void setFill(bool fill) { m_data.access()->fill = fill; } - const LengthBox& borderSlices() const { return data().m_borderSlices; } - void setBorderSlices(const LengthBox& slices) { ensureData(); m_data->m_borderSlices = slices; } + const LengthBox& borderSlices() const { return m_data->borderSlices; } + void setBorderSlices(const LengthBox& slices) { m_data.access()->borderSlices = slices; } - const LengthBox& outset() const { return data().m_outset; } - void setOutset(const LengthBox& outset) { ensureData(); m_data->m_outset = outset; } - - static LayoutUnit computeOutset(Length outsetSide, LayoutUnit borderSide) - { - if (outsetSide.isRelative()) - return outsetSide.value() * borderSide; - return outsetSide.value(); - } + const LengthBox& outset() const { return m_data->outset; } + void setOutset(const LengthBox& outset) { m_data.access()->outset = outset; } - ENinePieceImageRule horizontalRule() const { return static_cast<ENinePieceImageRule>(data().m_horizontalRule); } - void setHorizontalRule(ENinePieceImageRule rule) { ensureData(); m_data->m_horizontalRule = rule; } + ENinePieceImageRule horizontalRule() const { return static_cast<ENinePieceImageRule>(m_data->horizontalRule); } + void setHorizontalRule(ENinePieceImageRule rule) { m_data.access()->horizontalRule = rule; } - ENinePieceImageRule verticalRule() const { return static_cast<ENinePieceImageRule>(data().m_verticalRule); } - void setVerticalRule(ENinePieceImageRule rule) { ensureData(); m_data->m_verticalRule = rule; } + ENinePieceImageRule verticalRule() const { return static_cast<ENinePieceImageRule>(m_data->verticalRule); } + void setVerticalRule(ENinePieceImageRule rule) { m_data.access()->verticalRule = rule; } void copyImageSlicesFrom(const NinePieceImage& other) { - ensureData(); - m_data->m_imageSlices = other.data().m_imageSlices; - m_data->m_fill = other.data().m_fill; + m_data.access()->imageSlices = other.m_data->imageSlices; + m_data.access()->fill = other.m_data->fill; } void copyBorderSlicesFrom(const NinePieceImage& other) { - ensureData(); - m_data->m_borderSlices = other.data().m_borderSlices; + m_data.access()->borderSlices = other.m_data->borderSlices; } void copyOutsetFrom(const NinePieceImage& other) { - ensureData(); - m_data->m_outset = other.data().m_outset; + m_data.access()->outset = other.m_data->outset; } void copyRepeatFrom(const NinePieceImage& other) { - ensureData(); - m_data->m_horizontalRule = other.data().m_horizontalRule; - m_data->m_verticalRule = other.data().m_verticalRule; + m_data.access()->horizontalRule = other.m_data->horizontalRule; + m_data.access()->verticalRule = other.m_data->verticalRule; } void setMaskDefaults() { - ensureData(); - m_data->m_imageSlices = LengthBox(0); - m_data->m_fill = true; - m_data->m_borderSlices = LengthBox(); + m_data.access()->imageSlices = LengthBox(0); + m_data.access()->fill = true; + m_data.access()->borderSlices = LengthBox(); } -private: - static const NinePieceImageData& defaultData(); - - void ensureData() - { - if (!m_data) - m_data = adoptPtr(new NinePieceImageData); - } - - const NinePieceImageData& data() const + static LayoutUnit computeOutset(Length outsetSide, LayoutUnit borderSide) { - if (m_data) - return *m_data; - return defaultData(); + if (outsetSide.isRelative()) + return outsetSide.value() * borderSide; + return outsetSide.value(); } - OwnPtr<NinePieceImageData> m_data; +private: + DataRef<NinePieceImageData> m_data; }; } // namespace WebCore diff --git a/Source/WebCore/rendering/style/QuotesData.cpp b/Source/WebCore/rendering/style/QuotesData.cpp index a6a7c5ef2..cab873956 100644 --- a/Source/WebCore/rendering/style/QuotesData.cpp +++ b/Source/WebCore/rendering/style/QuotesData.cpp @@ -24,53 +24,63 @@ namespace WebCore { -PassRefPtr<QuotesData> QuotesData::create(String open, String close) +static size_t sizeForQuotesDataWithQuoteCount(unsigned count) { - RefPtr<QuotesData> data = QuotesData::create(); - data->addPair(std::make_pair(open, close)); - return data; + return sizeof(QuotesData) + sizeof(std::pair<String, String>) * count; } -PassRefPtr<QuotesData> QuotesData::create(String open1, String close1, String open2, String close2) +PassRefPtr<QuotesData> QuotesData::create(const Vector<std::pair<String, String> >& quotes) { - RefPtr<QuotesData> data = QuotesData::create(); - data->addPair(std::make_pair(open1, close1)); - data->addPair(std::make_pair(open2, close2)); - return data; + void* slot = fastMalloc(sizeForQuotesDataWithQuoteCount(quotes.size())); + return adoptRef(new (NotNull, slot) QuotesData(quotes)); } -void QuotesData::addPair(std::pair<String, String> quotePair) +QuotesData::QuotesData(const Vector<std::pair<String, String> >& quotes) + : m_quoteCount(quotes.size()) { - m_quotePairs.append(quotePair); + for (unsigned i = 0; i < m_quoteCount; ++i) + new (NotNull, &m_quotePairs[i]) std::pair<String, String>(quotes[i]); } -const String QuotesData::getOpenQuote(int index) const +QuotesData::~QuotesData() { - ASSERT(index >= 0); - if (!m_quotePairs.size() || index < 0) + for (unsigned i = 0; i < m_quoteCount; ++i) + m_quotePairs[i].~pair<String, String>(); +} + +const String& QuotesData::openQuote(unsigned index) const +{ + if (!m_quoteCount) return emptyString(); - if ((size_t)index >= m_quotePairs.size()) - return m_quotePairs.last().first; - return m_quotePairs.at(index).first; + + if (index >= m_quoteCount) + return m_quotePairs[m_quoteCount - 1].first; + + return m_quotePairs[index].first; } -const String QuotesData::getCloseQuote(int index) const +const String& QuotesData::closeQuote(unsigned index) const { - ASSERT(index >= -1); - if (!m_quotePairs.size() || index < 0) + if (!m_quoteCount) return emptyString(); - if ((size_t)index >= m_quotePairs.size()) - return m_quotePairs.last().second; - return m_quotePairs.at(index).second; + + if (index >= m_quoteCount) + return m_quotePairs[m_quoteCount - 1].second; + + return m_quotePairs[index].second; } -bool QuotesData::equals(const QuotesData* a, const QuotesData* b) +bool operator==(const QuotesData& a, const QuotesData& b) { - if (a == b) - return true; - if (!a || !b) + if (a.m_quoteCount != b.m_quoteCount) return false; - return a->m_quotePairs == b->m_quotePairs; + + for (unsigned i = 0; i < a.m_quoteCount; ++i) { + if (a.m_quotePairs[i] != b.m_quotePairs[i]) + return false; + } + + return true; } } // namespace WebCore diff --git a/Source/WebCore/rendering/style/QuotesData.h b/Source/WebCore/rendering/style/QuotesData.h index 5699f3245..df84d62ae 100644 --- a/Source/WebCore/rendering/style/QuotesData.h +++ b/Source/WebCore/rendering/style/QuotesData.h @@ -29,25 +29,37 @@ namespace WebCore { +#if COMPILER(MSVC) +#pragma warning(push) +#pragma warning(disable: 4200) // Disable "zero-sized array in struct/union" warning +#endif + class QuotesData : public RefCounted<QuotesData> { public: - static PassRefPtr<QuotesData> create() { return adoptRef(new QuotesData()); } - static PassRefPtr<QuotesData> create(const String open, const String close); - static PassRefPtr<QuotesData> create(const String open1, const String close1, const String open2, const String close2); + static PassRefPtr<QuotesData> create(const Vector<std::pair<String, String> >& quotes); + ~QuotesData(); - // FIXME: this should be an operator==. - static bool equals(const QuotesData*, const QuotesData*); + friend bool operator==(const QuotesData&, const QuotesData&); - void addPair(const std::pair<String, String> quotePair); - const String getOpenQuote(int index) const; - const String getCloseQuote(int index) const; + const String& openQuote(unsigned index) const; + const String& closeQuote(unsigned index) const; private: - QuotesData() { } + explicit QuotesData(const Vector<std::pair<String, String> >& quotes); - Vector<std::pair<String, String> > m_quotePairs; + unsigned m_quoteCount; + std::pair<String, String> m_quotePairs[0]; }; +#if COMPILER(MSVC) +#pragma warning(pop) +#endif + +inline bool operator!=(const QuotesData& a, const QuotesData& b) +{ + return !(a == b); +} + } // namespace WebCore #endif // QuotesData_h diff --git a/Source/WebCore/rendering/style/RenderStyle.cpp b/Source/WebCore/rendering/style/RenderStyle.cpp index 653f30fdf..3e5590c09 100644 --- a/Source/WebCore/rendering/style/RenderStyle.cpp +++ b/Source/WebCore/rendering/style/RenderStyle.cpp @@ -28,6 +28,7 @@ #include "CSSPropertyNames.h" #include "Font.h" #include "FontSelector.h" +#include "Pagination.h" #include "QuotesData.h" #include "RenderArena.h" #include "RenderObject.h" @@ -39,9 +40,7 @@ #if ENABLE(TOUCH_EVENTS) #include "RenderTheme.h" #endif -#include "WebCoreMemoryInstrumentation.h" -#include <wtf/MemoryInstrumentationVector.h> -#include <wtf/MemoryObjectInfo.h> +#include <wtf/MathExtras.h> #include <wtf/StdLibExtras.h> #include <algorithm> @@ -260,6 +259,20 @@ void RenderStyle::setHasPseudoStyle(PseudoId pseudo) noninherited_flags._pseudoBits |= pseudoBit(pseudo); } +bool RenderStyle::hasUniquePseudoStyle() const +{ + if (!m_cachedPseudoStyles || styleType() != NOPSEUDO) + return false; + + for (size_t i = 0; i < m_cachedPseudoStyles->size(); ++i) { + RenderStyle* pseudoStyle = m_cachedPseudoStyles->at(i).get(); + if (pseudoStyle->unique()) + return true; + } + + return false; +} + RenderStyle* RenderStyle::getCachedPseudoStyle(PseudoId pid) const { if (!m_cachedPseudoStyles || !m_cachedPseudoStyles->size()) @@ -328,7 +341,7 @@ bool RenderStyle::inheritedDataShared(const RenderStyle* other) const && rareInheritedData.get() == other->rareInheritedData.get(); } -static bool positionedObjectMoved(const LengthBox& a, const LengthBox& b) +static bool positionChangeIsMovementOnly(const LengthBox& a, const LengthBox& b, const Length& width) { // If any unit types are different, then we can't guarantee // that this was just a movement. @@ -345,44 +358,37 @@ static bool positionedObjectMoved(const LengthBox& a, const LengthBox& b) return false; if (!a.top().isIntrinsicOrAuto() && !a.bottom().isIntrinsicOrAuto()) return false; + // If our width is auto and left or right is specified then this + // is not just a movement - we need to resize to our container. + if ((!a.left().isIntrinsicOrAuto() || !a.right().isIntrinsicOrAuto()) && width.isIntrinsicOrAuto()) + return false; // One of the units is fixed or percent in both directions and stayed // that way in the new style. Therefore all we are doing is moving. return true; } -StyleDifference RenderStyle::diff(const RenderStyle* other, unsigned& changedContextSensitiveProperties) const +bool RenderStyle::changeRequiresLayout(const RenderStyle* other, unsigned& changedContextSensitiveProperties) const { - changedContextSensitiveProperties = ContextSensitivePropertyNone; - -#if ENABLE(SVG) - StyleDifference svgChange = StyleDifferenceEqual; - if (m_svgStyle != other->m_svgStyle) { - svgChange = m_svgStyle->diff(other->m_svgStyle.get()); - if (svgChange == StyleDifferenceLayout) - return svgChange; - } -#endif - if (m_box->width() != other->m_box->width() || m_box->minWidth() != other->m_box->minWidth() || m_box->maxWidth() != other->m_box->maxWidth() || m_box->height() != other->m_box->height() || m_box->minHeight() != other->m_box->minHeight() || m_box->maxHeight() != other->m_box->maxHeight()) - return StyleDifferenceLayout; + return true; if (m_box->verticalAlign() != other->m_box->verticalAlign() || noninherited_flags._vertical_align != other->noninherited_flags._vertical_align) - return StyleDifferenceLayout; + return true; if (m_box->boxSizing() != other->m_box->boxSizing()) - return StyleDifferenceLayout; + return true; if (surround->margin != other->surround->margin) - return StyleDifferenceLayout; + return true; if (surround->padding != other->surround->padding) - return StyleDifferenceLayout; + return true; if (rareNonInheritedData.get() != other->rareNonInheritedData.get()) { if (rareNonInheritedData->m_appearance != other->rareNonInheritedData->m_appearance @@ -390,41 +396,41 @@ StyleDifference RenderStyle::diff(const RenderStyle* other, unsigned& changedCon || rareNonInheritedData->marginAfterCollapse != other->rareNonInheritedData->marginAfterCollapse || rareNonInheritedData->lineClamp != other->rareNonInheritedData->lineClamp || rareNonInheritedData->textOverflow != other->rareNonInheritedData->textOverflow) - return StyleDifferenceLayout; + return true; - if (rareNonInheritedData->m_regionOverflow != other->rareNonInheritedData->m_regionOverflow) - return StyleDifferenceLayout; + if (rareNonInheritedData->m_regionFragment != other->rareNonInheritedData->m_regionFragment) + return true; if (rareNonInheritedData->m_wrapFlow != other->rareNonInheritedData->m_wrapFlow || rareNonInheritedData->m_wrapThrough != other->rareNonInheritedData->m_wrapThrough || rareNonInheritedData->m_shapeMargin != other->rareNonInheritedData->m_shapeMargin || rareNonInheritedData->m_shapePadding != other->rareNonInheritedData->m_shapePadding) - return StyleDifferenceLayout; + return true; if (rareNonInheritedData->m_deprecatedFlexibleBox.get() != other->rareNonInheritedData->m_deprecatedFlexibleBox.get() && *rareNonInheritedData->m_deprecatedFlexibleBox.get() != *other->rareNonInheritedData->m_deprecatedFlexibleBox.get()) - return StyleDifferenceLayout; + return true; if (rareNonInheritedData->m_flexibleBox.get() != other->rareNonInheritedData->m_flexibleBox.get() && *rareNonInheritedData->m_flexibleBox.get() != *other->rareNonInheritedData->m_flexibleBox.get()) - return StyleDifferenceLayout; + return true; if (rareNonInheritedData->m_order != other->rareNonInheritedData->m_order || rareNonInheritedData->m_alignContent != other->rareNonInheritedData->m_alignContent || rareNonInheritedData->m_alignItems != other->rareNonInheritedData->m_alignItems || rareNonInheritedData->m_alignSelf != other->rareNonInheritedData->m_alignSelf || rareNonInheritedData->m_justifyContent != other->rareNonInheritedData->m_justifyContent) - return StyleDifferenceLayout; + return true; // FIXME: We should add an optimized form of layout that just recomputes visual overflow. if (!rareNonInheritedData->shadowDataEquivalent(*other->rareNonInheritedData.get())) - return StyleDifferenceLayout; + return true; if (!rareNonInheritedData->reflectionDataEquivalent(*other->rareNonInheritedData.get())) - return StyleDifferenceLayout; + return true; if (rareNonInheritedData->m_multiCol.get() != other->rareNonInheritedData->m_multiCol.get() && *rareNonInheritedData->m_multiCol.get() != *other->rareNonInheritedData->m_multiCol.get()) - return StyleDifferenceLayout; + return true; if (rareNonInheritedData->m_transform.get() != other->rareNonInheritedData->m_transform.get() && *rareNonInheritedData->m_transform.get() != *other->rareNonInheritedData->m_transform.get()) { @@ -432,13 +438,13 @@ StyleDifference RenderStyle::diff(const RenderStyle* other, unsigned& changedCon changedContextSensitiveProperties |= ContextSensitivePropertyTransform; // Don't return; keep looking for another change #else - return StyleDifferenceLayout; + return true; #endif } if (rareNonInheritedData->m_grid.get() != other->rareNonInheritedData->m_grid.get() - && rareNonInheritedData->m_gridItem.get() != other->rareNonInheritedData->m_gridItem.get()) - return StyleDifferenceLayout; + || rareNonInheritedData->m_gridItem.get() != other->rareNonInheritedData->m_gridItem.get()) + return true; #if !USE(ACCELERATED_COMPOSITING) if (rareNonInheritedData.get() != other->rareNonInheritedData.get()) { @@ -447,27 +453,29 @@ StyleDifference RenderStyle::diff(const RenderStyle* other, unsigned& changedCon || rareNonInheritedData->m_perspective != other->rareNonInheritedData->m_perspective || rareNonInheritedData->m_perspectiveOriginX != other->rareNonInheritedData->m_perspectiveOriginX || rareNonInheritedData->m_perspectiveOriginY != other->rareNonInheritedData->m_perspectiveOriginY) - return StyleDifferenceLayout; + return true; } #endif #if ENABLE(DASHBOARD_SUPPORT) // If regions change, trigger a relayout to re-calc regions. if (rareNonInheritedData->m_dashboardRegions != other->rareNonInheritedData->m_dashboardRegions) - return StyleDifferenceLayout; + return true; #endif -#if ENABLE(CSS_EXCLUSIONS) +#if ENABLE(CSS_SHAPES) if (rareNonInheritedData->m_shapeInside != other->rareNonInheritedData->m_shapeInside) - return StyleDifferenceLayout; + return true; #endif } if (rareInheritedData.get() != other->rareInheritedData.get()) { if (rareInheritedData->highlight != other->rareInheritedData->highlight || rareInheritedData->indent != other->rareInheritedData->indent +#if ENABLE(CSS3_TEXT) + || rareInheritedData->m_textIndentLine != other->rareInheritedData->m_textIndentLine +#endif || rareInheritedData->m_effectiveZoom != other->rareInheritedData->m_effectiveZoom - || rareInheritedData->textSizeAdjust != other->rareInheritedData->textSizeAdjust || rareInheritedData->wordBreak != other->rareInheritedData->wordBreak || rareInheritedData->overflowWrap != other->rareInheritedData->overflowWrap || rareInheritedData->nbspMode != other->rareInheritedData->nbspMode @@ -482,6 +490,7 @@ StyleDifference RenderStyle::diff(const RenderStyle* other, unsigned& changedCon || rareInheritedData->textEmphasisMark != other->rareInheritedData->textEmphasisMark || rareInheritedData->textEmphasisPosition != other->rareInheritedData->textEmphasisPosition || rareInheritedData->textEmphasisCustomMark != other->rareInheritedData->textEmphasisCustomMark + || rareInheritedData->m_textOrientation != other->rareInheritedData->m_textOrientation || rareInheritedData->m_tabSize != other->rareInheritedData->m_tabSize || rareInheritedData->m_lineBoxContain != other->rareInheritedData->m_lineBoxContain || rareInheritedData->m_lineGrid != other->rareInheritedData->m_lineGrid @@ -493,18 +502,18 @@ StyleDifference RenderStyle::diff(const RenderStyle* other, unsigned& changedCon || rareInheritedData->m_lineSnap != other->rareInheritedData->m_lineSnap || rareInheritedData->m_lineAlign != other->rareInheritedData->m_lineAlign || rareInheritedData->listStyleImage != other->rareInheritedData->listStyleImage) - return StyleDifferenceLayout; + return true; if (!rareInheritedData->shadowDataEquivalent(*other->rareInheritedData.get())) - return StyleDifferenceLayout; + return true; if (textStrokeWidth() != other->textStrokeWidth()) - return StyleDifferenceLayout; + return true; } #if ENABLE(TEXT_AUTOSIZING) if (visual->m_textAutosizingMultiplier != other->visual->m_textAutosizingMultiplier) - return StyleDifferenceLayout; + return true; #endif if (inherited->line_height != other->inherited->line_height @@ -516,7 +525,7 @@ StyleDifference RenderStyle::diff(const RenderStyle* other, unsigned& changedCon || noninherited_flags._position != other->noninherited_flags._position || noninherited_flags._floating != other->noninherited_flags._floating || noninherited_flags._originalDisplay != other->noninherited_flags._originalDisplay) - return StyleDifferenceLayout; + return true; if (((int)noninherited_flags._effectiveDisplay) >= TABLE) { @@ -524,7 +533,7 @@ StyleDifference RenderStyle::diff(const RenderStyle* other, unsigned& changedCon || inherited_flags._empty_cells != other->inherited_flags._empty_cells || inherited_flags._caption_side != other->inherited_flags._caption_side || noninherited_flags._table_layout != other->noninherited_flags._table_layout) - return StyleDifferenceLayout; + return true; // In the collapsing border model, 'hidden' suppresses other borders, while 'none' // does not, so these style differences can be width differences. @@ -537,13 +546,13 @@ StyleDifference RenderStyle::diff(const RenderStyle* other, unsigned& changedCon || (borderLeftStyle() == BNONE && other->borderLeftStyle() == BHIDDEN) || (borderRightStyle() == BHIDDEN && other->borderRightStyle() == BNONE) || (borderRightStyle() == BNONE && other->borderRightStyle() == BHIDDEN))) - return StyleDifferenceLayout; + return true; } if (noninherited_flags._effectiveDisplay == LIST_ITEM) { if (inherited_flags._list_style_type != other->inherited_flags._list_style_type || inherited_flags._list_style_position != other->inherited_flags._list_style_position) - return StyleDifferenceLayout; + return true; } if (inherited_flags._text_align != other->inherited_flags._text_align @@ -552,20 +561,20 @@ StyleDifference RenderStyle::diff(const RenderStyle* other, unsigned& changedCon || inherited_flags._white_space != other->inherited_flags._white_space || noninherited_flags._clear != other->noninherited_flags._clear || noninherited_flags._unicodeBidi != other->noninherited_flags._unicodeBidi) - return StyleDifferenceLayout; + return true; // Check block flow direction. if (inherited_flags.m_writingMode != other->inherited_flags.m_writingMode) - return StyleDifferenceLayout; + return true; // Check text combine mode. if (rareNonInheritedData->m_textCombine != other->rareNonInheritedData->m_textCombine) - return StyleDifferenceLayout; + return true; // Overflow returns a layout hint. if (noninherited_flags._overflowX != other->noninherited_flags._overflowX || noninherited_flags._overflowY != other->noninherited_flags._overflowY) - return StyleDifferenceLayout; + return true; // If our border widths change, then we need to layout. Other changes to borders // only necessitate a repaint. @@ -573,16 +582,16 @@ StyleDifference RenderStyle::diff(const RenderStyle* other, unsigned& changedCon || borderTopWidth() != other->borderTopWidth() || borderBottomWidth() != other->borderBottomWidth() || borderRightWidth() != other->borderRightWidth()) - return StyleDifferenceLayout; + return true; // If the counter directives change, trigger a relayout to re-calculate counter values and rebuild the counter node tree. const CounterDirectiveMap* mapA = rareNonInheritedData->m_counterDirectives.get(); const CounterDirectiveMap* mapB = other->rareNonInheritedData->m_counterDirectives.get(); if (!(mapA == mapB || (mapA && mapB && *mapA == *mapB))) - return StyleDifferenceLayout; + return true; if ((visibility() == COLLAPSE) != (other->visibility() == COLLAPSE)) - return StyleDifferenceLayout; + return true; if ((rareNonInheritedData->opacity == 1 && other->rareNonInheritedData->opacity < 1) || (rareNonInheritedData->opacity < 1 && other->rareNonInheritedData->opacity == 1)) { @@ -591,41 +600,58 @@ StyleDifference RenderStyle::diff(const RenderStyle* other, unsigned& changedCon // to add a selfNeedsSimplifiedLayout bit in order to not get confused and taint every line). // In addition we need to solve the floating object issue when layers come and go. Right now // a full layout is necessary to keep floating object lists sane. - return StyleDifferenceLayout; + return true; } - if (!QuotesData::equals(rareInheritedData->quotes.get(), other->rareInheritedData->quotes.get())) - return StyleDifferenceLayout; - -#if ENABLE(SVG) - // SVGRenderStyle::diff() might have returned StyleDifferenceRepaint, eg. if fill changes. - // If eg. the font-size changed at the same time, we're not allowed to return StyleDifferenceRepaint, - // but have to return StyleDifferenceLayout, that's why this if branch comes after all branches - // that are relevant for SVG and might return StyleDifferenceLayout. - if (svgChange != StyleDifferenceEqual) - return svgChange; -#endif + const QuotesData* quotesDataA = rareInheritedData->quotes.get(); + const QuotesData* quotesDataB = other->rareInheritedData->quotes.get(); + if (!(quotesDataA == quotesDataB || (quotesDataA && quotesDataB && *quotesDataA == *quotesDataB))) + return true; - // Make sure these left/top/right/bottom checks stay below all layout checks and above - // all visible checks. if (position() != StaticPosition) { if (surround->offset != other->surround->offset) { - // Optimize for the case where a positioned layer is moving but not changing size. - if (position() == AbsolutePosition && positionedObjectMoved(surround->offset, other->surround->offset)) - return StyleDifferenceLayoutPositionedMovementOnly; - // FIXME: We would like to use SimplifiedLayout for relative positioning, but we can't quite do that yet. // We need to make sure SimplifiedLayout can operate correctly on RenderInlines (we will need // to add a selfNeedsSimplifiedLayout bit in order to not get confused and taint every line). - return StyleDifferenceLayout; - } else if (m_box->zIndex() != other->m_box->zIndex() || m_box->hasAutoZIndex() != other->m_box->hasAutoZIndex() - || visual->clip != other->visual->clip || visual->hasClip != other->visual->hasClip) - return StyleDifferenceRepaintLayer; + if (position() != AbsolutePosition) + return true; + + // Optimize for the case where a positioned layer is moving but not changing size. + if (!positionChangeIsMovementOnly(surround->offset, other->surround->offset, m_box->width())) + return true; + } } + return false; +} + +bool RenderStyle::changeRequiresPositionedLayoutOnly(const RenderStyle* other, unsigned&) const +{ + if (position() == StaticPosition) + return false; + + if (surround->offset != other->surround->offset) { + // Optimize for the case where a positioned layer is moving but not changing size. + if (position() == AbsolutePosition && positionChangeIsMovementOnly(surround->offset, other->surround->offset, m_box->width())) + return true; + } + + return false; +} + +bool RenderStyle::changeRequiresLayerRepaint(const RenderStyle* other, unsigned& changedContextSensitiveProperties) const +{ + if (position() != StaticPosition) { + if (m_box->zIndex() != other->m_box->zIndex() + || m_box->hasAutoZIndex() != other->m_box->hasAutoZIndex() + || visual->clip != other->visual->clip + || visual->hasClip != other->visual->hasClip) + return true; + } + #if ENABLE(CSS_COMPOSITING) if (rareNonInheritedData->m_effectiveBlendMode != other->rareNonInheritedData->m_effectiveBlendMode) - return StyleDifferenceRepaintLayer; + return true; #endif if (rareNonInheritedData->opacity != other->rareNonInheritedData->opacity) { @@ -633,7 +659,7 @@ StyleDifference RenderStyle::diff(const RenderStyle* other, unsigned& changedCon changedContextSensitiveProperties |= ContextSensitivePropertyOpacity; // Don't return; keep looking for another change. #else - return StyleDifferenceRepaintLayer; + return true; #endif } @@ -644,49 +670,66 @@ StyleDifference RenderStyle::diff(const RenderStyle* other, unsigned& changedCon changedContextSensitiveProperties |= ContextSensitivePropertyFilter; // Don't return; keep looking for another change. #else - return StyleDifferenceRepaintLayer; + return true; #endif } #endif if (rareNonInheritedData->m_mask != other->rareNonInheritedData->m_mask || rareNonInheritedData->m_maskBoxImage != other->rareNonInheritedData->m_maskBoxImage) - return StyleDifferenceRepaintLayer; + return true; - if (inherited->color != other->inherited->color - || inherited_flags._visibility != other->inherited_flags._visibility - || inherited_flags._text_decorations != other->inherited_flags._text_decorations + return false; +} + +bool RenderStyle::changeRequiresRepaint(const RenderStyle* other, unsigned&) const +{ + if (inherited_flags._visibility != other->inherited_flags._visibility || inherited_flags.m_printColorAdjust != other->inherited_flags.m_printColorAdjust || inherited_flags._insideLink != other->inherited_flags._insideLink || surround->border != other->surround->border - || *m_background.get() != *other->m_background.get() - || visual->textDecoration != other->visual->textDecoration + || !m_background->isEquivalentForPainting(*other->m_background) || rareInheritedData->userModify != other->rareInheritedData->userModify || rareInheritedData->userSelect != other->rareInheritedData->userSelect || rareNonInheritedData->userDrag != other->rareNonInheritedData->userDrag || rareNonInheritedData->m_borderFit != other->rareNonInheritedData->m_borderFit + || rareInheritedData->m_imageRendering != other->rareInheritedData->m_imageRendering) + return true; + + // FIXME: The current spec is being reworked to remove dependencies between exclusions and affected + // content. There's a proposal to use floats instead. In that case, wrap-shape should actually relayout + // the parent container. For sure, I will have to revisit this code, but for now I've added this in order + // to avoid having diff() == StyleDifferenceEqual where wrap-shapes actually differ. + // Tracking bug: https://bugs.webkit.org/show_bug.cgi?id=62991 + if (rareNonInheritedData->m_shapeOutside != other->rareNonInheritedData->m_shapeOutside) + return true; + + if (rareNonInheritedData->m_clipPath != other->rareNonInheritedData->m_clipPath) + return true; + + return false; +} + +bool RenderStyle::changeRequiresRepaintIfText(const RenderStyle* other, unsigned&) const +{ + if (inherited->color != other->inherited->color + || inherited_flags._text_decorations != other->inherited_flags._text_decorations + || visual->textDecoration != other->visual->textDecoration #if ENABLE(CSS3_TEXT) || rareNonInheritedData->m_textDecorationStyle != other->rareNonInheritedData->m_textDecorationStyle + || rareNonInheritedData->m_textDecorationColor != other->rareNonInheritedData->m_textDecorationColor #endif // CSS3_TEXT || rareInheritedData->textFillColor != other->rareInheritedData->textFillColor || rareInheritedData->textStrokeColor != other->rareInheritedData->textStrokeColor || rareInheritedData->textEmphasisColor != other->rareInheritedData->textEmphasisColor - || rareInheritedData->textEmphasisFill != other->rareInheritedData->textEmphasisFill - || rareInheritedData->m_imageRendering != other->rareInheritedData->m_imageRendering) - return StyleDifferenceRepaint; - - // FIXME: The current spec is being reworked to remove dependencies between exclusions and affected - // content. There's a proposal to use floats instead. In that case, wrap-shape should actually relayout - // the parent container. For sure, I will have to revisit this code, but for now I've added this in order - // to avoid having diff() == StyleDifferenceEqual where wrap-shapes actually differ. - // Tracking bug: https://bugs.webkit.org/show_bug.cgi?id=62991 - if (rareNonInheritedData->m_shapeOutside != other->rareNonInheritedData->m_shapeOutside) - return StyleDifferenceRepaint; - - if (rareNonInheritedData->m_clipPath != other->rareNonInheritedData->m_clipPath) - return StyleDifferenceRepaint; + || rareInheritedData->textEmphasisFill != other->rareInheritedData->textEmphasisFill) + return true; + return false; +} +bool RenderStyle::changeRequiresRecompositeLayer(const RenderStyle* other, unsigned&) const +{ #if USE(ACCELERATED_COMPOSITING) if (rareNonInheritedData.get() != other->rareNonInheritedData.get()) { if (rareNonInheritedData->m_transformStyle3D != other->rareNonInheritedData->m_transformStyle3D @@ -694,9 +737,53 @@ StyleDifference RenderStyle::diff(const RenderStyle* other, unsigned& changedCon || rareNonInheritedData->m_perspective != other->rareNonInheritedData->m_perspective || rareNonInheritedData->m_perspectiveOriginX != other->rareNonInheritedData->m_perspectiveOriginX || rareNonInheritedData->m_perspectiveOriginY != other->rareNonInheritedData->m_perspectiveOriginY) - return StyleDifferenceRecompositeLayer; + return true; } #endif + return false; +} + +StyleDifference RenderStyle::diff(const RenderStyle* other, unsigned& changedContextSensitiveProperties) const +{ + changedContextSensitiveProperties = ContextSensitivePropertyNone; + +#if ENABLE(SVG) + StyleDifference svgChange = StyleDifferenceEqual; + if (m_svgStyle != other->m_svgStyle) { + svgChange = m_svgStyle->diff(other->m_svgStyle.get()); + if (svgChange == StyleDifferenceLayout) + return svgChange; + } +#endif + + if (changeRequiresLayout(other, changedContextSensitiveProperties)) + return StyleDifferenceLayout; + +#if ENABLE(SVG) + // SVGRenderStyle::diff() might have returned StyleDifferenceRepaint, eg. if fill changes. + // If eg. the font-size changed at the same time, we're not allowed to return StyleDifferenceRepaint, + // but have to return StyleDifferenceLayout, that's why this if branch comes after all branches + // that are relevant for SVG and might return StyleDifferenceLayout. + if (svgChange != StyleDifferenceEqual) + return svgChange; +#endif + + if (changeRequiresPositionedLayoutOnly(other, changedContextSensitiveProperties)) + return StyleDifferenceLayoutPositionedMovementOnly; + + if (changeRequiresLayerRepaint(other, changedContextSensitiveProperties)) + return StyleDifferenceRepaintLayer; + + if (changeRequiresRepaint(other, changedContextSensitiveProperties)) + return StyleDifferenceRepaint; + +#if USE(ACCELERATED_COMPOSITING) + if (changeRequiresRecompositeLayer(other, changedContextSensitiveProperties)) + return StyleDifferenceRecompositeLayer; +#endif + + if (changeRequiresRepaintIfText(other, changedContextSensitiveProperties)) + return StyleDifferenceRepaintIfText; // Cursors are not checked, since they will be set appropriately in response to mouse events, // so they don't need to cause any repaint or layout. @@ -706,6 +793,12 @@ StyleDifference RenderStyle::diff(const RenderStyle* other, unsigned& changedCon return StyleDifferenceEqual; } +bool RenderStyle::diffRequiresRepaint(const RenderStyle* style) const +{ + unsigned changedContextSensitiveProperties = 0; + return changeRequiresRepaint(style, changedContextSensitiveProperties); +} + void RenderStyle::setClip(Length top, Length right, Length bottom, Length left) { StyleVisualData* data = visual.access(); @@ -729,8 +822,9 @@ void RenderStyle::setCursorList(PassRefPtr<CursorList> other) void RenderStyle::setQuotes(PassRefPtr<QuotesData> q) { - if (QuotesData::equals(rareInheritedData->quotes.get(), q.get())) + if (rareInheritedData->quotes == q || (rareInheritedData->quotes && q && *rareInheritedData->quotes == *q)) return; + rareInheritedData.access()->quotes = q; } @@ -975,13 +1069,13 @@ void RenderStyle::setListStyleImage(PassRefPtr<StyleImage> v) Color RenderStyle::color() const { return inherited->color; } Color RenderStyle::visitedLinkColor() const { return inherited->visitedLinkColor; } -void RenderStyle::setColor(const Color& v) { SET_VAR(inherited, color, v) }; -void RenderStyle::setVisitedLinkColor(const Color& v) { SET_VAR(inherited, visitedLinkColor, v) } +void RenderStyle::setColor(const Color& v) { SET_VAR(inherited, color, v); } +void RenderStyle::setVisitedLinkColor(const Color& v) { SET_VAR(inherited, visitedLinkColor, v); } short RenderStyle::horizontalBorderSpacing() const { return inherited->horizontal_border_spacing; } short RenderStyle::verticalBorderSpacing() const { return inherited->vertical_border_spacing; } -void RenderStyle::setHorizontalBorderSpacing(short v) { SET_VAR(inherited, horizontal_border_spacing, v) } -void RenderStyle::setVerticalBorderSpacing(short v) { SET_VAR(inherited, vertical_border_spacing, v) } +void RenderStyle::setHorizontalBorderSpacing(short v) { SET_VAR(inherited, horizontal_border_spacing, v); } +void RenderStyle::setVerticalBorderSpacing(short v) { SET_VAR(inherited, vertical_border_spacing, v); } RoundedRect RenderStyle::getRoundedBorderFor(const LayoutRect& borderRect, RenderView* renderView, bool includeLogicalLeftEdge, bool includeLogicalRightEdge) const { @@ -999,16 +1093,16 @@ RoundedRect RenderStyle::getRoundedInnerBorderFor(const LayoutRect& borderRect, { bool horizontal = isHorizontalWritingMode(); - LayoutUnit leftWidth = (!horizontal || includeLogicalLeftEdge) ? borderLeftWidth() : 0; - LayoutUnit rightWidth = (!horizontal || includeLogicalRightEdge) ? borderRightWidth() : 0; - LayoutUnit topWidth = (horizontal || includeLogicalLeftEdge) ? borderTopWidth() : 0; - LayoutUnit bottomWidth = (horizontal || includeLogicalRightEdge) ? borderBottomWidth() : 0; + int leftWidth = (!horizontal || includeLogicalLeftEdge) ? borderLeftWidth() : 0; + int rightWidth = (!horizontal || includeLogicalRightEdge) ? borderRightWidth() : 0; + int topWidth = (horizontal || includeLogicalLeftEdge) ? borderTopWidth() : 0; + int bottomWidth = (horizontal || includeLogicalRightEdge) ? borderBottomWidth() : 0; return getRoundedInnerBorderFor(borderRect, topWidth, bottomWidth, leftWidth, rightWidth, includeLogicalLeftEdge, includeLogicalRightEdge); } RoundedRect RenderStyle::getRoundedInnerBorderFor(const LayoutRect& borderRect, - LayoutUnit topWidth, LayoutUnit bottomWidth, LayoutUnit leftWidth, LayoutUnit rightWidth, bool includeLogicalLeftEdge, bool includeLogicalRightEdge) const + int topWidth, int bottomWidth, int leftWidth, int rightWidth, bool includeLogicalLeftEdge, bool includeLogicalRightEdge) const { LayoutRect innerRect(borderRect.x() + leftWidth, borderRect.y() + topWidth, @@ -1025,6 +1119,21 @@ RoundedRect RenderStyle::getRoundedInnerBorderFor(const LayoutRect& borderRect, return roundedRect; } +static bool allLayersAreFixed(const FillLayer* layer) +{ + bool allFixed = true; + + for (const FillLayer* currLayer = layer; currLayer; currLayer = currLayer->next()) + allFixed &= (currLayer->image() && currLayer->attachment() == FixedBackgroundAttachment); + + return layer && allFixed; +} + +bool RenderStyle::hasEntirelyFixedBackground() const +{ + return allLayersAreFixed(backgroundLayers()); +} + const CounterDirectiveMap* RenderStyle::counterDirectives() const { return rareNonInheritedData->m_counterDirectives.get(); @@ -1273,6 +1382,12 @@ void RenderStyle::setFontSize(float size) // size must be specifiedSize if Text Autosizing is enabled, but computedSize if text // zoom is enabled (if neither is enabled it's irrelevant as they're probably the same). + ASSERT(std::isfinite(size)); + if (!std::isfinite(size) || size < 0) + size = 0; + else + size = min(maximumAllowedFontSize, size); + FontSelector* currentFontSelector = font().fontSelector(); FontDescription desc(fontDescription()); desc.setSpecifiedSize(size); @@ -1281,7 +1396,8 @@ void RenderStyle::setFontSize(float size) #if ENABLE(TEXT_AUTOSIZING) float multiplier = textAutosizingMultiplier(); if (multiplier > 1) { - desc.setComputedSize(TextAutosizer::computeAutosizedFontSize(size, multiplier)); + float autosizedFontSize = TextAutosizer::computeAutosizedFontSize(size, multiplier); + desc.setComputedSize(min(maximumAllowedFontSize, autosizedFontSize)); } #endif @@ -1299,12 +1415,12 @@ void RenderStyle::getShadowExtent(const ShadowData* shadow, LayoutUnit &top, Lay for ( ; shadow; shadow = shadow->next()) { if (shadow->style() == Inset) continue; - int blurAndSpread = shadow->blur() + shadow->spread(); - top = min<LayoutUnit>(top, shadow->y() - blurAndSpread); - right = max<LayoutUnit>(right, shadow->x() + blurAndSpread); - bottom = max<LayoutUnit>(bottom, shadow->y() + blurAndSpread); - left = min<LayoutUnit>(left, shadow->x() - blurAndSpread); + int extentAndSpread = shadow->paintingExtent() + shadow->spread(); + top = min<LayoutUnit>(top, shadow->y() - extentAndSpread); + right = max<LayoutUnit>(right, shadow->x() + extentAndSpread); + bottom = max<LayoutUnit>(bottom, shadow->y() + extentAndSpread); + left = min<LayoutUnit>(left, shadow->x() - extentAndSpread); } } @@ -1318,11 +1434,12 @@ LayoutBoxExtent RenderStyle::getShadowInsetExtent(const ShadowData* shadow) cons for ( ; shadow; shadow = shadow->next()) { if (shadow->style() == Normal) continue; - int blurAndSpread = shadow->blur() + shadow->spread(); - top = max<LayoutUnit>(top, shadow->y() + blurAndSpread); - right = min<LayoutUnit>(right, shadow->x() - blurAndSpread); - bottom = min<LayoutUnit>(bottom, shadow->y() - blurAndSpread); - left = max<LayoutUnit>(left, shadow->x() + blurAndSpread); + + int extentAndSpread = shadow->paintingExtent() + shadow->spread(); + top = max<LayoutUnit>(top, shadow->y() + extentAndSpread); + right = min<LayoutUnit>(right, shadow->x() - extentAndSpread); + bottom = min<LayoutUnit>(bottom, shadow->y() - extentAndSpread); + left = max<LayoutUnit>(left, shadow->x() + extentAndSpread); } return LayoutBoxExtent(top, right, bottom, left); @@ -1336,10 +1453,10 @@ void RenderStyle::getShadowHorizontalExtent(const ShadowData* shadow, LayoutUnit for ( ; shadow; shadow = shadow->next()) { if (shadow->style() == Inset) continue; - int blurAndSpread = shadow->blur() + shadow->spread(); - left = min<LayoutUnit>(left, shadow->x() - blurAndSpread); - right = max<LayoutUnit>(right, shadow->x() + blurAndSpread); + int extentAndSpread = shadow->paintingExtent() + shadow->spread(); + left = min<LayoutUnit>(left, shadow->x() - extentAndSpread); + right = max<LayoutUnit>(right, shadow->x() + extentAndSpread); } } @@ -1351,10 +1468,10 @@ void RenderStyle::getShadowVerticalExtent(const ShadowData* shadow, LayoutUnit & for ( ; shadow; shadow = shadow->next()) { if (shadow->style() == Inset) continue; - int blurAndSpread = shadow->blur() + shadow->spread(); - top = min<LayoutUnit>(top, shadow->y() - blurAndSpread); - bottom = max<LayoutUnit>(bottom, shadow->y() + blurAndSpread); + int extentAndSpread = shadow->paintingExtent() + shadow->spread(); + top = min<LayoutUnit>(top, shadow->y() - extentAndSpread); + bottom = max<LayoutUnit>(bottom, shadow->y() + extentAndSpread); } } @@ -1390,6 +1507,11 @@ Color RenderStyle::colorIncludingFallback(int colorProperty, bool visitedLink) c case CSSPropertyWebkitColumnRuleColor: result = visitedLink ? visitedLinkColumnRuleColor() : columnRuleColor(); break; +#if ENABLE(CSS3_TEXT) + case CSSPropertyWebkitTextDecorationColor: + // Text decoration color fallback is handled in RenderObject::decorationColor. + return visitedLink ? visitedLinkTextDecorationColor() : textDecorationColor(); +#endif // CSS3_TEXT case CSSPropertyWebkitTextEmphasisColor: result = visitedLink ? visitedLinkTextEmphasisColor() : textEmphasisColor(); break; @@ -1421,6 +1543,12 @@ Color RenderStyle::visitedDependentColor(int colorProperty) const Color visitedColor = colorIncludingFallback(colorProperty, true); +#if ENABLE(CSS3_TEXT) + // Text decoration color validity is preserved (checked in RenderObject::decorationColor). + if (colorProperty == CSSPropertyWebkitTextDecorationColor) + return visitedColor; +#endif // CSS3_TEXT + // FIXME: Technically someone could explicitly specify the color transparent, but for now we'll just // assume that if the background color is transparent that it wasn't set. Note that it's weird that // we're returning unvisited info for a visited link, but given our restriction that the alpha values @@ -1582,23 +1710,78 @@ LayoutBoxExtent RenderStyle::imageOutsets(const NinePieceImage& image) const NinePieceImage::computeOutset(image.outset().left(), borderLeftWidth())); } -void RenderStyle::reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const -{ - MemoryClassInfo info(memoryObjectInfo, this, WebCoreMemoryTypes::CSS); - info.addMember(m_box); - info.addMember(visual); - // FIXME: m_background contains RefPtr<StyleImage> that might need to be instrumented. - info.addMember(m_background); - // FIXME: surrond contains some fields e.g. BorderData that might need to be instrumented. - info.addMember(surround); - info.addMember(rareNonInheritedData); - info.addMember(rareInheritedData); - // FIXME: inherited contains StyleImage and Font fields that might need to be instrumented. - info.addMember(inherited); - info.addMember(m_cachedPseudoStyles); -#if ENABLE(SVG) - info.addMember(m_svgStyle); -#endif +void RenderStyle::setBorderImageSource(PassRefPtr<StyleImage> image) +{ + if (surround->border.m_image.image() == image.get()) + return; + surround.access()->border.m_image.setImage(image); +} + +void RenderStyle::setBorderImageSlices(LengthBox slices) +{ + if (surround->border.m_image.imageSlices() == slices) + return; + surround.access()->border.m_image.setImageSlices(slices); +} + +void RenderStyle::setBorderImageWidth(LengthBox slices) +{ + if (surround->border.m_image.borderSlices() == slices) + return; + surround.access()->border.m_image.setBorderSlices(slices); +} + +void RenderStyle::setBorderImageOutset(LengthBox outset) +{ + if (surround->border.m_image.outset() == outset) + return; + surround.access()->border.m_image.setOutset(outset); +} + +ShapeValue* RenderStyle::initialShapeInside() +{ + DEFINE_STATIC_LOCAL(RefPtr<ShapeValue>, sOutsideValue, (ShapeValue::createOutsideValue())); + return sOutsideValue.get(); +} + +void RenderStyle::setColumnStylesFromPaginationMode(const Pagination::Mode& paginationMode) +{ + if (paginationMode == Pagination::Unpaginated) + return; + + switch (paginationMode) { + case Pagination::LeftToRightPaginated: + setColumnAxis(HorizontalColumnAxis); + if (isHorizontalWritingMode()) + setColumnProgression(isLeftToRightDirection() ? NormalColumnProgression : ReverseColumnProgression); + else + setColumnProgression(isFlippedBlocksWritingMode() ? ReverseColumnProgression : NormalColumnProgression); + break; + case Pagination::RightToLeftPaginated: + setColumnAxis(HorizontalColumnAxis); + if (isHorizontalWritingMode()) + setColumnProgression(isLeftToRightDirection() ? ReverseColumnProgression : NormalColumnProgression); + else + setColumnProgression(isFlippedBlocksWritingMode() ? NormalColumnProgression : ReverseColumnProgression); + break; + case Pagination::TopToBottomPaginated: + setColumnAxis(VerticalColumnAxis); + if (isHorizontalWritingMode()) + setColumnProgression(isFlippedBlocksWritingMode() ? ReverseColumnProgression : NormalColumnProgression); + else + setColumnProgression(isLeftToRightDirection() ? NormalColumnProgression : ReverseColumnProgression); + break; + case Pagination::BottomToTopPaginated: + setColumnAxis(VerticalColumnAxis); + if (isHorizontalWritingMode()) + setColumnProgression(isFlippedBlocksWritingMode() ? NormalColumnProgression : ReverseColumnProgression); + else + setColumnProgression(isLeftToRightDirection() ? ReverseColumnProgression : NormalColumnProgression); + break; + case Pagination::Unpaginated: + ASSERT_NOT_REACHED(); + break; + } } } // namespace WebCore diff --git a/Source/WebCore/rendering/style/RenderStyle.h b/Source/WebCore/rendering/style/RenderStyle.h index 06e5bb269..6a60d7a8a 100644 --- a/Source/WebCore/rendering/style/RenderStyle.h +++ b/Source/WebCore/rendering/style/RenderStyle.h @@ -34,7 +34,6 @@ #include "ColorSpace.h" #include "CounterDirectives.h" #include "DataRef.h" -#include "ExclusionShapeValue.h" #include "FontBaseline.h" #include "FontDescription.h" #include "GraphicsTypes.h" @@ -46,9 +45,11 @@ #include "LineClampValue.h" #include "NinePieceImage.h" #include "OutlineValue.h" +#include "Pagination.h" #include "RenderStyleConstants.h" #include "RoundedRect.h" #include "ShadowData.h" +#include "ShapeValue.h" #include "StyleBackgroundData.h" #include "StyleBoxData.h" #include "StyleDeprecatedFlexibleBoxData.h" @@ -64,7 +65,6 @@ #include "StyleTransformData.h" #include "StyleVisualData.h" #include "TextDirection.h" -#include "TextOrientation.h" #include "ThemeTypes.h" #include "TransformOperations.h" #include "UnicodeBidi.h" @@ -91,11 +91,11 @@ template<typename T, typename U> inline bool compareEqual(const T& t, const U& u #define SET_VAR(group, variable, value) \ if (!compareEqual(group->variable, value)) \ - group.access()->variable = value; + group.access()->variable = value #define SET_BORDERVALUE_COLOR(group, variable, value) \ if (!compareEqual(group->variable.color(), value)) \ - group.access()->variable.setColor(value); + group.access()->variable.setColor(value) namespace WebCore { @@ -125,12 +125,12 @@ typedef Vector<RefPtr<RenderStyle>, 4> PseudoStyleCache; class RenderStyle: public RefCounted<RenderStyle> { friend class CSSPropertyAnimation; // Used by CSS animations. We can't allow them to animate based off visited colors. friend class ApplyStyleCommand; // Editing has to only reveal unvisited info. + friend class DeprecatedStyleBuilder; // Sets members directly. friend class EditingStyle; // Editing has to only reveal unvisited info. - friend class CSSComputedStyleDeclaration; // Ignores visited styles, so needs to be able to see unvisited info. + friend class ComputedStyleExtractor; // Ignores visited styles, so needs to be able to see unvisited info. friend class PropertyWrapperMaybeInvalidColor; // Used by CSS animations. We can't allow them to animate based off visited colors. friend class RenderSVGResource; // FIXME: Needs to alter the visited state by hand. Should clean the SVG code up and move it into RenderStyle perhaps. friend class RenderTreeAsText; // FIXME: Only needed so the render tree can keep lying and dump the wrong colors. Rebaselining would allow this to be yanked. - friend class StyleBuilder; // Sets members directly. friend class StyleResolver; // Sets members directly. protected: @@ -167,6 +167,9 @@ protected: && (_text_transform == other._text_transform) && (_text_decorations == other._text_decorations) && (_cursor_style == other._cursor_style) +#if ENABLE(CURSOR_VISIBILITY) + && (m_cursorVisibility == other.m_cursorVisibility) +#endif && (_direction == other._direction) && (_white_space == other._white_space) && (_border_collapse == other._border_collapse) @@ -187,8 +190,11 @@ protected: unsigned _visibility : 2; // EVisibility unsigned _text_align : 4; // ETextAlign unsigned _text_transform : 2; // ETextTransform - unsigned _text_decorations : ETextDecorationBits; + unsigned _text_decorations : TextDecorationBits; unsigned _cursor_style : 6; // ECursor +#if ENABLE(CURSOR_VISIBILITY) + unsigned m_cursorVisibility : 1; // CursorVisibility +#endif unsigned _direction : 1; // TextDirection unsigned _white_space : 3; // EWhiteSpace // 32 bits @@ -294,6 +300,9 @@ protected: inherited_flags._text_transform = initialTextTransform(); inherited_flags._text_decorations = initialTextDecoration(); inherited_flags._cursor_style = initialCursor(); +#if ENABLE(CURSOR_VISIBILITY) + inherited_flags.m_cursorVisibility = initialCursorVisibility(); +#endif inherited_flags._direction = initialDirection(); inherited_flags._white_space = initialWhiteSpace(); inherited_flags._border_collapse = initialBorderCollapse(); @@ -371,6 +380,8 @@ public: void setAffectedByActive() { noninherited_flags.setAffectedByActive(true); } void setAffectedByDrag() { noninherited_flags.setAffectedByDrag(true); } + void setColumnStylesFromPaginationMode(const Pagination::Mode&); + bool operator==(const RenderStyle& other) const; bool operator!=(const RenderStyle& other) const { return !(*this == other); } bool isFloating() const { return noninherited_flags._floating != NoFloat; } @@ -378,9 +389,14 @@ public: bool hasBorder() const { return surround->border.hasBorder(); } bool hasPadding() const { return surround->padding.nonZero(); } bool hasOffset() const { return surround->offset.nonZero(); } + bool hasMarginBeforeQuirk() const { return marginBefore().quirk(); } + bool hasMarginAfterQuirk() const { return marginAfter().quirk(); } bool hasBackgroundImage() const { return m_background->background().hasImage(); } bool hasFixedBackgroundImage() const { return m_background->background().hasFixedImage(); } + + bool hasEntirelyFixedBackground() const; + bool hasAppearance() const { return appearance() != NoControlPart; } bool hasBackground() const @@ -407,20 +423,8 @@ public: } #if ENABLE(CSS_FILTERS) - void getFilterOutsets(int& top, int& right, int& bottom, int& left) const - { - if (hasFilter()) - filter().getOutsets(top, right, bottom, left); - else { - top = 0; - right = 0; - bottom = 0; - left = 0; - } - } bool hasFilterOutsets() const { return hasFilter() && filter().hasOutsets(); } -#else - bool hasFilterOutsets() const { return false; } + FilterOutsets filterOutsets() const { return hasFilter() ? filter().outsets() : FilterOutsets(); } #endif Order rtlOrdering() const { return static_cast<Order>(inherited_flags.m_rtlOrdering); } @@ -431,6 +435,7 @@ public: bool hasAnyPublicPseudoStyles() const; bool hasPseudoStyle(PseudoId pseudo) const; void setHasPseudoStyle(PseudoId pseudo); + bool hasUniquePseudoStyle() const; // attribute getter methods @@ -558,13 +563,19 @@ public: #endif Length textIndent() const { return rareInheritedData->indent; } +#if ENABLE(CSS3_TEXT) + TextIndentLine textIndentLine() const { return static_cast<TextIndentLine>(rareInheritedData->m_textIndentLine); } + TextIndentType textIndentType() const { return static_cast<TextIndentType>(rareInheritedData->m_textIndentType); } +#endif ETextAlign textAlign() const { return static_cast<ETextAlign>(inherited_flags._text_align); } ETextTransform textTransform() const { return static_cast<ETextTransform>(inherited_flags._text_transform); } - ETextDecoration textDecorationsInEffect() const { return static_cast<ETextDecoration>(inherited_flags._text_decorations); } - ETextDecoration textDecoration() const { return static_cast<ETextDecoration>(visual->textDecoration); } + TextDecoration textDecorationsInEffect() const { return static_cast<TextDecoration>(inherited_flags._text_decorations); } + TextDecoration textDecoration() const { return static_cast<TextDecoration>(visual->textDecoration); } #if ENABLE(CSS3_TEXT) TextDecorationStyle textDecorationStyle() const { return static_cast<TextDecorationStyle>(rareNonInheritedData->m_textDecorationStyle); } - ETextAlignLast textAlignLast() const { return static_cast<ETextAlignLast>(rareInheritedData->m_textAlignLast); } + TextAlignLast textAlignLast() const { return static_cast<TextAlignLast>(rareInheritedData->m_textAlignLast); } + TextJustify textJustify() const { return static_cast<TextJustify>(rareInheritedData->m_textJustify); } + TextUnderlinePosition textUnderlinePosition() const { return static_cast<TextUnderlinePosition>(rareInheritedData->m_textUnderlinePosition); } #else TextDecorationStyle textDecorationStyle() const { return TextDecorationStyleSolid; } #endif // CSS3_TEXT @@ -699,6 +710,9 @@ public: Length paddingEnd() const { return surround->padding.end(writingMode(), direction()); } ECursor cursor() const { return static_cast<ECursor>(inherited_flags._cursor_style); } +#if ENABLE(CURSOR_VISIBILITY) + CursorVisibility cursorVisibility() const { return static_cast<CursorVisibility>(inherited_flags.m_cursorVisibility); } +#endif CursorList* cursors() const { return rareInheritedData->cursorData.get(); } @@ -707,6 +721,8 @@ public: short widows() const { return rareInheritedData->widows; } short orphans() const { return rareInheritedData->orphans; } + bool hasAutoWidows() const { return rareInheritedData->m_hasAutoWidows; } + bool hasAutoOrphans() const { return rareInheritedData->m_hasAutoOrphans; } EPageBreak pageBreakInside() const { return static_cast<EPageBreak>(noninherited_flags._page_break_inside); } EPageBreak pageBreakBefore() const { return static_cast<EPageBreak>(noninherited_flags._page_break_before); } EPageBreak pageBreakAfter() const { return static_cast<EPageBreak>(noninherited_flags._page_break_after); } @@ -760,9 +776,14 @@ public: const Vector<GridTrackSize>& gridColumns() const { return rareNonInheritedData->m_grid->m_gridColumns; } const Vector<GridTrackSize>& gridRows() const { return rareNonInheritedData->m_grid->m_gridRows; } + GridAutoFlow gridAutoFlow() const { return rareNonInheritedData->m_grid->m_gridAutoFlow; } + const GridTrackSize& gridAutoColumns() const { return rareNonInheritedData->m_grid->m_gridAutoColumns; } + const GridTrackSize& gridAutoRows() const { return rareNonInheritedData->m_grid->m_gridAutoRows; } - const GridPosition& gridItemColumn() const { return rareNonInheritedData->m_gridItem->m_gridColumn; } - const GridPosition& gridItemRow() const { return rareNonInheritedData->m_gridItem->m_gridRow; } + const GridPosition& gridItemStart() const { return rareNonInheritedData->m_gridItem->m_gridStart; } + const GridPosition& gridItemEnd() const { return rareNonInheritedData->m_gridItem->m_gridEnd; } + const GridPosition& gridItemBefore() const { return rareNonInheritedData->m_gridItem->m_gridBefore; } + const GridPosition& gridItemAfter() const { return rareNonInheritedData->m_gridItem->m_gridAfter; } const ShadowData* boxShadow() const { return rareNonInheritedData->m_boxShadow.get(); } void getBoxShadowExtent(LayoutUnit& top, LayoutUnit& right, LayoutUnit& bottom, LayoutUnit& left) const { getShadowExtent(boxShadow(), top, right, bottom, left); } @@ -838,6 +859,8 @@ public: RubyPosition rubyPosition() const { return static_cast<RubyPosition>(rareInheritedData->m_rubyPosition); } + TextOrientation textOrientation() const { return static_cast<TextOrientation>(rareInheritedData->m_textOrientation); } + // Return true if any transform related property (currently transform, transformStyle3D or perspective) // indicates that we are transforming bool hasTransformRelatedProperty() const { return hasTransform() || preserves3D() || hasPerspective(); } @@ -858,7 +881,7 @@ public: const AtomicString& flowThread() const { return rareNonInheritedData->m_flowThread; } const AtomicString& regionThread() const { return rareNonInheritedData->m_regionThread; } - RegionOverflow regionOverflow() const { return static_cast<RegionOverflow>(rareNonInheritedData->m_regionOverflow); } + RegionFragment regionFragment() const { return static_cast<RegionFragment>(rareNonInheritedData->m_regionFragment); } const AtomicString& lineGrid() const { return rareInheritedData->m_lineGrid; } LineSnap lineSnap() const { return static_cast<LineSnap>(rareInheritedData->m_lineSnap); } @@ -905,7 +928,6 @@ public: #if ENABLE(ACCELERATED_OVERFLOW_SCROLLING) bool useTouchOverflowScrolling() const { return rareInheritedData->useTouchOverflowScrolling; } #endif - bool textSizeAdjust() const { return rareInheritedData->textSizeAdjust; } ETextSecurity textSecurity() const { return static_cast<ETextSecurity>(rareInheritedData->textSecurity); } WritingMode writingMode() const { return static_cast<WritingMode>(inherited_flags.m_writingMode); } @@ -956,18 +978,36 @@ public: void setPosition(EPosition v) { noninherited_flags._position = v; } void setFloating(EFloat v) { noninherited_flags._floating = v; } - void setLeft(Length v) { SET_VAR(surround, offset.m_left, v) } - void setRight(Length v) { SET_VAR(surround, offset.m_right, v) } - void setTop(Length v) { SET_VAR(surround, offset.m_top, v) } - void setBottom(Length v) { SET_VAR(surround, offset.m_bottom, v) } + void setLeft(Length v) { SET_VAR(surround, offset.m_left, v); } + void setRight(Length v) { SET_VAR(surround, offset.m_right, v); } + void setTop(Length v) { SET_VAR(surround, offset.m_top, v); } + void setBottom(Length v) { SET_VAR(surround, offset.m_bottom, v); } - void setWidth(Length v) { SET_VAR(m_box, m_width, v) } - void setHeight(Length v) { SET_VAR(m_box, m_height, v) } + void setWidth(Length v) { SET_VAR(m_box, m_width, v); } + void setHeight(Length v) { SET_VAR(m_box, m_height, v); } - void setMinWidth(Length v) { SET_VAR(m_box, m_minWidth, v) } - void setMaxWidth(Length v) { SET_VAR(m_box, m_maxWidth, v) } - void setMinHeight(Length v) { SET_VAR(m_box, m_minHeight, v) } - void setMaxHeight(Length v) { SET_VAR(m_box, m_maxHeight, v) } + void setLogicalWidth(Length v) + { + if (isHorizontalWritingMode()) { + SET_VAR(m_box, m_width, v); + } else { + SET_VAR(m_box, m_height, v); + } + } + + void setLogicalHeight(Length v) + { + if (isHorizontalWritingMode()) { + SET_VAR(m_box, m_height, v); + } else { + SET_VAR(m_box, m_width, v); + } + } + + void setMinWidth(Length v) { SET_VAR(m_box, m_minWidth, v); } + void setMaxWidth(Length v) { SET_VAR(m_box, m_maxWidth, v); } + void setMinHeight(Length v) { SET_VAR(m_box, m_minHeight, v); } + void setMaxHeight(Length v) { SET_VAR(m_box, m_maxHeight, v); } #if ENABLE(DASHBOARD_SUPPORT) Vector<StyleDashboardRegion> dashboardRegions() const { return rareNonInheritedData->m_dashboardRegions; } @@ -994,34 +1034,34 @@ public: #endif void resetBorder() { resetBorderImage(); resetBorderTop(); resetBorderRight(); resetBorderBottom(); resetBorderLeft(); resetBorderRadius(); } - void resetBorderTop() { SET_VAR(surround, border.m_top, BorderValue()) } - void resetBorderRight() { SET_VAR(surround, border.m_right, BorderValue()) } - void resetBorderBottom() { SET_VAR(surround, border.m_bottom, BorderValue()) } - void resetBorderLeft() { SET_VAR(surround, border.m_left, BorderValue()) } - void resetBorderImage() { SET_VAR(surround, border.m_image, NinePieceImage()) } + void resetBorderTop() { SET_VAR(surround, border.m_top, BorderValue()); } + void resetBorderRight() { SET_VAR(surround, border.m_right, BorderValue()); } + void resetBorderBottom() { SET_VAR(surround, border.m_bottom, BorderValue()); } + void resetBorderLeft() { SET_VAR(surround, border.m_left, BorderValue()); } + void resetBorderImage() { SET_VAR(surround, border.m_image, NinePieceImage()); } void resetBorderRadius() { resetBorderTopLeftRadius(); resetBorderTopRightRadius(); resetBorderBottomLeftRadius(); resetBorderBottomRightRadius(); } - void resetBorderTopLeftRadius() { SET_VAR(surround, border.m_topLeft, initialBorderRadius()) } - void resetBorderTopRightRadius() { SET_VAR(surround, border.m_topRight, initialBorderRadius()) } - void resetBorderBottomLeftRadius() { SET_VAR(surround, border.m_bottomLeft, initialBorderRadius()) } - void resetBorderBottomRightRadius() { SET_VAR(surround, border.m_bottomRight, initialBorderRadius()) } + void resetBorderTopLeftRadius() { SET_VAR(surround, border.m_topLeft, initialBorderRadius()); } + void resetBorderTopRightRadius() { SET_VAR(surround, border.m_topRight, initialBorderRadius()); } + void resetBorderBottomLeftRadius() { SET_VAR(surround, border.m_bottomLeft, initialBorderRadius()); } + void resetBorderBottomRightRadius() { SET_VAR(surround, border.m_bottomRight, initialBorderRadius()); } - void setBackgroundColor(const Color& v) { SET_VAR(m_background, m_color, v) } + void setBackgroundColor(const Color& v) { SET_VAR(m_background, m_color, v); } - void setBackgroundXPosition(Length l) { SET_VAR(m_background, m_background.m_xPosition, l) } - void setBackgroundYPosition(Length l) { SET_VAR(m_background, m_background.m_yPosition, l) } - void setBackgroundSize(EFillSizeType b) { SET_VAR(m_background, m_background.m_sizeType, b) } - void setBackgroundSizeLength(LengthSize l) { SET_VAR(m_background, m_background.m_sizeLength, l) } + void setBackgroundXPosition(Length length) { SET_VAR(m_background, m_background.m_xPosition, length); } + void setBackgroundYPosition(Length length) { SET_VAR(m_background, m_background.m_yPosition, length); } + void setBackgroundSize(EFillSizeType b) { SET_VAR(m_background, m_background.m_sizeType, b); } + void setBackgroundSizeLength(LengthSize s) { SET_VAR(m_background, m_background.m_sizeLength, s); } - void setBorderImage(const NinePieceImage& b) { SET_VAR(surround, border.m_image, b) } - void setBorderImageSource(PassRefPtr<StyleImage> v) { surround.access()->border.m_image.setImage(v); } - void setBorderImageSlices(LengthBox slices) { surround.access()->border.m_image.setImageSlices(slices); } - void setBorderImageWidth(LengthBox slices) { surround.access()->border.m_image.setBorderSlices(slices); } - void setBorderImageOutset(LengthBox outset) { surround.access()->border.m_image.setOutset(outset); } + void setBorderImage(const NinePieceImage& b) { SET_VAR(surround, border.m_image, b); } + void setBorderImageSource(PassRefPtr<StyleImage>); + void setBorderImageSlices(LengthBox); + void setBorderImageWidth(LengthBox); + void setBorderImageOutset(LengthBox); - void setBorderTopLeftRadius(LengthSize s) { SET_VAR(surround, border.m_topLeft, s) } - void setBorderTopRightRadius(LengthSize s) { SET_VAR(surround, border.m_topRight, s) } - void setBorderBottomLeftRadius(LengthSize s) { SET_VAR(surround, border.m_bottomLeft, s) } - void setBorderBottomRightRadius(LengthSize s) { SET_VAR(surround, border.m_bottomRight, s) } + void setBorderTopLeftRadius(LengthSize s) { SET_VAR(surround, border.m_topLeft, s); } + void setBorderTopRightRadius(LengthSize s) { SET_VAR(surround, border.m_topRight, s); } + void setBorderBottomLeftRadius(LengthSize s) { SET_VAR(surround, border.m_bottomLeft, s); } + void setBorderBottomRightRadius(LengthSize s) { SET_VAR(surround, border.m_bottomRight, s); } void setBorderRadius(LengthSize s) { @@ -1039,39 +1079,39 @@ public: RoundedRect getRoundedInnerBorderFor(const LayoutRect& borderRect, bool includeLogicalLeftEdge = true, bool includeLogicalRightEdge = true) const; RoundedRect getRoundedInnerBorderFor(const LayoutRect& borderRect, - LayoutUnit topWidth, LayoutUnit bottomWidth, LayoutUnit leftWidth, LayoutUnit rightWidth, bool includeLogicalLeftEdge, bool includeLogicalRightEdge) const; - - void setBorderLeftWidth(unsigned v) { SET_VAR(surround, border.m_left.m_width, v) } - void setBorderLeftStyle(EBorderStyle v) { SET_VAR(surround, border.m_left.m_style, v) } - void setBorderLeftColor(const Color& v) { SET_BORDERVALUE_COLOR(surround, border.m_left, v) } - void setBorderRightWidth(unsigned v) { SET_VAR(surround, border.m_right.m_width, v) } - void setBorderRightStyle(EBorderStyle v) { SET_VAR(surround, border.m_right.m_style, v) } - void setBorderRightColor(const Color& v) { SET_BORDERVALUE_COLOR(surround, border.m_right, v) } - void setBorderTopWidth(unsigned v) { SET_VAR(surround, border.m_top.m_width, v) } - void setBorderTopStyle(EBorderStyle v) { SET_VAR(surround, border.m_top.m_style, v) } - void setBorderTopColor(const Color& v) { SET_BORDERVALUE_COLOR(surround, border.m_top, v) } - void setBorderBottomWidth(unsigned v) { SET_VAR(surround, border.m_bottom.m_width, v) } - void setBorderBottomStyle(EBorderStyle v) { SET_VAR(surround, border.m_bottom.m_style, v) } - void setBorderBottomColor(const Color& v) { SET_BORDERVALUE_COLOR(surround, border.m_bottom, v) } - - void setOutlineWidth(unsigned short v) { SET_VAR(m_background, m_outline.m_width, v) } - void setOutlineStyleIsAuto(OutlineIsAuto isAuto) { SET_VAR(m_background, m_outline.m_isAuto, isAuto) } - void setOutlineStyle(EBorderStyle v) { SET_VAR(m_background, m_outline.m_style, v) } - void setOutlineColor(const Color& v) { SET_BORDERVALUE_COLOR(m_background, m_outline, v) } + int topWidth, int bottomWidth, int leftWidth, int rightWidth, bool includeLogicalLeftEdge, bool includeLogicalRightEdge) const; + + void setBorderLeftWidth(unsigned v) { SET_VAR(surround, border.m_left.m_width, v); } + void setBorderLeftStyle(EBorderStyle v) { SET_VAR(surround, border.m_left.m_style, v); } + void setBorderLeftColor(const Color& v) { SET_BORDERVALUE_COLOR(surround, border.m_left, v); } + void setBorderRightWidth(unsigned v) { SET_VAR(surround, border.m_right.m_width, v); } + void setBorderRightStyle(EBorderStyle v) { SET_VAR(surround, border.m_right.m_style, v); } + void setBorderRightColor(const Color& v) { SET_BORDERVALUE_COLOR(surround, border.m_right, v); } + void setBorderTopWidth(unsigned v) { SET_VAR(surround, border.m_top.m_width, v); } + void setBorderTopStyle(EBorderStyle v) { SET_VAR(surround, border.m_top.m_style, v); } + void setBorderTopColor(const Color& v) { SET_BORDERVALUE_COLOR(surround, border.m_top, v); } + void setBorderBottomWidth(unsigned v) { SET_VAR(surround, border.m_bottom.m_width, v); } + void setBorderBottomStyle(EBorderStyle v) { SET_VAR(surround, border.m_bottom.m_style, v); } + void setBorderBottomColor(const Color& v) { SET_BORDERVALUE_COLOR(surround, border.m_bottom, v); } + + void setOutlineWidth(unsigned short v) { SET_VAR(m_background, m_outline.m_width, v); } + void setOutlineStyleIsAuto(OutlineIsAuto isAuto) { SET_VAR(m_background, m_outline.m_isAuto, isAuto); } + void setOutlineStyle(EBorderStyle v) { SET_VAR(m_background, m_outline.m_style, v); } + void setOutlineColor(const Color& v) { SET_BORDERVALUE_COLOR(m_background, m_outline, v); } void setOverflowX(EOverflow v) { noninherited_flags._overflowX = v; } void setOverflowY(EOverflow v) { noninherited_flags._overflowY = v; } void setVisibility(EVisibility v) { inherited_flags._visibility = v; } void setVerticalAlign(EVerticalAlign v) { noninherited_flags._vertical_align = v; } - void setVerticalAlignLength(Length length) { setVerticalAlign(LENGTH); SET_VAR(m_box, m_verticalAlign, length) } + void setVerticalAlignLength(Length length) { setVerticalAlign(LENGTH); SET_VAR(m_box, m_verticalAlign, length); } - void setHasClip(bool b = true) { SET_VAR(visual, hasClip, b) } - void setClipLeft(Length v) { SET_VAR(visual, clip.m_left, v) } - void setClipRight(Length v) { SET_VAR(visual, clip.m_right, v) } - void setClipTop(Length v) { SET_VAR(visual, clip.m_top, v) } - void setClipBottom(Length v) { SET_VAR(visual, clip.m_bottom, v) } + void setHasClip(bool b = true) { SET_VAR(visual, hasClip, b); } + void setClipLeft(Length v) { SET_VAR(visual, clip.m_left, v); } + void setClipRight(Length v) { SET_VAR(visual, clip.m_right, v); } + void setClipTop(Length v) { SET_VAR(visual, clip.m_top, v); } + void setClipBottom(Length v) { SET_VAR(visual, clip.m_bottom, v); } void setClip(Length top, Length right, Length bottom, Length left); - void setClip(LengthBox box) { SET_VAR(visual, clip, box) } + void setClip(LengthBox box) { SET_VAR(visual, clip, box); } void setUnicodeBidi(EUnicodeBidi b) { noninherited_flags._unicodeBidi = b; } @@ -1085,21 +1125,27 @@ public: #if ENABLE(TEXT_AUTOSIZING) void setTextAutosizingMultiplier(float v) { - SET_VAR(visual, m_textAutosizingMultiplier, v) + SET_VAR(visual, m_textAutosizingMultiplier, v); setFontSize(fontDescription().specifiedSize()); } #endif void setColor(const Color&); - void setTextIndent(Length v) { SET_VAR(rareInheritedData, indent, v) } + void setTextIndent(Length v) { SET_VAR(rareInheritedData, indent, v); } +#if ENABLE(CSS3_TEXT) + void setTextIndentLine(TextIndentLine v) { SET_VAR(rareInheritedData, m_textIndentLine, v); } + void setTextIndentType(TextIndentType v) { SET_VAR(rareInheritedData, m_textIndentType, v); } +#endif void setTextAlign(ETextAlign v) { inherited_flags._text_align = v; } void setTextTransform(ETextTransform v) { inherited_flags._text_transform = v; } - void addToTextDecorationsInEffect(ETextDecoration v) { inherited_flags._text_decorations |= v; } - void setTextDecorationsInEffect(ETextDecoration v) { inherited_flags._text_decorations = v; } - void setTextDecoration(ETextDecoration v) { SET_VAR(visual, textDecoration, v); } + void addToTextDecorationsInEffect(TextDecoration v) { inherited_flags._text_decorations |= v; } + void setTextDecorationsInEffect(TextDecoration v) { inherited_flags._text_decorations = v; } + void setTextDecoration(TextDecoration v) { SET_VAR(visual, textDecoration, v); } #if ENABLE(CSS3_TEXT) void setTextDecorationStyle(TextDecorationStyle v) { SET_VAR(rareNonInheritedData, m_textDecorationStyle, v); } - void setTextAlignLast(ETextAlignLast v) { SET_VAR(rareInheritedData, m_textAlignLast, v) } + void setTextAlignLast(TextAlignLast v) { SET_VAR(rareInheritedData, m_textAlignLast, v); } + void setTextJustify(TextJustify v) { SET_VAR(rareInheritedData, m_textJustify, v); } + void setTextUnderlinePosition(TextUnderlinePosition v) { SET_VAR(rareInheritedData, m_textUnderlinePosition, v); } #endif // CSS3_TEXT void setDirection(TextDirection v) { inherited_flags._direction = v; } void setLineHeight(Length specifiedLineHeight); @@ -1108,15 +1154,15 @@ public: bool setEffectiveZoom(float); #if ENABLE(CSS_IMAGE_ORIENTATION) - void setImageOrientation(ImageOrientationEnum v) { SET_VAR(rareInheritedData, m_imageOrientation, static_cast<int>(v)) } + void setImageOrientation(ImageOrientationEnum v) { SET_VAR(rareInheritedData, m_imageOrientation, static_cast<int>(v)); } #endif - void setImageRendering(EImageRendering v) { SET_VAR(rareInheritedData, m_imageRendering, v) } + void setImageRendering(EImageRendering v) { SET_VAR(rareInheritedData, m_imageRendering, v); } #if ENABLE(CSS_IMAGE_RESOLUTION) - void setImageResolutionSource(ImageResolutionSource v) { SET_VAR(rareInheritedData, m_imageResolutionSource, v) } - void setImageResolutionSnap(ImageResolutionSnap v) { SET_VAR(rareInheritedData, m_imageResolutionSnap, v) } - void setImageResolution(float f) { SET_VAR(rareInheritedData, m_imageResolution, f) } + void setImageResolutionSource(ImageResolutionSource v) { SET_VAR(rareInheritedData, m_imageResolutionSource, v); } + void setImageResolutionSnap(ImageResolutionSnap v) { SET_VAR(rareInheritedData, m_imageResolutionSnap, v); } + void setImageResolution(float f) { SET_VAR(rareInheritedData, m_imageResolution, f); } #endif void setWhiteSpace(EWhiteSpace v) { inherited_flags._white_space = v; } @@ -1148,11 +1194,11 @@ public: void setMaskImage(PassRefPtr<StyleImage> v) { rareNonInheritedData.access()->m_mask.setImage(v); } - void setMaskBoxImage(const NinePieceImage& b) { SET_VAR(rareNonInheritedData, m_maskBoxImage, b) } + void setMaskBoxImage(const NinePieceImage& b) { SET_VAR(rareNonInheritedData, m_maskBoxImage, b); } void setMaskBoxImageSource(PassRefPtr<StyleImage> v) { rareNonInheritedData.access()->m_maskBoxImage.setImage(v); } - void setMaskXPosition(Length l) { SET_VAR(rareNonInheritedData, m_mask.m_xPosition, l) } - void setMaskYPosition(Length l) { SET_VAR(rareNonInheritedData, m_mask.m_yPosition, l) } - void setMaskSize(LengthSize l) { SET_VAR(rareNonInheritedData, m_mask.m_sizeLength, l) } + void setMaskXPosition(Length length) { SET_VAR(rareNonInheritedData, m_mask.m_xPosition, length); } + void setMaskYPosition(Length length) { SET_VAR(rareNonInheritedData, m_mask.m_yPosition, length); } + void setMaskSize(LengthSize s) { SET_VAR(rareNonInheritedData, m_mask.m_sizeLength, s); } void setBorderCollapse(EBorderCollapse collapse) { inherited_flags._border_collapse = collapse; } void setHorizontalBorderSpacing(short); @@ -1168,26 +1214,30 @@ public: void setListStyleImage(PassRefPtr<StyleImage>); void setListStylePosition(EListStylePosition v) { inherited_flags._list_style_position = v; } - void resetMargin() { SET_VAR(surround, margin, LengthBox(Fixed)) } - void setMarginTop(Length v) { SET_VAR(surround, margin.m_top, v) } - void setMarginBottom(Length v) { SET_VAR(surround, margin.m_bottom, v) } - void setMarginLeft(Length v) { SET_VAR(surround, margin.m_left, v) } - void setMarginRight(Length v) { SET_VAR(surround, margin.m_right, v) } + void resetMargin() { SET_VAR(surround, margin, LengthBox(Fixed)); } + void setMarginTop(Length v) { SET_VAR(surround, margin.m_top, v); } + void setMarginBottom(Length v) { SET_VAR(surround, margin.m_bottom, v); } + void setMarginLeft(Length v) { SET_VAR(surround, margin.m_left, v); } + void setMarginRight(Length v) { SET_VAR(surround, margin.m_right, v); } void setMarginStart(Length); void setMarginEnd(Length); - void resetPadding() { SET_VAR(surround, padding, LengthBox(Auto)) } - void setPaddingBox(const LengthBox& b) { SET_VAR(surround, padding, b) } - void setPaddingTop(Length v) { SET_VAR(surround, padding.m_top, v) } - void setPaddingBottom(Length v) { SET_VAR(surround, padding.m_bottom, v) } - void setPaddingLeft(Length v) { SET_VAR(surround, padding.m_left, v) } - void setPaddingRight(Length v) { SET_VAR(surround, padding.m_right, v) } + void resetPadding() { SET_VAR(surround, padding, LengthBox(Auto)); } + void setPaddingBox(const LengthBox& b) { SET_VAR(surround, padding, b); } + void setPaddingTop(Length v) { SET_VAR(surround, padding.m_top, v); } + void setPaddingBottom(Length v) { SET_VAR(surround, padding.m_bottom, v); } + void setPaddingLeft(Length v) { SET_VAR(surround, padding.m_left, v); } + void setPaddingRight(Length v) { SET_VAR(surround, padding.m_right, v); } void setCursor(ECursor c) { inherited_flags._cursor_style = c; } void addCursor(PassRefPtr<StyleImage>, const IntPoint& hotSpot = IntPoint()); void setCursorList(PassRefPtr<CursorList>); void clearCursorList(); +#if ENABLE(CURSOR_VISIBILITY) + void setCursorVisibility(CursorVisibility c) { inherited_flags.m_cursorVisibility = c; } +#endif + void setInsideLink(EInsideLink insideLink) { inherited_flags._insideLink = insideLink; } void setIsLink(bool b) { noninherited_flags.setIsLink(b); } @@ -1195,24 +1245,28 @@ public: void setPrintColorAdjust(PrintColorAdjust value) { inherited_flags.m_printColorAdjust = value; } bool hasAutoZIndex() const { return m_box->hasAutoZIndex(); } - void setHasAutoZIndex() { SET_VAR(m_box, m_hasAutoZIndex, true); SET_VAR(m_box, m_zIndex, 0) } + void setHasAutoZIndex() { SET_VAR(m_box, m_hasAutoZIndex, true); SET_VAR(m_box, m_zIndex, 0); } int zIndex() const { return m_box->zIndex(); } - void setZIndex(int v) { SET_VAR(m_box, m_hasAutoZIndex, false); SET_VAR(m_box, m_zIndex, v) } + void setZIndex(int v) { SET_VAR(m_box, m_hasAutoZIndex, false); SET_VAR(m_box, m_zIndex, v); } + + void setHasAutoWidows() { SET_VAR(rareInheritedData, m_hasAutoWidows, true); SET_VAR(rareInheritedData, widows, initialWidows()); } + void setWidows(short w) { SET_VAR(rareInheritedData, m_hasAutoWidows, false); SET_VAR(rareInheritedData, widows, w); } + + void setHasAutoOrphans() { SET_VAR(rareInheritedData, m_hasAutoOrphans, true); SET_VAR(rareInheritedData, orphans, initialOrphans()); } + void setOrphans(short o) { SET_VAR(rareInheritedData, m_hasAutoOrphans, false); SET_VAR(rareInheritedData, orphans, o); } - void setWidows(short w) { SET_VAR(rareInheritedData, widows, w); } - void setOrphans(short o) { SET_VAR(rareInheritedData, orphans, o); } // For valid values of page-break-inside see http://www.w3.org/TR/CSS21/page.html#page-break-props void setPageBreakInside(EPageBreak b) { ASSERT(b == PBAUTO || b == PBAVOID); noninherited_flags._page_break_inside = b; } void setPageBreakBefore(EPageBreak b) { noninherited_flags._page_break_before = b; } void setPageBreakAfter(EPageBreak b) { noninherited_flags._page_break_after = b; } // CSS3 Setters - void setOutlineOffset(int v) { SET_VAR(m_background, m_outline.m_offset, v) } + void setOutlineOffset(int v) { SET_VAR(m_background, m_outline.m_offset, v); } void setTextShadow(PassOwnPtr<ShadowData>, bool add = false); - void setTextStrokeColor(const Color& c) { SET_VAR(rareInheritedData, textStrokeColor, c) } - void setTextStrokeWidth(float w) { SET_VAR(rareInheritedData, textStrokeWidth, w) } - void setTextFillColor(const Color& c) { SET_VAR(rareInheritedData, textFillColor, c) } - void setColorSpace(ColorSpace space) { SET_VAR(rareInheritedData, colorSpace, space) } + void setTextStrokeColor(const Color& c) { SET_VAR(rareInheritedData, textStrokeColor, c); } + void setTextStrokeWidth(float w) { SET_VAR(rareInheritedData, textStrokeWidth, w); } + void setTextFillColor(const Color& c) { SET_VAR(rareInheritedData, textFillColor, c); } + void setColorSpace(ColorSpace space) { SET_VAR(rareInheritedData, colorSpace, space); } void setOpacity(float f) { float v = clampTo<float>(f, 0, 1); SET_VAR(rareNonInheritedData, opacity, v); } void setAppearance(ControlPart a) { SET_VAR(rareNonInheritedData, m_appearance, a); } // For valid values of box-align see http://www.w3.org/TR/2009/WD-css3-flexbox-20090723/#alignment @@ -1240,12 +1294,17 @@ public: void setFlexDirection(EFlexDirection direction) { SET_VAR(rareNonInheritedData.access()->m_flexibleBox, m_flexDirection, direction); } void setFlexWrap(EFlexWrap w) { SET_VAR(rareNonInheritedData.access()->m_flexibleBox, m_flexWrap, w); } void setJustifyContent(EJustifyContent p) { SET_VAR(rareNonInheritedData, m_justifyContent, p); } + void setGridAutoColumns(const GridTrackSize& length) { SET_VAR(rareNonInheritedData.access()->m_grid, m_gridAutoColumns, length); } + void setGridAutoRows(const GridTrackSize& length) { SET_VAR(rareNonInheritedData.access()->m_grid, m_gridAutoRows, length); } void setGridColumns(const Vector<GridTrackSize>& lengths) { SET_VAR(rareNonInheritedData.access()->m_grid, m_gridColumns, lengths); } void setGridRows(const Vector<GridTrackSize>& lengths) { SET_VAR(rareNonInheritedData.access()->m_grid, m_gridRows, lengths); } - void setGridItemColumn(const GridPosition& columnPosition) { SET_VAR(rareNonInheritedData.access()->m_gridItem, m_gridColumn, columnPosition); } - void setGridItemRow(const GridPosition& rowPosition) { SET_VAR(rareNonInheritedData.access()->m_gridItem, m_gridRow, rowPosition); } + void setGridAutoFlow(GridAutoFlow flow) { SET_VAR(rareNonInheritedData.access()->m_grid, m_gridAutoFlow, flow); } + void setGridItemStart(const GridPosition& startPosition) { SET_VAR(rareNonInheritedData.access()->m_gridItem, m_gridStart, startPosition); } + void setGridItemEnd(const GridPosition& endPosition) { SET_VAR(rareNonInheritedData.access()->m_gridItem, m_gridEnd, endPosition); } + void setGridItemBefore(const GridPosition& beforePosition) { SET_VAR(rareNonInheritedData.access()->m_gridItem, m_gridBefore, beforePosition); } + void setGridItemAfter(const GridPosition& afterPosition) { SET_VAR(rareNonInheritedData.access()->m_gridItem, m_gridAfter, afterPosition); } - void setMarqueeIncrement(const Length& f) { SET_VAR(rareNonInheritedData.access()->m_marquee, increment, f); } + void setMarqueeIncrement(Length f) { SET_VAR(rareNonInheritedData.access()->m_marquee, increment, f); } void setMarqueeSpeed(int f) { SET_VAR(rareNonInheritedData.access()->m_marquee, speed, f); } void setMarqueeDirection(EMarqueeDirection d) { SET_VAR(rareNonInheritedData.access()->m_marquee, direction, d); } void setMarqueeBehavior(EMarqueeBehavior b) { SET_VAR(rareNonInheritedData.access()->m_marquee, behavior, b); } @@ -1280,7 +1339,7 @@ public: void setColumnRuleColor(const Color& c) { SET_BORDERVALUE_COLOR(rareNonInheritedData.access()->m_multiCol, m_rule, c); } void setColumnRuleStyle(EBorderStyle b) { SET_VAR(rareNonInheritedData.access()->m_multiCol, m_rule.m_style, b); } void setColumnRuleWidth(unsigned short w) { SET_VAR(rareNonInheritedData.access()->m_multiCol, m_rule.m_width, w); } - void resetColumnRule() { SET_VAR(rareNonInheritedData.access()->m_multiCol, m_rule, BorderValue()) } + void resetColumnRule() { SET_VAR(rareNonInheritedData.access()->m_multiCol, m_rule, BorderValue()); } void setColumnSpan(ColumnSpan columnSpan) { SET_VAR(rareNonInheritedData.access()->m_multiCol, m_columnSpan, columnSpan); } void setColumnBreakBefore(EPageBreak p) { SET_VAR(rareNonInheritedData.access()->m_multiCol, m_breakBefore, p); } // For valid values of column-break-inside see http://www.w3.org/TR/css3-multicol/#break-before-break-after-break-inside @@ -1296,11 +1355,15 @@ public: void setTransformOriginZ(float f) { SET_VAR(rareNonInheritedData.access()->m_transform, m_z, f); } void setSpeak(ESpeak s) { SET_VAR(rareInheritedData, speak, s); } void setTextCombine(TextCombine v) { SET_VAR(rareNonInheritedData, m_textCombine, v); } - void setTextEmphasisColor(const Color& c) { SET_VAR(rareInheritedData, textEmphasisColor, c) } +#if ENABLE(CSS3_TEXT) + void setTextDecorationColor(const Color& c) { SET_VAR(rareNonInheritedData, m_textDecorationColor, c); } +#endif // CSS3_TEXT + void setTextEmphasisColor(const Color& c) { SET_VAR(rareInheritedData, textEmphasisColor, c); } void setTextEmphasisFill(TextEmphasisFill fill) { SET_VAR(rareInheritedData, textEmphasisFill, fill); } void setTextEmphasisMark(TextEmphasisMark mark) { SET_VAR(rareInheritedData, textEmphasisMark, mark); } void setTextEmphasisCustomMark(const AtomicString& mark) { SET_VAR(rareInheritedData, textEmphasisCustomMark, mark); } void setTextEmphasisPosition(TextEmphasisPosition position) { SET_VAR(rareInheritedData, textEmphasisPosition, position); } + bool setTextOrientation(TextOrientation); void setRubyPosition(RubyPosition position) { SET_VAR(rareInheritedData, m_rubyPosition, position); } @@ -1318,7 +1381,7 @@ public: void setFlowThread(const AtomicString& flowThread) { SET_VAR(rareNonInheritedData, m_flowThread, flowThread); } void setRegionThread(const AtomicString& regionThread) { SET_VAR(rareNonInheritedData, m_regionThread, regionThread); } - void setRegionOverflow(RegionOverflow regionOverflow) { SET_VAR(rareNonInheritedData, m_regionOverflow, regionOverflow); } + void setRegionFragment(RegionFragment regionFragment) { SET_VAR(rareNonInheritedData, m_regionFragment, regionFragment); } void setWrapFlow(WrapFlow wrapFlow) { SET_VAR(rareNonInheritedData, m_wrapFlow, wrapFlow); } void setWrapThrough(WrapThrough wrapThrough) { SET_VAR(rareNonInheritedData, m_wrapThrough, wrapThrough); } @@ -1362,7 +1425,6 @@ public: #if ENABLE(ACCELERATED_OVERFLOW_SCROLLING) void setUseTouchOverflowScrolling(bool v) { SET_VAR(rareInheritedData, useTouchOverflowScrolling, v); } #endif - bool setTextSizeAdjust(bool); void setTextSecurity(ETextSecurity aTextSecurity) { SET_VAR(rareInheritedData, textSecurity, aTextSecurity); } #if ENABLE(SVG) @@ -1403,24 +1465,31 @@ public: void setKerning(SVGLength k) { accessSVGStyle()->setKerning(k); } #endif - void setShapeInside(PassRefPtr<ExclusionShapeValue> value) + void setShapeInside(PassRefPtr<ShapeValue> value) { if (rareNonInheritedData->m_shapeInside == value) return; rareNonInheritedData.access()->m_shapeInside = value; } - ExclusionShapeValue* shapeInside() const { return rareNonInheritedData->m_shapeInside.get(); } + ShapeValue* shapeInside() const { return rareNonInheritedData->m_shapeInside.get(); } + ShapeValue* resolvedShapeInside() const + { + ShapeValue* shapeInside = this->shapeInside(); + if (shapeInside && shapeInside->type() == ShapeValue::Outside) + return shapeOutside(); + return shapeInside; + } - void setShapeOutside(PassRefPtr<ExclusionShapeValue> value) + void setShapeOutside(PassRefPtr<ShapeValue> value) { if (rareNonInheritedData->m_shapeOutside == value) return; rareNonInheritedData.access()->m_shapeOutside = value; } - ExclusionShapeValue* shapeOutside() const { return rareNonInheritedData->m_shapeOutside.get(); } + ShapeValue* shapeOutside() const { return rareNonInheritedData->m_shapeOutside.get(); } - static ExclusionShapeValue* initialShapeInside() { return 0; } - static ExclusionShapeValue* initialShapeOutside() { return 0; } + static ShapeValue* initialShapeInside(); + static ShapeValue* initialShapeOutside() { return 0; } void setClipPath(PassRefPtr<ClipPathOperation> operation) { @@ -1461,6 +1530,7 @@ public: bool inheritedDataShared(const RenderStyle*) const; StyleDifference diff(const RenderStyle*, unsigned& changedContextSensitiveProperties) const; + bool diffRequiresRepaint(const RenderStyle*) const; bool isDisplayReplacedType() const { return isDisplayReplacedType(display()); } bool isDisplayInlineType() const { return isDisplayInlineType(display()); } @@ -1472,7 +1542,14 @@ public: || display() == LIST_ITEM; } - void setWritingMode(WritingMode v) { inherited_flags.m_writingMode = v; } + bool setWritingMode(WritingMode v) + { + if (v == writingMode()) + return false; + + inherited_flags.m_writingMode = v; + return true; + } // A unique style is one that has matches something that makes it impossible to share. bool unique() const { return noninherited_flags.unique; } @@ -1489,8 +1566,6 @@ public: void setHasExplicitlyInheritedProperties() { noninherited_flags.explicitInheritance = true; } bool hasExplicitlyInheritedProperties() const { return noninherited_flags.explicitInheritance; } - - void reportMemoryUsage(MemoryObjectInfo*) const; // Initial values for all the properties static EBorderCollapse initialBorderCollapse() { return BSEPARATE; } @@ -1524,6 +1599,9 @@ public: static short initialHorizontalBorderSpacing() { return 0; } static short initialVerticalBorderSpacing() { return 0; } static ECursor initialCursor() { return CURSOR_AUTO; } +#if ENABLE(CURSOR_VISIBILITY) + static CursorVisibility initialCursorVisibility() { return CursorVisibilityAuto; } +#endif static Color initialColor() { return Color::black; } static StyleImage* initialListStyleImage() { return 0; } static unsigned initialBorderWidth() { return 3; } @@ -1531,21 +1609,27 @@ public: static unsigned short initialOutlineWidth() { return 3; } static int initialLetterWordSpacing() { return 0; } static Length initialSize() { return Length(); } - static Length initialMinSize() { return Length(); } + static Length initialMinSize() { return Length(Fixed); } static Length initialMaxSize() { return Length(Undefined); } static Length initialOffset() { return Length(); } static Length initialMargin() { return Length(Fixed); } static Length initialPadding() { return Length(Fixed); } static Length initialTextIndent() { return Length(Fixed); } +#if ENABLE(CSS3_TEXT) + static TextIndentLine initialTextIndentLine() { return TextIndentFirstLine; } + static TextIndentType initialTextIndentType() { return TextIndentNormal; } +#endif static EVerticalAlign initialVerticalAlign() { return BASELINE; } static short initialWidows() { return 2; } static short initialOrphans() { return 2; } static Length initialLineHeight() { return Length(-100.0, Percent); } static ETextAlign initialTextAlign() { return TASTART; } - static ETextDecoration initialTextDecoration() { return TDNONE; } + static TextDecoration initialTextDecoration() { return TextDecorationNone; } #if ENABLE(CSS3_TEXT) static TextDecorationStyle initialTextDecorationStyle() { return TextDecorationStyleSolid; } - static ETextAlignLast initialTextAlignLast() { return TextAlignLastAuto; } + static TextAlignLast initialTextAlignLast() { return TextAlignLastAuto; } + static TextJustify initialTextJustify() { return TextJustifyAuto; } + static TextUnderlinePosition initialTextUnderlinePosition() { return TextUnderlinePositionAuto; } #endif // CSS3_TEXT static float initialZoom() { return 1.0f; } static int initialOutlineOffset() { return 0; } @@ -1635,9 +1719,13 @@ public: static Vector<GridTrackSize> initialGridColumns() { return Vector<GridTrackSize>(); } static Vector<GridTrackSize> initialGridRows() { return Vector<GridTrackSize>(); } + static GridAutoFlow initialGridAutoFlow() { return AutoFlowNone; } + + static GridTrackSize initialGridAutoColumns() { return GridTrackSize(Auto); } + static GridTrackSize initialGridAutoRows() { return GridTrackSize(Auto); } + // 'auto' is the default. - static GridPosition initialGridItemColumn() { return GridPosition(); } - static GridPosition initialGridItemRow() { return GridPosition(); } + static GridPosition initialGridPosition() { return GridPosition(); } static unsigned initialTabSize() { return 8; } @@ -1647,14 +1735,13 @@ public: static const AtomicString& initialFlowThread() { return nullAtom; } static const AtomicString& initialRegionThread() { return nullAtom; } - static RegionOverflow initialRegionOverflow() { return AutoRegionOverflow; } + static RegionFragment initialRegionFragment() { return AutoRegionFragment; } static WrapFlow initialWrapFlow() { return WrapFlowAuto; } static WrapThrough initialWrapThrough() { return WrapThroughWrap; } // Keep these at the end. static LineClampValue initialLineClamp() { return LineClampValue(); } - static bool initialTextSizeAdjust() { return true; } static ETextSecurity initialTextSecurity() { return TSNONE; } #if ENABLE(TOUCH_EVENTS) static Color initialTapHighlightColor(); @@ -1672,18 +1759,29 @@ public: #if ENABLE(CSS_COMPOSITING) static BlendMode initialBlendMode() { return BlendModeNormal; } #endif + private: + bool changeRequiresLayout(const RenderStyle*, unsigned& changedContextSensitiveProperties) const; + bool changeRequiresPositionedLayoutOnly(const RenderStyle*, unsigned& changedContextSensitiveProperties) const; + bool changeRequiresLayerRepaint(const RenderStyle*, unsigned& changedContextSensitiveProperties) const; + bool changeRequiresRepaint(const RenderStyle*, unsigned& changedContextSensitiveProperties) const; + bool changeRequiresRepaintIfText(const RenderStyle*, unsigned& changedContextSensitiveProperties) const; + bool changeRequiresRecompositeLayer(const RenderStyle*, unsigned& changedContextSensitiveProperties) const; + void setVisitedLinkColor(const Color&); - void setVisitedLinkBackgroundColor(const Color& v) { SET_VAR(rareNonInheritedData, m_visitedLinkBackgroundColor, v) } - void setVisitedLinkBorderLeftColor(const Color& v) { SET_VAR(rareNonInheritedData, m_visitedLinkBorderLeftColor, v) } - void setVisitedLinkBorderRightColor(const Color& v) { SET_VAR(rareNonInheritedData, m_visitedLinkBorderRightColor, v) } - void setVisitedLinkBorderBottomColor(const Color& v) { SET_VAR(rareNonInheritedData, m_visitedLinkBorderBottomColor, v) } - void setVisitedLinkBorderTopColor(const Color& v) { SET_VAR(rareNonInheritedData, m_visitedLinkBorderTopColor, v) } - void setVisitedLinkOutlineColor(const Color& v) { SET_VAR(rareNonInheritedData, m_visitedLinkOutlineColor, v) } - void setVisitedLinkColumnRuleColor(const Color& v) { SET_VAR(rareNonInheritedData.access()->m_multiCol, m_visitedLinkColumnRuleColor, v) } - void setVisitedLinkTextEmphasisColor(const Color& v) { SET_VAR(rareInheritedData, visitedLinkTextEmphasisColor, v) } - void setVisitedLinkTextFillColor(const Color& v) { SET_VAR(rareInheritedData, visitedLinkTextFillColor, v) } - void setVisitedLinkTextStrokeColor(const Color& v) { SET_VAR(rareInheritedData, visitedLinkTextStrokeColor, v) } + void setVisitedLinkBackgroundColor(const Color& v) { SET_VAR(rareNonInheritedData, m_visitedLinkBackgroundColor, v); } + void setVisitedLinkBorderLeftColor(const Color& v) { SET_VAR(rareNonInheritedData, m_visitedLinkBorderLeftColor, v); } + void setVisitedLinkBorderRightColor(const Color& v) { SET_VAR(rareNonInheritedData, m_visitedLinkBorderRightColor, v); } + void setVisitedLinkBorderBottomColor(const Color& v) { SET_VAR(rareNonInheritedData, m_visitedLinkBorderBottomColor, v); } + void setVisitedLinkBorderTopColor(const Color& v) { SET_VAR(rareNonInheritedData, m_visitedLinkBorderTopColor, v); } + void setVisitedLinkOutlineColor(const Color& v) { SET_VAR(rareNonInheritedData, m_visitedLinkOutlineColor, v); } + void setVisitedLinkColumnRuleColor(const Color& v) { SET_VAR(rareNonInheritedData.access()->m_multiCol, m_visitedLinkColumnRuleColor, v); } +#if ENABLE(CSS3_TEXT) + void setVisitedLinkTextDecorationColor(const Color& v) { SET_VAR(rareNonInheritedData, m_visitedLinkTextDecorationColor, v); } +#endif // CSS3_TEXT + void setVisitedLinkTextEmphasisColor(const Color& v) { SET_VAR(rareInheritedData, visitedLinkTextEmphasisColor, v); } + void setVisitedLinkTextFillColor(const Color& v) { SET_VAR(rareInheritedData, visitedLinkTextFillColor, v); } + void setVisitedLinkTextStrokeColor(const Color& v) { SET_VAR(rareInheritedData, visitedLinkTextStrokeColor, v); } void inheritUnicodeBidiFrom(const RenderStyle* parent) { noninherited_flags._unicodeBidi = parent->noninherited_flags._unicodeBidi; } void getShadowExtent(const ShadowData*, LayoutUnit& top, LayoutUnit& right, LayoutUnit& bottom, LayoutUnit& left) const; @@ -1731,6 +1829,10 @@ private: Color visitedLinkBorderTopColor() const { return rareNonInheritedData->m_visitedLinkBorderTopColor; } Color visitedLinkOutlineColor() const { return rareNonInheritedData->m_visitedLinkOutlineColor; } Color visitedLinkColumnRuleColor() const { return rareNonInheritedData->m_multiCol->m_visitedLinkColumnRuleColor; } +#if ENABLE(CSS3_TEXT) + Color textDecorationColor() const { return rareNonInheritedData->m_textDecorationColor; } + Color visitedLinkTextDecorationColor() const { return rareNonInheritedData->m_visitedLinkTextDecorationColor; } +#endif // CSS3_TEXT Color visitedLinkTextEmphasisColor() const { return rareInheritedData->visitedLinkTextEmphasisColor; } Color visitedLinkTextFillColor() const { return rareInheritedData->visitedLinkTextFillColor; } Color visitedLinkTextStrokeColor() const { return rareInheritedData->visitedLinkTextStrokeColor; } @@ -1767,6 +1869,13 @@ inline float adjustFloatForAbsoluteZoom(float value, const RenderStyle* style) return value / style->effectiveZoom(); } +#if ENABLE(SUBPIXEL_LAYOUT) +inline LayoutUnit adjustLayoutUnitForAbsoluteZoom(LayoutUnit value, const RenderStyle* style) +{ + return value / style->effectiveZoom(); +} +#endif + inline bool RenderStyle::setZoom(float f) { if (compareEqual(visual->m_zoom, f)) @@ -1784,11 +1893,12 @@ inline bool RenderStyle::setEffectiveZoom(float f) return true; } -inline bool RenderStyle::setTextSizeAdjust(bool b) +inline bool RenderStyle::setTextOrientation(TextOrientation textOrientation) { - if (compareEqual(rareInheritedData->textSizeAdjust, b)) + if (compareEqual(rareInheritedData->m_textOrientation, textOrientation)) return false; - rareInheritedData.access()->textSizeAdjust = b; + + rareInheritedData.access()->m_textOrientation = textOrientation; return true; } diff --git a/Source/WebCore/rendering/style/RenderStyleConstants.h b/Source/WebCore/rendering/style/RenderStyleConstants.h index 341813299..830612e57 100644 --- a/Source/WebCore/rendering/style/RenderStyleConstants.h +++ b/Source/WebCore/rendering/style/RenderStyleConstants.h @@ -35,20 +35,22 @@ enum PrintColorAdjust { }; // The difference between two styles. The following values are used: -// (1) StyleDifferenceEqual - The two styles are identical -// (2) StyleDifferenceRecompositeLayer - The layer needs its position and transform updated, but no repaint -// (3) StyleDifferenceRepaint - The object just needs to be repainted. -// (4) StyleDifferenceRepaintLayer - The layer and its descendant layers needs to be repainted. -// (5) StyleDifferenceLayoutPositionedMovementOnly - Only the position of this positioned object has been updated -// (6) StyleDifferenceSimplifiedLayout - Only overflow needs to be recomputed -// (7) StyleDifferenceSimplifiedLayoutAndPositionedMovement - Both positioned movement and simplified layout updates are required. -// (8) StyleDifferenceLayout - A full layout is required. +// - StyleDifferenceEqual - The two styles are identical +// - StyleDifferenceRecompositeLayer - The layer needs its position and transform updated, but no repaint +// - StyleDifferenceRepaint - The object just needs to be repainted. +// - StyleDifferenceRepaintIfText - The object needs to be repainted if it contains text. +// - StyleDifferenceRepaintLayer - The layer and its descendant layers needs to be repainted. +// - StyleDifferenceLayoutPositionedMovementOnly - Only the position of this positioned object has been updated +// - StyleDifferenceSimplifiedLayout - Only overflow needs to be recomputed +// - StyleDifferenceSimplifiedLayoutAndPositionedMovement - Both positioned movement and simplified layout updates are required. +// - StyleDifferenceLayout - A full layout is required. enum StyleDifference { StyleDifferenceEqual, #if USE(ACCELERATED_COMPOSITING) StyleDifferenceRecompositeLayer, #endif StyleDifferenceRepaint, + StyleDifferenceRepaintIfText, StyleDifferenceRepaintLayer, StyleDifferenceLayoutPositionedMovementOnly, StyleDifferenceSimplifiedLayout, @@ -94,7 +96,12 @@ enum EBorderPrecedence { BOFF, BTABLE, BCOLGROUP, BCOL, BROWGROUP, BROW, BCELL } enum OutlineIsAuto { AUTO_OFF = 0, AUTO_ON }; enum EPosition { - StaticPosition, RelativePosition, AbsolutePosition, FixedPosition, StickyPosition + StaticPosition = 0, + RelativePosition = 1, + AbsolutePosition = 2, + StickyPosition = 3, + // This value is required to pack our bits efficiently in RenderObject. + FixedPosition = 6 }; enum EFloat { @@ -153,6 +160,9 @@ enum EFillLayerType { // CSS3 Background Values enum EFillSizeType { Contain, Cover, SizeLength, SizeNone }; +// CSS3 Background Position +enum BackgroundEdgeOrigin { TopEdge, RightEdge, BottomEdge, LeftEdge }; + // CSS3 Marquee Properties enum EMarqueeBehavior { MNONE, MSCROLL, MSLIDE, MALTERNATE }; @@ -329,12 +339,12 @@ enum ETextTransform { CAPITALIZE, UPPERCASE, LOWERCASE, TTNONE }; -static const size_t ETextDecorationBits = 4; -enum ETextDecoration { - TDNONE = 0x0 , UNDERLINE = 0x1, OVERLINE = 0x2, LINE_THROUGH= 0x4, BLINK = 0x8 +static const size_t TextDecorationBits = 4; +enum TextDecoration { + TextDecorationNone = 0x0, TextDecorationUnderline = 0x1, TextDecorationOverline = 0x2, TextDecorationLineThrough = 0x4, TextDecorationBlink = 0x8 }; -inline ETextDecoration operator|(ETextDecoration a, ETextDecoration b) { return ETextDecoration(int(a) | int(b)); } -inline ETextDecoration& operator|=(ETextDecoration& a, ETextDecoration b) { return a = a | b; } +inline TextDecoration operator| (TextDecoration a, TextDecoration b) { return TextDecoration(int(a) | int(b)); } +inline TextDecoration& operator|= (TextDecoration& a, TextDecoration b) { return a = a | b; } enum TextDecorationStyle { TextDecorationStyleSolid, @@ -347,9 +357,18 @@ enum TextDecorationStyle { }; #if ENABLE(CSS3_TEXT) -enum ETextAlignLast { +enum TextAlignLast { TextAlignLastAuto, TextAlignLastStart, TextAlignLastEnd, TextAlignLastLeft, TextAlignLastRight, TextAlignLastCenter, TextAlignLastJustify }; + +enum TextUnderlinePosition { + // FIXME: Implement support for 'under left' and 'under right' values. + TextUnderlinePositionAuto = 0x1, TextUnderlinePositionAlphabetic = 0x2, TextUnderlinePositionUnder = 0x4 +}; + +enum TextJustify { + TextJustifyAuto, TextJustifyNone, TextJustifyInterWord, TextJustifyInterIdeograph, TextJustifyInterCluster, TextJustifyDistribute, TextJustifyKashida +}; #endif // CSS3_TEXT enum EPageBreak { @@ -410,6 +429,13 @@ enum ECursor { CURSOR_NONE }; +#if ENABLE(CURSOR_VISIBILITY) +enum CursorVisibility { + CursorVisibilityAuto, + CursorVisibilityAutoHide, +}; +#endif + // The order of this enum must match the order of the display values in CSSValueKeywords.in. enum EDisplay { INLINE, BLOCK, LIST_ITEM, RUN_IN, COMPACT, INLINE_BLOCK, @@ -451,9 +477,11 @@ enum TextEmphasisMark { TextEmphasisMarkNone, TextEmphasisMarkAuto, TextEmphasis enum TextEmphasisPosition { TextEmphasisPositionOver, TextEmphasisPositionUnder }; +enum TextOrientation { TextOrientationVerticalRight, TextOrientationUpright, TextOrientationSideways, TextOrientationSidewaysRight }; + enum TextOverflow { TextOverflowClip = 0, TextOverflowEllipsis }; -enum EImageRendering { ImageRenderingAuto, ImageRenderingOptimizeSpeed, ImageRenderingOptimizeQuality, ImageRenderingOptimizeContrast }; +enum EImageRendering { ImageRenderingAuto = 0, ImageRenderingOptimizeSpeed, ImageRenderingOptimizeQuality, ImageRenderingCrispEdges }; enum ImageResolutionSource { ImageResolutionSpecified = 0, ImageResolutionFromImage }; @@ -461,7 +489,7 @@ enum ImageResolutionSnap { ImageResolutionNoSnap = 0, ImageResolutionSnapPixels enum Order { LogicalOrder = 0, VisualOrder }; -enum RegionOverflow { AutoRegionOverflow, BreakRegionOverflow }; +enum RegionFragment { AutoRegionFragment, BreakRegionFragment }; enum ColumnAxis { HorizontalColumnAxis, VerticalColumnAxis, AutoColumnAxis }; @@ -477,10 +505,20 @@ enum WrapThrough { WrapThroughWrap, WrapThroughNone }; enum RubyPosition { RubyPositionBefore, RubyPositionAfter }; +enum GridAutoFlow { AutoFlowNone, AutoFlowColumn, AutoFlowRow }; + #if ENABLE(DRAGGABLE_REGION) enum DraggableRegionMode { DraggableRegionNone, DraggableRegionDrag, DraggableRegionNoDrag }; #endif +// Reasonable maximum to prevent insane font sizes from causing crashes on some platforms (such as Windows). +static const float maximumAllowedFontSize = 1000000.0f; + +#if ENABLE(CSS3_TEXT) +enum TextIndentLine { TextIndentFirstLine, TextIndentEachLine }; +enum TextIndentType { TextIndentNormal, TextIndentHanging }; +#endif + } // namespace WebCore #endif // RenderStyleConstants_h diff --git a/Source/WebCore/rendering/style/SVGRenderStyle.cpp b/Source/WebCore/rendering/style/SVGRenderStyle.cpp index d74019d1c..cbd16cd42 100644 --- a/Source/WebCore/rendering/style/SVGRenderStyle.cpp +++ b/Source/WebCore/rendering/style/SVGRenderStyle.cpp @@ -223,6 +223,9 @@ StyleDifference SVGRenderStyle::diff(const SVGRenderStyle* other) const if (svg_noninherited_flags.f._vectorEffect != other->svg_noninherited_flags.f._vectorEffect) return StyleDifferenceRepaint; + if (svg_noninherited_flags.f.bufferedRendering != other->svg_noninherited_flags.f.bufferedRendering) + return StyleDifferenceRepaint; + if (svg_noninherited_flags.f.maskType != other->svg_noninherited_flags.f.maskType) return StyleDifferenceRepaint; diff --git a/Source/WebCore/rendering/style/SVGRenderStyle.h b/Source/WebCore/rendering/style/SVGRenderStyle.h index be0a479bc..4a224b76e 100644 --- a/Source/WebCore/rendering/style/SVGRenderStyle.h +++ b/Source/WebCore/rendering/style/SVGRenderStyle.h @@ -26,6 +26,7 @@ #if ENABLE(SVG) #include "CSSValueList.h" #include "DataRef.h" +#include "ExceptionCodePlaceholder.h" #include "GraphicsTypes.h" #include "Path.h" #include "RenderStyleConstants.h" @@ -58,6 +59,7 @@ public: static EDominantBaseline initialDominantBaseline() { return DB_AUTO; } static EBaselineShift initialBaselineShift() { return BS_BASELINE; } static EVectorEffect initialVectorEffect() { return VE_NONE; } + static EBufferedRendering initialBufferedRendering() { return BR_AUTO; } static LineCap initialCapStyle() { return ButtCap; } static WindRule initialClipRule() { return RULE_NONZERO; } static EColorInterpolation initialColorInterpolation() { return CI_SRGB; } @@ -97,36 +99,28 @@ public: static SVGLength initialBaselineShiftValue() { SVGLength length; - ExceptionCode ec = 0; - length.newValueSpecifiedUnits(LengthTypeNumber, 0, ec); - ASSERT(!ec); + length.newValueSpecifiedUnits(LengthTypeNumber, 0, ASSERT_NO_EXCEPTION); return length; } static SVGLength initialKerning() { SVGLength length; - ExceptionCode ec = 0; - length.newValueSpecifiedUnits(LengthTypeNumber, 0, ec); - ASSERT(!ec); + length.newValueSpecifiedUnits(LengthTypeNumber, 0, ASSERT_NO_EXCEPTION); return length; } static SVGLength initialStrokeDashOffset() { SVGLength length; - ExceptionCode ec = 0; - length.newValueSpecifiedUnits(LengthTypeNumber, 0, ec); - ASSERT(!ec); + length.newValueSpecifiedUnits(LengthTypeNumber, 0, ASSERT_NO_EXCEPTION); return length; } static SVGLength initialStrokeWidth() { SVGLength length; - ExceptionCode ec = 0; - length.newValueSpecifiedUnits(LengthTypeNumber, 1, ec); - ASSERT(!ec); + length.newValueSpecifiedUnits(LengthTypeNumber, 1, ASSERT_NO_EXCEPTION); return length; } @@ -135,6 +129,7 @@ public: void setDominantBaseline(EDominantBaseline val) { svg_noninherited_flags.f._dominantBaseline = val; } void setBaselineShift(EBaselineShift val) { svg_noninherited_flags.f._baselineShift = val; } void setVectorEffect(EVectorEffect val) { svg_noninherited_flags.f._vectorEffect = val; } + void setBufferedRendering(EBufferedRendering val) { svg_noninherited_flags.f.bufferedRendering = val; } void setCapStyle(LineCap val) { svg_inherited_flags._capStyle = val; } void setClipRule(WindRule val) { svg_inherited_flags._clipRule = val; } void setColorInterpolation(EColorInterpolation val) { svg_inherited_flags._colorInterpolation = val; } @@ -312,6 +307,7 @@ public: EDominantBaseline dominantBaseline() const { return (EDominantBaseline) svg_noninherited_flags.f._dominantBaseline; } EBaselineShift baselineShift() const { return (EBaselineShift) svg_noninherited_flags.f._baselineShift; } EVectorEffect vectorEffect() const { return (EVectorEffect) svg_noninherited_flags.f._vectorEffect; } + EBufferedRendering bufferedRendering() const { return (EBufferedRendering) svg_noninherited_flags.f.bufferedRendering; } LineCap capStyle() const { return (LineCap) svg_inherited_flags._capStyle; } WindRule clipRule() const { return (WindRule) svg_inherited_flags._clipRule; } EColorInterpolation colorInterpolation() const { return (EColorInterpolation) svg_inherited_flags._colorInterpolation; } @@ -419,8 +415,9 @@ protected: unsigned _dominantBaseline : 4; // EDominantBaseline unsigned _baselineShift : 2; // EBaselineShift unsigned _vectorEffect: 1; // EVectorEffect + unsigned bufferedRendering: 2; // EBufferedRendering unsigned maskType: 1; // EMaskType - // 20 bits unused + // 18 bits unused } f; uint32_t _niflags; }; @@ -465,6 +462,7 @@ private: svg_noninherited_flags.f._dominantBaseline = initialDominantBaseline(); svg_noninherited_flags.f._baselineShift = initialBaselineShift(); svg_noninherited_flags.f._vectorEffect = initialVectorEffect(); + svg_noninherited_flags.f.bufferedRendering = initialBufferedRendering(); svg_noninherited_flags.f.maskType = initialMaskType(); } }; diff --git a/Source/WebCore/rendering/style/SVGRenderStyleDefs.h b/Source/WebCore/rendering/style/SVGRenderStyleDefs.h index e7308ca99..b4c066805 100644 --- a/Source/WebCore/rendering/style/SVGRenderStyleDefs.h +++ b/Source/WebCore/rendering/style/SVGRenderStyleDefs.h @@ -83,6 +83,12 @@ namespace WebCore { VE_NON_SCALING_STROKE }; + enum EBufferedRendering { + BR_AUTO, + BR_DYNAMIC, + BR_STATIC + }; + enum EMaskType { MT_LUMINANCE, MT_ALPHA diff --git a/Source/WebCore/rendering/style/ShadowData.cpp b/Source/WebCore/rendering/style/ShadowData.cpp index 381b59033..edd5b3755 100644 --- a/Source/WebCore/rendering/style/ShadowData.cpp +++ b/Source/WebCore/rendering/style/ShadowData.cpp @@ -30,7 +30,7 @@ namespace WebCore { ShadowData::ShadowData(const ShadowData& o) : m_location(o.m_location) - , m_blur(o.m_blur) + , m_radius(o.m_radius) , m_spread(o.m_spread) , m_color(o.m_color) , m_style(o.m_style) @@ -46,7 +46,7 @@ bool ShadowData::operator==(const ShadowData& o) const return false; return m_location == o.m_location - && m_blur == o.m_blur + && m_radius == o.m_radius && m_spread == o.m_spread && m_style == o.m_style && m_color == o.m_color @@ -56,12 +56,12 @@ bool ShadowData::operator==(const ShadowData& o) const static inline void calculateShadowExtent(const ShadowData* shadow, int additionalOutlineSize, int& shadowLeft, int& shadowRight, int& shadowTop, int& shadowBottom) { do { - int blurAndSpread = shadow->blur() + shadow->spread() + additionalOutlineSize; + int extentAndSpread = shadow->paintingExtent() + shadow->spread() + additionalOutlineSize; if (shadow->style() == Normal) { - shadowLeft = min(shadow->x() - blurAndSpread, shadowLeft); - shadowRight = max(shadow->x() + blurAndSpread, shadowRight); - shadowTop = min(shadow->y() - blurAndSpread, shadowTop); - shadowBottom = max(shadow->y() + blurAndSpread, shadowBottom); + shadowLeft = min(shadow->x() - extentAndSpread, shadowLeft); + shadowRight = max(shadow->x() + extentAndSpread, shadowRight); + shadowTop = min(shadow->y() - extentAndSpread, shadowTop); + shadowBottom = max(shadow->y() + extentAndSpread, shadowBottom); } shadow = shadow->next(); diff --git a/Source/WebCore/rendering/style/ShadowData.h b/Source/WebCore/rendering/style/ShadowData.h index 77d71e997..19be48d6c 100644 --- a/Source/WebCore/rendering/style/ShadowData.h +++ b/Source/WebCore/rendering/style/ShadowData.h @@ -41,16 +41,16 @@ class ShadowData { WTF_MAKE_FAST_ALLOCATED; public: ShadowData() - : m_blur(0) + : m_radius(0) , m_spread(0) , m_style(Normal) , m_isWebkitBoxShadow(false) { } - ShadowData(const IntPoint& location, int blur, int spread, ShadowStyle style, bool isWebkitBoxShadow, const Color& color) + ShadowData(const IntPoint& location, int radius, int spread, ShadowStyle style, bool isWebkitBoxShadow, const Color& color) : m_location(location) - , m_blur(blur) + , m_radius(radius) , m_spread(spread) , m_color(color) , m_style(style) @@ -69,7 +69,15 @@ public: int x() const { return m_location.x(); } int y() const { return m_location.y(); } IntPoint location() const { return m_location; } - int blur() const { return m_blur; } + int radius() const { return m_radius; } + int paintingExtent() const + { + // Blurring uses a Gaussian function whose std. deviation is m_radius/2, and which in theory + // extends to infinity. In 8-bit contexts, however, rounding causes the effect to become + // undetectable at around 1.4x the radius. + const float radiusExtentMultiplier = 1.4; + return ceilf(m_radius * radiusExtentMultiplier); + } int spread() const { return m_spread; } ShadowStyle style() const { return m_style; } const Color& color() const { return m_color; } @@ -83,7 +91,7 @@ public: private: IntPoint m_location; - int m_blur; + int m_radius; // This is the "blur radius", or twice the standard deviation of the Gaussian blur. int m_spread; Color m_color; ShadowStyle m_style; diff --git a/Source/WebCore/rendering/style/ExclusionShapeValue.h b/Source/WebCore/rendering/style/ShapeValue.h index 5a533af8f..674aeeb29 100644 --- a/Source/WebCore/rendering/style/ExclusionShapeValue.h +++ b/Source/WebCore/rendering/style/ShapeValue.h @@ -27,41 +27,67 @@ * SUCH DAMAGE. */ -#ifndef ExclusionShapeValue_h -#define ExclusionShapeValue_h +#ifndef ShapeValue_h +#define ShapeValue_h #include "BasicShapes.h" +#include "StyleImage.h" #include <wtf/PassRefPtr.h> namespace WebCore { -class ExclusionShapeValue : public RefCounted<ExclusionShapeValue> { +class ShapeValue : public RefCounted<ShapeValue> { public: - enum ExclusionShapeValueType { - // The AUTO value is defined by a null ExclusionShapeValue* - SHAPE, - OUTSIDE + enum ShapeValueType { + // The Auto value is defined by a null ShapeValue* + Shape, + Outside, + Image }; - static PassRefPtr<ExclusionShapeValue> createShapeValue(PassRefPtr<BasicShape> shape) + static PassRefPtr<ShapeValue> createShapeValue(PassRefPtr<BasicShape> shape) { - return adoptRef(new ExclusionShapeValue(shape)); + return adoptRef(new ShapeValue(shape)); } - static PassRefPtr<ExclusionShapeValue> createOutsideValue() + static PassRefPtr<ShapeValue> createOutsideValue() { - return adoptRef(new ExclusionShapeValue(OUTSIDE)); + return adoptRef(new ShapeValue(Outside)); } - ExclusionShapeValueType type() const { return m_type; } + static PassRefPtr<ShapeValue> createImageValue(PassRefPtr<StyleImage> image) + { + return adoptRef(new ShapeValue(image)); + } + + ShapeValueType type() const { return m_type; } BasicShape* shape() const { return m_shape.get(); } - bool operator==(const ExclusionShapeValue& other) const { return type() == other.type(); } + StyleImage* image() const { return m_image.get(); } + void setImage(PassRefPtr<StyleImage> image) + { + if (m_image != image) + m_image = image; + } + bool operator==(const ShapeValue& other) const { return type() == other.type(); } private: - ExclusionShapeValue(PassRefPtr<BasicShape> shape) : m_type(SHAPE), m_shape(shape) { } - ExclusionShapeValue(ExclusionShapeValueType type) : m_type(type) { } - ExclusionShapeValueType m_type; + ShapeValue(PassRefPtr<BasicShape> shape) + : m_type(Shape) + , m_shape(shape) + { + } + ShapeValue(ShapeValueType type) + : m_type(type) + { + } + ShapeValue(PassRefPtr<StyleImage> image) + : m_type(Image) + , m_image(image) + { + } + ShapeValueType m_type; RefPtr<BasicShape> m_shape; + RefPtr<StyleImage> m_image; }; } diff --git a/Source/WebCore/rendering/style/StyleBackgroundData.cpp b/Source/WebCore/rendering/style/StyleBackgroundData.cpp index 08f5527ad..9407c1283 100644 --- a/Source/WebCore/rendering/style/StyleBackgroundData.cpp +++ b/Source/WebCore/rendering/style/StyleBackgroundData.cpp @@ -46,4 +46,13 @@ bool StyleBackgroundData::operator==(const StyleBackgroundData& o) const return m_background == o.m_background && m_color == o.m_color && m_outline == o.m_outline; } +bool StyleBackgroundData::isEquivalentForPainting(const StyleBackgroundData& other) const +{ + if (m_background != other.m_background || m_color != other.m_color) + return false; + if (!m_outline.isVisible() && !other.m_outline.isVisible()) + return true; + return m_outline == other.m_outline; +} + } // namespace WebCore diff --git a/Source/WebCore/rendering/style/StyleBackgroundData.h b/Source/WebCore/rendering/style/StyleBackgroundData.h index 48a700ecc..f632ca292 100644 --- a/Source/WebCore/rendering/style/StyleBackgroundData.h +++ b/Source/WebCore/rendering/style/StyleBackgroundData.h @@ -45,6 +45,8 @@ public: return !(*this == o); } + bool isEquivalentForPainting(const StyleBackgroundData&) const; + const FillLayer& background() const { return m_background; } const Color& color() const { return m_color; } const OutlineValue& outline() const { return m_outline; } diff --git a/Source/WebCore/rendering/style/StyleCachedImage.cpp b/Source/WebCore/rendering/style/StyleCachedImage.cpp index 4269edd12..6a71286e0 100644 --- a/Source/WebCore/rendering/style/StyleCachedImage.cpp +++ b/Source/WebCore/rendering/style/StyleCachedImage.cpp @@ -106,9 +106,9 @@ PassRefPtr<Image> StyleCachedImage::image(RenderObject* renderer, const IntSize& return m_image->imageForRenderer(renderer); } -bool StyleCachedImage::hasAlpha(const RenderObject* renderer) const +bool StyleCachedImage::knownToBeOpaque(const RenderObject* renderer) const { - return m_image->currentFrameHasAlpha(renderer); + return m_image->currentFrameKnownToBeOpaque(renderer); } } diff --git a/Source/WebCore/rendering/style/StyleCachedImage.h b/Source/WebCore/rendering/style/StyleCachedImage.h index f6c4fda97..b81b43a52 100644 --- a/Source/WebCore/rendering/style/StyleCachedImage.h +++ b/Source/WebCore/rendering/style/StyleCachedImage.h @@ -42,8 +42,6 @@ public: virtual PassRefPtr<CSSValue> cssValue() const; - CachedImage* cachedImage() const { return m_image.get(); } - virtual bool canRender(const RenderObject*, float multiplier) const; virtual bool isLoaded() const; virtual bool errorOccurred() const; @@ -56,7 +54,8 @@ public: virtual void addClient(RenderObject*); virtual void removeClient(RenderObject*); virtual PassRefPtr<Image> image(RenderObject*, const IntSize&) const; - virtual bool hasAlpha(const RenderObject*) const OVERRIDE; + virtual bool knownToBeOpaque(const RenderObject*) const OVERRIDE; + virtual CachedImage* cachedImage() const OVERRIDE { return m_image.get(); } private: explicit StyleCachedImage(CachedImage*); diff --git a/Source/WebCore/rendering/style/StyleCachedImageSet.cpp b/Source/WebCore/rendering/style/StyleCachedImageSet.cpp index c9f138270..f4f2f8b30 100644 --- a/Source/WebCore/rendering/style/StyleCachedImageSet.cpp +++ b/Source/WebCore/rendering/style/StyleCachedImageSet.cpp @@ -116,9 +116,9 @@ PassRefPtr<Image> StyleCachedImageSet::image(RenderObject* renderer, const IntSi return m_bestFitImage->imageForRenderer(renderer); } -bool StyleCachedImageSet::hasAlpha(const RenderObject* renderer) const +bool StyleCachedImageSet::knownToBeOpaque(const RenderObject* renderer) const { - return m_bestFitImage->currentFrameHasAlpha(renderer); + return m_bestFitImage->currentFrameKnownToBeOpaque(renderer); } } // namespace WebCore diff --git a/Source/WebCore/rendering/style/StyleCachedImageSet.h b/Source/WebCore/rendering/style/StyleCachedImageSet.h index 9ffd4b263..21b0e0cab 100644 --- a/Source/WebCore/rendering/style/StyleCachedImageSet.h +++ b/Source/WebCore/rendering/style/StyleCachedImageSet.h @@ -56,7 +56,7 @@ public: // meaningful enough or not. virtual WrappedImagePtr data() const { return m_bestFitImage.get(); } - CachedImage* cachedImage() const { return m_bestFitImage.get(); } + void clearImageSetValue() { m_imageSetValue = 0; } virtual bool canRender(const RenderObject*, float multiplier) const; virtual bool isLoaded() const; @@ -71,7 +71,8 @@ public: virtual void removeClient(RenderObject*); virtual PassRefPtr<Image> image(RenderObject*, const IntSize&) const; virtual float imageScaleFactor() const { return m_imageScaleFactor; } - virtual bool hasAlpha(const RenderObject*) const OVERRIDE; + virtual bool knownToBeOpaque(const RenderObject*) const OVERRIDE; + virtual CachedImage* cachedImage() const OVERRIDE { return m_bestFitImage.get(); } private: StyleCachedImageSet(CachedImage*, float imageScaleFactor, CSSImageSetValue*); diff --git a/Source/WebCore/rendering/style/StyleCachedShader.h b/Source/WebCore/rendering/style/StyleCachedShader.h index c2e007756..15234295b 100644 --- a/Source/WebCore/rendering/style/StyleCachedShader.h +++ b/Source/WebCore/rendering/style/StyleCachedShader.h @@ -34,6 +34,7 @@ #include "CachedResourceHandle.h" #include "StyleShader.h" +#include <wtf/PassRefPtr.h> namespace WebCore { diff --git a/Source/WebCore/rendering/style/StyleCustomFilterProgram.cpp b/Source/WebCore/rendering/style/StyleCustomFilterProgram.cpp new file mode 100644 index 000000000..5f4012f3f --- /dev/null +++ b/Source/WebCore/rendering/style/StyleCustomFilterProgram.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2013 Adobe Systems Incorporated. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(CSS_SHADERS) + +#include "StyleCustomFilterProgram.h" + +#include "StyleCustomFilterProgramCache.h" + +namespace WebCore { + +StyleCustomFilterProgram::~StyleCustomFilterProgram() +{ + if (m_cache) + m_cache->remove(this); +} + +} // namespace WebCore + +#endif // ENABLE(CSS_SHADERS) + diff --git a/Source/WebCore/rendering/style/StyleCustomFilterProgram.h b/Source/WebCore/rendering/style/StyleCustomFilterProgram.h index ae68d954a..26ac02c9b 100644 --- a/Source/WebCore/rendering/style/StyleCustomFilterProgram.h +++ b/Source/WebCore/rendering/style/StyleCustomFilterProgram.h @@ -35,6 +35,7 @@ #include "CachedResourceHandle.h" #include "CachedShader.h" #include "CustomFilterProgram.h" +#include "KURL.h" #include "StyleShader.h" #include <wtf/FastAllocBase.h> @@ -42,18 +43,32 @@ namespace WebCore { // CSS Shaders +class StyleCustomFilterProgramCache; + class StyleCustomFilterProgram : public CustomFilterProgram, public CachedResourceClient { WTF_MAKE_FAST_ALLOCATED; public: - static PassRefPtr<StyleCustomFilterProgram> create(PassRefPtr<StyleShader> vertexShader, PassRefPtr<StyleShader> fragmentShader, CustomFilterProgramType programType, const CustomFilterProgramMixSettings& mixSettings, CustomFilterMeshType meshType) + static PassRefPtr<StyleCustomFilterProgram> create(KURL vertexShaderURL, PassRefPtr<StyleShader> vertexShader, + KURL fragmentShaderURL, PassRefPtr<StyleShader> fragmentShader, CustomFilterProgramType programType, + const CustomFilterProgramMixSettings& mixSettings, CustomFilterMeshType meshType) { - return adoptRef(new StyleCustomFilterProgram(vertexShader, fragmentShader, programType, mixSettings, meshType)); + return adoptRef(new StyleCustomFilterProgram(vertexShaderURL, vertexShader, fragmentShaderURL, fragmentShader, programType, mixSettings, meshType)); } - void setVertexShader(PassRefPtr<StyleShader> shader) { m_vertexShader = shader; } + void setVertexShader(PassRefPtr<StyleShader> shader) + { + // The shader is immutable while in the cache. + ASSERT(!m_cache); + m_vertexShader = shader; + } StyleShader* vertexShader() const { return m_vertexShader.get(); } - void setFragmentShader(PassRefPtr<StyleShader> shader) { m_fragmentShader = shader; } + void setFragmentShader(PassRefPtr<StyleShader> shader) + { + // The shader is immutable while in the cache. + ASSERT(!m_cache); + m_fragmentShader = shader; + } StyleShader* fragmentShader() const { return m_fragmentShader.get(); } virtual String vertexShaderString() const @@ -72,6 +87,9 @@ public: { // Do not use the CachedResource:isLoaded method here, because it actually means !isLoading(), // so missing and canceled resources will have isLoaded set to true, even if they are not loaded yet. + ASSERT(!m_vertexShader || m_vertexShader->isCachedShader()); + ASSERT(!m_fragmentShader || m_fragmentShader->isCachedShader()); + ASSERT(m_cachedVertexShader.get() || m_cachedFragmentShader.get()); return (!m_cachedVertexShader.get() || m_isVertexShaderLoaded) && (!m_cachedFragmentShader.get() || m_isFragmentShaderLoaded); } @@ -114,36 +132,49 @@ public: if (isLoaded()) notifyClients(); } - - CachedShader* cachedVertexShader() const { return m_vertexShader ? m_vertexShader->cachedShader() : 0; } - CachedShader* cachedFragmentShader() const { return m_fragmentShader ? m_fragmentShader->cachedShader() : 0; } - - virtual bool operator==(const CustomFilterProgram& o) const - { - // We don't use the != operator because that would recursively call this method. - if (!CustomFilterProgram::operator==(o)) - return false; - // The following cast is ugly, but StyleCustomFilterProgram is the single implementation of CustomFilterProgram. - const StyleCustomFilterProgram* other = static_cast<const StyleCustomFilterProgram*>(&o); - return cachedVertexShader() == other->cachedVertexShader() && cachedFragmentShader() == other->cachedFragmentShader(); + bool hasPendingShaders() const + { + return (m_vertexShader && m_vertexShader->isPendingShader()) + || (m_fragmentShader && m_fragmentShader->isPendingShader()); } + // StyleCustomFilterProgramCache is responsible with updating the reference to the cache. + void setCache(StyleCustomFilterProgramCache* cache) { m_cache = cache; } + bool inCache() const { return m_cache; } + + KURL vertexShaderURL() const { return m_vertexShaderURL; } + KURL fragmentShaderURL() const { return m_fragmentShaderURL; } + private: - StyleCustomFilterProgram(PassRefPtr<StyleShader> vertexShader, PassRefPtr<StyleShader> fragmentShader, CustomFilterProgramType programType, const CustomFilterProgramMixSettings& mixSettings, CustomFilterMeshType meshType) + StyleCustomFilterProgram(KURL vertexShaderURL, PassRefPtr<StyleShader> vertexShader, KURL fragmentShaderURL, PassRefPtr<StyleShader> fragmentShader, + CustomFilterProgramType programType, const CustomFilterProgramMixSettings& mixSettings, CustomFilterMeshType meshType) : CustomFilterProgram(programType, mixSettings, meshType) , m_vertexShader(vertexShader) , m_fragmentShader(fragmentShader) + , m_vertexShaderURL(vertexShaderURL) + , m_fragmentShaderURL(fragmentShaderURL) + , m_cache(0) , m_isVertexShaderLoaded(false) , m_isFragmentShaderLoaded(false) { } + + ~StyleCustomFilterProgram(); RefPtr<StyleShader> m_vertexShader; RefPtr<StyleShader> m_fragmentShader; - + CachedResourceHandle<CachedShader> m_cachedVertexShader; CachedResourceHandle<CachedShader> m_cachedFragmentShader; + + // The URLs form the key of the StyleCustomFilterProgram in the cache and are used + // to lookup the StyleCustomFilterProgram when it's removed from the cache. + KURL m_vertexShaderURL; + KURL m_fragmentShaderURL; + + // The Cache is responsible of invalidating this reference. + StyleCustomFilterProgramCache* m_cache; bool m_isVertexShaderLoaded; bool m_isFragmentShaderLoaded; diff --git a/Source/WebCore/rendering/style/StyleCustomFilterProgramCache.cpp b/Source/WebCore/rendering/style/StyleCustomFilterProgramCache.cpp new file mode 100644 index 000000000..16b579f49 --- /dev/null +++ b/Source/WebCore/rendering/style/StyleCustomFilterProgramCache.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2013 Adobe Systems Incorporated. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(CSS_SHADERS) + +#include "StyleCustomFilterProgramCache.h" + +#include "CustomFilterProgramInfo.h" +#include "StyleCustomFilterProgram.h" + +namespace WebCore { + +static CustomFilterProgramInfo programCacheKey(StyleCustomFilterProgram* program) +{ + ASSERT(program->vertexShaderURL().isValid() || program->fragmentShaderURL().isValid()); + return CustomFilterProgramInfo(program->vertexShaderURL(), program->fragmentShaderURL(), + program->programType(), program->mixSettings(), program->meshType()); +} + +StyleCustomFilterProgramCache::StyleCustomFilterProgramCache() +{ +} + +StyleCustomFilterProgramCache::~StyleCustomFilterProgramCache() +{ + // Make sure the programs are not calling back into this object. + for (CacheMap::iterator iter = m_cache.begin(), end = m_cache.end(); iter != end; ++iter) + iter->value->setCache(0); +} + +StyleCustomFilterProgram* StyleCustomFilterProgramCache::lookup(const CustomFilterProgramInfo& programInfo) const +{ + CacheMap::const_iterator iter = m_cache.find(programInfo); + return iter != m_cache.end() ? iter->value : 0; +} + +StyleCustomFilterProgram* StyleCustomFilterProgramCache::lookup(StyleCustomFilterProgram* program) const +{ + return lookup(programCacheKey(program)); +} + +void StyleCustomFilterProgramCache::add(StyleCustomFilterProgram* program) +{ + CustomFilterProgramInfo key = programCacheKey(program); + ASSERT(m_cache.find(key) == m_cache.end()); + m_cache.set(key, program); + program->setCache(this); +} + +void StyleCustomFilterProgramCache::remove(StyleCustomFilterProgram* program) +{ + CacheMap::iterator iter = m_cache.find(programCacheKey(program)); + ASSERT(iter != m_cache.end()); + m_cache.remove(iter); +} + + +} // namespace WebCore + +#endif // ENABLE(CSS_SHADERS) + diff --git a/Source/WebCore/rendering/style/StyleCustomFilterProgramCache.h b/Source/WebCore/rendering/style/StyleCustomFilterProgramCache.h new file mode 100644 index 000000000..e151836ca --- /dev/null +++ b/Source/WebCore/rendering/style/StyleCustomFilterProgramCache.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2013 Adobe Systems Incorporated. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef StyleCustomFilterProgramCache_h +#define StyleCustomFilterProgramCache_h + +#if ENABLE(CSS_SHADERS) +#include "CustomFilterProgramInfo.h" +#include <wtf/FastAllocBase.h> +#include <wtf/HashMap.h> + +namespace WebCore { + +class StyleCustomFilterProgram; +class CustomFilterProgramInfo; + +class StyleCustomFilterProgramCache { + WTF_MAKE_FAST_ALLOCATED; +public: + StyleCustomFilterProgramCache(); + ~StyleCustomFilterProgramCache(); + + // Lookups a StyleCustomFilterProgram that has similar parameters with the specified program. + StyleCustomFilterProgram* lookup(StyleCustomFilterProgram*) const; + StyleCustomFilterProgram* lookup(const CustomFilterProgramInfo&) const; + + void add(StyleCustomFilterProgram*); + void remove(StyleCustomFilterProgram*); + +private: + typedef HashMap<CustomFilterProgramInfo, StyleCustomFilterProgram*> CacheMap; + CacheMap m_cache; +}; + +} // namespace WebCore + +#endif // ENABLE(CSS_SHADERS) + +#endif // StyleCustomFilterProgramCache_h diff --git a/Source/WebCore/rendering/style/StyleGeneratedImage.cpp b/Source/WebCore/rendering/style/StyleGeneratedImage.cpp index 09781a61a..b8161fefd 100644 --- a/Source/WebCore/rendering/style/StyleGeneratedImage.cpp +++ b/Source/WebCore/rendering/style/StyleGeneratedImage.cpp @@ -76,7 +76,7 @@ void StyleGeneratedImage::computeIntrinsicDimensions(const RenderObject* rendere void StyleGeneratedImage::addClient(RenderObject* renderer) { - m_imageGeneratorValue->addClient(renderer, IntSize()); + m_imageGeneratorValue->addClient(renderer); } void StyleGeneratedImage::removeClient(RenderObject* renderer) @@ -89,9 +89,9 @@ PassRefPtr<Image> StyleGeneratedImage::image(RenderObject* renderer, const IntSi return m_imageGeneratorValue->image(renderer, size); } -bool StyleGeneratedImage::hasAlpha(const RenderObject* renderer) const +bool StyleGeneratedImage::knownToBeOpaque(const RenderObject* renderer) const { - return m_imageGeneratorValue->hasAlpha(renderer); + return m_imageGeneratorValue->knownToBeOpaque(renderer); } } diff --git a/Source/WebCore/rendering/style/StyleGeneratedImage.h b/Source/WebCore/rendering/style/StyleGeneratedImage.h index 7ab70f2db..57f78dd58 100644 --- a/Source/WebCore/rendering/style/StyleGeneratedImage.h +++ b/Source/WebCore/rendering/style/StyleGeneratedImage.h @@ -51,7 +51,7 @@ public: virtual void addClient(RenderObject*); virtual void removeClient(RenderObject*); virtual PassRefPtr<Image> image(RenderObject*, const IntSize&) const; - virtual bool hasAlpha(const RenderObject*) const OVERRIDE; + virtual bool knownToBeOpaque(const RenderObject*) const OVERRIDE; private: StyleGeneratedImage(PassRefPtr<CSSImageGeneratorValue>); diff --git a/Source/WebCore/rendering/style/StyleGridData.cpp b/Source/WebCore/rendering/style/StyleGridData.cpp index 7b6bc0a37..9cffeabcf 100644 --- a/Source/WebCore/rendering/style/StyleGridData.cpp +++ b/Source/WebCore/rendering/style/StyleGridData.cpp @@ -33,6 +33,9 @@ namespace WebCore { StyleGridData::StyleGridData() : m_gridColumns(RenderStyle::initialGridColumns()) , m_gridRows(RenderStyle::initialGridRows()) + , m_gridAutoFlow(RenderStyle::initialGridAutoFlow()) + , m_gridAutoRows(RenderStyle::initialGridAutoRows()) + , m_gridAutoColumns(RenderStyle::initialGridAutoColumns()) { } @@ -40,6 +43,9 @@ StyleGridData::StyleGridData(const StyleGridData& o) : RefCounted<StyleGridData>() , m_gridColumns(o.m_gridColumns) , m_gridRows(o.m_gridRows) + , m_gridAutoFlow(o.m_gridAutoFlow) + , m_gridAutoRows(o.m_gridAutoRows) + , m_gridAutoColumns(o.m_gridAutoColumns) { } diff --git a/Source/WebCore/rendering/style/StyleGridData.h b/Source/WebCore/rendering/style/StyleGridData.h index 18ffcaba6..f6849820c 100644 --- a/Source/WebCore/rendering/style/StyleGridData.h +++ b/Source/WebCore/rendering/style/StyleGridData.h @@ -27,6 +27,7 @@ #define StyleGridData_h #include "GridTrackSize.h" +#include "RenderStyleConstants.h" #include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> #include <wtf/Vector.h> @@ -40,7 +41,7 @@ public: bool operator==(const StyleGridData& o) const { - return m_gridColumns == o.m_gridColumns && m_gridRows == o.m_gridRows; + return m_gridColumns == o.m_gridColumns && m_gridRows == o.m_gridRows && m_gridAutoFlow == o.m_gridAutoFlow && m_gridAutoRows == o.m_gridAutoRows && m_gridAutoColumns == o.m_gridAutoColumns; } bool operator!=(const StyleGridData& o) const @@ -52,6 +53,11 @@ public: Vector<GridTrackSize> m_gridColumns; Vector<GridTrackSize> m_gridRows; + GridAutoFlow m_gridAutoFlow; + + GridTrackSize m_gridAutoRows; + GridTrackSize m_gridAutoColumns; + private: StyleGridData(); StyleGridData(const StyleGridData&); diff --git a/Source/WebCore/rendering/style/StyleGridItemData.cpp b/Source/WebCore/rendering/style/StyleGridItemData.cpp index 4edb9edd4..9a64f051e 100644 --- a/Source/WebCore/rendering/style/StyleGridItemData.cpp +++ b/Source/WebCore/rendering/style/StyleGridItemData.cpp @@ -35,15 +35,19 @@ namespace WebCore { StyleGridItemData::StyleGridItemData() - : m_gridColumn(RenderStyle::initialGridItemColumn()) - , m_gridRow(RenderStyle::initialGridItemRow()) + : m_gridStart(RenderStyle::initialGridPosition()) + , m_gridEnd(RenderStyle::initialGridPosition()) + , m_gridBefore(RenderStyle::initialGridPosition()) + , m_gridAfter(RenderStyle::initialGridPosition()) { } StyleGridItemData::StyleGridItemData(const StyleGridItemData& o) : RefCounted<StyleGridItemData>() - , m_gridColumn(o.m_gridColumn) - , m_gridRow(o.m_gridRow) + , m_gridStart(o.m_gridStart) + , m_gridEnd(o.m_gridEnd) + , m_gridBefore(o.m_gridBefore) + , m_gridAfter(o.m_gridAfter) { } diff --git a/Source/WebCore/rendering/style/StyleGridItemData.h b/Source/WebCore/rendering/style/StyleGridItemData.h index 2f7487b86..32fcb6f76 100644 --- a/Source/WebCore/rendering/style/StyleGridItemData.h +++ b/Source/WebCore/rendering/style/StyleGridItemData.h @@ -46,7 +46,8 @@ public: bool operator==(const StyleGridItemData& o) const { - return m_gridColumn == o.m_gridColumn && m_gridRow == o.m_gridRow; + return m_gridStart == o.m_gridStart && m_gridEnd == o.m_gridEnd + && m_gridBefore == o.m_gridBefore && m_gridAfter == o.m_gridAfter; } bool operator!=(const StyleGridItemData& o) const @@ -54,8 +55,10 @@ public: return !(*this == o); } - GridPosition m_gridColumn; - GridPosition m_gridRow; + GridPosition m_gridStart; + GridPosition m_gridEnd; + GridPosition m_gridBefore; + GridPosition m_gridAfter; private: StyleGridItemData(); diff --git a/Source/WebCore/rendering/style/StyleImage.h b/Source/WebCore/rendering/style/StyleImage.h index 486ec3dab..70f336088 100644 --- a/Source/WebCore/rendering/style/StyleImage.h +++ b/Source/WebCore/rendering/style/StyleImage.h @@ -34,6 +34,7 @@ namespace WebCore { +class CachedImage; class CSSValue; class RenderObject; @@ -64,7 +65,8 @@ public: virtual PassRefPtr<Image> image(RenderObject*, const IntSize&) const = 0; virtual WrappedImagePtr data() const = 0; virtual float imageScaleFactor() const { return 1; } - virtual bool hasAlpha(const RenderObject*) const = 0; + virtual bool knownToBeOpaque(const RenderObject*) const = 0; + virtual CachedImage* cachedImage() const { return 0; } ALWAYS_INLINE bool isCachedImage() const { return m_isCachedImage; } ALWAYS_INLINE bool isPendingImage() const { return m_isPendingImage; } diff --git a/Source/WebCore/rendering/style/StylePendingImage.h b/Source/WebCore/rendering/style/StylePendingImage.h index 192063be7..b689ee779 100644 --- a/Source/WebCore/rendering/style/StylePendingImage.h +++ b/Source/WebCore/rendering/style/StylePendingImage.h @@ -26,6 +26,7 @@ #ifndef StylePendingImage_h #define StylePendingImage_h +#include "CSSCursorImageValue.h" #include "CSSImageGeneratorValue.h" #if ENABLE(CSS_IMAGE_SET) #include "CSSImageSetValue.h" @@ -49,6 +50,7 @@ public: virtual PassRefPtr<CSSValue> cssValue() const { return m_value; } CSSImageValue* cssImageValue() const { return m_value->isImageValue() ? static_cast<CSSImageValue*>(m_value) : 0; } CSSImageGeneratorValue* cssImageGeneratorValue() const { return m_value->isImageGeneratorValue() ? static_cast<CSSImageGeneratorValue*>(m_value) : 0; } + CSSCursorImageValue* cssCursorImageValue() const { return m_value->isCursorImageValue() ? static_cast<CSSCursorImageValue*>(m_value) : 0; } #if ENABLE(CSS_IMAGE_SET) CSSImageSetValue* cssImageSetValue() const { return m_value->isImageSetValue() ? static_cast<CSSImageSetValue*>(m_value) : 0; } #endif @@ -66,7 +68,7 @@ public: ASSERT_NOT_REACHED(); return 0; } - virtual bool hasAlpha(const RenderObject*) const { return true; } + virtual bool knownToBeOpaque(const RenderObject*) const { return false; } private: StylePendingImage(CSSValue* value) diff --git a/Source/WebCore/rendering/style/StyleRareInheritedData.cpp b/Source/WebCore/rendering/style/StyleRareInheritedData.cpp index 14715e0a5..2e1d9d0a6 100644 --- a/Source/WebCore/rendering/style/StyleRareInheritedData.cpp +++ b/Source/WebCore/rendering/style/StyleRareInheritedData.cpp @@ -28,8 +28,6 @@ #include "RenderStyleConstants.h" #include "ShadowData.h" #include "StyleImage.h" -#include "WebCoreMemoryInstrumentation.h" -#include <wtf/MemoryObjectInfo.h> namespace WebCore { @@ -70,13 +68,14 @@ StyleRareInheritedData::StyleRareInheritedData() , m_effectiveZoom(RenderStyle::initialZoom()) , widows(RenderStyle::initialWidows()) , orphans(RenderStyle::initialOrphans()) + , m_hasAutoWidows(true) + , m_hasAutoOrphans(true) , textSecurity(RenderStyle::initialTextSecurity()) , userModify(READ_ONLY) , wordBreak(RenderStyle::initialWordBreak()) , overflowWrap(RenderStyle::initialOverflowWrap()) , nbspMode(NBNORMAL) , lineBreak(LineBreakAuto) - , textSizeAdjust(RenderStyle::initialTextSizeAdjust()) , resize(RenderStyle::initialResize()) , userSelect(RenderStyle::initialUserSelect()) , colorSpace(ColorSpaceDeviceRGB) @@ -85,6 +84,11 @@ StyleRareInheritedData::StyleRareInheritedData() , textEmphasisFill(TextEmphasisFillFilled) , textEmphasisMark(TextEmphasisMarkNone) , textEmphasisPosition(TextEmphasisPositionOver) + , m_textOrientation(TextOrientationVerticalRight) +#if ENABLE(CSS3_TEXT) + , m_textIndentLine(RenderStyle::initialTextIndentLine()) + , m_textIndentType(RenderStyle::initialTextIndentType()) +#endif , m_lineBoxContain(RenderStyle::initialLineBoxContain()) #if ENABLE(CSS_IMAGE_ORIENTATION) , m_imageOrientation(RenderStyle::initialImageOrientation()) @@ -101,6 +105,8 @@ StyleRareInheritedData::StyleRareInheritedData() #endif #if ENABLE(CSS3_TEXT) , m_textAlignLast(RenderStyle::initialTextAlignLast()) + , m_textJustify(RenderStyle::initialTextJustify()) + , m_textUnderlinePosition(RenderStyle::initialTextUnderlinePosition()) #endif // CSS3_TEXT , m_rubyPosition(RenderStyle::initialRubyPosition()) , hyphenationLimitBefore(-1) @@ -137,13 +143,14 @@ StyleRareInheritedData::StyleRareInheritedData(const StyleRareInheritedData& o) , m_effectiveZoom(o.m_effectiveZoom) , widows(o.widows) , orphans(o.orphans) + , m_hasAutoWidows(o.m_hasAutoWidows) + , m_hasAutoOrphans(o.m_hasAutoOrphans) , textSecurity(o.textSecurity) , userModify(o.userModify) , wordBreak(o.wordBreak) , overflowWrap(o.overflowWrap) , nbspMode(o.nbspMode) , lineBreak(o.lineBreak) - , textSizeAdjust(o.textSizeAdjust) , resize(o.resize) , userSelect(o.userSelect) , colorSpace(o.colorSpace) @@ -152,6 +159,11 @@ StyleRareInheritedData::StyleRareInheritedData(const StyleRareInheritedData& o) , textEmphasisFill(o.textEmphasisFill) , textEmphasisMark(o.textEmphasisMark) , textEmphasisPosition(o.textEmphasisPosition) + , m_textOrientation(o.m_textOrientation) +#if ENABLE(CSS3_TEXT) + , m_textIndentLine(o.m_textIndentLine) + , m_textIndentType(o.m_textIndentType) +#endif , m_lineBoxContain(o.m_lineBoxContain) #if ENABLE(CSS_IMAGE_ORIENTATION) , m_imageOrientation(o.m_imageOrientation) @@ -168,6 +180,8 @@ StyleRareInheritedData::StyleRareInheritedData(const StyleRareInheritedData& o) #endif #if ENABLE(CSS3_TEXT) , m_textAlignLast(o.m_textAlignLast) + , m_textJustify(o.m_textJustify) + , m_textUnderlinePosition(o.m_textUnderlinePosition) #endif // CSS3_TEXT , m_rubyPosition(o.m_rubyPosition) , hyphenationString(o.hyphenationString) @@ -203,6 +217,15 @@ static bool cursorDataEquivalent(const CursorList* c1, const CursorList* c2) return (*c1 == *c2); } +static bool quotesDataEquivalent(const QuotesData* q1, const QuotesData* q2) +{ + if (q1 == q2) + return true; + if ((!q1 && q2) || (q1 && !q2)) + return false; + return (*q1 == *q2); +} + bool StyleRareInheritedData::operator==(const StyleRareInheritedData& o) const { return textStrokeColor == o.textStrokeColor @@ -222,6 +245,8 @@ bool StyleRareInheritedData::operator==(const StyleRareInheritedData& o) const && m_effectiveZoom == o.m_effectiveZoom && widows == o.widows && orphans == o.orphans + && m_hasAutoWidows == o.m_hasAutoWidows + && m_hasAutoOrphans == o.m_hasAutoOrphans && textSecurity == o.textSecurity && userModify == o.userModify && wordBreak == o.wordBreak @@ -231,7 +256,6 @@ bool StyleRareInheritedData::operator==(const StyleRareInheritedData& o) const #if ENABLE(ACCELERATED_OVERFLOW_SCROLLING) && useTouchOverflowScrolling == o.useTouchOverflowScrolling #endif - && textSizeAdjust == o.textSizeAdjust && resize == o.resize && userSelect == o.userSelect && colorSpace == o.colorSpace @@ -243,11 +267,16 @@ bool StyleRareInheritedData::operator==(const StyleRareInheritedData& o) const && textEmphasisFill == o.textEmphasisFill && textEmphasisMark == o.textEmphasisMark && textEmphasisPosition == o.textEmphasisPosition + && m_textOrientation == o.m_textOrientation +#if ENABLE(CSS3_TEXT) + && m_textIndentLine == o.m_textIndentLine + && m_textIndentType == o.m_textIndentType +#endif && m_lineBoxContain == o.m_lineBoxContain && hyphenationString == o.hyphenationString && locale == o.locale && textEmphasisCustomMark == o.textEmphasisCustomMark - && QuotesData::equals(quotes.get(), o.quotes.get()) + && quotesDataEquivalent(quotes.get(), o.quotes.get()) && m_tabSize == o.m_tabSize && m_lineGrid == o.m_lineGrid #if ENABLE(CSS_IMAGE_ORIENTATION) @@ -261,6 +290,8 @@ bool StyleRareInheritedData::operator==(const StyleRareInheritedData& o) const #endif #if ENABLE(CSS3_TEXT) && m_textAlignLast == o.m_textAlignLast + && m_textJustify == o.m_textJustify + && m_textUnderlinePosition == o.m_textUnderlinePosition #endif // CSS3_TEXT && m_rubyPosition == o.m_rubyPosition && m_lineSnap == o.m_lineSnap @@ -280,20 +311,4 @@ bool StyleRareInheritedData::shadowDataEquivalent(const StyleRareInheritedData& return true; } -void StyleRareInheritedData::reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const -{ - MemoryClassInfo info(memoryObjectInfo, this, WebCoreMemoryTypes::CSS); - info.addMember(textShadow); - info.addMember(highlight); - info.addMember(cursorData); - info.addMember(hyphenationString); - info.addMember(locale); - info.addMember(textEmphasisCustomMark); - info.addMember(quotes); - info.addMember(m_lineGrid); -#if ENABLE(CSS_VARIABLES) - info.addMember(m_variables); -#endif -} - } // namespace WebCore diff --git a/Source/WebCore/rendering/style/StyleRareInheritedData.h b/Source/WebCore/rendering/style/StyleRareInheritedData.h index 216c10179..34b80ea52 100644 --- a/Source/WebCore/rendering/style/StyleRareInheritedData.h +++ b/Source/WebCore/rendering/style/StyleRareInheritedData.h @@ -59,8 +59,6 @@ public: } bool shadowDataEquivalent(const StyleRareInheritedData&) const; - void reportMemoryUsage(MemoryObjectInfo*) const; - RefPtr<StyleImage> listStyleImage; Color textStrokeColor; @@ -82,6 +80,8 @@ public: // Paged media properties. short widows; short orphans; + unsigned m_hasAutoWidows : 1; + unsigned m_hasAutoOrphans : 1; unsigned textSecurity : 2; // ETextSecurity unsigned userModify : 2; // EUserModify (editing) @@ -89,7 +89,6 @@ public: unsigned overflowWrap : 1; // EOverflowWrap unsigned nbspMode : 1; // ENBSPMode unsigned lineBreak : 3; // LineBreak - unsigned textSizeAdjust : 1; // An Apple extension. unsigned resize : 2; // EResize unsigned userSelect : 2; // EUserSelect unsigned colorSpace : 1; // ColorSpace @@ -98,6 +97,11 @@ public: unsigned textEmphasisFill : 1; // TextEmphasisFill unsigned textEmphasisMark : 3; // TextEmphasisMark unsigned textEmphasisPosition : 1; // TextEmphasisPosition + unsigned m_textOrientation : 2; // TextOrientation +#if ENABLE(CSS3_TEXT) + unsigned m_textIndentLine : 1; // TextIndentLine + unsigned m_textIndentType : 1; // TextIndentType +#endif unsigned m_lineBoxContain: 7; // LineBoxContain // CSS Image Values Level 3 #if ENABLE(CSS_IMAGE_ORIENTATION) @@ -114,7 +118,9 @@ public: unsigned m_imageResolutionSnap : 1; // ImageResolutionSnap #endif #if ENABLE(CSS3_TEXT) - unsigned m_textAlignLast : 3; // ETextAlignLast + unsigned m_textAlignLast : 3; // TextAlignLast + unsigned m_textJustify : 3; // TextJustify + unsigned m_textUnderlinePosition : 3; // TextUnderlinePosition #endif // CSS3_TEXT unsigned m_rubyPosition : 1; // RubyPosition diff --git a/Source/WebCore/rendering/style/StyleRareNonInheritedData.cpp b/Source/WebCore/rendering/style/StyleRareNonInheritedData.cpp index ed9e9a2ef..966bfcb15 100644 --- a/Source/WebCore/rendering/style/StyleRareNonInheritedData.cpp +++ b/Source/WebCore/rendering/style/StyleRareNonInheritedData.cpp @@ -30,10 +30,6 @@ #include "StyleTransformData.h" #include "StyleImage.h" #include "StyleResolver.h" -#include "WebCoreMemoryInstrumentation.h" -#include <wtf/MemoryInstrumentationHashMap.h> -#include <wtf/MemoryInstrumentationVector.h> -#include <wtf/MemoryObjectInfo.h> namespace WebCore { @@ -59,7 +55,7 @@ StyleRareNonInheritedData::StyleRareNonInheritedData() , m_order(RenderStyle::initialOrder()) , m_flowThread(RenderStyle::initialFlowThread()) , m_regionThread(RenderStyle::initialRegionThread()) - , m_regionOverflow(RenderStyle::initialRegionOverflow()) + , m_regionFragment(RenderStyle::initialRegionFragment()) , m_regionBreakAfter(RenderStyle::initialPageBreak()) , m_regionBreakBefore(RenderStyle::initialPageBreak()) , m_regionBreakInside(RenderStyle::initialPageBreak()) @@ -129,6 +125,10 @@ StyleRareNonInheritedData::StyleRareNonInheritedData(const StyleRareNonInherited , m_shapeMargin(o.m_shapeMargin) , m_shapePadding(o.m_shapePadding) , m_clipPath(o.m_clipPath) +#if ENABLE(CSS3_TEXT) + , m_textDecorationColor(o.m_textDecorationColor) + , m_visitedLinkTextDecorationColor(o.m_visitedLinkTextDecorationColor) +#endif // CSS3_TEXT , m_visitedLinkBackgroundColor(o.m_visitedLinkBackgroundColor) , m_visitedLinkOutlineColor(o.m_visitedLinkOutlineColor) , m_visitedLinkBorderLeftColor(o.m_visitedLinkBorderLeftColor) @@ -138,7 +138,7 @@ StyleRareNonInheritedData::StyleRareNonInheritedData(const StyleRareNonInherited , m_order(o.m_order) , m_flowThread(o.m_flowThread) , m_regionThread(o.m_regionThread) - , m_regionOverflow(o.m_regionOverflow) + , m_regionFragment(o.m_regionFragment) , m_regionBreakAfter(o.m_regionBreakAfter) , m_regionBreakBefore(o.m_regionBreakBefore) , m_regionBreakInside(o.m_regionBreakInside) @@ -214,6 +214,10 @@ bool StyleRareNonInheritedData::operator==(const StyleRareNonInheritedData& o) c && m_shapeMargin == o.m_shapeMargin && m_shapePadding == o.m_shapePadding && m_clipPath == o.m_clipPath +#if ENABLE(CSS3_TEXT) + && m_textDecorationColor == o.m_textDecorationColor + && m_visitedLinkTextDecorationColor == o.m_visitedLinkTextDecorationColor +#endif // CSS3_TEXT && m_visitedLinkBackgroundColor == o.m_visitedLinkBackgroundColor && m_visitedLinkOutlineColor == o.m_visitedLinkOutlineColor && m_visitedLinkBorderLeftColor == o.m_visitedLinkBorderLeftColor @@ -223,7 +227,7 @@ bool StyleRareNonInheritedData::operator==(const StyleRareNonInheritedData& o) c && m_order == o.m_order && m_flowThread == o.m_flowThread && m_regionThread == o.m_regionThread - && m_regionOverflow == o.m_regionOverflow + && m_regionFragment == o.m_regionFragment && m_regionBreakAfter == o.m_regionBreakAfter && m_regionBreakBefore == o.m_regionBreakBefore && m_regionBreakInside == o.m_regionBreakInside @@ -316,33 +320,4 @@ bool StyleRareNonInheritedData::transitionDataEquivalent(const StyleRareNonInher return true; } -void StyleRareNonInheritedData::reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const -{ - MemoryClassInfo info(memoryObjectInfo, this, WebCoreMemoryTypes::CSS); -#if ENABLE(DASHBOARD_SUPPORT) - info.addMember(m_dashboardRegions); -#endif - info.addMember(m_deprecatedFlexibleBox); - info.addMember(m_flexibleBox); - info.addMember(m_marquee); - info.addMember(m_multiCol); - info.addMember(m_transform); -#if ENABLE(CSS_FILTERS) - info.addMember(m_filter); -#endif - info.addMember(m_grid); - info.addMember(m_gridItem); - info.addMember(m_content); - info.addMember(m_counterDirectives); - info.addMember(m_boxShadow); - info.addMember(m_boxReflect); - info.addMember(m_animations); - info.addMember(m_transitions); - info.addMember(m_shapeInside); - info.addMember(m_shapeOutside); - info.addMember(m_clipPath); - info.addMember(m_flowThread); - info.addMember(m_regionThread); -} - } // namespace WebCore diff --git a/Source/WebCore/rendering/style/StyleRareNonInheritedData.h b/Source/WebCore/rendering/style/StyleRareNonInheritedData.h index c4ac0f6f3..11625b3de 100644 --- a/Source/WebCore/rendering/style/StyleRareNonInheritedData.h +++ b/Source/WebCore/rendering/style/StyleRareNonInheritedData.h @@ -30,10 +30,10 @@ #include "CounterDirectives.h" #include "CursorData.h" #include "DataRef.h" -#include "ExclusionShapeValue.h" #include "FillLayer.h" #include "LineClampValue.h" #include "NinePieceImage.h" +#include "ShapeValue.h" #include <wtf/OwnPtr.h> #include <wtf/PassRefPtr.h> #include <wtf/Vector.h> @@ -91,8 +91,6 @@ public: bool animationDataEquivalent(const StyleRareNonInheritedData&) const; bool transitionDataEquivalent(const StyleRareNonInheritedData&) const; - void reportMemoryUsage(MemoryObjectInfo*) const; - float opacity; // Whether or not we're transparent. float m_aspectRatioDenominator; @@ -138,13 +136,17 @@ public: LengthSize m_pageSize; - RefPtr<ExclusionShapeValue> m_shapeInside; - RefPtr<ExclusionShapeValue> m_shapeOutside; + RefPtr<ShapeValue> m_shapeInside; + RefPtr<ShapeValue> m_shapeOutside; Length m_shapeMargin; Length m_shapePadding; RefPtr<ClipPathOperation> m_clipPath; +#if ENABLE(CSS3_TEXT) + Color m_textDecorationColor; + Color m_visitedLinkTextDecorationColor; +#endif // CSS3_TEXT Color m_visitedLinkBackgroundColor; Color m_visitedLinkOutlineColor; Color m_visitedLinkBorderLeftColor; @@ -156,7 +158,7 @@ public: AtomicString m_flowThread; AtomicString m_regionThread; - unsigned m_regionOverflow : 1; // RegionOverflow + unsigned m_regionFragment : 1; // RegionFragment unsigned m_regionBreakAfter : 2; // EPageBreak unsigned m_regionBreakBefore : 2; // EPageBreak diff --git a/Source/WebCore/rendering/style/StyleVisualData.h b/Source/WebCore/rendering/style/StyleVisualData.h index b5140a78d..117cff1aa 100644 --- a/Source/WebCore/rendering/style/StyleVisualData.h +++ b/Source/WebCore/rendering/style/StyleVisualData.h @@ -52,7 +52,7 @@ public: LengthBox clip; bool hasClip : 1; - unsigned textDecoration : ETextDecorationBits; // Text decorations defined *only* by this element. + unsigned textDecoration : TextDecorationBits; // Text decorations defined *only* by this element. #if ENABLE(TEXT_AUTOSIZING) float m_textAutosizingMultiplier; diff --git a/Source/WebCore/rendering/svg/RenderSVGAllInOne.cpp b/Source/WebCore/rendering/svg/RenderSVGAllInOne.cpp index a6bcd0296..234e7ce6c 100644 --- a/Source/WebCore/rendering/svg/RenderSVGAllInOne.cpp +++ b/Source/WebCore/rendering/svg/RenderSVGAllInOne.cpp @@ -77,3 +77,4 @@ #include "SVGTextMetrics.cpp" #include "SVGTextMetricsBuilder.cpp" #include "SVGTextQuery.cpp" + diff --git a/Source/WebCore/rendering/svg/RenderSVGBlock.cpp b/Source/WebCore/rendering/svg/RenderSVGBlock.cpp index 9f39357c1..f120b9946 100644 --- a/Source/WebCore/rendering/svg/RenderSVGBlock.cpp +++ b/Source/WebCore/rendering/svg/RenderSVGBlock.cpp @@ -31,8 +31,8 @@ namespace WebCore { -RenderSVGBlock::RenderSVGBlock(SVGElement* node) - : RenderBlock(node) +RenderSVGBlock::RenderSVGBlock(SVGElement* element) + : RenderBlock(element) { } diff --git a/Source/WebCore/rendering/svg/RenderSVGBlock.h b/Source/WebCore/rendering/svg/RenderSVGBlock.h index 222d5ee20..0bd300c1c 100644 --- a/Source/WebCore/rendering/svg/RenderSVGBlock.h +++ b/Source/WebCore/rendering/svg/RenderSVGBlock.h @@ -41,6 +41,8 @@ private: virtual void setStyle(PassRefPtr<RenderStyle>); virtual void updateFromStyle() OVERRIDE; + virtual bool isRenderSVGBlock() const OVERRIDE { return true; }; + virtual void absoluteRects(Vector<IntRect>&, const LayoutPoint& accumulatedOffset) const; virtual void styleWillChange(StyleDifference, const RenderStyle* newStyle); diff --git a/Source/WebCore/rendering/svg/RenderSVGContainer.cpp b/Source/WebCore/rendering/svg/RenderSVGContainer.cpp index 5069f41aa..9305dc6b3 100644 --- a/Source/WebCore/rendering/svg/RenderSVGContainer.cpp +++ b/Source/WebCore/rendering/svg/RenderSVGContainer.cpp @@ -35,6 +35,7 @@ #include "SVGResources.h" #include "SVGResourcesCache.h" #include "SVGStyledElement.h" +#include <wtf/StackStats.h> namespace WebCore { @@ -137,7 +138,7 @@ void RenderSVGContainer::paint(PaintInfo& paintInfo, const LayoutPoint&) } if (continueRendering) { - childPaintInfo.updatePaintingRootForChildren(this); + childPaintInfo.updateSubtreePaintRootForChildren(this); for (RenderObject* child = firstChild(); child; child = child->nextSibling()) child->paint(childPaintInfo, IntPoint()); } @@ -150,12 +151,12 @@ void RenderSVGContainer::paint(PaintInfo& paintInfo, const LayoutPoint&) // We should instead disable our clip during PaintPhaseOutline if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && style()->outlineWidth() && style()->visibility() == VISIBLE) { IntRect paintRectInParent = enclosingIntRect(localToParentTransform().mapRect(repaintRect)); - paintOutline(paintInfo.context, paintRectInParent); + paintOutline(paintInfo, paintRectInParent); } } // addFocusRingRects is called from paintOutline and needs to be in the same coordinates as the paintOuline call -void RenderSVGContainer::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint&) +void RenderSVGContainer::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint&, const RenderLayerModelObject*) { IntRect paintRectInParent = enclosingIntRect(localToParentTransform().mapRect(repaintRectInLocalCoordinates())); if (!paintRectInParent.isEmpty()) @@ -186,6 +187,12 @@ bool RenderSVGContainer::nodeAtFloatPoint(const HitTestRequest& request, HitTest } } + // Accessibility wants to return SVG containers, if appropriate. + if (request.type() & HitTestRequest::AccessibilityHitTest && m_objectBoundingBox.contains(localPoint)) { + updateHitTestResult(result, roundedLayoutPoint(localPoint)); + return true; + } + // Spec: Only graphical elements can be targeted by the mouse, period. // 16.4: "If there are no graphics elements whose relevant graphics content is under the pointer (i.e., there is no target element), the event is not dispatched." return false; diff --git a/Source/WebCore/rendering/svg/RenderSVGContainer.h b/Source/WebCore/rendering/svg/RenderSVGContainer.h index 3c0fa9814..70e1ec8d5 100644 --- a/Source/WebCore/rendering/svg/RenderSVGContainer.h +++ b/Source/WebCore/rendering/svg/RenderSVGContainer.h @@ -44,6 +44,7 @@ public: virtual void paint(PaintInfo&, const LayoutPoint&); virtual void setNeedsBoundariesUpdate() { m_needsBoundariesUpdate = true; } + virtual bool needsBoundariesUpdate() OVERRIDE { return m_needsBoundariesUpdate; } virtual bool didTransformToRootUpdate() { return false; } bool isObjectBoundingBoxValid() const { return m_objectBoundingBoxValid; } @@ -58,7 +59,7 @@ protected: virtual void addChild(RenderObject* child, RenderObject* beforeChild = 0) OVERRIDE; virtual void removeChild(RenderObject*) OVERRIDE; - virtual void addFocusRingRects(Vector<IntRect>&, const LayoutPoint&); + virtual void addFocusRingRects(Vector<IntRect>&, const LayoutPoint& additionalOffset, const RenderLayerModelObject* paintContainer = 0) OVERRIDE; virtual FloatRect objectBoundingBox() const { return m_objectBoundingBox; } virtual FloatRect strokeBoundingBox() const { return m_strokeBoundingBox; } @@ -90,13 +91,13 @@ private: inline RenderSVGContainer* toRenderSVGContainer(RenderObject* object) { - ASSERT(!object || object->isSVGContainer()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isSVGContainer()); return static_cast<RenderSVGContainer*>(object); } inline const RenderSVGContainer* toRenderSVGContainer(const RenderObject* object) { - ASSERT(!object || object->isSVGContainer()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isSVGContainer()); return static_cast<const RenderSVGContainer*>(object); } diff --git a/Source/WebCore/rendering/svg/RenderSVGEllipse.cpp b/Source/WebCore/rendering/svg/RenderSVGEllipse.cpp index b4a76531e..8c1df2da4 100644 --- a/Source/WebCore/rendering/svg/RenderSVGEllipse.cpp +++ b/Source/WebCore/rendering/svg/RenderSVGEllipse.cpp @@ -31,12 +31,12 @@ #include "SVGCircleElement.h" #include "SVGEllipseElement.h" +#include "SVGGraphicsElement.h" #include "SVGNames.h" -#include "SVGStyledTransformableElement.h" namespace WebCore { -RenderSVGEllipse::RenderSVGEllipse(SVGStyledTransformableElement* node) +RenderSVGEllipse::RenderSVGEllipse(SVGGraphicsElement* node) : RenderSVGShape(node) , m_usePathFallback(false) { diff --git a/Source/WebCore/rendering/svg/RenderSVGEllipse.h b/Source/WebCore/rendering/svg/RenderSVGEllipse.h index f5129df90..a5af34bc4 100644 --- a/Source/WebCore/rendering/svg/RenderSVGEllipse.h +++ b/Source/WebCore/rendering/svg/RenderSVGEllipse.h @@ -29,13 +29,13 @@ #if ENABLE(SVG) #include "RenderSVGShape.h" -#include "SVGStyledTransformableElement.h" +#include "SVGGraphicsElement.h" namespace WebCore { class RenderSVGEllipse : public RenderSVGShape { public: - explicit RenderSVGEllipse(SVGStyledTransformableElement*); + explicit RenderSVGEllipse(SVGGraphicsElement*); virtual ~RenderSVGEllipse(); private: diff --git a/Source/WebCore/rendering/svg/RenderSVGForeignObject.cpp b/Source/WebCore/rendering/svg/RenderSVGForeignObject.cpp index 408d887f0..9bd4672ed 100644 --- a/Source/WebCore/rendering/svg/RenderSVGForeignObject.cpp +++ b/Source/WebCore/rendering/svg/RenderSVGForeignObject.cpp @@ -35,6 +35,7 @@ #include "SVGResourcesCache.h" #include "SVGSVGElement.h" #include "TransformState.h" +#include <wtf/StackStats.h> namespace WebCore { @@ -193,9 +194,9 @@ bool RenderSVGForeignObject::nodeAtPoint(const HitTestRequest&, HitTestResult&, return false; } -void RenderSVGForeignObject::mapLocalToContainer(const RenderLayerModelObject* repaintContainer, TransformState& transformState, MapCoordinatesFlags mode, bool* wasFixed) const +void RenderSVGForeignObject::mapLocalToContainer(const RenderLayerModelObject* repaintContainer, TransformState& transformState, MapCoordinatesFlags, bool* wasFixed) const { - SVGRenderSupport::mapLocalToContainer(this, repaintContainer, transformState, mode & SnapOffsetForTransforms, wasFixed); + SVGRenderSupport::mapLocalToContainer(this, repaintContainer, transformState, wasFixed); } const RenderObject* RenderSVGForeignObject::pushMappingToContainer(const RenderLayerModelObject* ancestorToStopAt, RenderGeometryMap& geometryMap) const diff --git a/Source/WebCore/rendering/svg/RenderSVGForeignObject.h b/Source/WebCore/rendering/svg/RenderSVGForeignObject.h index 00979de61..02c29cc05 100644 --- a/Source/WebCore/rendering/svg/RenderSVGForeignObject.h +++ b/Source/WebCore/rendering/svg/RenderSVGForeignObject.h @@ -54,7 +54,7 @@ public: virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction) OVERRIDE; virtual bool isSVGForeignObject() const { return true; } - virtual void mapLocalToContainer(const RenderLayerModelObject* repaintContainer, TransformState&, MapCoordinatesFlags = ApplyContainerFlip | SnapOffsetForTransforms, bool* wasFixed = 0) const OVERRIDE; + virtual void mapLocalToContainer(const RenderLayerModelObject* repaintContainer, TransformState&, MapCoordinatesFlags = ApplyContainerFlip, bool* wasFixed = 0) const OVERRIDE; virtual const RenderObject* pushMappingToContainer(const RenderLayerModelObject* ancestorToStopAt, RenderGeometryMap&) const OVERRIDE; virtual void setNeedsTransformUpdate() { m_needsTransformUpdate = true; } diff --git a/Source/WebCore/rendering/svg/RenderSVGGradientStop.cpp b/Source/WebCore/rendering/svg/RenderSVGGradientStop.cpp index 31d5da5ca..9b41ae319 100644 --- a/Source/WebCore/rendering/svg/RenderSVGGradientStop.cpp +++ b/Source/WebCore/rendering/svg/RenderSVGGradientStop.cpp @@ -27,6 +27,7 @@ #include "SVGNames.h" #include "SVGResourcesCache.h" #include "SVGStopElement.h" +#include <wtf/StackStats.h> namespace WebCore { @@ -72,7 +73,7 @@ SVGGradientElement* RenderSVGGradientStop::gradientElement() const { ContainerNode* parentNode = node()->parentNode(); if (parentNode->hasTagName(linearGradientTag) || parentNode->hasTagName(radialGradientTag)) - return static_cast<SVGGradientElement*>(parentNode); + return toSVGGradientElement(parentNode); return 0; } diff --git a/Source/WebCore/rendering/svg/RenderSVGGradientStop.h b/Source/WebCore/rendering/svg/RenderSVGGradientStop.h index 1c0e4033b..ac8b97ac9 100644 --- a/Source/WebCore/rendering/svg/RenderSVGGradientStop.h +++ b/Source/WebCore/rendering/svg/RenderSVGGradientStop.h @@ -47,6 +47,7 @@ public: virtual FloatRect objectBoundingBox() const { return FloatRect(); } virtual FloatRect strokeBoundingBox() const { return FloatRect(); } virtual FloatRect repaintRectInLocalCoordinates() const { return FloatRect(); } + virtual bool nodeAtFloatPoint(const HitTestRequest&, HitTestResult&, const FloatPoint&, HitTestAction) OVERRIDE { return false; } protected: virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); @@ -57,7 +58,7 @@ private: inline const RenderSVGGradientStop* toRenderSVGGradientStop(const RenderObject* object) { - ASSERT(!object || object->isSVGGradientStop()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isSVGGradientStop()); return static_cast<const RenderSVGGradientStop*>(object); } diff --git a/Source/WebCore/rendering/svg/RenderSVGHiddenContainer.cpp b/Source/WebCore/rendering/svg/RenderSVGHiddenContainer.cpp index 4d37e13d7..2a015a5c0 100644 --- a/Source/WebCore/rendering/svg/RenderSVGHiddenContainer.cpp +++ b/Source/WebCore/rendering/svg/RenderSVGHiddenContainer.cpp @@ -24,6 +24,7 @@ #include "RenderSVGPath.h" #include "SVGStyledElement.h" +#include <wtf/StackStats.h> namespace WebCore { diff --git a/Source/WebCore/rendering/svg/RenderSVGImage.cpp b/Source/WebCore/rendering/svg/RenderSVGImage.cpp index 45db954df..76d7c5c2e 100644 --- a/Source/WebCore/rendering/svg/RenderSVGImage.cpp +++ b/Source/WebCore/rendering/svg/RenderSVGImage.cpp @@ -44,6 +44,7 @@ #include "SVGRenderingContext.h" #include "SVGResources.h" #include "SVGResourcesCache.h" +#include <wtf/StackStats.h> namespace WebCore { @@ -63,7 +64,7 @@ RenderSVGImage::~RenderSVGImage() bool RenderSVGImage::updateImageViewport() { - SVGImageElement* image = static_cast<SVGImageElement*>(node()); + SVGImageElement* image = toSVGImageElement(node()); FloatRect oldBoundaries = m_objectBoundingBox; SVGLengthContext lengthContext(image); @@ -87,7 +88,7 @@ void RenderSVGImage::layout() bool transformOrBoundariesUpdate = m_needsTransformUpdate || m_needsBoundariesUpdate; if (m_needsTransformUpdate) { - m_localTransform = static_cast<SVGImageElement*>(node())->animatedLocalTransform(); + m_localTransform = toSVGImageElement(node())->animatedLocalTransform(); m_needsTransformUpdate = false; } @@ -132,22 +133,35 @@ void RenderSVGImage::paint(PaintInfo& paintInfo, const LayoutPoint&) SVGRenderingContext renderingContext(this, childPaintInfo); if (renderingContext.isRenderingPrepared()) { - RefPtr<Image> image = m_imageResource->image(); - FloatRect destRect = m_objectBoundingBox; - FloatRect srcRect(0, 0, image->width(), image->height()); + if (style()->svgStyle()->bufferedRendering() == BR_STATIC && renderingContext.bufferForeground(m_bufferedForeground)) + return; - SVGImageElement* imageElement = static_cast<SVGImageElement*>(node()); - imageElement->preserveAspectRatio().transformRect(destRect, srcRect); - - childPaintInfo.context->drawImage(image.get(), ColorSpaceDeviceRGB, destRect, srcRect); + paintForeground(childPaintInfo); } } if (drawsOutline) - paintOutline(childPaintInfo.context, IntRect(boundingBox)); + paintOutline(childPaintInfo, IntRect(boundingBox)); } } +void RenderSVGImage::paintForeground(PaintInfo& paintInfo) +{ + RefPtr<Image> image = m_imageResource->image(); + FloatRect destRect = m_objectBoundingBox; + FloatRect srcRect(0, 0, image->width(), image->height()); + + SVGImageElement* imageElement = toSVGImageElement(node()); + imageElement->preserveAspectRatio().transformRect(destRect, srcRect); + + paintInfo.context->drawImage(image.get(), ColorSpaceDeviceRGB, destRect, srcRect); +} + +void RenderSVGImage::invalidateBufferedForeground() +{ + m_bufferedForeground.clear(); +} + bool RenderSVGImage::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction) { // We only draw in the forground phase, so we only hit-test then. @@ -188,10 +202,12 @@ void RenderSVGImage::imageChanged(WrappedImagePtr, const IntRect*) m_objectBoundingBox = FloatRect(); updateImageViewport(); + invalidateBufferedForeground(); + repaint(); } -void RenderSVGImage::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint&) +void RenderSVGImage::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint&, const RenderLayerModelObject*) { // this is called from paint() after the localTransform has already been applied IntRect contentRect = enclosingIntRect(repaintRectInLocalCoordinates()); diff --git a/Source/WebCore/rendering/svg/RenderSVGImage.h b/Source/WebCore/rendering/svg/RenderSVGImage.h index 905a8d37d..af85b895b 100644 --- a/Source/WebCore/rendering/svg/RenderSVGImage.h +++ b/Source/WebCore/rendering/svg/RenderSVGImage.h @@ -43,14 +43,18 @@ public: bool updateImageViewport(); virtual void setNeedsBoundariesUpdate() { m_needsBoundariesUpdate = true; } + virtual bool needsBoundariesUpdate() OVERRIDE { return m_needsBoundariesUpdate; } virtual void setNeedsTransformUpdate() { m_needsTransformUpdate = true; } RenderImageResource* imageResource() { return m_imageResource.get(); } const RenderImageResource* imageResource() const { return m_imageResource.get(); } + // Note: Assumes the PaintInfo context has had all local transforms applied. + void paintForeground(PaintInfo&); + private: virtual const char* renderName() const { return "RenderSVGImage"; } - virtual bool isSVGImage() const { return true; } + virtual bool isSVGImage() const OVERRIDE { return true; } virtual const AffineTransform& localToParentTransform() const { return m_localTransform; } @@ -59,13 +63,15 @@ private: virtual FloatRect repaintRectInLocalCoordinates() const { return m_repaintBoundingBox; } virtual FloatRect repaintRectInLocalCoordinatesExcludingSVGShadow() const OVERRIDE { return m_repaintBoundingBoxExcludingShadow; } - virtual void addFocusRingRects(Vector<IntRect>&, const LayoutPoint&); + virtual void addFocusRingRects(Vector<IntRect>&, const LayoutPoint& additionalOffset, const RenderLayerModelObject* paintContainer = 0) OVERRIDE; virtual void imageChanged(WrappedImagePtr, const IntRect* = 0); virtual void layout(); virtual void paint(PaintInfo&, const LayoutPoint&); + void invalidateBufferedForeground(); + virtual bool nodeAtFloatPoint(const HitTestRequest&, HitTestResult&, const FloatPoint& pointInParent, HitTestAction); virtual AffineTransform localTransform() const { return m_localTransform; } @@ -78,17 +84,19 @@ private: FloatRect m_repaintBoundingBox; FloatRect m_repaintBoundingBoxExcludingShadow; OwnPtr<RenderImageResource> m_imageResource; + + OwnPtr<ImageBuffer> m_bufferedForeground; }; inline RenderSVGImage* toRenderSVGImage(RenderObject* object) { - ASSERT(!object || object->isSVGImage()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isSVGImage()); return static_cast<RenderSVGImage*>(object); } inline const RenderSVGImage* toRenderSVGImage(const RenderObject* object) { - ASSERT(!object || object->isSVGImage()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isSVGImage()); return static_cast<const RenderSVGImage*>(object); } diff --git a/Source/WebCore/rendering/svg/RenderSVGInline.cpp b/Source/WebCore/rendering/svg/RenderSVGInline.cpp index 9252c54f3..e8dfe03e4 100644 --- a/Source/WebCore/rendering/svg/RenderSVGInline.cpp +++ b/Source/WebCore/rendering/svg/RenderSVGInline.cpp @@ -32,8 +32,8 @@ namespace WebCore { -RenderSVGInline::RenderSVGInline(Node* n) - : RenderInline(n) +RenderSVGInline::RenderSVGInline(Element* element) + : RenderInline(element) { setAlwaysCreateLineBoxes(); } @@ -79,9 +79,9 @@ void RenderSVGInline::computeFloatRectForRepaint(const RenderLayerModelObject* r SVGRenderSupport::computeFloatRectForRepaint(this, repaintContainer, repaintRect, fixed); } -void RenderSVGInline::mapLocalToContainer(const RenderLayerModelObject* repaintContainer, TransformState& transformState, MapCoordinatesFlags mode, bool* wasFixed) const +void RenderSVGInline::mapLocalToContainer(const RenderLayerModelObject* repaintContainer, TransformState& transformState, MapCoordinatesFlags, bool* wasFixed) const { - SVGRenderSupport::mapLocalToContainer(this, repaintContainer, transformState, mode & SnapOffsetForTransforms, wasFixed); + SVGRenderSupport::mapLocalToContainer(this, repaintContainer, transformState, wasFixed); } const RenderObject* RenderSVGInline::pushMappingToContainer(const RenderLayerModelObject* ancestorToStopAt, RenderGeometryMap& geometryMap) const diff --git a/Source/WebCore/rendering/svg/RenderSVGInline.h b/Source/WebCore/rendering/svg/RenderSVGInline.h index 50dec2ddd..dbeb0d3ed 100644 --- a/Source/WebCore/rendering/svg/RenderSVGInline.h +++ b/Source/WebCore/rendering/svg/RenderSVGInline.h @@ -30,7 +30,7 @@ namespace WebCore { class RenderSVGInline : public RenderInline { public: - explicit RenderSVGInline(Node*); + explicit RenderSVGInline(Element*); virtual const char* renderName() const { return "RenderSVGInline"; } virtual bool requiresLayer() const { return false; } @@ -47,7 +47,7 @@ public: virtual LayoutRect clippedOverflowRectForRepaint(const RenderLayerModelObject* repaintContainer) const OVERRIDE; virtual void computeFloatRectForRepaint(const RenderLayerModelObject* repaintContainer, FloatRect&, bool fixed = false) const OVERRIDE; - virtual void mapLocalToContainer(const RenderLayerModelObject* repaintContainer, TransformState&, MapCoordinatesFlags = ApplyContainerFlip | SnapOffsetForTransforms, bool* wasFixed = 0) const OVERRIDE; + virtual void mapLocalToContainer(const RenderLayerModelObject* repaintContainer, TransformState&, MapCoordinatesFlags = ApplyContainerFlip, bool* wasFixed = 0) const OVERRIDE; virtual const RenderObject* pushMappingToContainer(const RenderLayerModelObject* ancestorToStopAt, RenderGeometryMap&) const OVERRIDE; virtual void absoluteQuads(Vector<FloatQuad>&, bool* wasFixed) const; diff --git a/Source/WebCore/rendering/svg/RenderSVGInlineText.cpp b/Source/WebCore/rendering/svg/RenderSVGInlineText.cpp index 2371d1abf..4bf0ec595 100644 --- a/Source/WebCore/rendering/svg/RenderSVGInlineText.cpp +++ b/Source/WebCore/rendering/svg/RenderSVGInlineText.cpp @@ -185,7 +185,7 @@ VisiblePosition RenderSVGInlineText::positionForPoint(const LayoutPoint& point) if (!box->isSVGInlineTextBox()) continue; - SVGInlineTextBox* textBox = static_cast<SVGInlineTextBox*>(box); + SVGInlineTextBox* textBox = toSVGInlineTextBox(box); Vector<SVGTextFragment>& fragments = textBox->textFragments(); unsigned textFragmentsSize = fragments.size(); @@ -228,8 +228,7 @@ void RenderSVGInlineText::computeNewScaledFontForStyle(RenderObject* renderer, c Document* document = renderer->document(); ASSERT(document); - StyleResolver* styleResolver = document->styleResolver(); - ASSERT(styleResolver); + StyleResolver* styleResolver = document->ensureStyleResolver(); // Alter font-size to the right on-screen value to avoid scaling the glyphs themselves, except when GeometricPrecision is specified scalingFactor = SVGRenderingContext::calculateScreenFontSizeScalingFactor(renderer); diff --git a/Source/WebCore/rendering/svg/RenderSVGInlineText.h b/Source/WebCore/rendering/svg/RenderSVGInlineText.h index 7643cf1ab..b6fc83008 100644 --- a/Source/WebCore/rendering/svg/RenderSVGInlineText.h +++ b/Source/WebCore/rendering/svg/RenderSVGInlineText.h @@ -69,13 +69,13 @@ private: inline RenderSVGInlineText* toRenderSVGInlineText(RenderObject* object) { - ASSERT(!object || object->isSVGInlineText()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isSVGInlineText()); return static_cast<RenderSVGInlineText*>(object); } inline const RenderSVGInlineText* toRenderSVGInlineText(const RenderObject* object) { - ASSERT(!object || object->isSVGInlineText()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isSVGInlineText()); return static_cast<const RenderSVGInlineText*>(object); } diff --git a/Source/WebCore/rendering/svg/RenderSVGModelObject.cpp b/Source/WebCore/rendering/svg/RenderSVGModelObject.cpp index 019204e4e..6733a2e05 100644 --- a/Source/WebCore/rendering/svg/RenderSVGModelObject.cpp +++ b/Source/WebCore/rendering/svg/RenderSVGModelObject.cpp @@ -57,9 +57,9 @@ void RenderSVGModelObject::computeFloatRectForRepaint(const RenderLayerModelObje SVGRenderSupport::computeFloatRectForRepaint(this, repaintContainer, repaintRect, fixed); } -void RenderSVGModelObject::mapLocalToContainer(const RenderLayerModelObject* repaintContainer, TransformState& transformState, MapCoordinatesFlags mode, bool* wasFixed) const +void RenderSVGModelObject::mapLocalToContainer(const RenderLayerModelObject* repaintContainer, TransformState& transformState, MapCoordinatesFlags, bool* wasFixed) const { - SVGRenderSupport::mapLocalToContainer(this, repaintContainer, transformState, mode & SnapOffsetForTransforms, wasFixed); + SVGRenderSupport::mapLocalToContainer(this, repaintContainer, transformState, wasFixed); } const RenderObject* RenderSVGModelObject::pushMappingToContainer(const RenderLayerModelObject* ancestorToStopAt, RenderGeometryMap& geometryMap) const @@ -131,8 +131,8 @@ static void getElementCTM(SVGElement* element, AffineTransform& transform) Node* current = element; while (current && current->isSVGElement()) { - SVGElement* currentElement = static_cast<SVGElement*>(current); - if (currentElement->isStyled()) { + SVGElement* currentElement = toSVGElement(current); + if (currentElement->isSVGStyledElement()) { localTransform = currentElement->renderer()->localToParentTransform(); transform = localTransform.multiply(transform); } @@ -140,7 +140,7 @@ static void getElementCTM(SVGElement* element, AffineTransform& transform) if (currentElement == stopAtElement) break; - current = current->parentOrHostNode(); + current = current->parentOrShadowHostNode(); } } @@ -180,7 +180,7 @@ bool RenderSVGModelObject::checkIntersection(RenderObject* renderer, const Float if (!isGraphicsElement(renderer)) return false; AffineTransform ctm; - SVGElement* svgElement = static_cast<SVGElement*>(renderer->node()); + SVGElement* svgElement = toSVGElement(renderer->node()); getElementCTM(svgElement, ctm); ASSERT(svgElement->renderer()); return intersectsAllowingEmpty(rect, ctm.mapRect(svgElement->renderer()->repaintRectInLocalCoordinates())); @@ -193,7 +193,7 @@ bool RenderSVGModelObject::checkEnclosure(RenderObject* renderer, const FloatRec if (!isGraphicsElement(renderer)) return false; AffineTransform ctm; - SVGElement* svgElement = static_cast<SVGElement*>(renderer->node()); + SVGElement* svgElement = toSVGElement(renderer->node()); getElementCTM(svgElement, ctm); ASSERT(svgElement->renderer()); return rect.contains(ctm.mapRect(svgElement->renderer()->repaintRectInLocalCoordinates())); diff --git a/Source/WebCore/rendering/svg/RenderSVGModelObject.h b/Source/WebCore/rendering/svg/RenderSVGModelObject.h index dc2d5da39..0511f2772 100644 --- a/Source/WebCore/rendering/svg/RenderSVGModelObject.h +++ b/Source/WebCore/rendering/svg/RenderSVGModelObject.h @@ -58,7 +58,7 @@ public: virtual void absoluteRects(Vector<IntRect>&, const LayoutPoint& accumulatedOffset) const; virtual void absoluteQuads(Vector<FloatQuad>&, bool* wasFixed) const; - virtual void mapLocalToContainer(const RenderLayerModelObject* repaintContainer, TransformState&, MapCoordinatesFlags = ApplyContainerFlip | SnapOffsetForTransforms, bool* wasFixed = 0) const OVERRIDE; + virtual void mapLocalToContainer(const RenderLayerModelObject* repaintContainer, TransformState&, MapCoordinatesFlags = ApplyContainerFlip, bool* wasFixed = 0) const OVERRIDE; virtual const RenderObject* pushMappingToContainer(const RenderLayerModelObject* ancestorToStopAt, RenderGeometryMap&) const OVERRIDE; virtual void styleWillChange(StyleDifference, const RenderStyle* newStyle); virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); diff --git a/Source/WebCore/rendering/svg/RenderSVGPath.cpp b/Source/WebCore/rendering/svg/RenderSVGPath.cpp index cf658359d..97f713012 100644 --- a/Source/WebCore/rendering/svg/RenderSVGPath.cpp +++ b/Source/WebCore/rendering/svg/RenderSVGPath.cpp @@ -30,13 +30,13 @@ #if ENABLE(SVG) #include "RenderSVGPath.h" +#include "SVGGraphicsElement.h" #include "SVGPathElement.h" -#include "SVGStyledTransformableElement.h" #include "SVGSubpathData.h" namespace WebCore { -RenderSVGPath::RenderSVGPath(SVGStyledTransformableElement* node) +RenderSVGPath::RenderSVGPath(SVGGraphicsElement* node) : RenderSVGShape(node) { } diff --git a/Source/WebCore/rendering/svg/RenderSVGPath.h b/Source/WebCore/rendering/svg/RenderSVGPath.h index ab1cf1c0b..674289564 100644 --- a/Source/WebCore/rendering/svg/RenderSVGPath.h +++ b/Source/WebCore/rendering/svg/RenderSVGPath.h @@ -33,11 +33,11 @@ namespace WebCore { class RenderSVGPath : public RenderSVGShape { public: - explicit RenderSVGPath(SVGStyledTransformableElement*); + explicit RenderSVGPath(SVGGraphicsElement*); virtual ~RenderSVGPath(); private: - virtual bool isSVGPath() const { return true; } + virtual bool isSVGPath() const OVERRIDE { return true; } virtual const char* renderName() const { return "RenderSVGPath"; } virtual void updateShapeFromElement() OVERRIDE; @@ -54,6 +54,12 @@ private: Vector<FloatPoint> m_zeroLengthLinecapLocations; }; +inline RenderSVGPath* toRenderSVGPath(RenderObject* object) +{ + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isSVGPath()); + return static_cast<RenderSVGPath*>(object); +} + } #endif // ENABLE(SVG) diff --git a/Source/WebCore/rendering/svg/RenderSVGRect.cpp b/Source/WebCore/rendering/svg/RenderSVGRect.cpp index c7f9c5b7b..1fa46a4a1 100644 --- a/Source/WebCore/rendering/svg/RenderSVGRect.cpp +++ b/Source/WebCore/rendering/svg/RenderSVGRect.cpp @@ -32,7 +32,6 @@ #include "SVGNames.h" #include "SVGRectElement.h" -#include <wtf/Platform.h> namespace WebCore { diff --git a/Source/WebCore/rendering/svg/RenderSVGResource.cpp b/Source/WebCore/rendering/svg/RenderSVGResource.cpp index fd16ef29d..6ced1c79c 100644 --- a/Source/WebCore/rendering/svg/RenderSVGResource.cpp +++ b/Source/WebCore/rendering/svg/RenderSVGResource.cpp @@ -180,7 +180,7 @@ static inline void removeFromCacheAndInvalidateDependencies(RenderObject* object if (!object->node() || !object->node()->isSVGElement()) return; - HashSet<SVGElement*>* dependencies = object->document()->accessSVGExtensions()->setOfElementsReferencingTarget(static_cast<SVGElement*>(object->node())); + HashSet<SVGElement*>* dependencies = object->document()->accessSVGExtensions()->setOfElementsReferencingTarget(toSVGElement(object->node())); if (!dependencies) return; HashSet<SVGElement*>::iterator end = dependencies->end(); diff --git a/Source/WebCore/rendering/svg/RenderSVGResourceClipper.cpp b/Source/WebCore/rendering/svg/RenderSVGResourceClipper.cpp index 7339e2859..9967d70ed 100644 --- a/Source/WebCore/rendering/svg/RenderSVGResourceClipper.cpp +++ b/Source/WebCore/rendering/svg/RenderSVGResourceClipper.cpp @@ -39,16 +39,15 @@ #include "RenderStyle.h" #include "SVGClipPathElement.h" #include "SVGElement.h" +#include "SVGGraphicsElement.h" #include "SVGNames.h" #include "SVGRenderSupport.h" #include "SVGRenderingContext.h" #include "SVGResources.h" #include "SVGResourcesCache.h" #include "SVGStyledElement.h" -#include "SVGStyledTransformableElement.h" #include "SVGUnitTypes.h" #include "SVGUseElement.h" -#include <wtf/UnusedParam.h> namespace WebCore { @@ -117,9 +116,9 @@ bool RenderSVGResourceClipper::pathOnlyClipping(GraphicsContext* context, const // Only shapes or paths are supported for direct clipping. We need to fallback to masking for texts. if (renderer->isSVGText()) return false; - if (!childNode->isSVGElement() || !static_cast<SVGElement*>(childNode)->isStyledTransformable()) + if (!childNode->isSVGElement() || !toSVGElement(childNode)->isSVGGraphicsElement()) continue; - SVGStyledTransformableElement* styled = static_cast<SVGStyledTransformableElement*>(childNode); + SVGGraphicsElement* styled = toSVGGraphicsElement(childNode); RenderStyle* style = renderer->style(); if (!style || style->display() == NONE || style->visibility() != VISIBLE) continue; @@ -169,7 +168,7 @@ bool RenderSVGResourceClipper::applyClippingToContext(RenderObject* object, cons } AffineTransform absoluteTransform; - SVGRenderingContext::calculateTransformationToOutermostSVGCoordinateSystem(object, absoluteTransform); + SVGRenderingContext::calculateTransformationToOutermostCoordinateSystem(object, absoluteTransform); if (shouldCreateClipData && !repaintRect.isEmpty()) { if (!SVGRenderingContext::createImageBuffer(repaintRect, absoluteTransform, clipperData->clipMaskImage, ColorSpaceDeviceRGB, Unaccelerated)) @@ -234,7 +233,7 @@ bool RenderSVGResourceClipper::drawContentIntoMaskImage(ClipperData* clipperData // Draw all clipPath children into a global mask. for (Node* childNode = node()->firstChild(); childNode; childNode = childNode->nextSibling()) { RenderObject* renderer = childNode->renderer(); - if (!childNode->isSVGElement() || !static_cast<SVGElement*>(childNode)->isStyled() || !renderer) + if (!childNode->isSVGElement() || !toSVGElement(childNode)->isSVGStyledElement() || !renderer) continue; if (renderer->needsLayout()) { frame()->view()->setPaintBehavior(oldBehavior); @@ -247,7 +246,7 @@ bool RenderSVGResourceClipper::drawContentIntoMaskImage(ClipperData* clipperData WindRule newClipRule = style->svgStyle()->clipRule(); bool isUseElement = childNode->hasTagName(SVGNames::useTag); if (isUseElement) { - SVGUseElement* useElement = static_cast<SVGUseElement*>(childNode); + SVGUseElement* useElement = toSVGUseElement(childNode); renderer = useElement->rendererClipChild(); if (!renderer) continue; @@ -276,7 +275,7 @@ void RenderSVGResourceClipper::calculateClipContentRepaintRect() // This is a rough heuristic to appraise the clip size and doesn't consider clip on clip. for (Node* childNode = node()->firstChild(); childNode; childNode = childNode->nextSibling()) { RenderObject* renderer = childNode->renderer(); - if (!childNode->isSVGElement() || !static_cast<SVGElement*>(childNode)->isStyled() || !renderer) + if (!childNode->isSVGElement() || !toSVGElement(childNode)->isSVGStyledElement() || !renderer) continue; if (!renderer->isSVGShape() && !renderer->isSVGText() && !childNode->hasTagName(SVGNames::useTag)) continue; @@ -306,13 +305,13 @@ bool RenderSVGResourceClipper::hitTestClipContent(const FloatRect& objectBoundin for (Node* childNode = node()->firstChild(); childNode; childNode = childNode->nextSibling()) { RenderObject* renderer = childNode->renderer(); - if (!childNode->isSVGElement() || !static_cast<SVGElement*>(childNode)->isStyled() || !renderer) + if (!childNode->isSVGElement() || !toSVGElement(childNode)->isSVGStyledElement() || !renderer) continue; if (!renderer->isSVGShape() && !renderer->isSVGText() && !childNode->hasTagName(SVGNames::useTag)) continue; IntPoint hitPoint; HitTestResult result(hitPoint); - if (renderer->nodeAtFloatPoint(HitTestRequest(HitTestRequest::SVGClipContent), result, point, HitTestForeground)) + if (renderer->nodeAtFloatPoint(HitTestRequest(HitTestRequest::SVGClipContent | HitTestRequest::DisallowShadowContent), result, point, HitTestForeground)) return true; } diff --git a/Source/WebCore/rendering/svg/RenderSVGResourceContainer.cpp b/Source/WebCore/rendering/svg/RenderSVGResourceContainer.cpp index e311c1c21..38bd67a63 100644 --- a/Source/WebCore/rendering/svg/RenderSVGResourceContainer.cpp +++ b/Source/WebCore/rendering/svg/RenderSVGResourceContainer.cpp @@ -25,9 +25,10 @@ #include "RenderLayer.h" #include "RenderSVGRoot.h" #include "RenderView.h" +#include "SVGGraphicsElement.h" #include "SVGRenderingContext.h" #include "SVGResourcesCache.h" -#include "SVGStyledTransformableElement.h" +#include <wtf/StackStats.h> namespace WebCore { @@ -86,7 +87,7 @@ void RenderSVGResourceContainer::idChanged() // Remove old id, that is guaranteed to be present in cache. SVGDocumentExtensions* extensions = svgExtensionsFromNode(node()); extensions->removeResource(m_id); - m_id = static_cast<Element*>(node())->getIdAttribute(); + m_id = toElement(node())->getIdAttribute(); registerResource(); } @@ -114,13 +115,18 @@ void RenderSVGResourceContainer::markAllClientsForInvalidation(InvalidationMode RenderSVGResource::markForLayoutAndParentResourceInvalidation(client, needsLayout); } + markAllClientLayersForInvalidation(); + + m_isInvalidating = false; +} + +void RenderSVGResourceContainer::markAllClientLayersForInvalidation() +{ #if ENABLE(CSS_FILTERS) HashSet<RenderLayer*>::iterator layerEnd = m_clientLayers.end(); for (HashSet<RenderLayer*>::iterator it = m_clientLayers.begin(); it != layerEnd; ++it) (*it)->filterNeedsRepaint(); #endif - - m_isInvalidating = false; } void RenderSVGResourceContainer::markClientForInvalidation(RenderObject* client, InvalidationMode mode) @@ -184,7 +190,7 @@ void RenderSVGResourceContainer::registerResource() const SVGDocumentExtensions::SVGPendingElements::const_iterator end = clients->end(); for (SVGDocumentExtensions::SVGPendingElements::const_iterator it = clients->begin(); it != end; ++it) { ASSERT((*it)->hasPendingResources()); - (*it)->clearHasPendingResourcesIfPossible(); + extensions->clearHasPendingResourcesIfPossible(*it); RenderObject* renderer = (*it)->renderer(); if (!renderer) continue; @@ -220,7 +226,7 @@ AffineTransform RenderSVGResourceContainer::transformOnNonScalingStroke(RenderOb if (!object->isSVGShape()) return resourceTransform; - SVGStyledTransformableElement* element = static_cast<SVGStyledTransformableElement*>(object->node()); + SVGGraphicsElement* element = toSVGGraphicsElement(object->node()); AffineTransform transform = element->getScreenCTM(SVGLocatable::DisallowStyleUpdate); transform *= resourceTransform; return transform; diff --git a/Source/WebCore/rendering/svg/RenderSVGResourceContainer.h b/Source/WebCore/rendering/svg/RenderSVGResourceContainer.h index 0f074863b..0f425cd45 100644 --- a/Source/WebCore/rendering/svg/RenderSVGResourceContainer.h +++ b/Source/WebCore/rendering/svg/RenderSVGResourceContainer.h @@ -57,6 +57,7 @@ protected: // Used from the invalidateClient/invalidateClients methods from classes, inheriting from us. void markAllClientsForInvalidation(InvalidationMode); + void markAllClientLayersForInvalidation(); void markClientForInvalidation(RenderObject*, InvalidationMode); private: diff --git a/Source/WebCore/rendering/svg/RenderSVGResourceFilter.cpp b/Source/WebCore/rendering/svg/RenderSVGResourceFilter.cpp index 7d4eedfc9..c5e6bbb26 100644 --- a/Source/WebCore/rendering/svg/RenderSVGResourceFilter.cpp +++ b/Source/WebCore/rendering/svg/RenderSVGResourceFilter.cpp @@ -49,8 +49,6 @@ #include "Settings.h" #include "SourceAlpha.h" #include "SourceGraphic.h" - -#include <wtf/UnusedParam.h> #include <wtf/Vector.h> using namespace std; @@ -99,7 +97,7 @@ void RenderSVGResourceFilter::removeClientFromCache(RenderObject* client, bool m PassRefPtr<SVGFilterBuilder> RenderSVGResourceFilter::buildPrimitives(SVGFilter* filter) { - SVGFilterElement* filterElement = static_cast<SVGFilterElement*>(node()); + SVGFilterElement* filterElement = toSVGFilterElement(node()); FloatRect targetBoundingBox = filter->targetBoundingBox(); // Add effects to the builder @@ -108,7 +106,7 @@ PassRefPtr<SVGFilterBuilder> RenderSVGResourceFilter::buildPrimitives(SVGFilter* if (!node->isSVGElement()) continue; - SVGElement* element = static_cast<SVGElement*>(node); + SVGElement* element = toSVGElement(node); if (!element->isFilterEffect()) continue; @@ -121,8 +119,8 @@ PassRefPtr<SVGFilterBuilder> RenderSVGResourceFilter::buildPrimitives(SVGFilter* builder->appendEffectToEffectReferences(effect, effectElement->renderer()); effectElement->setStandardAttributes(effect.get()); effect->setEffectBoundaries(SVGLengthContext::resolveRectangle<SVGFilterPrimitiveStandardAttributes>(effectElement, filterElement->primitiveUnits(), targetBoundingBox)); - effect->setColorSpace(effectElement->renderer()->style()->svgStyle()->colorInterpolationFilters() == CI_LINEARRGB - ? ColorSpaceLinearRGB : ColorSpaceDeviceRGB); + effect->setOperatingColorSpace( + effectElement->renderer()->style()->svgStyle()->colorInterpolationFilters() == CI_LINEARRGB ? ColorSpaceLinearRGB : ColorSpaceDeviceRGB); builder->add(effectElement->result(), effect); } return builder.release(); @@ -159,14 +157,14 @@ bool RenderSVGResourceFilter::applyResource(RenderObject* object, RenderStyle*, OwnPtr<FilterData> filterData(adoptPtr(new FilterData)); FloatRect targetBoundingBox = object->objectBoundingBox(); - SVGFilterElement* filterElement = static_cast<SVGFilterElement*>(node()); + SVGFilterElement* filterElement = toSVGFilterElement(node()); filterData->boundaries = SVGLengthContext::resolveRectangle<SVGFilterElement>(filterElement, filterElement->filterUnits(), targetBoundingBox); if (filterData->boundaries.isEmpty()) return false; // Determine absolute transformation matrix for filter. AffineTransform absoluteTransform; - SVGRenderingContext::calculateTransformationToOutermostSVGCoordinateSystem(object, absoluteTransform); + SVGRenderingContext::calculateTransformationToOutermostCoordinateSystem(object, absoluteTransform); if (!absoluteTransform.isInvertible()) return false; @@ -175,9 +173,9 @@ bool RenderSVGResourceFilter::applyResource(RenderObject* object, RenderStyle*, // Determine absolute boundaries of the filter and the drawing region. FloatRect absoluteFilterBoundaries = filterData->shearFreeAbsoluteTransform.mapRect(filterData->boundaries); - FloatRect drawingRegion = object->strokeBoundingBox(); - drawingRegion.intersect(filterData->boundaries); - FloatRect absoluteDrawingRegion = filterData->shearFreeAbsoluteTransform.mapRect(drawingRegion); + filterData->drawingRegion = object->strokeBoundingBox(); + filterData->drawingRegion.intersect(filterData->boundaries); + FloatRect absoluteDrawingRegion = filterData->shearFreeAbsoluteTransform.mapRect(filterData->drawingRegion); // Create the SVGFilter object. bool primitiveBoundingBoxMode = filterElement->primitiveUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX; @@ -222,7 +220,7 @@ bool RenderSVGResourceFilter::applyResource(RenderObject* object, RenderStyle*, // If the drawingRegion is empty, we have something like <g filter=".."/>. // Even if the target objectBoundingBox() is empty, we still have to draw the last effect result image in postApplyResource. - if (drawingRegion.isEmpty()) { + if (filterData->drawingRegion.isEmpty()) { ASSERT(!m_filter.contains(object)); filterData->savedContext = context; m_filter.set(object, filterData.leakPtr()); @@ -236,7 +234,7 @@ bool RenderSVGResourceFilter::applyResource(RenderObject* object, RenderStyle*, OwnPtr<ImageBuffer> sourceGraphic; RenderingMode renderingMode = object->document()->page()->settings()->acceleratedFiltersEnabled() ? Accelerated : Unaccelerated; - if (!SVGRenderingContext::createImageBuffer(drawingRegion, effectiveTransform, sourceGraphic, ColorSpaceLinearRGB, renderingMode)) { + if (!SVGRenderingContext::createImageBuffer(filterData->drawingRegion, effectiveTransform, sourceGraphic, ColorSpaceLinearRGB, renderingMode)) { ASSERT(!m_filter.contains(object)); filterData->savedContext = context; m_filter.set(object, filterData.leakPtr()); @@ -309,13 +307,9 @@ void RenderSVGResourceFilter::postApplyResource(RenderObject* object, GraphicsCo // Always true if filterData is just built (filterData->state == FilterData::Built). if (!lastEffect->hasResult()) { filterData->state = FilterData::Applying; - lastEffect->apply(); + lastEffect->applyAll(); lastEffect->correctFilterResultIfNeeded(); -#if !USE(CG) - ImageBuffer* resultImage = lastEffect->asImageBuffer(); - if (resultImage) - resultImage->transformColorSpace(lastEffect->colorSpace(), ColorSpaceDeviceRGB); -#endif + lastEffect->transformResultColorSpace(ColorSpaceDeviceRGB); } filterData->state = FilterData::Built; @@ -335,7 +329,7 @@ void RenderSVGResourceFilter::postApplyResource(RenderObject* object, GraphicsCo FloatRect RenderSVGResourceFilter::resourceBoundingBox(RenderObject* object) { - if (SVGFilterElement* element = static_cast<SVGFilterElement*>(node())) + if (SVGFilterElement* element = toSVGFilterElement(node())) return SVGLengthContext::resolveRectangle<SVGFilterElement>(element, element->filterUnits(), object->objectBoundingBox()); return FloatRect(); @@ -365,6 +359,13 @@ void RenderSVGResourceFilter::primitiveAttributeChanged(RenderObject* object, co // Repaint the image on the screen. markClientForInvalidation(it->key, RepaintInvalidation); } + markAllClientLayersForInvalidation(); +} + +FloatRect RenderSVGResourceFilter::drawingRegion(RenderObject* object) const +{ + FilterData* filterData = m_filter.get(object); + return filterData ? filterData->drawingRegion : FloatRect(); } } diff --git a/Source/WebCore/rendering/svg/RenderSVGResourceFilter.h b/Source/WebCore/rendering/svg/RenderSVGResourceFilter.h index db422c17e..4ad9ad037 100644 --- a/Source/WebCore/rendering/svg/RenderSVGResourceFilter.h +++ b/Source/WebCore/rendering/svg/RenderSVGResourceFilter.h @@ -56,6 +56,7 @@ public: GraphicsContext* savedContext; AffineTransform shearFreeAbsoluteTransform; FloatRect boundaries; + FloatRect drawingRegion; FloatSize scale; FilterDataState state; }; @@ -68,7 +69,7 @@ public: virtual ~RenderSVGResourceFilter(); virtual const char* renderName() const { return "RenderSVGResourceFilter"; } - virtual bool isSVGResourceFilter() const { return true; } + virtual bool isSVGResourceFilter() const OVERRIDE { return true; } virtual void removeAllClientsFromCache(bool markForInvalidation = true); virtual void removeClientFromCache(RenderObject*, bool markForInvalidation = true); @@ -80,20 +81,27 @@ public: PassRefPtr<SVGFilterBuilder> buildPrimitives(SVGFilter*); - SVGUnitTypes::SVGUnitType filterUnits() const { return static_cast<SVGFilterElement*>(node())->filterUnits(); } - SVGUnitTypes::SVGUnitType primitiveUnits() const { return static_cast<SVGFilterElement*>(node())->primitiveUnits(); } + SVGUnitTypes::SVGUnitType filterUnits() const { return toSVGFilterElement(node())->filterUnits(); } + SVGUnitTypes::SVGUnitType primitiveUnits() const { return toSVGFilterElement(node())->primitiveUnits(); } void primitiveAttributeChanged(RenderObject*, const QualifiedName&); virtual RenderSVGResourceType resourceType() const { return s_resourceType; } static RenderSVGResourceType s_resourceType; + FloatRect drawingRegion(RenderObject*) const; private: bool fitsInMaximumImageSize(const FloatSize&, FloatSize&); HashMap<RenderObject*, FilterData*> m_filter; }; +inline RenderSVGResourceFilter* toRenderSVGFilter(RenderObject* object) +{ + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isSVGResourceFilter()); + return static_cast<RenderSVGResourceFilter*>(object); +} + } #endif diff --git a/Source/WebCore/rendering/svg/RenderSVGResourceFilterPrimitive.cpp b/Source/WebCore/rendering/svg/RenderSVGResourceFilterPrimitive.cpp index f3da929ef..b5177fad9 100644 --- a/Source/WebCore/rendering/svg/RenderSVGResourceFilterPrimitive.cpp +++ b/Source/WebCore/rendering/svg/RenderSVGResourceFilterPrimitive.cpp @@ -53,12 +53,12 @@ void RenderSVGResourceFilterPrimitive::styleDidChange(StyleDifference diff, cons const SVGRenderStyle* newStyle = this->style()->svgStyle(); if (node()->hasTagName(SVGNames::feFloodTag)) { if (newStyle->floodColor() != oldStyle->svgStyle()->floodColor()) - static_cast<RenderSVGResourceFilter*>(filter)->primitiveAttributeChanged(this, SVGNames::flood_colorAttr); + toRenderSVGFilter(filter)->primitiveAttributeChanged(this, SVGNames::flood_colorAttr); if (newStyle->floodOpacity() != oldStyle->svgStyle()->floodOpacity()) - static_cast<RenderSVGResourceFilter*>(filter)->primitiveAttributeChanged(this, SVGNames::flood_opacityAttr); + toRenderSVGFilter(filter)->primitiveAttributeChanged(this, SVGNames::flood_opacityAttr); } else if (node()->hasTagName(SVGNames::feDiffuseLightingTag) || node()->hasTagName(SVGNames::feSpecularLightingTag)) { if (newStyle->lightingColor() != oldStyle->svgStyle()->lightingColor()) - static_cast<RenderSVGResourceFilter*>(filter)->primitiveAttributeChanged(this, SVGNames::lighting_colorAttr); + toRenderSVGFilter(filter)->primitiveAttributeChanged(this, SVGNames::lighting_colorAttr); } } diff --git a/Source/WebCore/rendering/svg/RenderSVGResourceGradient.cpp b/Source/WebCore/rendering/svg/RenderSVGResourceGradient.cpp index 30ce1b8cb..25a61239f 100644 --- a/Source/WebCore/rendering/svg/RenderSVGResourceGradient.cpp +++ b/Source/WebCore/rendering/svg/RenderSVGResourceGradient.cpp @@ -31,7 +31,6 @@ #include "RenderSVGText.h" #include "SVGRenderSupport.h" #include "SVGRenderingContext.h" -#include <wtf/UnusedParam.h> namespace WebCore { @@ -68,7 +67,7 @@ static inline bool createMaskAndSwapContextForTextGradient(GraphicsContext*& con ASSERT(textRootBlock); AffineTransform absoluteTransform; - SVGRenderingContext::calculateTransformationToOutermostSVGCoordinateSystem(textRootBlock, absoluteTransform); + SVGRenderingContext::calculateTransformationToOutermostCoordinateSystem(textRootBlock, absoluteTransform); FloatRect repaintRect = textRootBlock->repaintRectInLocalCoordinates(); OwnPtr<ImageBuffer> maskImage; @@ -95,7 +94,7 @@ static inline AffineTransform clipToTextMask(GraphicsContext* context, ASSERT(textRootBlock); AffineTransform absoluteTransform; - SVGRenderingContext::calculateTransformationToOutermostSVGCoordinateSystem(textRootBlock, absoluteTransform); + SVGRenderingContext::calculateTransformationToOutermostCoordinateSystem(textRootBlock, absoluteTransform); targetRect = textRootBlock->repaintRectInLocalCoordinates(); SVGRenderingContext::clipToImageBuffer(context, absoluteTransform, targetRect, imageBuffer, false); @@ -122,12 +121,12 @@ bool RenderSVGResourceGradient::applyResource(RenderObject* object, RenderStyle* // Otherwhise the call to collectGradientAttributes() in createTileImage(), may cause the SVG DOM property // synchronization to kick in, which causes removeAllClientsFromCache() to be called, which in turn deletes our // GradientData object! Leaving out the line below will cause svg/dynamic-updates/SVG*GradientElement-svgdom* to crash. - SVGGradientElement* gradientElement = static_cast<SVGGradientElement*>(node()); + SVGGradientElement* gradientElement = toSVGGradientElement(node()); if (!gradientElement) return false; if (m_shouldCollectGradientAttributes) { - gradientElement->updateAnimatedSVGAttribute(anyQName()); + gradientElement->synchronizeAnimatedSVGAttribute(anyQName()); if (!collectGradientAttributes(gradientElement)) return false; diff --git a/Source/WebCore/rendering/svg/RenderSVGResourceMarker.cpp b/Source/WebCore/rendering/svg/RenderSVGResourceMarker.cpp index 7aaef0e9e..bcf517aef 100644 --- a/Source/WebCore/rendering/svg/RenderSVGResourceMarker.cpp +++ b/Source/WebCore/rendering/svg/RenderSVGResourceMarker.cpp @@ -28,10 +28,11 @@ #include "RenderSVGContainer.h" #include "RenderSVGRoot.h" #include "SVGElement.h" +#include "SVGGraphicsElement.h" #include "SVGMarkerElement.h" #include "SVGRenderSupport.h" #include "SVGStyledElement.h" -#include "SVGStyledTransformableElement.h" +#include <wtf/StackStats.h> namespace WebCore { @@ -96,7 +97,7 @@ const AffineTransform& RenderSVGResourceMarker::localToParentTransform() const FloatPoint RenderSVGResourceMarker::referencePoint() const { - SVGMarkerElement* marker = static_cast<SVGMarkerElement*>(node()); + SVGMarkerElement* marker = toSVGMarkerElement(node()); ASSERT(marker); SVGLengthContext lengthContext(marker); @@ -105,7 +106,7 @@ FloatPoint RenderSVGResourceMarker::referencePoint() const float RenderSVGResourceMarker::angle() const { - SVGMarkerElement* marker = static_cast<SVGMarkerElement*>(node()); + SVGMarkerElement* marker = toSVGMarkerElement(node()); ASSERT(marker); float angle = -1; @@ -117,7 +118,7 @@ float RenderSVGResourceMarker::angle() const AffineTransform RenderSVGResourceMarker::markerTransformation(const FloatPoint& origin, float autoAngle, float strokeWidth) const { - SVGMarkerElement* marker = static_cast<SVGMarkerElement*>(node()); + SVGMarkerElement* marker = toSVGMarkerElement(node()); ASSERT(marker); float markerAngle = angle(); @@ -132,6 +133,12 @@ AffineTransform RenderSVGResourceMarker::markerTransformation(const FloatPoint& void RenderSVGResourceMarker::draw(PaintInfo& paintInfo, const AffineTransform& transform) { + // An empty viewBox disables rendering. + SVGMarkerElement* marker = toSVGMarkerElement(toSVGElement(node())); + ASSERT(marker); + if (marker->hasAttribute(SVGNames::viewBoxAttr) && marker->viewBoxIsValid() && marker->viewBox().isEmpty()) + return; + PaintInfo info(paintInfo); GraphicsContextStateSaver stateSaver(*info.context); info.applyTransform(transform); @@ -153,7 +160,7 @@ AffineTransform RenderSVGResourceMarker::markerContentTransformation(const Affin AffineTransform RenderSVGResourceMarker::viewportTransform() const { - SVGMarkerElement* marker = static_cast<SVGMarkerElement*>(node()); + SVGMarkerElement* marker = toSVGMarkerElement(node()); ASSERT(marker); return marker->viewBoxToViewTransform(m_viewport.width(), m_viewport.height()); @@ -164,7 +171,7 @@ void RenderSVGResourceMarker::calcViewport() if (!selfNeedsLayout()) return; - SVGMarkerElement* marker = static_cast<SVGMarkerElement*>(node()); + SVGMarkerElement* marker = toSVGMarkerElement(node()); ASSERT(marker); SVGLengthContext lengthContext(marker); diff --git a/Source/WebCore/rendering/svg/RenderSVGResourceMarker.h b/Source/WebCore/rendering/svg/RenderSVGResourceMarker.h index 26fbc6f93..1838450cf 100644 --- a/Source/WebCore/rendering/svg/RenderSVGResourceMarker.h +++ b/Source/WebCore/rendering/svg/RenderSVGResourceMarker.h @@ -60,7 +60,7 @@ public: FloatPoint referencePoint() const; float angle() const; - SVGMarkerUnitsType markerUnits() const { return static_cast<SVGMarkerElement*>(node())->markerUnits(); } + SVGMarkerUnitsType markerUnits() const { return toSVGMarkerElement(node())->markerUnits(); } virtual RenderSVGResourceType resourceType() const { return s_resourceType; } static RenderSVGResourceType s_resourceType; diff --git a/Source/WebCore/rendering/svg/RenderSVGResourceMasker.cpp b/Source/WebCore/rendering/svg/RenderSVGResourceMasker.cpp index 1f501a88b..fa66dfa7b 100644 --- a/Source/WebCore/rendering/svg/RenderSVGResourceMasker.cpp +++ b/Source/WebCore/rendering/svg/RenderSVGResourceMasker.cpp @@ -36,8 +36,6 @@ #include "SVGRenderingContext.h" #include "SVGStyledElement.h" #include "SVGUnitTypes.h" - -#include <wtf/UnusedParam.h> #include <wtf/Vector.h> namespace WebCore { @@ -92,7 +90,7 @@ bool RenderSVGResourceMasker::applyResource(RenderObject* object, RenderStyle*, MaskerData* maskerData = m_masker.get(object); AffineTransform absoluteTransform; - SVGRenderingContext::calculateTransformationToOutermostSVGCoordinateSystem(object, absoluteTransform); + SVGRenderingContext::calculateTransformationToOutermostCoordinateSystem(object, absoluteTransform); FloatRect repaintRect = object->repaintRectInLocalCoordinates(); @@ -137,7 +135,7 @@ bool RenderSVGResourceMasker::drawContentIntoMaskImage(MaskerData* maskerData, C // Draw the content into the ImageBuffer. for (Node* node = maskElement->firstChild(); node; node = node->nextSibling()) { RenderObject* renderer = node->renderer(); - if (!node->isSVGElement() || !static_cast<SVGElement*>(node)->isStyled() || !renderer) + if (!node->isSVGElement() || !toSVGElement(node)->isSVGStyledElement() || !renderer) continue; if (renderer->needsLayout()) return false; @@ -166,7 +164,7 @@ void RenderSVGResourceMasker::calculateMaskContentRepaintRect() { for (Node* childNode = node()->firstChild(); childNode; childNode = childNode->nextSibling()) { RenderObject* renderer = childNode->renderer(); - if (!childNode->isSVGElement() || !static_cast<SVGElement*>(childNode)->isStyled() || !renderer) + if (!childNode->isSVGElement() || !toSVGElement(childNode)->isSVGStyledElement() || !renderer) continue; RenderStyle* style = renderer->style(); if (!style || style->display() == NONE || style->visibility() != VISIBLE) diff --git a/Source/WebCore/rendering/svg/RenderSVGResourcePattern.cpp b/Source/WebCore/rendering/svg/RenderSVGResourcePattern.cpp index 55dbb8319..4df4c22a9 100644 --- a/Source/WebCore/rendering/svg/RenderSVGResourcePattern.cpp +++ b/Source/WebCore/rendering/svg/RenderSVGResourcePattern.cpp @@ -66,7 +66,7 @@ PatternData* RenderSVGResourcePattern::buildPattern(RenderObject* object, unsign return 0; if (m_shouldCollectPatternAttributes) { - patternElement->updateAnimatedSVGAttribute(anyQName()); + patternElement->synchronizeAnimatedSVGAttribute(anyQName()); m_attributes = PatternAttributes(); patternElement->collectPatternAttributes(m_attributes); @@ -77,6 +77,10 @@ PatternData* RenderSVGResourcePattern::buildPattern(RenderObject* object, unsign if (!m_attributes.patternContentElement()) return 0; + // An empty viewBox disables rendering. + if (m_attributes.hasViewBox() && m_attributes.viewBox().isEmpty()) + return 0; + // Compute all necessary transformations to build the tile image & the pattern. FloatRect tileBoundaries; AffineTransform tileImageTransform; @@ -84,7 +88,7 @@ PatternData* RenderSVGResourcePattern::buildPattern(RenderObject* object, unsign return 0; AffineTransform absoluteTransformIgnoringRotation; - SVGRenderingContext::calculateTransformationToOutermostSVGCoordinateSystem(object, absoluteTransformIgnoringRotation); + SVGRenderingContext::calculateTransformationToOutermostCoordinateSystem(object, absoluteTransformIgnoringRotation); // Ignore 2D rotation, as it doesn't affect the size of the tile. SVGRenderingContext::clear2DRotation(absoluteTransformIgnoringRotation); @@ -269,7 +273,7 @@ PassOwnPtr<ImageBuffer> RenderSVGResourcePattern::createTileImage(const PatternA // Draw the content into the ImageBuffer. for (Node* node = attributes.patternContentElement()->firstChild(); node; node = node->nextSibling()) { - if (!node->isSVGElement() || !static_cast<SVGElement*>(node)->isStyled() || !node->renderer()) + if (!node->isSVGElement() || !toSVGElement(node)->isSVGStyledElement() || !node->renderer()) continue; if (node->renderer()->needsLayout()) return nullptr; diff --git a/Source/WebCore/rendering/svg/RenderSVGResourceSolidColor.cpp b/Source/WebCore/rendering/svg/RenderSVGResourceSolidColor.cpp index a81c3698d..ca5cad4c5 100644 --- a/Source/WebCore/rendering/svg/RenderSVGResourceSolidColor.cpp +++ b/Source/WebCore/rendering/svg/RenderSVGResourceSolidColor.cpp @@ -29,10 +29,6 @@ #include "RenderStyle.h" #include "SVGRenderSupport.h" -#if USE(SKIA) -#include "PlatformContextSkia.h" -#endif - namespace WebCore { RenderSVGResourceType RenderSVGResourceSolidColor::s_resourceType = SolidColorResourceType; diff --git a/Source/WebCore/rendering/svg/RenderSVGRoot.cpp b/Source/WebCore/rendering/svg/RenderSVGRoot.cpp index 048c4f604..c3d159734 100644 --- a/Source/WebCore/rendering/svg/RenderSVGRoot.cpp +++ b/Source/WebCore/rendering/svg/RenderSVGRoot.cpp @@ -46,6 +46,7 @@ #include "SVGStyledElement.h" #include "SVGViewSpec.h" #include "TransformState.h" +#include <wtf/StackStats.h> #if ENABLE(FILTERS) #include "RenderSVGResourceFilter.h" @@ -77,7 +78,8 @@ void RenderSVGRoot::computeIntrinsicRatioInformation(FloatSize& intrinsicSize, d // the same as the CSS width and height properties. Specifically, percentage values do not provide an intrinsic width or height, // and do not indicate a percentage of the containing block. Rather, once the viewport is established, they indicate the portion // of the viewport that is actually covered by image data. - SVGSVGElement* svg = static_cast<SVGSVGElement*>(node()); + SVGSVGElement* svg = toSVGSVGElement(node()); + ASSERT(svg); Length intrinsicWidthAttribute = svg->intrinsicWidth(SVGSVGElement::IgnoreCSSProperties); Length intrinsicHeightAttribute = svg->intrinsicHeight(SVGSVGElement::IgnoreCSSProperties); @@ -128,10 +130,10 @@ bool RenderSVGRoot::isEmbeddedThroughSVGImage() const return false; // Test whether we're embedded through an img. - if (!frame->page() || !frame->page()->chrome()) + if (!frame->page()) return false; - ChromeClient* chromeClient = frame->page()->chrome()->client(); + ChromeClient* chromeClient = frame->page()->chrome().client(); if (!chromeClient || !chromeClient->isSVGImageChromeClient()) return false; @@ -159,9 +161,9 @@ static inline LayoutUnit resolveLengthAttributeForSVG(const Length& length, floa return static_cast<LayoutUnit>(valueForLength(length, maxSize, renderView) * (length.isFixed() ? scale : 1)); } -LayoutUnit RenderSVGRoot::computeReplacedLogicalWidth(bool includeMaxWidth) const +LayoutUnit RenderSVGRoot::computeReplacedLogicalWidth(ShouldComputePreferred shouldComputePreferred) const { - SVGSVGElement* svg = static_cast<SVGSVGElement*>(node()); + SVGSVGElement* svg = toSVGSVGElement(node()); ASSERT(svg); // When we're embedded through SVGImage (border-image/background-image/<html:img>/...) we're forced to resize to a specific size. @@ -169,7 +171,7 @@ LayoutUnit RenderSVGRoot::computeReplacedLogicalWidth(bool includeMaxWidth) cons return m_containerSize.width(); if (style()->logicalWidth().isSpecified() || style()->logicalMaxWidth().isSpecified()) - return RenderReplaced::computeReplacedLogicalWidth(includeMaxWidth); + return RenderReplaced::computeReplacedLogicalWidth(shouldComputePreferred); if (svg->widthAttributeEstablishesViewport()) return resolveLengthAttributeForSVG(svg->intrinsicWidth(SVGSVGElement::IgnoreCSSProperties), style()->effectiveZoom(), containingBlock()->availableLogicalWidth(), view()); @@ -179,12 +181,12 @@ LayoutUnit RenderSVGRoot::computeReplacedLogicalWidth(bool includeMaxWidth) cons return document()->frame()->ownerRenderer()->availableLogicalWidth(); // SVG embedded via SVGImage (background-image/border-image/etc) / Inline SVG. - return RenderReplaced::computeReplacedLogicalWidth(includeMaxWidth); + return RenderReplaced::computeReplacedLogicalWidth(shouldComputePreferred); } LayoutUnit RenderSVGRoot::computeReplacedLogicalHeight() const { - SVGSVGElement* svg = static_cast<SVGSVGElement*>(node()); + SVGSVGElement* svg = toSVGSVGElement(node()); ASSERT(svg); // When we're embedded through SVGImage (border-image/background-image/<html:img>/...) we're forced to resize to a specific size. @@ -206,12 +208,12 @@ LayoutUnit RenderSVGRoot::computeReplacedLogicalHeight() const } else RenderBlock::removePercentHeightDescendant(const_cast<RenderSVGRoot*>(this)); - return resolveLengthAttributeForSVG(height, style()->effectiveZoom(), containingBlock()->availableLogicalHeight(), view()); + return resolveLengthAttributeForSVG(height, style()->effectiveZoom(), containingBlock()->availableLogicalHeight(IncludeMarginBorderPadding), view()); } // SVG embedded through object/embed/iframe. if (isEmbeddedThroughFrameContainingSVGDocument()) - return document()->frame()->ownerRenderer()->availableLogicalHeight(); + return document()->frame()->ownerRenderer()->availableLogicalHeight(IncludeMarginBorderPadding); // SVG embedded via SVGImage (background-image/border-image/etc) / Inline SVG. return RenderReplaced::computeReplacedLogicalHeight(); @@ -235,7 +237,8 @@ void RenderSVGRoot::layout() updateLogicalHeight(); buildLocalToBorderBoxTransform(); - SVGSVGElement* svg = static_cast<SVGSVGElement*>(node()); + SVGSVGElement* svg = toSVGSVGElement(node()); + ASSERT(svg); m_isLayoutSizeChanged = needsLayout || (svg->hasRelativeLengths() && oldSize != size()); SVGRenderSupport::layoutChildren(this, needsLayout || SVGRenderSupport::filtersForceContainerLayout(this)); @@ -256,6 +259,8 @@ void RenderSVGRoot::layout() m_needsBoundariesOrTransformUpdate = false; } + updateLayerTransform(); + repainter.repaintAfterLayout(); setNeedsLayout(false); @@ -267,10 +272,17 @@ void RenderSVGRoot::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paint if (pixelSnappedBorderBoxRect().isEmpty()) return; - // Don't paint, if the context explicitely disabled it. + // Don't paint, if the context explicitly disabled it. if (paintInfo.context->paintingDisabled()) return; + // An empty viewBox also disables rendering. + // (http://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute) + SVGSVGElement* svg = toSVGSVGElement(node()); + ASSERT(svg); + if (svg->hasEmptyViewBox()) + return; + Page* page = 0; if (Frame* frame = this->frame()) page = frame->page(); @@ -354,12 +366,13 @@ void RenderSVGRoot::removeChild(RenderObject* child) // relative to our borderBox origin. This method gives us exactly that. void RenderSVGRoot::buildLocalToBorderBoxTransform() { - SVGSVGElement* svg = static_cast<SVGSVGElement*>(node()); + SVGSVGElement* svg = toSVGSVGElement(node()); + ASSERT(svg); float scale = style()->effectiveZoom(); - FloatPoint translate = svg->currentTranslate(); + SVGPoint translate = svg->currentTranslate(); LayoutSize borderAndPadding(borderLeft() + paddingLeft(), borderTop() + paddingTop()); m_localToBorderBoxTransform = svg->viewBoxToViewTransform(contentWidth() / scale, contentHeight() / scale); - if (borderAndPadding.isEmpty() && scale == 1 && translate == FloatPoint::zero()) + if (borderAndPadding.isEmpty() && scale == 1 && translate == SVGPoint::zero()) return; m_localToBorderBoxTransform = AffineTransform(scale, 0, 0, scale, borderAndPadding.width() + translate.x(), borderAndPadding.height() + translate.y()) * m_localToBorderBoxTransform; } @@ -463,15 +476,22 @@ bool RenderSVGRoot::nodeAtPoint(const HitTestRequest& request, HitTestResult& re bool RenderSVGRoot::hasRelativeDimensions() const { - SVGSVGElement* svg = static_cast<SVGSVGElement*>(node()); + SVGSVGElement* svg = toSVGSVGElement(node()); ASSERT(svg); return svg->intrinsicHeight(SVGSVGElement::IgnoreCSSProperties).isPercent() || svg->intrinsicWidth(SVGSVGElement::IgnoreCSSProperties).isPercent(); } +bool RenderSVGRoot::hasRelativeIntrinsicLogicalWidth() const +{ + SVGSVGElement* svg = toSVGSVGElement(node()); + ASSERT(svg); + return svg->intrinsicWidth(SVGSVGElement::IgnoreCSSProperties).isPercent(); +} + bool RenderSVGRoot::hasRelativeLogicalHeight() const { - SVGSVGElement* svg = static_cast<SVGSVGElement*>(node()); + SVGSVGElement* svg = toSVGSVGElement(node()); ASSERT(svg); return svg->intrinsicHeight(SVGSVGElement::IgnoreCSSProperties).isPercent(); @@ -484,7 +504,7 @@ void RenderSVGRoot::addResourceForClientInvalidation(RenderSVGResourceContainer* svgRoot = svgRoot->parent(); if (!svgRoot) return; - static_cast<RenderSVGRoot*>(svgRoot)->m_resourcesNeedingToInvalidateClients.add(resource); + toRenderSVGRoot(svgRoot)->m_resourcesNeedingToInvalidateClients.add(resource); } } diff --git a/Source/WebCore/rendering/svg/RenderSVGRoot.h b/Source/WebCore/rendering/svg/RenderSVGRoot.h index 38e89179e..e218e291e 100644 --- a/Source/WebCore/rendering/svg/RenderSVGRoot.h +++ b/Source/WebCore/rendering/svg/RenderSVGRoot.h @@ -52,13 +52,15 @@ public: bool isLayoutSizeChanged() const { return m_isLayoutSizeChanged; } virtual void setNeedsBoundariesUpdate() { m_needsBoundariesOrTransformUpdate = true; } + virtual bool needsBoundariesUpdate() OVERRIDE { return m_needsBoundariesOrTransformUpdate; } virtual void setNeedsTransformUpdate() { m_needsBoundariesOrTransformUpdate = true; } IntSize containerSize() const { return m_containerSize; } void setContainerSize(const IntSize& containerSize) { m_containerSize = containerSize; } - virtual bool hasRelativeDimensions() const; - virtual bool hasRelativeLogicalHeight() const; + virtual bool hasRelativeDimensions() const OVERRIDE; + virtual bool hasRelativeIntrinsicLogicalWidth() const OVERRIDE; + virtual bool hasRelativeLogicalHeight() const OVERRIDE; // localToBorderBoxTransform maps local SVG viewport coordinates to local CSS box coordinates. const AffineTransform& localToBorderBoxTransform() const { return m_localToBorderBoxTransform; } @@ -77,7 +79,7 @@ private: virtual bool isSVGRoot() const { return true; } virtual const char* renderName() const { return "RenderSVGRoot"; } - virtual LayoutUnit computeReplacedLogicalWidth(bool includeMaxWidth = true) const; + virtual LayoutUnit computeReplacedLogicalWidth(ShouldComputePreferred = ComputeActual) const OVERRIDE; virtual LayoutUnit computeReplacedLogicalHeight() const; virtual void layout(); virtual void paintReplaced(PaintInfo&, const LayoutPoint&); @@ -103,7 +105,7 @@ private: virtual LayoutRect clippedOverflowRectForRepaint(const RenderLayerModelObject* repaintContainer) const OVERRIDE; virtual void computeFloatRectForRepaint(const RenderLayerModelObject* repaintContainer, FloatRect& repaintRect, bool fixed) const OVERRIDE; - virtual void mapLocalToContainer(const RenderLayerModelObject* repaintContainer, TransformState&, MapCoordinatesFlags = ApplyContainerFlip | SnapOffsetForTransforms, bool* wasFixed = 0) const OVERRIDE; + virtual void mapLocalToContainer(const RenderLayerModelObject* repaintContainer, TransformState&, MapCoordinatesFlags = ApplyContainerFlip, bool* wasFixed = 0) const OVERRIDE; virtual const RenderObject* pushMappingToContainer(const RenderLayerModelObject* ancestorToStopAt, RenderGeometryMap&) const OVERRIDE; virtual bool canBeSelectionLeaf() const { return false; } @@ -129,13 +131,13 @@ private: inline RenderSVGRoot* toRenderSVGRoot(RenderObject* object) { - ASSERT(!object || object->isSVGRoot()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isSVGRoot()); return static_cast<RenderSVGRoot*>(object); } inline const RenderSVGRoot* toRenderSVGRoot(const RenderObject* object) { - ASSERT(!object || object->isSVGRoot()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isSVGRoot()); return static_cast<const RenderSVGRoot*>(object); } diff --git a/Source/WebCore/rendering/svg/RenderSVGShape.cpp b/Source/WebCore/rendering/svg/RenderSVGShape.cpp index 7e329b2b7..8070a392a 100644 --- a/Source/WebCore/rendering/svg/RenderSVGShape.cpp +++ b/Source/WebCore/rendering/svg/RenderSVGShape.cpp @@ -39,23 +39,24 @@ #include "RenderSVGContainer.h" #include "RenderSVGResourceMarker.h" #include "RenderSVGResourceSolidColor.h" +#include "SVGGraphicsElement.h" #include "SVGPathData.h" #include "SVGRenderingContext.h" #include "SVGResources.h" #include "SVGResourcesCache.h" -#include "SVGStyledTransformableElement.h" #include "SVGTransformList.h" #include "SVGURIReference.h" #include "StrokeStyleApplier.h" #include <wtf/MathExtras.h> +#include <wtf/StackStats.h> namespace WebCore { -RenderSVGShape::RenderSVGShape(SVGStyledTransformableElement* node) +RenderSVGShape::RenderSVGShape(SVGGraphicsElement* node) : RenderSVGModelObject(node) , m_needsBoundariesUpdate(false) // Default is false, the cached rects are empty from the beginning. - , m_needsShapeUpdate(true) // Default is true, so we grab a Path object once from SVGStyledTransformableElement. - , m_needsTransformUpdate(true) // Default is true, so we grab a AffineTransform object once from SVGStyledTransformableElement. + , m_needsShapeUpdate(true) // Default is true, so we grab a Path object once from SVGGraphicsElement. + , m_needsTransformUpdate(true) // Default is true, so we grab a AffineTransform object once from SVGGraphicsElement. { } @@ -69,7 +70,7 @@ void RenderSVGShape::updateShapeFromElement() m_path = adoptPtr(new Path); ASSERT(RenderSVGShape::isEmpty()); - SVGStyledTransformableElement* element = static_cast<SVGStyledTransformableElement*>(node()); + SVGGraphicsElement* element = toSVGGraphicsElement(node()); updatePathFromGraphicsElement(element, path()); processMarkerPositions(); @@ -146,7 +147,7 @@ void RenderSVGShape::layout() { StackStats::LayoutCheckPoint layoutCheckPoint; LayoutRepainter repainter(*this, SVGRenderSupport::checkForSVGRepaintDuringLayout(this) && selfNeedsLayout()); - SVGStyledTransformableElement* element = static_cast<SVGStyledTransformableElement*>(node()); + SVGGraphicsElement* element = toSVGGraphicsElement(node()); bool updateCachedBoundariesInParents = false; @@ -198,7 +199,7 @@ bool RenderSVGShape::setupNonScalingStrokeContext(AffineTransform& strokeTransfo AffineTransform RenderSVGShape::nonScalingStrokeTransform() const { - SVGStyledTransformableElement* element = static_cast<SVGStyledTransformableElement*>(node()); + SVGGraphicsElement* element = toSVGGraphicsElement(node()); return element->getScreenCTM(SVGLocatable::DisallowStyleUpdate); } @@ -207,7 +208,7 @@ bool RenderSVGShape::shouldGenerateMarkerPositions() const if (!style()->svgStyle()->hasMarkers()) return false; - SVGStyledTransformableElement* element = static_cast<SVGStyledTransformableElement*>(node()); + SVGGraphicsElement* element = toSVGGraphicsElement(node()); if (!element->supportsMarkers()) return false; @@ -258,7 +259,6 @@ void RenderSVGShape::fillAndStrokeShape(GraphicsContext* context) return; GraphicsContextStateSaver stateSaver(*context, false); - AffineTransform nonScalingTransform; if (hasNonScalingStroke()) { AffineTransform nonScalingTransform = nonScalingStrokeTransform(); @@ -298,13 +298,13 @@ void RenderSVGShape::paint(PaintInfo& paintInfo, const LayoutPoint&) } if (drawsOutline) - paintOutline(childPaintInfo.context, IntRect(boundingBox)); + paintOutline(childPaintInfo, IntRect(boundingBox)); } } // This method is called from inside paintOutline() since we call paintOutline() // while transformed to our coord system, return local coords -void RenderSVGShape::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint&) +void RenderSVGShape::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint&, const RenderLayerModelObject*) { IntRect rect = enclosingIntRect(repaintRectInLocalCoordinates()); if (!rect.isEmpty()) @@ -416,7 +416,7 @@ void RenderSVGShape::updateRepaintBoundingBox() float RenderSVGShape::strokeWidth() const { - SVGElement* svgElement = static_cast<SVGElement*>(node()); + SVGElement* svgElement = toSVGElement(node()); SVGLengthContext lengthContext(svgElement); return style()->svgStyle()->strokeWidth().value(lengthContext); } diff --git a/Source/WebCore/rendering/svg/RenderSVGShape.h b/Source/WebCore/rendering/svg/RenderSVGShape.h index 60a2daeb4..289369698 100644 --- a/Source/WebCore/rendering/svg/RenderSVGShape.h +++ b/Source/WebCore/rendering/svg/RenderSVGShape.h @@ -42,7 +42,7 @@ class GraphicsContextStateSaver; class RenderSVGContainer; class RenderSVGPath; class RenderSVGResource; -class SVGStyledTransformableElement; +class SVGGraphicsElement; class BoundingRectStrokeStyleApplier : public StrokeStyleApplier { public: @@ -66,16 +66,18 @@ private: class RenderSVGShape : public RenderSVGModelObject { public: - explicit RenderSVGShape(SVGStyledTransformableElement*); - RenderSVGShape(SVGStyledTransformableElement*, Path*, bool); + explicit RenderSVGShape(SVGGraphicsElement*); + RenderSVGShape(SVGGraphicsElement*, Path*, bool); virtual ~RenderSVGShape(); void setNeedsShapeUpdate() { m_needsShapeUpdate = true; } virtual void setNeedsBoundariesUpdate() { m_needsBoundariesUpdate = true; } + virtual bool needsBoundariesUpdate() OVERRIDE { return m_needsBoundariesUpdate; } virtual void setNeedsTransformUpdate() { m_needsTransformUpdate = true; } virtual void fillShape(GraphicsContext*) const; virtual void strokeShape(GraphicsContext*) const; + bool hasPath() const { return m_path.get(); } Path& path() const { ASSERT(m_path); @@ -88,7 +90,6 @@ protected: virtual bool shapeDependentStrokeContains(const FloatPoint&); virtual bool shapeDependentFillContains(const FloatPoint&, const WindRule) const; float strokeWidth() const; - bool hasPath() const { return m_path.get(); } bool hasSmoothStroke() const; bool hasNonScalingStroke() const { return style()->svgStyle()->vectorEffect() == VE_NON_SCALING_STROKE; } @@ -113,7 +114,7 @@ private: virtual void layout(); virtual void paint(PaintInfo&, const LayoutPoint&); - virtual void addFocusRingRects(Vector<IntRect>&, const LayoutPoint&); + virtual void addFocusRingRects(Vector<IntRect>&, const LayoutPoint& additionalOffset, const RenderLayerModelObject* paintContainer = 0) OVERRIDE; virtual bool nodeAtFloatPoint(const HitTestRequest&, HitTestResult&, const FloatPoint& pointInParent, HitTestAction); @@ -148,13 +149,13 @@ private: inline RenderSVGShape* toRenderSVGShape(RenderObject* object) { - ASSERT(!object || object->isSVGShape()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isSVGShape()); return static_cast<RenderSVGShape*>(object); } inline const RenderSVGShape* toRenderSVGShape(const RenderObject* object) { - ASSERT(!object || object->isSVGShape()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isSVGShape()); return static_cast<const RenderSVGShape*>(object); } diff --git a/Source/WebCore/rendering/svg/RenderSVGTSpan.cpp b/Source/WebCore/rendering/svg/RenderSVGTSpan.cpp index 872d07642..0831b2409 100644 --- a/Source/WebCore/rendering/svg/RenderSVGTSpan.cpp +++ b/Source/WebCore/rendering/svg/RenderSVGTSpan.cpp @@ -27,8 +27,8 @@ namespace WebCore { -RenderSVGTSpan::RenderSVGTSpan(Node* n) - : RenderSVGInline(n) +RenderSVGTSpan::RenderSVGTSpan(Element* element) + : RenderSVGInline(element) { } diff --git a/Source/WebCore/rendering/svg/RenderSVGTSpan.h b/Source/WebCore/rendering/svg/RenderSVGTSpan.h index d5e2ed768..bd9ce9abe 100644 --- a/Source/WebCore/rendering/svg/RenderSVGTSpan.h +++ b/Source/WebCore/rendering/svg/RenderSVGTSpan.h @@ -28,7 +28,7 @@ namespace WebCore { class RenderSVGTSpan : public RenderSVGInline { public: - explicit RenderSVGTSpan(Node*); + explicit RenderSVGTSpan(Element*); virtual const char* renderName() const { return "RenderSVGTSpan"; } }; } diff --git a/Source/WebCore/rendering/svg/RenderSVGText.cpp b/Source/WebCore/rendering/svg/RenderSVGText.cpp index fd49f9b46..a65a3e498 100644 --- a/Source/WebCore/rendering/svg/RenderSVGText.cpp +++ b/Source/WebCore/rendering/svg/RenderSVGText.cpp @@ -52,6 +52,7 @@ #include "SimpleFontData.h" #include "TransformState.h" #include "VisiblePosition.h" +#include <wtf/StackStats.h> namespace WebCore { @@ -111,9 +112,9 @@ void RenderSVGText::computeFloatRectForRepaint(const RenderLayerModelObject* rep SVGRenderSupport::computeFloatRectForRepaint(this, repaintContainer, repaintRect, fixed); } -void RenderSVGText::mapLocalToContainer(const RenderLayerModelObject* repaintContainer, TransformState& transformState, MapCoordinatesFlags mode, bool* wasFixed) const +void RenderSVGText::mapLocalToContainer(const RenderLayerModelObject* repaintContainer, TransformState& transformState, MapCoordinatesFlags, bool* wasFixed) const { - SVGRenderSupport::mapLocalToContainer(this, repaintContainer, transformState, mode & SnapOffsetForTransforms, wasFixed); + SVGRenderSupport::mapLocalToContainer(this, repaintContainer, transformState, wasFixed); } const RenderObject* RenderSVGText::pushMappingToContainer(const RenderLayerModelObject* ancestorToStopAt, RenderGeometryMap& geometryMap) const @@ -472,7 +473,7 @@ VisiblePosition RenderSVGText::positionForPoint(const LayoutPoint& pointInConten if (!rootBox) return createVisiblePosition(0, DOWNSTREAM); - ASSERT(rootBox->isSVGRootInlineBox()); + ASSERT_WITH_SECURITY_IMPLICATION(rootBox->isSVGRootInlineBox()); ASSERT(!rootBox->nextRootBox()); ASSERT(childrenInline()); @@ -513,7 +514,7 @@ FloatRect RenderSVGText::strokeBoundingBox() const ASSERT(node()); ASSERT(node()->isSVGElement()); - SVGLengthContext lengthContext(static_cast<SVGElement*>(node())); + SVGLengthContext lengthContext(toSVGElement(node())); strokeBoundaries.inflate(svgStyle->strokeWidth().value(lengthContext)); return strokeBoundaries; } diff --git a/Source/WebCore/rendering/svg/RenderSVGText.h b/Source/WebCore/rendering/svg/RenderSVGText.h index cae5ba721..4d4f4ae3d 100644 --- a/Source/WebCore/rendering/svg/RenderSVGText.h +++ b/Source/WebCore/rendering/svg/RenderSVGText.h @@ -75,7 +75,7 @@ private: virtual void computeRectForRepaint(const RenderLayerModelObject* repaintContainer, LayoutRect&, bool fixed = false) const OVERRIDE; virtual void computeFloatRectForRepaint(const RenderLayerModelObject* repaintContainer, FloatRect&, bool fixed = false) const OVERRIDE; - virtual void mapLocalToContainer(const RenderLayerModelObject* repaintContainer, TransformState&, MapCoordinatesFlags = ApplyContainerFlip | SnapOffsetForTransforms, bool* wasFixed = 0) const OVERRIDE; + virtual void mapLocalToContainer(const RenderLayerModelObject* repaintContainer, TransformState&, MapCoordinatesFlags = ApplyContainerFlip, bool* wasFixed = 0) const OVERRIDE; virtual const RenderObject* pushMappingToContainer(const RenderLayerModelObject* ancestorToStopAt, RenderGeometryMap&) const OVERRIDE; virtual void addChild(RenderObject* child, RenderObject* beforeChild = 0); virtual void removeChild(RenderObject*) OVERRIDE; @@ -104,13 +104,13 @@ private: inline RenderSVGText* toRenderSVGText(RenderObject* object) { - ASSERT(!object || object->isSVGText()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isSVGText()); return static_cast<RenderSVGText*>(object); } inline const RenderSVGText* toRenderSVGText(const RenderObject* object) { - ASSERT(!object || object->isSVGText()); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isSVGText()); return static_cast<const RenderSVGText*>(object); } diff --git a/Source/WebCore/rendering/svg/RenderSVGTextPath.cpp b/Source/WebCore/rendering/svg/RenderSVGTextPath.cpp index 15b48f590..e816f9e47 100644 --- a/Source/WebCore/rendering/svg/RenderSVGTextPath.cpp +++ b/Source/WebCore/rendering/svg/RenderSVGTextPath.cpp @@ -34,8 +34,8 @@ namespace WebCore { -RenderSVGTextPath::RenderSVGTextPath(Node* n) - : RenderSVGInline(n) +RenderSVGTextPath::RenderSVGTextPath(Element* element) + : RenderSVGInline(element) { } @@ -46,7 +46,7 @@ Path RenderSVGTextPath::layoutPath() const if (!targetElement || !targetElement->hasTagName(SVGNames::pathTag)) return Path(); - SVGPathElement* pathElement = static_cast<SVGPathElement*>(targetElement); + SVGPathElement* pathElement = toSVGPathElement(targetElement); Path pathData; updatePathFromGraphicsElement(pathElement, pathData); diff --git a/Source/WebCore/rendering/svg/RenderSVGTextPath.h b/Source/WebCore/rendering/svg/RenderSVGTextPath.h index 2758a0692..f1c872138 100644 --- a/Source/WebCore/rendering/svg/RenderSVGTextPath.h +++ b/Source/WebCore/rendering/svg/RenderSVGTextPath.h @@ -28,7 +28,7 @@ namespace WebCore { class RenderSVGTextPath : public RenderSVGInline { public: - RenderSVGTextPath(Node*); + RenderSVGTextPath(Element*); Path layoutPath() const; float startOffset() const; @@ -45,7 +45,7 @@ private: inline RenderSVGTextPath* toRenderSVGTextPath(RenderObject* object) { - ASSERT(!object || !strcmp(object->renderName(), "RenderSVGTextPath")); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isSVGTextPath()); return static_cast<RenderSVGTextPath*>(object); } diff --git a/Source/WebCore/rendering/svg/RenderSVGTransformableContainer.cpp b/Source/WebCore/rendering/svg/RenderSVGTransformableContainer.cpp index ee4d74e24..06df24dc6 100644 --- a/Source/WebCore/rendering/svg/RenderSVGTransformableContainer.cpp +++ b/Source/WebCore/rendering/svg/RenderSVGTransformableContainer.cpp @@ -24,14 +24,14 @@ #if ENABLE(SVG) #include "RenderSVGTransformableContainer.h" +#include "SVGGraphicsElement.h" #include "SVGNames.h" #include "SVGRenderSupport.h" -#include "SVGStyledTransformableElement.h" #include "SVGUseElement.h" namespace WebCore { -RenderSVGTransformableContainer::RenderSVGTransformableContainer(SVGStyledTransformableElement* node) +RenderSVGTransformableContainer::RenderSVGTransformableContainer(SVGGraphicsElement* node) : RenderSVGContainer(node) , m_needsTransformUpdate(true) , m_didTransformToRootUpdate(false) @@ -40,18 +40,18 @@ RenderSVGTransformableContainer::RenderSVGTransformableContainer(SVGStyledTransf bool RenderSVGTransformableContainer::calculateLocalTransform() { - SVGStyledTransformableElement* element = static_cast<SVGStyledTransformableElement*>(node()); + SVGGraphicsElement* element = toSVGGraphicsElement(node()); // If we're either the renderer for a <use> element, or for any <g> element inside the shadow // tree, that was created during the use/symbol/svg expansion in SVGUseElement. These containers // need to respect the translations induced by their corresponding use elements x/y attributes. SVGUseElement* useElement = 0; if (element->hasTagName(SVGNames::useTag)) - useElement = static_cast<SVGUseElement*>(element); + useElement = toSVGUseElement(element); else if (element->isInShadowTree() && element->hasTagName(SVGNames::gTag)) { SVGElement* correspondingElement = element->correspondingElement(); if (correspondingElement && correspondingElement->hasTagName(SVGNames::useTag)) - useElement = static_cast<SVGUseElement*>(correspondingElement); + useElement = toSVGUseElement(correspondingElement); } if (useElement) { diff --git a/Source/WebCore/rendering/svg/RenderSVGTransformableContainer.h b/Source/WebCore/rendering/svg/RenderSVGTransformableContainer.h index c10a08dc2..b59327efd 100644 --- a/Source/WebCore/rendering/svg/RenderSVGTransformableContainer.h +++ b/Source/WebCore/rendering/svg/RenderSVGTransformableContainer.h @@ -26,10 +26,10 @@ namespace WebCore { -class SVGStyledTransformableElement; +class SVGGraphicsElement; class RenderSVGTransformableContainer : public RenderSVGContainer { public: - explicit RenderSVGTransformableContainer(SVGStyledTransformableElement*); + explicit RenderSVGTransformableContainer(SVGGraphicsElement*); virtual bool isSVGTransformableContainer() const { return true; } virtual const AffineTransform& localToParentTransform() const { return m_localTransform; } diff --git a/Source/WebCore/rendering/svg/RenderSVGViewportContainer.cpp b/Source/WebCore/rendering/svg/RenderSVGViewportContainer.cpp index 27a319f28..834452d20 100644 --- a/Source/WebCore/rendering/svg/RenderSVGViewportContainer.cpp +++ b/Source/WebCore/rendering/svg/RenderSVGViewportContainer.cpp @@ -47,7 +47,7 @@ void RenderSVGViewportContainer::determineIfLayoutSizeChanged() if (!node()->hasTagName(SVGNames::svgTag)) return; - m_isLayoutSizeChanged = static_cast<SVGSVGElement*>(node())->hasRelativeLengths() && selfNeedsLayout(); + m_isLayoutSizeChanged = toSVGSVGElement(node())->hasRelativeLengths() && selfNeedsLayout(); } void RenderSVGViewportContainer::applyViewportClip(PaintInfo& paintInfo) @@ -58,10 +58,10 @@ void RenderSVGViewportContainer::applyViewportClip(PaintInfo& paintInfo) void RenderSVGViewportContainer::calcViewport() { - SVGElement* element = static_cast<SVGElement*>(node()); + SVGElement* element = toSVGElement(node()); if (!element->hasTagName(SVGNames::svgTag)) return; - SVGSVGElement* svg = static_cast<SVGSVGElement*>(element); + SVGSVGElement* svg = toSVGSVGElement(element); FloatRect oldViewport = m_viewport; SVGLengthContext lengthContext(element); @@ -133,7 +133,7 @@ bool RenderSVGViewportContainer::calculateLocalTransform() AffineTransform RenderSVGViewportContainer::viewportTransform() const { if (node()->hasTagName(SVGNames::svgTag)) { - SVGSVGElement* svg = static_cast<SVGSVGElement*>(node()); + SVGSVGElement* svg = toSVGSVGElement(node()); return svg->viewBoxToViewTransform(m_viewport.width(), m_viewport.height()); } @@ -149,6 +149,17 @@ bool RenderSVGViewportContainer::pointIsInsideViewportClip(const FloatPoint& poi return m_viewport.contains(pointInParent); } +void RenderSVGViewportContainer::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) +{ + // An empty viewBox disables rendering. + if (node()->hasTagName(SVGNames::svgTag)) { + if (toSVGSVGElement(node())->hasEmptyViewBox()) + return; + } + + RenderSVGContainer::paint(paintInfo, paintOffset); +} + } #endif // ENABLE(SVG) diff --git a/Source/WebCore/rendering/svg/RenderSVGViewportContainer.h b/Source/WebCore/rendering/svg/RenderSVGViewportContainer.h index eb7041a30..427e423da 100644 --- a/Source/WebCore/rendering/svg/RenderSVGViewportContainer.h +++ b/Source/WebCore/rendering/svg/RenderSVGViewportContainer.h @@ -41,6 +41,8 @@ public: virtual void determineIfLayoutSizeChanged(); virtual void setNeedsTransformUpdate() { m_needsTransformUpdate = true; } + virtual void paint(PaintInfo&, const LayoutPoint&) OVERRIDE; + private: virtual bool isSVGViewportContainer() const { return true; } virtual const char* renderName() const { return "RenderSVGViewportContainer"; } @@ -63,13 +65,13 @@ private: inline RenderSVGViewportContainer* toRenderSVGViewportContainer(RenderObject* object) { - ASSERT(!object || !strcmp(object->renderName(), "RenderSVGViewportContainer")); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isSVGViewportContainer()); return static_cast<RenderSVGViewportContainer*>(object); } inline const RenderSVGViewportContainer* toRenderSVGViewportContainer(const RenderObject* object) { - ASSERT(!object || !strcmp(object->renderName(), "RenderSVGViewportContainer")); + ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isSVGViewportContainer()); return static_cast<const RenderSVGViewportContainer*>(object); } diff --git a/Source/WebCore/rendering/svg/SVGInlineFlowBox.cpp b/Source/WebCore/rendering/svg/SVGInlineFlowBox.cpp index 21a909224..3e97b6722 100644 --- a/Source/WebCore/rendering/svg/SVGInlineFlowBox.cpp +++ b/Source/WebCore/rendering/svg/SVGInlineFlowBox.cpp @@ -43,9 +43,9 @@ void SVGInlineFlowBox::paintSelectionBackground(PaintInfo& paintInfo) PaintInfo childPaintInfo(paintInfo); for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) { if (child->isSVGInlineTextBox()) - static_cast<SVGInlineTextBox*>(child)->paintSelectionBackground(childPaintInfo); + toSVGInlineTextBox(child)->paintSelectionBackground(childPaintInfo); else if (child->isSVGInlineFlowBox()) - static_cast<SVGInlineFlowBox*>(child)->paintSelectionBackground(childPaintInfo); + toSVGInlineFlowBox(child)->paintSelectionBackground(childPaintInfo); } } @@ -61,7 +61,7 @@ void SVGInlineFlowBox::paint(PaintInfo& paintInfo, const LayoutPoint&, LayoutUni if (renderingContext.isRenderingPrepared()) { for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) { if (child->isSVGInlineTextBox()) - computeTextMatchMarkerRectForRenderer(toRenderSVGInlineText(static_cast<SVGInlineTextBox*>(child)->textRenderer())); + computeTextMatchMarkerRectForRenderer(toRenderSVGInlineText(toSVGInlineTextBox(child)->textRenderer())); child->paint(paintInfo, LayoutPoint(), 0, 0); } @@ -107,7 +107,7 @@ void SVGInlineFlowBox::computeTextMatchMarkerRectForRenderer(RenderSVGInlineText if (!box->isSVGInlineTextBox()) continue; - SVGInlineTextBox* textBox = static_cast<SVGInlineTextBox*>(box); + SVGInlineTextBox* textBox = toSVGInlineTextBox(box); int markerStartPosition = max<int>(marker->startOffset() - textBox->start(), 0); int markerEndPosition = min<int>(marker->endOffset() - textBox->start(), textBox->len()); diff --git a/Source/WebCore/rendering/svg/SVGInlineFlowBox.h b/Source/WebCore/rendering/svg/SVGInlineFlowBox.h index bc44c7e55..18b8a527f 100644 --- a/Source/WebCore/rendering/svg/SVGInlineFlowBox.h +++ b/Source/WebCore/rendering/svg/SVGInlineFlowBox.h @@ -28,7 +28,7 @@ namespace WebCore { class RenderSVGInlineText; -class SVGInlineFlowBox : public InlineFlowBox { +class SVGInlineFlowBox FINAL : public InlineFlowBox { public: SVGInlineFlowBox(RenderObject* obj) : InlineFlowBox(obj) @@ -51,6 +51,12 @@ private: float m_logicalHeight; }; +inline SVGInlineFlowBox* toSVGInlineFlowBox(InlineBox* box) +{ + ASSERT_WITH_SECURITY_IMPLICATION(!box || box->isSVGInlineFlowBox()); + return static_cast<SVGInlineFlowBox*>(box); +} + } // namespace WebCore #endif // ENABLE(SVG) diff --git a/Source/WebCore/rendering/svg/SVGInlineTextBox.cpp b/Source/WebCore/rendering/svg/SVGInlineTextBox.cpp index 7864d4ee8..08a9e14c7 100644 --- a/Source/WebCore/rendering/svg/SVGInlineTextBox.cpp +++ b/Source/WebCore/rendering/svg/SVGInlineTextBox.cpp @@ -318,10 +318,10 @@ void SVGInlineTextBox::paint(PaintInfo& paintInfo, const LayoutPoint&, LayoutUni // Spec: All text decorations except line-through should be drawn before the text is filled and stroked; thus, the text is rendered on top of these decorations. int decorations = style->textDecorationsInEffect(); - if (decorations & UNDERLINE) - paintDecoration(paintInfo.context, UNDERLINE, fragment); - if (decorations & OVERLINE) - paintDecoration(paintInfo.context, OVERLINE, fragment); + if (decorations & TextDecorationUnderline) + paintDecoration(paintInfo.context, TextDecorationUnderline, fragment); + if (decorations & TextDecorationOverline) + paintDecoration(paintInfo.context, TextDecorationOverline, fragment); // Fill text if (hasFill) { @@ -336,8 +336,8 @@ void SVGInlineTextBox::paint(PaintInfo& paintInfo, const LayoutPoint&, LayoutUni } // Spec: Line-through should be drawn after the text is filled and stroked; thus, the line-through is rendered on top of the text. - if (decorations & LINE_THROUGH) - paintDecoration(paintInfo.context, LINE_THROUGH, fragment); + if (decorations & TextDecorationLineThrough) + paintDecoration(paintInfo.context, TextDecorationLineThrough, fragment); m_paintingResourceMode = ApplyToDefaultMode; } @@ -478,22 +478,22 @@ bool SVGInlineTextBox::mapStartEndPositionsIntoFragmentCoordinates(const SVGText return true; } -static inline float positionOffsetForDecoration(ETextDecoration decoration, const FontMetrics& fontMetrics, float thickness) +static inline float positionOffsetForDecoration(TextDecoration decoration, const FontMetrics& fontMetrics, float thickness) { // FIXME: For SVG Fonts we need to use the attributes defined in the <font-face> if specified. // Compatible with Batik/Opera. - if (decoration == UNDERLINE) + if (decoration == TextDecorationUnderline) return fontMetrics.floatAscent() + thickness * 1.5f; - if (decoration == OVERLINE) + if (decoration == TextDecorationOverline) return thickness; - if (decoration == LINE_THROUGH) + if (decoration == TextDecorationLineThrough) return fontMetrics.floatAscent() * 5 / 8.0f; ASSERT_NOT_REACHED(); return 0.0f; } -static inline float thicknessForDecoration(ETextDecoration, const Font& font) +static inline float thicknessForDecoration(TextDecoration, const Font& font) { // FIXME: For SVG Fonts we need to use the attributes defined in the <font-face> if specified. // Compatible with Batik/Opera @@ -507,7 +507,7 @@ static inline RenderObject* findRenderObjectDefininingTextDecoration(InlineFlowB while (parentBox) { renderer = parentBox->renderer(); - if (renderer->style() && renderer->style()->textDecoration() != TDNONE) + if (renderer->style() && renderer->style()->textDecoration() != TextDecorationNone) break; parentBox = parentBox->parent(); @@ -517,9 +517,9 @@ static inline RenderObject* findRenderObjectDefininingTextDecoration(InlineFlowB return renderer; } -void SVGInlineTextBox::paintDecoration(GraphicsContext* context, ETextDecoration decoration, const SVGTextFragment& fragment) +void SVGInlineTextBox::paintDecoration(GraphicsContext* context, TextDecoration decoration, const SVGTextFragment& fragment) { - if (textRenderer()->style()->textDecorationsInEffect() == TDNONE) + if (textRenderer()->style()->textDecorationsInEffect() == TextDecorationNone) return; // Find out which render style defined the text-decoration, as its fill/stroke properties have to be used for drawing instead of ours. @@ -547,7 +547,7 @@ void SVGInlineTextBox::paintDecoration(GraphicsContext* context, ETextDecoration } } -void SVGInlineTextBox::paintDecorationWithStyle(GraphicsContext* context, ETextDecoration decoration, const SVGTextFragment& fragment, RenderObject* decorationRenderer) +void SVGInlineTextBox::paintDecorationWithStyle(GraphicsContext* context, TextDecoration decoration, const SVGTextFragment& fragment, RenderObject* decorationRenderer) { ASSERT(!m_paintingResource); ASSERT(m_paintingResourceMode != ApplyToDefaultMode); diff --git a/Source/WebCore/rendering/svg/SVGInlineTextBox.h b/Source/WebCore/rendering/svg/SVGInlineTextBox.h index ef399c724..6fea28db3 100644 --- a/Source/WebCore/rendering/svg/SVGInlineTextBox.h +++ b/Source/WebCore/rendering/svg/SVGInlineTextBox.h @@ -31,7 +31,7 @@ namespace WebCore { class RenderSVGResource; class SVGRootInlineBox; -class SVGInlineTextBox : public InlineTextBox { +class SVGInlineTextBox FINAL : public InlineTextBox { public: SVGInlineTextBox(RenderObject*); @@ -57,7 +57,7 @@ public: Vector<SVGTextFragment>& textFragments() { return m_textFragments; } const Vector<SVGTextFragment>& textFragments() const { return m_textFragments; } - void dirtyLineBoxes() OVERRIDE; + virtual void dirtyLineBoxes() OVERRIDE; bool startsNewTextChunk() const { return m_startsNewTextChunk; } void setStartsNewTextChunk(bool newTextChunk) { m_startsNewTextChunk = newTextChunk; } @@ -74,8 +74,8 @@ private: bool prepareGraphicsContextForTextPainting(GraphicsContext*&, float scalingFactor, TextRun&, RenderStyle*); void restoreGraphicsContextAfterTextPainting(GraphicsContext*&, TextRun&); - void paintDecoration(GraphicsContext*, ETextDecoration, const SVGTextFragment&); - void paintDecorationWithStyle(GraphicsContext*, ETextDecoration, const SVGTextFragment&, RenderObject* decorationRenderer); + void paintDecoration(GraphicsContext*, TextDecoration, const SVGTextFragment&); + void paintDecorationWithStyle(GraphicsContext*, TextDecoration, const SVGTextFragment&, RenderObject* decorationRenderer); void paintTextWithShadows(GraphicsContext*, RenderStyle*, TextRun&, const SVGTextFragment&, int startPosition, int endPosition); void paintText(GraphicsContext*, RenderStyle*, RenderStyle* selectionStyle, const SVGTextFragment&, bool hasSelection, bool paintSelectedTextOnly); @@ -89,6 +89,12 @@ private: Vector<SVGTextFragment> m_textFragments; }; +inline SVGInlineTextBox* toSVGInlineTextBox(InlineBox* box) +{ + ASSERT_WITH_SECURITY_IMPLICATION(!box || box->isSVGInlineTextBox()); + return static_cast<SVGInlineTextBox*>(box); +} + } // namespace WebCore #endif diff --git a/Source/WebCore/rendering/svg/SVGMarkerData.h b/Source/WebCore/rendering/svg/SVGMarkerData.h index 7e2ce0edc..6c70f4297 100644 --- a/Source/WebCore/rendering/svg/SVGMarkerData.h +++ b/Source/WebCore/rendering/svg/SVGMarkerData.h @@ -82,19 +82,23 @@ public: private: float currentAngle(SVGMarkerType type) const { - FloatSize inslopeChange = m_inslopePoints[1] - m_inslopePoints[0]; - FloatSize outslopeChange = m_outslopePoints[1] - m_outslopePoints[0]; + // For details of this calculation, see: http://www.w3.org/TR/SVG/single-page.html#painting-MarkerElement + FloatPoint inSlope(m_inslopePoints[1] - m_inslopePoints[0]); + FloatPoint outSlope(m_outslopePoints[1] - m_outslopePoints[0]); - double inslope = rad2deg(atan2(inslopeChange.height(), inslopeChange.width())); - double outslope = rad2deg(atan2(outslopeChange.height(), outslopeChange.width())); + double inAngle = rad2deg(inSlope.slopeAngleRadians()); + double outAngle = rad2deg(outSlope.slopeAngleRadians()); switch (type) { case StartMarker: - return narrowPrecisionToFloat(outslope); + return narrowPrecisionToFloat(outAngle); case MidMarker: - return narrowPrecisionToFloat((inslope + outslope) / 2); + // WK193015: Prevent bugs due to angles being non-continuous. + if (fabs(inAngle - outAngle) > 180) + inAngle += 360; + return narrowPrecisionToFloat((inAngle + outAngle) / 2); case EndMarker: - return narrowPrecisionToFloat(inslope); + return narrowPrecisionToFloat(inAngle); } ASSERT_NOT_REACHED(); diff --git a/Source/WebCore/rendering/svg/SVGPathData.cpp b/Source/WebCore/rendering/svg/SVGPathData.cpp index 98c80b22f..234122510 100644 --- a/Source/WebCore/rendering/svg/SVGPathData.cpp +++ b/Source/WebCore/rendering/svg/SVGPathData.cpp @@ -73,8 +73,7 @@ static void updatePathFromLineElement(SVGElement* element, Path& path) static void updatePathFromPathElement(SVGElement* element, Path& path) { - ASSERT(element->hasTagName(SVGNames::pathTag)); - buildPathFromByteStream(static_cast<SVGPathElement*>(element)->pathByteStream(), path); + buildPathFromByteStream(toSVGPathElement(element)->pathByteStream(), path); } static void updatePathFromPolygonElement(SVGElement* element, Path& path) diff --git a/Source/WebCore/rendering/svg/SVGRenderSupport.cpp b/Source/WebCore/rendering/svg/SVGRenderSupport.cpp index c75015d57..4fba77407 100644 --- a/Source/WebCore/rendering/svg/SVGRenderSupport.cpp +++ b/Source/WebCore/rendering/svg/SVGRenderSupport.cpp @@ -42,7 +42,6 @@ #include "SVGResourcesCache.h" #include "SVGStyledElement.h" #include "TransformState.h" -#include <wtf/UnusedParam.h> namespace WebCore { @@ -50,6 +49,7 @@ FloatRect SVGRenderSupport::repaintRectForRendererInLocalCoordinatesExcludingSVG { // FIXME: Add support for RenderSVGBlock. + // FIXME: This should use a safer cast such as toRenderSVGModelObject(). if (object->isSVGShape() || object->isSVGImage() || object->isSVGContainer()) return static_cast<const RenderSVGModelObject*>(object)->repaintRectInLocalCoordinatesExcludingSVGShadow(); @@ -84,7 +84,7 @@ void SVGRenderSupport::computeFloatRectForRepaint(const RenderObject* object, co object->parent()->computeFloatRectForRepaint(repaintContainer, repaintRect, fixed); } -void SVGRenderSupport::mapLocalToContainer(const RenderObject* object, const RenderLayerModelObject* repaintContainer, TransformState& transformState, bool snapOffsetForTransforms, bool* wasFixed) +void SVGRenderSupport::mapLocalToContainer(const RenderObject* object, const RenderLayerModelObject* repaintContainer, TransformState& transformState, bool* wasFixed) { transformState.applyTransform(object->localToParentTransform()); @@ -97,8 +97,6 @@ void SVGRenderSupport::mapLocalToContainer(const RenderObject* object, const Ren transformState.applyTransform(toRenderSVGRoot(parent)->localToBorderBoxTransform()); MapCoordinatesFlags mode = UseTransforms; - if (snapOffsetForTransforms) - mode |= SnapOffsetForTransforms; parent->mapLocalToContainer(repaintContainer, transformState, mode, wasFixed); } @@ -231,12 +229,21 @@ void SVGRenderSupport::layoutChildren(RenderObject* start, bool selfNeedsLayout) { bool layoutSizeChanged = layoutSizeOfNearestViewportChanged(start); bool transformChanged = transformToRootChanged(start); + bool hasSVGShadow = rendererHasSVGShadow(start); + bool needsBoundariesUpdate = start->needsBoundariesUpdate(); HashSet<RenderObject*> notlayoutedObjects; for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) { bool needsLayout = selfNeedsLayout; bool childEverHadLayout = child->everHadLayout(); + if (needsBoundariesUpdate && hasSVGShadow) { + // If we have a shadow, our shadow is baked into our children's cached boundaries, + // so they need to update. + child->setNeedsBoundariesUpdate(); + needsLayout = true; + } + if (transformChanged) { // If the transform changed we need to update the text metrics (note: this also happens for layoutSizeChanged=true). if (child->isSVGText()) @@ -246,8 +253,8 @@ void SVGRenderSupport::layoutChildren(RenderObject* start, bool selfNeedsLayout) if (layoutSizeChanged) { // When selfNeedsLayout is false and the layout size changed, we have to check whether this child uses relative lengths - if (SVGElement* element = child->node()->isSVGElement() ? static_cast<SVGElement*>(child->node()) : 0) { - if (element->isStyled() && static_cast<SVGStyledElement*>(element)->hasRelativeLengths()) { + if (SVGElement* element = child->node()->isSVGElement() ? toSVGElement(child->node()) : 0) { + if (element->isSVGStyledElement() && toSVGStyledElement(element)->hasRelativeLengths()) { // When the layout size changed and when using relative values tell the RenderSVGShape to update its shape object if (child->isSVGShape()) toRenderSVGShape(child)->setNeedsShapeUpdate(); @@ -307,11 +314,12 @@ bool SVGRenderSupport::rendererHasSVGShadow(const RenderObject* object) { // FIXME: Add support for RenderSVGBlock. + // FIXME: This should use a safer cast such as toRenderSVGModelObject(). if (object->isSVGShape() || object->isSVGImage() || object->isSVGContainer()) return static_cast<const RenderSVGModelObject*>(object)->hasSVGShadow(); if (object->isSVGRoot()) - return static_cast<const RenderSVGRoot*>(object)->hasSVGShadow(); + return toRenderSVGRoot(object)->hasSVGShadow(); return false; } @@ -320,6 +328,7 @@ void SVGRenderSupport::setRendererHasSVGShadow(RenderObject* object, bool hasSha { // FIXME: Add support for RenderSVGBlock. + // FIXME: This should use a safer cast such as toRenderSVGModelObject(). if (object->isSVGShape() || object->isSVGImage() || object->isSVGContainer()) return static_cast<RenderSVGModelObject*>(object)->setHasSVGShadow(hasShadow); @@ -424,7 +433,7 @@ void SVGRenderSupport::applyStrokeStyleToContext(GraphicsContext* context, const const SVGRenderStyle* svgStyle = style->svgStyle(); ASSERT(svgStyle); - SVGLengthContext lengthContext(static_cast<SVGElement*>(object->node())); + SVGLengthContext lengthContext(toSVGElement(object->node())); context->setStrokeThickness(svgStyle->strokeWidth().value(lengthContext)); context->setLineCap(svgStyle->capStyle()); context->setLineJoin(svgStyle->joinStyle()); diff --git a/Source/WebCore/rendering/svg/SVGRenderSupport.h b/Source/WebCore/rendering/svg/SVGRenderSupport.h index f7375e43b..0b1871617 100644 --- a/Source/WebCore/rendering/svg/SVGRenderSupport.h +++ b/Source/WebCore/rendering/svg/SVGRenderSupport.h @@ -68,7 +68,7 @@ public: static FloatRect repaintRectForRendererInLocalCoordinatesExcludingSVGShadow(const RenderObject*); static LayoutRect clippedOverflowRectForRepaint(const RenderObject*, const RenderLayerModelObject* repaintContainer); static void computeFloatRectForRepaint(const RenderObject*, const RenderLayerModelObject* repaintContainer, FloatRect&, bool fixed); - static void mapLocalToContainer(const RenderObject*, const RenderLayerModelObject* repaintContainer, TransformState&, bool snapOffsetForTransforms = true, bool* wasFixed = 0); + static void mapLocalToContainer(const RenderObject*, const RenderLayerModelObject* repaintContainer, TransformState&, bool* wasFixed = 0); static const RenderObject* pushMappingToContainer(const RenderObject*, const RenderLayerModelObject* ancestorToStopAt, RenderGeometryMap&); static bool checkForSVGRepaintDuringLayout(RenderObject*); diff --git a/Source/WebCore/rendering/svg/SVGRenderTreeAsText.cpp b/Source/WebCore/rendering/svg/SVGRenderTreeAsText.cpp index 12352ae72..532db27c6 100644 --- a/Source/WebCore/rendering/svg/SVGRenderTreeAsText.cpp +++ b/Source/WebCore/rendering/svg/SVGRenderTreeAsText.cpp @@ -262,7 +262,7 @@ static void writeSVGPaintingResource(TextStream& ts, RenderSVGResource* resource else if (resource->resourceType() == RadialGradientResourceType) ts << "[type=RADIAL-GRADIENT]"; - ts << " [id=\"" << static_cast<SVGElement*>(node)->getIdAttribute() << "\"]"; + ts << " [id=\"" << toSVGElement(node)->getIdAttribute() << "\"]"; } static void writeStyle(TextStream& ts, const RenderObject& object) @@ -285,7 +285,7 @@ static void writeStyle(TextStream& ts, const RenderObject& object) ts << " [stroke={" << s; writeSVGPaintingResource(ts, strokePaintingResource); - SVGLengthContext lengthContext(static_cast<SVGElement*>(shape.node())); + SVGLengthContext lengthContext(toSVGElement(shape.node())); double dashOffset = svgStyle->strokeDashOffset().value(lengthContext); double strokeWidth = svgStyle->strokeWidth().value(lengthContext); const Vector<SVGLength>& dashes = svgStyle->strokeDashArray(); @@ -336,7 +336,7 @@ static TextStream& operator<<(TextStream& ts, const RenderSVGShape& shape) writePositionAndStyle(ts, shape); ASSERT(shape.node()->isSVGElement()); - SVGElement* svgElement = static_cast<SVGElement*>(shape.node()); + SVGElement* svgElement = toSVGElement(shape.node()); SVGLengthContext lengthContext(svgElement); if (svgElement->hasTagName(SVGNames::rectTag)) { @@ -366,7 +366,7 @@ static TextStream& operator<<(TextStream& ts, const RenderSVGShape& shape) SVGPolyElement* element = static_cast<SVGPolyElement*>(svgElement); writeNameAndQuotedValue(ts, "points", element->pointList().valueAsString()); } else if (svgElement->hasTagName(SVGNames::pathTag)) { - SVGPathElement* element = static_cast<SVGPathElement*>(svgElement); + SVGPathElement* element = toSVGPathElement(svgElement); String pathString; // FIXME: We should switch to UnalteredParsing here - this will affect the path dumping output of dozens of tests. buildStringFromByteStream(element->pathByteStream(), pathString, NormalizedParsing); @@ -459,7 +459,7 @@ static inline void writeSVGInlineTextBoxes(TextStream& ts, const RenderText& tex if (!box->isSVGInlineTextBox()) continue; - writeSVGInlineTextBox(ts, static_cast<SVGInlineTextBox*>(box), indent); + writeSVGInlineTextBox(ts, toSVGInlineTextBox(box), indent); } } @@ -493,7 +493,7 @@ void writeSVGResourceContainer(TextStream& ts, const RenderObject& object, int i { writeStandardPrefix(ts, object, indent); - Element* element = static_cast<Element*>(object.node()); + Element* element = toElement(object.node()); const AtomicString& id = element->getIdAttribute(); writeNameAndQuotedValue(ts, "id", id); diff --git a/Source/WebCore/rendering/svg/SVGRenderingContext.cpp b/Source/WebCore/rendering/svg/SVGRenderingContext.cpp index 041aed418..a65d39fe6 100644 --- a/Source/WebCore/rendering/svg/SVGRenderingContext.cpp +++ b/Source/WebCore/rendering/svg/SVGRenderingContext.cpp @@ -30,6 +30,8 @@ #include "BasicShapes.h" #include "Frame.h" #include "FrameView.h" +#include "RenderLayer.h" +#include "RenderSVGImage.h" #include "RenderSVGResource.h" #include "RenderSVGResourceClipper.h" #include "RenderSVGResourceFilter.h" @@ -59,8 +61,9 @@ SVGRenderingContext::~SVGRenderingContext() #if ENABLE(FILTERS) if (m_renderingFlags & EndFilterLayer) { ASSERT(m_filter); - m_filter->postApplyResource(static_cast<RenderSVGShape*>(m_object), m_paintInfo->context, ApplyToDefaultMode, 0, 0); + m_filter->postApplyResource(m_object, m_paintInfo->context, ApplyToDefaultMode, 0, 0); m_paintInfo->context = m_savedContext; + m_paintInfo->rect = m_savedPaintRect; } #endif @@ -117,7 +120,7 @@ void SVGRenderingContext::prepareToRenderSVGContent(RenderObject* object, PaintI if (shadow) { m_paintInfo->context->clip(repaintRect); - m_paintInfo->context->setShadow(IntSize(roundToInt(shadow->x()), roundToInt(shadow->y())), shadow->blur(), shadow->color(), style->colorSpace()); + m_paintInfo->context->setShadow(IntSize(roundToInt(shadow->x()), roundToInt(shadow->y())), shadow->radius(), shadow->color(), style->colorSpace()); m_paintInfo->context->beginTransparencyLayer(1); m_renderingFlags |= EndShadowLayer; } @@ -157,11 +160,18 @@ void SVGRenderingContext::prepareToRenderSVGContent(RenderObject* object, PaintI m_filter = resources->filter(); if (m_filter) { m_savedContext = m_paintInfo->context; + m_savedPaintRect = m_paintInfo->rect; // Return with false here may mean that we don't need to draw the content // (because it was either drawn before or empty) but we still need to apply the filter. m_renderingFlags |= EndFilterLayer; if (!m_filter->applyResource(m_object, style, m_paintInfo->context, ApplyToDefaultMode)) return; + + // Since we're caching the resulting bitmap and do not invalidate it on repaint rect + // changes, we need to paint the whole filter region. Otherwise, elements not visible + // at the time of the initial paint (due to scrolling, window size, etc.) will never + // be drawn. + m_paintInfo->rect = IntRect(m_filter->drawingRegion(m_object)); } } #endif @@ -180,21 +190,34 @@ float SVGRenderingContext::calculateScreenFontSizeScalingFactor(const RenderObje ASSERT(renderer); AffineTransform ctm; - calculateTransformationToOutermostSVGCoordinateSystem(renderer, ctm); + calculateTransformationToOutermostCoordinateSystem(renderer, ctm); return narrowPrecisionToFloat(sqrt((pow(ctm.xScale(), 2) + pow(ctm.yScale(), 2)) / 2)); } -void SVGRenderingContext::calculateTransformationToOutermostSVGCoordinateSystem(const RenderObject* renderer, AffineTransform& absoluteTransform) +void SVGRenderingContext::calculateTransformationToOutermostCoordinateSystem(const RenderObject* renderer, AffineTransform& absoluteTransform) { - const RenderObject* current = renderer; - ASSERT(current); - + ASSERT(renderer); absoluteTransform = currentContentTransformation(); - while (current) { - absoluteTransform = current->localToParentTransform() * absoluteTransform; - if (current->isSVGRoot()) + + // Walk up the render tree, accumulating SVG transforms. + while (renderer) { + absoluteTransform = renderer->localToParentTransform() * absoluteTransform; + if (renderer->isSVGRoot()) break; - current = current->parent(); + renderer = renderer->parent(); + } + + // Continue walking up the layer tree, accumulating CSS transforms. + RenderLayer* layer = renderer ? renderer->enclosingLayer() : 0; + while (layer) { + if (TransformationMatrix* layerTransform = layer->transform()) + absoluteTransform = layerTransform->toAffineTransform() * absoluteTransform; + + // We can stop at compositing layers, to match the backing resolution. + if (layer->isComposited()) + break; + + layer = layer->parent(); } } @@ -251,7 +274,7 @@ void SVGRenderingContext::renderSubtreeToImageBuffer(ImageBuffer* image, RenderO ASSERT(image); ASSERT(image->context()); - PaintInfo info(image->context(), PaintInfo::infiniteRect(), PaintPhaseForeground, 0, 0, 0, 0); + PaintInfo info(image->context(), PaintInfo::infiniteRect(), PaintPhaseForeground, PaintBehaviorNormal); AffineTransform& contentTransformation = currentContentTransformation(); AffineTransform savedContentTransformation = contentTransformation; @@ -302,6 +325,37 @@ void SVGRenderingContext::clear2DRotation(AffineTransform& transform) transform.recompose(decomposition); } +bool SVGRenderingContext::bufferForeground(OwnPtr<ImageBuffer>& imageBuffer) +{ + ASSERT(m_paintInfo); + ASSERT(m_object->isSVGImage()); + FloatRect boundingBox = m_object->objectBoundingBox(); + + // Invalidate an existing buffer if the scale is not correct. + if (imageBuffer) { + AffineTransform transform = m_paintInfo->context->getCTM(GraphicsContext::DefinitelyIncludeDeviceScale); + IntSize expandedBoundingBox = expandedIntSize(boundingBox.size()); + IntSize bufferSize(static_cast<int>(ceil(expandedBoundingBox.width() * transform.xScale())), static_cast<int>(ceil(expandedBoundingBox.height() * transform.yScale()))); + if (bufferSize != imageBuffer->internalSize()) + imageBuffer.clear(); + } + + // Create a new buffer and paint the foreground into it. + if (!imageBuffer) { + if ((imageBuffer = m_paintInfo->context->createCompatibleBuffer(expandedIntSize(boundingBox.size()), true))) { + GraphicsContext* bufferedRenderingContext = imageBuffer->context(); + bufferedRenderingContext->translate(-boundingBox.x(), -boundingBox.y()); + PaintInfo bufferedInfo(*m_paintInfo); + bufferedInfo.context = bufferedRenderingContext; + toRenderSVGImage(m_object)->paintForeground(bufferedInfo); + } else + return false; + } + + m_paintInfo->context->drawImageBuffer(imageBuffer.get(), ColorSpaceDeviceRGB, boundingBox); + return true; +} + } #endif diff --git a/Source/WebCore/rendering/svg/SVGRenderingContext.h b/Source/WebCore/rendering/svg/SVGRenderingContext.h index c88168968..1fc3f1a2e 100644 --- a/Source/WebCore/rendering/svg/SVGRenderingContext.h +++ b/Source/WebCore/rendering/svg/SVGRenderingContext.h @@ -83,7 +83,7 @@ public: static void clipToImageBuffer(GraphicsContext*, const AffineTransform& absoluteTransform, const FloatRect& targetRect, OwnPtr<ImageBuffer>&, bool safeToClear); static float calculateScreenFontSizeScalingFactor(const RenderObject*); - static void calculateTransformationToOutermostSVGCoordinateSystem(const RenderObject*, AffineTransform& absoluteTransform); + static void calculateTransformationToOutermostCoordinateSystem(const RenderObject*, AffineTransform& absoluteTransform); static IntSize clampedAbsoluteSize(const IntSize&); static FloatRect clampedAbsoluteTargetRect(const FloatRect& absoluteTargetRect); static void clear2DRotation(AffineTransform&); @@ -93,6 +93,9 @@ public: return enclosingIntRect(absoluteTransform.mapRect(targetRect)); } + // Support for the buffered-rendering hint. + bool bufferForeground(OwnPtr<ImageBuffer>&); + private: // To properly revert partially successful initializtions in the destructor, we record all successful steps. enum RenderingFlags { @@ -111,6 +114,7 @@ private: RenderObject* m_object; PaintInfo* m_paintInfo; GraphicsContext* m_savedContext; + IntRect m_savedPaintRect; #if ENABLE(FILTERS) RenderSVGResourceFilter* m_filter; #endif diff --git a/Source/WebCore/rendering/svg/SVGResources.cpp b/Source/WebCore/rendering/svg/SVGResources.cpp index d9097409d..015190d27 100644 --- a/Source/WebCore/rendering/svg/SVGResources.cpp +++ b/Source/WebCore/rendering/svg/SVGResources.cpp @@ -146,10 +146,10 @@ static inline String targetReferenceFromResource(SVGElement* element) if (element->hasTagName(SVGNames::patternTag)) target = static_cast<SVGPatternElement*>(element)->href(); else if (element->hasTagName(SVGNames::linearGradientTag) || element->hasTagName(SVGNames::radialGradientTag)) - target = static_cast<SVGGradientElement*>(element)->href(); + target = toSVGGradientElement(element)->href(); #if ENABLE(FILTERS) else if (element->hasTagName(SVGNames::filterTag)) - target = static_cast<SVGFilterElement*>(element)->href(); + target = toSVGFilterElement(element)->href(); #endif else ASSERT_NOT_REACHED(); @@ -179,8 +179,8 @@ static inline RenderSVGResourceContainer* paintingResourceFromSVGPaint(Document* static inline void registerPendingResource(SVGDocumentExtensions* extensions, const AtomicString& id, SVGElement* element) { ASSERT(element); - ASSERT(element->isStyled()); - extensions->addPendingResource(id, static_cast<SVGStyledElement*>(element)); + ASSERT_WITH_SECURITY_IMPLICATION(element->isSVGStyledElement()); + extensions->addPendingResource(id, toSVGStyledElement(element)); } bool SVGResources::buildCachedResources(const RenderObject* object, const SVGRenderStyle* style) @@ -190,9 +190,9 @@ bool SVGResources::buildCachedResources(const RenderObject* object, const SVGRen Node* node = object->node(); ASSERT(node); - ASSERT(node->isSVGElement()); + ASSERT_WITH_SECURITY_IMPLICATION(node->isSVGElement()); - SVGElement* element = static_cast<SVGElement*>(node); + SVGElement* element = toSVGElement(node); if (!element) return false; diff --git a/Source/WebCore/rendering/svg/SVGResourcesCache.cpp b/Source/WebCore/rendering/svg/SVGResourcesCache.cpp index 8b48cf53e..3f6e7dee1 100644 --- a/Source/WebCore/rendering/svg/SVGResourcesCache.cpp +++ b/Source/WebCore/rendering/svg/SVGResourcesCache.cpp @@ -36,7 +36,6 @@ SVGResourcesCache::SVGResourcesCache() SVGResourcesCache::~SVGResourcesCache() { - deleteAllValues(m_cache); } void SVGResourcesCache::addResourcesFromRenderObject(RenderObject* object, const RenderStyle* style) @@ -49,14 +48,12 @@ void SVGResourcesCache::addResourcesFromRenderObject(RenderObject* object, const ASSERT(svgStyle); // Build a list of all resources associated with the passed RenderObject - SVGResources* resources = new SVGResources; - if (!resources->buildCachedResources(object, svgStyle)) { - delete resources; + OwnPtr<SVGResources> newResources = adoptPtr(new SVGResources); + if (!newResources->buildCachedResources(object, svgStyle)) return; - } // Put object in cache. - m_cache.set(object, resources); + SVGResources* resources = m_cache.set(object, newResources.release()).iterator->value.get(); // Run cycle-detection _afterwards_, so self-references can be caught as well. SVGResourcesCycleSolver solver(object, resources); @@ -76,7 +73,7 @@ void SVGResourcesCache::removeResourcesFromRenderObject(RenderObject* object) if (!m_cache.contains(object)) return; - SVGResources* resources = m_cache.get(object); + OwnPtr<SVGResources> resources = m_cache.take(object); // Walk resources and register the render object at each resources. HashSet<RenderSVGResourceContainer*> resourceSet; @@ -85,8 +82,6 @@ void SVGResourcesCache::removeResourcesFromRenderObject(RenderObject* object) HashSet<RenderSVGResourceContainer*>::iterator end = resourceSet.end(); for (HashSet<RenderSVGResourceContainer*>::iterator it = resourceSet.begin(); it != end; ++it) (*it)->removeClient(object); - - delete m_cache.take(object); } static inline SVGResourcesCache* resourcesCacheFromRenderObject(const RenderObject* renderer) @@ -106,11 +101,7 @@ static inline SVGResourcesCache* resourcesCacheFromRenderObject(const RenderObje SVGResources* SVGResourcesCache::cachedResourcesForRenderObject(const RenderObject* renderer) { ASSERT(renderer); - SVGResourcesCache* cache = resourcesCacheFromRenderObject(renderer); - if (!cache->m_cache.contains(renderer)) - return 0; - - return cache->m_cache.get(renderer); + return resourcesCacheFromRenderObject(renderer)->m_cache.get(renderer); } void SVGResourcesCache::clientLayoutChanged(RenderObject* object) @@ -125,6 +116,12 @@ void SVGResourcesCache::clientLayoutChanged(RenderObject* object) resources->removeClientFromCache(object); } +static inline bool rendererCanHaveResources(RenderObject* renderer) +{ + ASSERT(renderer); + return renderer->node() && renderer->node()->isSVGElement() && !renderer->isSVGInlineText(); +} + void SVGResourcesCache::clientStyleChanged(RenderObject* renderer, StyleDifference diff, const RenderStyle* newStyle) { ASSERT(renderer); @@ -132,24 +129,22 @@ void SVGResourcesCache::clientStyleChanged(RenderObject* renderer, StyleDifferen return; // In this case the proper SVGFE*Element will decide whether the modified CSS properties require a relayout or repaint. - if (renderer->isSVGResourceFilterPrimitive() && diff == StyleDifferenceRepaint) + if (renderer->isSVGResourceFilterPrimitive() && (diff == StyleDifferenceRepaint || diff == StyleDifferenceRepaintIfText)) return; // Dynamic changes of CSS properties like 'clip-path' may require us to recompute the associated resources for a renderer. // FIXME: Avoid passing in a useless StyleDifference, but instead compare oldStyle/newStyle to see which resources changed // to be able to selectively rebuild individual resources, instead of all of them. - SVGResourcesCache* cache = resourcesCacheFromRenderObject(renderer); - cache->removeResourcesFromRenderObject(renderer); - cache->addResourcesFromRenderObject(renderer, newStyle); + if (rendererCanHaveResources(renderer)) { + SVGResourcesCache* cache = resourcesCacheFromRenderObject(renderer); + cache->removeResourcesFromRenderObject(renderer); + cache->addResourcesFromRenderObject(renderer, newStyle); + } RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer, false); -} -static inline bool rendererCanHaveResources(RenderObject* renderer) -{ - ASSERT(renderer); - ASSERT(renderer->parent()); - return renderer->node() && !renderer->isSVGInlineText(); + if (renderer->node() && !renderer->node()->isSVGElement()) + renderer->node()->setNeedsStyleRecalc(SyntheticStyleChange); } void SVGResourcesCache::clientWasAddedToTree(RenderObject* renderer, const RenderStyle* newStyle) @@ -196,13 +191,13 @@ void SVGResourcesCache::resourceDestroyed(RenderSVGResourceContainer* resource) // The resource itself may have clients, that need to be notified. cache->removeResourcesFromRenderObject(resource); - HashMap<const RenderObject*, SVGResources*>::iterator end = cache->m_cache.end(); - for (HashMap<const RenderObject*, SVGResources*>::iterator it = cache->m_cache.begin(); it != end; ++it) { + CacheMap::iterator end = cache->m_cache.end(); + for (CacheMap::iterator it = cache->m_cache.begin(); it != end; ++it) { it->value->resourceDestroyed(resource); // Mark users of destroyed resources as pending resolution based on the id of the old resource. Element* resourceElement = toElement(resource->node()); - SVGStyledElement* clientElement = toSVGStyledElement(it->key->node()); + Element* clientElement = toElement(it->key->node()); SVGDocumentExtensions* extensions = clientElement->document()->accessSVGExtensions(); extensions->addPendingResource(resourceElement->fastGetAttribute(HTMLNames::idAttr), clientElement); diff --git a/Source/WebCore/rendering/svg/SVGResourcesCache.h b/Source/WebCore/rendering/svg/SVGResourcesCache.h index 633fcd73d..67a01059c 100644 --- a/Source/WebCore/rendering/svg/SVGResourcesCache.h +++ b/Source/WebCore/rendering/svg/SVGResourcesCache.h @@ -23,6 +23,7 @@ #if ENABLE(SVG) #include "RenderStyleConstants.h" #include <wtf/HashMap.h> +#include <wtf/OwnPtr.h> namespace WebCore { @@ -61,7 +62,8 @@ private: void addResourcesFromRenderObject(RenderObject*, const RenderStyle*); void removeResourcesFromRenderObject(RenderObject*); - HashMap<const RenderObject*, SVGResources*> m_cache; + typedef HashMap<const RenderObject*, OwnPtr<SVGResources> > CacheMap; + CacheMap m_cache; }; } diff --git a/Source/WebCore/rendering/svg/SVGRootInlineBox.cpp b/Source/WebCore/rendering/svg/SVGRootInlineBox.cpp index 546fec26c..d2df3f16d 100644 --- a/Source/WebCore/rendering/svg/SVGRootInlineBox.cpp +++ b/Source/WebCore/rendering/svg/SVGRootInlineBox.cpp @@ -51,9 +51,9 @@ void SVGRootInlineBox::paint(PaintInfo& paintInfo, const LayoutPoint&, LayoutUni if (hasSelection) { for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) { if (child->isSVGInlineTextBox()) - static_cast<SVGInlineTextBox*>(child)->paintSelectionBackground(childPaintInfo); + toSVGInlineTextBox(child)->paintSelectionBackground(childPaintInfo); else if (child->isSVGInlineFlowBox()) - static_cast<SVGInlineFlowBox*>(child)->paintSelectionBackground(childPaintInfo); + toSVGInlineFlowBox(child)->paintSelectionBackground(childPaintInfo); } } @@ -61,7 +61,7 @@ void SVGRootInlineBox::paint(PaintInfo& paintInfo, const LayoutPoint&, LayoutUni if (renderingContext.isRenderingPrepared()) { for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) { if (child->isSVGInlineTextBox()) - SVGInlineFlowBox::computeTextMatchMarkerRectForRenderer(toRenderSVGInlineText(static_cast<SVGInlineTextBox*>(child)->textRenderer())); + SVGInlineFlowBox::computeTextMatchMarkerRectForRenderer(toRenderSVGInlineText(toSVGInlineTextBox(child)->textRenderer())); child->paint(paintInfo, LayoutPoint(), 0, 0); } @@ -101,7 +101,7 @@ void SVGRootInlineBox::layoutCharactersInTextBoxes(InlineFlowBox* start, SVGText ASSERT(child->renderer()); ASSERT(child->renderer()->isSVGInlineText()); - SVGInlineTextBox* textBox = static_cast<SVGInlineTextBox*>(child); + SVGInlineTextBox* textBox = toSVGInlineTextBox(child); characterLayout.layoutInlineTextBox(textBox); } else { // Skip generated content. @@ -109,9 +109,9 @@ void SVGRootInlineBox::layoutCharactersInTextBoxes(InlineFlowBox* start, SVGText if (!node) continue; - ASSERT(child->isInlineFlowBox()); + ASSERT_WITH_SECURITY_IMPLICATION(child->isInlineFlowBox()); - SVGInlineFlowBox* flowBox = static_cast<SVGInlineFlowBox*>(child); + SVGInlineFlowBox* flowBox = toSVGInlineFlowBox(child); bool isTextPath = node->hasTagName(SVGNames::textPathTag); if (isTextPath) { // Build text chunks for all <textPath> children, using the line layout algorithm. @@ -138,7 +138,7 @@ void SVGRootInlineBox::layoutChildBoxes(InlineFlowBox* start, FloatRect* childRe ASSERT(child->renderer()); ASSERT(child->renderer()->isSVGInlineText()); - SVGInlineTextBox* textBox = static_cast<SVGInlineTextBox*>(child); + SVGInlineTextBox* textBox = toSVGInlineTextBox(child); boxRect = textBox->calculateBoundaries(); textBox->setX(boxRect.x()); textBox->setY(boxRect.y()); @@ -149,9 +149,9 @@ void SVGRootInlineBox::layoutChildBoxes(InlineFlowBox* start, FloatRect* childRe if (!child->renderer()->node()) continue; - ASSERT(child->isInlineFlowBox()); + ASSERT_WITH_SECURITY_IMPLICATION(child->isInlineFlowBox()); - SVGInlineFlowBox* flowBox = static_cast<SVGInlineFlowBox*>(child); + SVGInlineFlowBox* flowBox = toSVGInlineFlowBox(child); layoutChildBoxes(flowBox); boxRect = flowBox->calculateBoundaries(); @@ -171,9 +171,9 @@ void SVGRootInlineBox::layoutRootBox(const FloatRect& childRect) ASSERT(parentBlock); // Finally, assign the root block position, now that all content is laid out. - IntRect roundedChildRect = enclosingIntRect(childRect); - parentBlock->setLocation(roundedChildRect.location()); - parentBlock->setSize(roundedChildRect.size()); + LayoutRect boundingRect = enclosingLayoutRect(childRect); + parentBlock->setLocation(boundingRect.location()); + parentBlock->setSize(boundingRect.size()); // Position all children relative to the parent block. for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) { @@ -188,7 +188,7 @@ void SVGRootInlineBox::layoutRootBox(const FloatRect& childRect) setY(0); setLogicalWidth(childRect.width()); setLogicalHeight(childRect.height()); - setLineTopBottomPositions(0, roundedChildRect.height(), 0, roundedChildRect.height()); + setLineTopBottomPositions(0, boundingRect.height(), 0, boundingRect.height()); } InlineBox* SVGRootInlineBox::closestLeafChildForPosition(const LayoutPoint& point) @@ -278,8 +278,8 @@ static inline void reverseInlineBoxRangeAndValueListsIfNeeded(void* userData, Ve continue; } - SVGInlineTextBox* firstTextBox = static_cast<SVGInlineTextBox*>(*first); - SVGInlineTextBox* lastTextBox = static_cast<SVGInlineTextBox*>(*last); + SVGInlineTextBox* firstTextBox = toSVGInlineTextBox(*first); + SVGInlineTextBox* lastTextBox = toSVGInlineTextBox(*last); // Reordering is only necessary for BiDi text that is _absolutely_ positioned. if (firstTextBox->len() == 1 && firstTextBox->len() == lastTextBox->len()) { diff --git a/Source/WebCore/rendering/svg/SVGRootInlineBox.h b/Source/WebCore/rendering/svg/SVGRootInlineBox.h index 7aec8d888..5bab4f784 100644 --- a/Source/WebCore/rendering/svg/SVGRootInlineBox.h +++ b/Source/WebCore/rendering/svg/SVGRootInlineBox.h @@ -32,7 +32,7 @@ namespace WebCore { class SVGInlineTextBox; -class SVGRootInlineBox : public RootInlineBox { +class SVGRootInlineBox FINAL : public RootInlineBox { public: SVGRootInlineBox(RenderBlock* block) : RootInlineBox(block) diff --git a/Source/WebCore/rendering/svg/SVGTextLayoutEngine.cpp b/Source/WebCore/rendering/svg/SVGTextLayoutEngine.cpp index ba2b7639d..e31104575 100644 --- a/Source/WebCore/rendering/svg/SVGTextLayoutEngine.cpp +++ b/Source/WebCore/rendering/svg/SVGTextLayoutEngine.cpp @@ -171,6 +171,8 @@ void SVGTextLayoutEngine::beginTextPathLayout(RenderObject* object, SVGTextLayou RenderSVGTextPath* textPath = toRenderSVGTextPath(object); m_textPath = textPath->layoutPath(); + if (m_textPath.isEmpty()) + return; m_textPathStartOffset = textPath->startOffset(); m_textPathLength = m_textPath.length(); if (m_textPathStartOffset > 0 && m_textPathStartOffset <= 1) @@ -423,7 +425,10 @@ void SVGTextLayoutEngine::advanceToNextVisualCharacter(const SVGTextMetrics& vis void SVGTextLayoutEngine::layoutTextOnLineOrPath(SVGInlineTextBox* textBox, RenderSVGInlineText* text, const RenderStyle* style) { - SVGElement* lengthContext = static_cast<SVGElement*>(text->parent()->node()); + if (m_inPathLayout && m_textPath.isEmpty()) + return; + + SVGElement* lengthContext = toSVGElement(text->parent()->node()); RenderObject* textParent = text->parent(); bool definesTextLength = textParent ? parentDefinesTextLength(textParent) : false; @@ -578,20 +583,9 @@ void SVGTextLayoutEngine::layoutTextOnLineOrPath(SVGInlineTextBox* textBox, Rend y += m_dy; } - // Determine wheter we have to start a new fragment. - bool shouldStartNewFragment = false; - - if (m_dx || m_dy) - shouldStartNewFragment = true; - - if (!shouldStartNewFragment && (m_isVerticalText || m_inPathLayout)) - shouldStartNewFragment = true; - - if (!shouldStartNewFragment && (angle || angle != lastAngle || orientationAngle)) - shouldStartNewFragment = true; - - if (!shouldStartNewFragment && (kerning || applySpacingToNextCharacter || definesTextLength)) - shouldStartNewFragment = true; + // Determine whether we have to start a new fragment. + bool shouldStartNewFragment = m_dx || m_dy || m_isVerticalText || m_inPathLayout || angle || angle != lastAngle + || orientationAngle || kerning || applySpacingToNextCharacter || definesTextLength; // If we already started a fragment, close it now. if (didStartTextFragment && shouldStartNewFragment) { diff --git a/Source/WebCore/rendering/svg/SVGTextLayoutEngineSpacing.cpp b/Source/WebCore/rendering/svg/SVGTextLayoutEngineSpacing.cpp index 074a8ea66..f0107c4ab 100644 --- a/Source/WebCore/rendering/svg/SVGTextLayoutEngineSpacing.cpp +++ b/Source/WebCore/rendering/svg/SVGTextLayoutEngineSpacing.cpp @@ -30,8 +30,6 @@ #include "SVGFontData.h" #include "SVGFontElement.h" #include "SVGFontFaceElement.h" -#else -#include <wtf/UnusedParam.h> #endif namespace WebCore { diff --git a/Source/WebCore/rendering/svg/SVGTextMetricsBuilder.cpp b/Source/WebCore/rendering/svg/SVGTextMetricsBuilder.cpp index dfeebb2f8..901ec8179 100644 --- a/Source/WebCore/rendering/svg/SVGTextMetricsBuilder.cpp +++ b/Source/WebCore/rendering/svg/SVGTextMetricsBuilder.cpp @@ -65,9 +65,6 @@ void SVGTextMetricsBuilder::advanceSimpleText() return; } - if (currentCharacterStartsSurrogatePair()) - ASSERT(metricsLength == 2); - float currentWidth = m_simpleWidthIterator->runWidthSoFar() - m_totalWidth; m_totalWidth = m_simpleWidthIterator->runWidthSoFar(); diff --git a/Source/WebCore/rendering/svg/SVGTextQuery.cpp b/Source/WebCore/rendering/svg/SVGTextQuery.cpp index a7ee787c4..bcc8d87ba 100644 --- a/Source/WebCore/rendering/svg/SVGTextQuery.cpp +++ b/Source/WebCore/rendering/svg/SVGTextQuery.cpp @@ -101,7 +101,7 @@ void SVGTextQuery::collectTextBoxesInFlowBox(InlineFlowBox* flowBox) } if (child->isSVGInlineTextBox()) - m_textBoxes.append(static_cast<SVGInlineTextBox*>(child)); + m_textBoxes.append(toSVGInlineTextBox(child)); } } @@ -352,10 +352,10 @@ bool SVGTextQuery::startPositionOfCharacterCallback(Data* queryData, const SVGTe return true; } -FloatPoint SVGTextQuery::startPositionOfCharacter(unsigned position) const +SVGPoint SVGTextQuery::startPositionOfCharacter(unsigned position) const { if (m_textBoxes.isEmpty()) - return FloatPoint(); + return SVGPoint(); StartPositionOfCharacterData data(position); executeQuery(&data, &SVGTextQuery::startPositionOfCharacterCallback); @@ -399,10 +399,10 @@ bool SVGTextQuery::endPositionOfCharacterCallback(Data* queryData, const SVGText return true; } -FloatPoint SVGTextQuery::endPositionOfCharacter(unsigned position) const +SVGPoint SVGTextQuery::endPositionOfCharacter(unsigned position) const { if (m_textBoxes.isEmpty()) - return FloatPoint(); + return SVGPoint(); EndPositionOfCharacterData data(position); executeQuery(&data, &SVGTextQuery::endPositionOfCharacterCallback); @@ -543,7 +543,7 @@ bool SVGTextQuery::characterNumberAtPositionCallback(Data* queryData, const SVGT return false; } -int SVGTextQuery::characterNumberAtPosition(const FloatPoint& position) const +int SVGTextQuery::characterNumberAtPosition(const SVGPoint& position) const { if (m_textBoxes.isEmpty()) return -1; diff --git a/Source/WebCore/rendering/svg/SVGTextQuery.h b/Source/WebCore/rendering/svg/SVGTextQuery.h index 331dd945c..bf60a6da6 100644 --- a/Source/WebCore/rendering/svg/SVGTextQuery.h +++ b/Source/WebCore/rendering/svg/SVGTextQuery.h @@ -22,6 +22,7 @@ #if ENABLE(SVG) #include "FloatRect.h" +#include "SVGPoint.h" #include "SVGTextFragment.h" #include <wtf/Vector.h> @@ -38,11 +39,11 @@ public: unsigned numberOfCharacters() const; float textLength() const; float subStringLength(unsigned startPosition, unsigned length) const; - FloatPoint startPositionOfCharacter(unsigned position) const; - FloatPoint endPositionOfCharacter(unsigned position) const; + SVGPoint startPositionOfCharacter(unsigned position) const; + SVGPoint endPositionOfCharacter(unsigned position) const; float rotationOfCharacter(unsigned position) const; FloatRect extentOfCharacter(unsigned position) const; - int characterNumberAtPosition(const FloatPoint&) const; + int characterNumberAtPosition(const SVGPoint&) const; // Public helper struct. Private classes in SVGTextQuery inherit from it. struct Data; diff --git a/Source/WebCore/rendering/svg/SVGTextRunRenderingContext.cpp b/Source/WebCore/rendering/svg/SVGTextRunRenderingContext.cpp index 7dd60d49e..f94b283e6 100644 --- a/Source/WebCore/rendering/svg/SVGTextRunRenderingContext.cpp +++ b/Source/WebCore/rendering/svg/SVGTextRunRenderingContext.cpp @@ -126,7 +126,7 @@ void SVGTextRunRenderingContext::drawSVGGlyphs(GraphicsContext* context, const T if (!glyph) continue; - float advance = glyphBuffer.advanceAt(from + i); + float advance = glyphBuffer.advanceAt(from + i).width(); SVGGlyph svgGlyph = fontElement->svgGlyphForGlyph(glyph); ASSERT(!svgGlyph.isPartOfLigature); ASSERT(svgGlyph.tableEntry == glyph); @@ -174,7 +174,7 @@ GlyphData SVGTextRunRenderingContext::glyphDataForCharacter(const Font& font, co const SimpleFontData* primaryFont = font.primaryFont(); ASSERT(primaryFont); - pair<GlyphData, GlyphPage*> pair = font.glyphDataAndPageForCharacter(character, mirror); + pair<GlyphData, GlyphPage*> pair = font.glyphDataAndPageForCharacter(character, mirror, AutoVariant); GlyphData glyphData = pair.first; // Check if we have the missing glyph data, in which case we can just return. @@ -186,9 +186,9 @@ GlyphData SVGTextRunRenderingContext::glyphDataForCharacter(const Font& font, co // Save data fromt he font fallback list because we may modify it later. Do this before the // potential change to glyphData.fontData below. - FontFallbackList* fontList = font.fontList(); - ASSERT(fontList); - FontFallbackList::GlyphPagesStateSaver glyphPagesSaver(*fontList); + FontGlyphs* glyph = font.glyphs(); + ASSERT(glyph); + FontGlyphs::GlyphPagesStateSaver glyphPagesSaver(*glyph); // Characters enclosed by an <altGlyph> element, may not be registered in the GlyphPage. const SimpleFontData* originalFontData = glyphData.fontData; @@ -230,7 +230,7 @@ GlyphData SVGTextRunRenderingContext::glyphDataForCharacter(const Font& font, co // No suitable glyph found that is compatible with the requirments (same language, arabic-form, orientation etc.) // Even though our GlyphPage contains an entry for eg. glyph "a", it's not compatible. So we have to temporarily // remove the glyph data information from the GlyphPage, and retry the lookup, which handles font fallbacks correctly. - page->setGlyphDataForCharacter(character, glyphData.glyph, 0); + page->setGlyphDataForCharacter(character, 0, 0); // Assure that the font fallback glyph selection worked, aka. the fallbackGlyphData font data is not the same as before. GlyphData fallbackGlyphData = font.glyphDataForCharacter(character, mirror); |
